diff --git a/.travis.yml b/.travis.yml index 4480a85785..ba11f1a166 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ osx_image: xcode9.2 env: - SHARD=analyze - SHARD=tests + - SHARD=tests_dart2 - SHARD=docs - SHARD=build_and_deploy_gallery matrix: @@ -14,6 +15,8 @@ matrix: env: SHARD=analyze - os: osx env: SHARD=docs + allow_failures: + - env: SHARD=tests_dart2 sudo: false filter_secrets: false diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 2e574424f0..5f3c2e7047 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -29,6 +29,7 @@ const Map _kShards = const { 'docs': _generateDocs, 'analyze': _analyzeRepo, 'tests': _runTests, + 'tests_dart2': _runTestsDart2, 'coverage': _runCoverage, }; @@ -129,35 +130,50 @@ Future _analyzeRepo() async { print('${bold}DONE: Analysis successful.$reset'); } -Future _runTests() async { +Future _runTestsDart2() async { + if (Platform.isWindows) { + // AppVeyor platform is overloaded, won't be able to handle additional + // load of dart2 testing. + return; + } + _runTests(options: ['--preview-dart-2']); +} + +Future _runTests({List options: const []}) async { // Verify that the tests actually return failure on failure and success on success. final String automatedTests = path.join(flutterRoot, 'dev', 'automated_tests'); await _runFlutterTest(automatedTests, script: path.join('test_smoke_test', 'fail_test.dart'), + options: options, expectFailure: true, printOutput: false, ); await _runFlutterTest(automatedTests, script: path.join('test_smoke_test', 'pass_test.dart'), + options: options, printOutput: false, ); await _runFlutterTest(automatedTests, script: path.join('test_smoke_test', 'crash1_test.dart'), + options: options, expectFailure: true, printOutput: false, ); await _runFlutterTest(automatedTests, script: path.join('test_smoke_test', 'crash2_test.dart'), + options: options, expectFailure: true, printOutput: false, ); await _runFlutterTest(automatedTests, script: path.join('test_smoke_test', 'syntax_error_test.broken_dart'), + options: options, expectFailure: true, printOutput: false, ); await _runFlutterTest(automatedTests, script: path.join('test_smoke_test', 'missing_import_test.broken_dart'), + options: options, expectFailure: true, printOutput: false, ); @@ -171,21 +187,21 @@ Future _runTests() async { await _verifyVersion(path.join(flutterRoot, 'version')); // Run tests. - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver')); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test')); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), options: options); await _pubRunTest(path.join(flutterRoot, 'packages', 'flutter_tools')); await _pubRunTest(path.join(flutterRoot, 'dev', 'bots')); - await _runAllDartTests(path.join(flutterRoot, 'dev', 'devicelab')); - await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests')); - await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'vitool')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'stocks')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery')); - await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog')); + await _runAllDartTests(path.join(flutterRoot, 'dev', 'devicelab'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'vitool'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'stocks'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: options); + await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog'), options: options); print('${bold}DONE: All tests successful.$reset'); } @@ -356,8 +372,13 @@ Future _runFlutterTest(String workingDirectory, { Future _runAllDartTests(String workingDirectory, { Map environment, + List options, }) { - final List args = ['--checked', path.join('test', 'all.dart')]; + final List args = ['--checked']; + if (options != null) { + args.addAll(options); + } + args.add(path.join('test', 'all.dart')); return _runCommand(dart, args, workingDirectory: workingDirectory, environment: environment, diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index 7a7ebc25dd..dc7a28170f 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -125,23 +125,27 @@ Future compile( /// The wrapper is intended to stay resident in memory as user changes, reloads, /// restarts the Flutter app. class ResidentCompiler { - ResidentCompiler(this._sdkRoot, {bool trackWidgetCreation: false}) + ResidentCompiler(this._sdkRoot, {bool trackWidgetCreation: false, + String packagesPath}) : assert(_sdkRoot != null), - _trackWidgetCreation = trackWidgetCreation { + _trackWidgetCreation = trackWidgetCreation, + _packagesPath = packagesPath { // This is a URI, not a file path, so the forward slash is correct even on Windows. if (!_sdkRoot.endsWith('/')) _sdkRoot = '$_sdkRoot/'; } final bool _trackWidgetCreation; + final String _packagesPath; String _sdkRoot; Process _server; final _StdoutHandler stdoutHandler = new _StdoutHandler(); /// If invoked for the first time, it compiles Dart script identified by /// [mainPath], [invalidatedFiles] list is ignored. - /// Otherwise, [mainPath] is ignored, but [invalidatedFiles] is recompiled - /// into new binary. + /// On successive runs [invalidatedFiles] indicates which files need to be + /// recompiled. If [mainPath] is [null], previously used [mainPath] entry + /// point that is used for recompilation. /// Binary file name is returned if compilation was successful, otherwise /// null is returned. Future recompile(String mainPath, List invalidatedFiles, @@ -154,7 +158,7 @@ class ResidentCompiler { return _compile(mainPath, outputPath); final String inputKey = new Uuid().generateV4(); - _server.stdin.writeln('recompile $inputKey'); + _server.stdin.writeln('recompile ${mainPath != null ? mainPath + " ": ""}$inputKey'); invalidatedFiles.forEach(_server.stdin.writeln); _server.stdin.writeln(inputKey); @@ -179,6 +183,9 @@ class ResidentCompiler { if (_trackWidgetCreation) { args.add('--track-widget-creation'); } + if (_packagesPath != null) { + args.addAll(['--packages', _packagesPath]); + } _server = await processManager.start(args); _server.stdout .transform(UTF8.decoder) diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 08d07d8dac..a51e2b698a 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -83,6 +83,13 @@ enum _InitialResult { crashed, timedOut, connected } enum _TestResult { crashed, harnessBailed, testBailed } typedef Future _Finalizer(); +class CompilationRequest { + String path; + Completer result; + + CompilationRequest(this.path, this.result); +} + class _FlutterPlatform extends PlatformPlugin { _FlutterPlatform({ @required this.shellPath, @@ -93,7 +100,30 @@ class _FlutterPlatform extends PlatformPlugin { this.explicitObservatoryPort, this.host, this.previewDart2, - }) : assert(shellPath != null); + }) : assert(shellPath != null) { + compilerController.stream.listen((CompilationRequest request) async { + final bool isEmpty = compilationQueue.isEmpty; + compilationQueue.add(request); + // Only trigger processing if queue was empty - i.e. no other requests + // are currently being processed. This effectively enforces "one + // compilation request at a time". + if (isEmpty) { + while (compilationQueue.isNotEmpty) { + final CompilationRequest request = compilationQueue.first; + printTrace('Compiling ${request.path}'); + final String outputPath = await compiler.recompile(request.path, + [request.path] + ); + print('Finished compilation of ${request.path} into $outputPath'); + compiler.accept(); + compiler.reset(); + request.result.complete(outputPath); + // Only remove now when we finished processing the element + compilationQueue.removeAt(0); + } + } + }); + } final String shellPath; final TestWatcher watcher; @@ -103,6 +133,12 @@ class _FlutterPlatform extends PlatformPlugin { final int explicitObservatoryPort; final InternetAddress host; final bool previewDart2; + final StreamController compilerController = + new StreamController(); + ResidentCompiler compiler = + new ResidentCompiler(artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), + packagesPath: PackageMap.globalPackagesPath); + final List compilationQueue = []; // Each time loadChannel() is called, we spin up a local WebSocket server, // then spin up the engine in a subprocess. We pass the engine a Dart file @@ -203,12 +239,9 @@ class _FlutterPlatform extends PlatformPlugin { String bundlePath; if (previewDart2) { - mainDart = await compile( - sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), - mainPath: listenerFile.path, - packagesPath: PackageMap.globalPackagesPath, - linkPlatformKernelIn: true - ); + final Completer completer = new Completer(); + compilerController.add(new CompilationRequest(listenerFile.path, completer)); + mainDart = await completer.future; if (mainDart == null) { controller.sink.addError(_getErrorMessage('Compilation failed', testPath, shellPath));