// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:io'; import 'dart:isolate'; import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:platform/platform.dart'; final Matcher throwsRemoteError = throwsA(isA<RemoteError>()); int test1(int value) { return value + 1; } int test2(int value) { throw 2; } int test3(int value) { Isolate.exit(); } int test4(int value) { Isolate.current.kill(); return value + 1; } int test5(int value) { Isolate.current.kill(priority: Isolate.immediate); return value + 1; } Future<int> test1Async(int value) async { return value + 1; } Future<int> test2Async(int value) async { throw 2; } Future<int> test3Async(int value) async { Isolate.exit(); } Future<int> test4Async(int value) async { Isolate.current.kill(); return value + 1; } Future<int> test5Async(int value) async { Isolate.current.kill(priority: Isolate.immediate); return value + 1; } Future<int> test1CallCompute(int value) { return compute(test1, value); } Future<int> test2CallCompute(int value) { return compute(test2, value); } Future<int> test3CallCompute(int value) { return compute(test3, value); } Future<int> test4CallCompute(int value) { return compute(test4, value); } Future<int> test5CallCompute(int value) { return compute(test5, value); } Future<void> expectFileSuccessfullyCompletes(String filename) async { // Run a Dart script that calls compute(). // The Dart process will terminate only if the script exits cleanly with // all isolate ports closed. const FileSystem fs = LocalFileSystem(); const Platform platform = LocalPlatform(); final String flutterRoot = platform.environment['FLUTTER_ROOT']!; final String dartPath = fs.path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', 'dart'); final String packageRoot = fs.path.dirname(fs.path.fromUri(platform.script)); final String scriptPath = fs.path.join(packageRoot, 'test', 'foundation', filename); // Enable asserts to also catch potentially invalid assertions. final ProcessResult result = await Process.run( dartPath, <String>['run', '--enable-asserts', scriptPath]); expect(result.exitCode, 0); } class ComputeTestSubject { ComputeTestSubject(this.base, [this.additional]); final int base; final dynamic additional; int method(int x) { return base * x; } static int staticMethod(int square) { return square * square; } } Future<int> computeStaticMethod(int square) { return compute(ComputeTestSubject.staticMethod, square); } Future<int> computeClosure(int square) { return compute((_) => square * square, null); } Future<int> computeInvalidClosure(int square) { final ReceivePort r = ReceivePort(); return compute((_) { r.sendPort.send('Computing!'); return square * square; }, null); } Future<int> computeInstanceMethod(int square) { final ComputeTestSubject subject = ComputeTestSubject(square); return compute(subject.method, square); } Future<int> computeInvalidInstanceMethod(int square) { final ComputeTestSubject subject = ComputeTestSubject(square, ReceivePort()); expect(subject.additional, isA<ReceivePort>()); return compute(subject.method, square); } dynamic testInvalidResponse(int square) { final ReceivePort r = ReceivePort(); try { return r; } finally { r.close(); } } dynamic testInvalidError(int square) { final ReceivePort r = ReceivePort(); try { throw r; } finally { r.close(); } } String? testDebugName(_) { return Isolate.current.debugName; } int? testReturnNull(_) { return null; } void main() { test('compute()', () async { expect(await compute(test1, 0), 1); expect(compute(test2, 0), throwsA(2)); expect(compute(test3, 0), throwsRemoteError); expect(await compute(test4, 0), 1); expect(compute(test5, 0), throwsRemoteError); expect(await compute(test1Async, 0), 1); expect(compute(test2Async, 0), throwsA(2)); expect(compute(test3Async, 0), throwsRemoteError); expect(await compute(test4Async, 0), 1); expect(compute(test5Async, 0), throwsRemoteError); expect(await compute(test1CallCompute, 0), 1); expect(compute(test2CallCompute, 0), throwsA(2)); expect(compute(test3CallCompute, 0), throwsRemoteError); expect(await compute(test4CallCompute, 0), 1); expect(compute(test5CallCompute, 0), throwsRemoteError); expect(compute(testInvalidResponse, 0), throwsRemoteError); expect(compute(testInvalidError, 0), throwsRemoteError); expect(await computeStaticMethod(10), 100); expect(await computeClosure(10), 100); expect(computeInvalidClosure(10), throwsArgumentError); expect(await computeInstanceMethod(10), 100); expect(computeInvalidInstanceMethod(10), throwsArgumentError); expect(await compute(testDebugName, null, debugLabel: 'debug_name'), 'debug_name'); expect(await compute(testReturnNull, null), null); }, skip: kIsWeb); // [intended] isn't supported on the web. group('compute() closes all ports', () { test('with valid message', () async { await expectFileSuccessfullyCompletes('_compute_caller.dart'); }); test('with invalid message', () async { await expectFileSuccessfullyCompletes( '_compute_caller_invalid_message.dart'); }); test('with valid error', () async { await expectFileSuccessfullyCompletes('_compute_caller.dart'); }); test('with invalid error', () async { await expectFileSuccessfullyCompletes( '_compute_caller_invalid_message.dart'); }); }, skip: kIsWeb); // [intended] isn't supported on the web. }