isolates_test.dart 6.02 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'dart:io';
6
import 'dart:isolate';
7 8
import 'package:file/file.dart';
import 'package:file/local.dart';
9
import 'package:flutter/foundation.dart';
10
import 'package:flutter_test/flutter_test.dart';
11
import 'package:platform/platform.dart';
12

13 14
final Matcher throwsRemoteError = throwsA(isA<RemoteError>());

15 16 17 18 19 20 21 22
int test1(int value) {
  return value + 1;
}

int test2(int value) {
  throw 2;
}

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
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;
}

39 40 41 42 43 44 45 46
Future<int> test1Async(int value) async {
  return value + 1;
}

Future<int> test2Async(int value) async {
  throw 2;
}

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
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);
}

83
Future<void> expectFileSuccessfullyCompletes(String filename) async {
84 85 86 87 88 89 90 91 92
  // 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);
93 94 95

  // Enable asserts to also catch potentially invalid assertions.
  final ProcessResult result = await Process.run(dartPath, <String>['run', '--enable-asserts', scriptPath]);
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
  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());
  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;
}

164 165 166 167
int? testReturnNull(_) {
  return null;
}

168 169 170
void main() {
  test('compute()', () async {
    expect(await compute(test1, 0), 1);
171 172 173 174
    expect(compute(test2, 0), throwsA(2));
    expect(compute(test3, 0), throwsRemoteError);
    expect(await compute(test4, 0), 1);
    expect(compute(test5, 0), throwsRemoteError);
175 176

    expect(await compute(test1Async, 0), 1);
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
    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');
198
    expect(await compute(testReturnNull, null), null);
199
  }, skip: kIsWeb); // [intended] isn't supported on the web.
200

201
  group('compute() closes all ports', () {
202
    test('with valid message', () async {
203
      await expectFileSuccessfullyCompletes('_compute_caller.dart');
204 205
    });
    test('with invalid message', () async {
206
      await expectFileSuccessfullyCompletes('_compute_caller_invalid_message.dart');
207 208
    });
    test('with valid error', () async {
209
      await expectFileSuccessfullyCompletes('_compute_caller.dart');
210 211
    });
    test('with invalid error', () async {
212 213 214 215 216 217 218 219 220 221
      await expectFileSuccessfullyCompletes('_compute_caller_invalid_message.dart');
    });
  }, skip: kIsWeb); // [intended] isn't supported on the web.

  group('compute() works with unsound null safety caller', () {
    test('returning', () async {
      await expectFileSuccessfullyCompletes('_compute_caller_unsound_null_safety.dart');
    });
    test('erroring', () async {
      await expectFileSuccessfullyCompletes('_compute_caller_unsound_null_safety_error.dart');
222
    });
223
  }, skip: kIsWeb); // [intended] isn't supported on the web.
224
}