|  | // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | // @dart = 2.9 | 
|  |  | 
|  | import 'dart:async'; | 
|  | import 'dart:convert'; | 
|  | import 'dart:io'; | 
|  |  | 
|  | import 'package:bazel_worker/bazel_worker.dart'; | 
|  | import 'package:bazel_worker/testing.dart'; | 
|  | import 'package:test/test.dart'; | 
|  |  | 
|  | Directory tmp = Directory.systemTemp.createTempSync('ddc_worker_test'); | 
|  | File file(String path) => File.fromUri(tmp.uri.resolve(path)); | 
|  |  | 
|  | void main() { | 
|  | var baseArgs = <String>[]; | 
|  | final executableArgs = <String>[ | 
|  | Platform.script | 
|  | .resolve('../../bin/dartdevc.dart') | 
|  | .toFilePath(windows: Platform.isWindows), | 
|  | '--sound-null-safety', | 
|  | '--dart-sdk-summary', | 
|  | Uri.file(Platform.resolvedExecutable, windows: Platform.isWindows) | 
|  | .resolve('ddc_outline_sound.dill') | 
|  | .path | 
|  | ]; | 
|  | group('DDC: Hello World', () { | 
|  | final argsFile = file('hello_world.args'); | 
|  | final inputDartFile = file('hello_world.dart'); | 
|  | final outputJsFile = file('out/hello_world.js'); | 
|  | final compilerArgs = baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '--no-summarize', | 
|  | '-o', | 
|  | outputJsFile.path, | 
|  | inputDartFile.path, | 
|  | ]; | 
|  |  | 
|  | setUp(() { | 
|  | inputDartFile.createSync(); | 
|  | inputDartFile.writeAsStringSync('main() => print("hello world");'); | 
|  | }); | 
|  |  | 
|  | tearDown(() { | 
|  | if (inputDartFile.existsSync()) inputDartFile.deleteSync(); | 
|  | if (outputJsFile.parent.existsSync()) { | 
|  | outputJsFile.parent.deleteSync(recursive: true); | 
|  | } | 
|  | if (argsFile.existsSync()) argsFile.deleteSync(); | 
|  | }); | 
|  |  | 
|  | test('can compile in worker mode', () async { | 
|  | var args = executableArgs.toList()..add('--persistent_worker'); | 
|  | var process = await Process.start(Platform.executable, args); | 
|  | var messageGrouper = AsyncMessageGrouper(process.stdout); | 
|  |  | 
|  | var request = WorkRequest(); | 
|  | request.arguments.addAll(compilerArgs); | 
|  | process.stdin.add(protoToDelimitedBuffer(request)); | 
|  |  | 
|  | var response = await _readResponse(messageGrouper); | 
|  | expect(response.exitCode, EXIT_CODE_OK, reason: response.output); | 
|  | expect(response.output, isEmpty); | 
|  |  | 
|  | expect(outputJsFile.existsSync(), isTrue); | 
|  | expect(outputJsFile.readAsStringSync(), contains('hello world')); | 
|  |  | 
|  | /// Now update hello_world.dart and send another [WorkRequest]. | 
|  | inputDartFile.writeAsStringSync('main() => print("goodbye world");'); | 
|  | process.stdin.add(protoToDelimitedBuffer(request)); | 
|  |  | 
|  | response = await _readResponse(messageGrouper); | 
|  | expect(response.exitCode, EXIT_CODE_OK, reason: response.output); | 
|  | expect(response.output, isEmpty); | 
|  | expect(outputJsFile.readAsStringSync(), contains('goodbye world')); | 
|  |  | 
|  | // Try to compile an error. | 
|  | inputDartFile.writeAsStringSync('main() { int x = "hello"; }'); | 
|  | process.stdin.add(protoToDelimitedBuffer(request)); | 
|  |  | 
|  | response = await _readResponse(messageGrouper); | 
|  | expect(response.exitCode, 1, reason: response.output); | 
|  | expect( | 
|  | response.output, | 
|  | contains( | 
|  | 'A value of type \'String\' can\'t be assigned to a variable of type \'int\'.')); | 
|  |  | 
|  | process.kill(); | 
|  |  | 
|  | // TODO(jakemac): This shouldn't be necessary, but it is for the process | 
|  | // to exit properly. | 
|  | expect(await messageGrouper.next, isNull); | 
|  | }); | 
|  |  | 
|  | test('can compile in basic mode', () { | 
|  | var args = executableArgs.toList()..addAll(compilerArgs); | 
|  | var result = Process.runSync(Platform.executable, args); | 
|  |  | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(outputJsFile.existsSync(), isTrue); | 
|  | }); | 
|  |  | 
|  | test('unknown options', () { | 
|  | var args = List<String>.from(executableArgs) | 
|  | ..add('--does-not-exist') | 
|  | ..addAll(compilerArgs); | 
|  | var result = Process.runSync(Platform.executable, args); | 
|  |  | 
|  | expect(result.exitCode, 64); | 
|  | expect(result.stdout, | 
|  | contains('Could not find an option named "does-not-exist"')); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(outputJsFile.existsSync(), isFalse); | 
|  | }); | 
|  |  | 
|  | test('unknown options ignored', () { | 
|  | var args = List<String>.from(executableArgs) | 
|  | ..add('--does-not-exist') | 
|  | ..add('--ignore-unrecognized-flags') | 
|  | ..addAll(compilerArgs); | 
|  | var result = Process.runSync(Platform.executable, args); | 
|  |  | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(outputJsFile.existsSync(), isTrue); | 
|  | }); | 
|  |  | 
|  | test('can compile in basic mode with args in a file', () async { | 
|  | argsFile.createSync(); | 
|  | argsFile.writeAsStringSync(compilerArgs.join('\n')); | 
|  | var args = executableArgs.toList()..add('@${argsFile.path}'); | 
|  | var process = await Process.start(Platform.executable, args); | 
|  | await stderr.addStream(process.stderr); | 
|  | var futureProcessOutput = process.stdout.map(utf8.decode).toList(); | 
|  |  | 
|  | expect(await process.exitCode, EXIT_CODE_OK); | 
|  | expect((await futureProcessOutput).join(), isEmpty); | 
|  | expect(outputJsFile.existsSync(), isTrue); | 
|  | }); | 
|  |  | 
|  | test('can compile in basic mode with "legacy" modules', () async { | 
|  | var args = List<String>.from(executableArgs) | 
|  | ..add('--modules') | 
|  | ..add('legacy') | 
|  | ..addAll(compilerArgs); | 
|  | var result = Process.runSync(Platform.executable, args); | 
|  |  | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(outputJsFile.existsSync(), isTrue); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | group('DDC: Hello World with Summaries', () { | 
|  | final greetingDart = file('greeting.dart'); | 
|  | final helloDart = file('hello.dart'); | 
|  |  | 
|  | final greetingJS = file('greeting.js'); | 
|  | final greetingSummary = file('greeting.dill'); | 
|  | final helloJS = file('hello_world.js'); | 
|  |  | 
|  | final greeting2JS = file('greeting2.js'); | 
|  | final greeting2Summary = file('greeting2.dill'); | 
|  |  | 
|  | setUp(() { | 
|  | greetingDart.writeAsStringSync('String greeting = "hello";'); | 
|  | helloDart.writeAsStringSync('import "greeting.dart";' | 
|  | 'main() => print(greeting);'); | 
|  | }); | 
|  |  | 
|  | tearDown(() { | 
|  | if (greetingDart.existsSync()) greetingDart.deleteSync(); | 
|  | if (helloDart.existsSync()) helloDart.deleteSync(); | 
|  | if (greetingJS.existsSync()) greetingJS.deleteSync(); | 
|  | if (greetingSummary.existsSync()) greetingSummary.deleteSync(); | 
|  | if (greeting2JS.existsSync()) greeting2JS.deleteSync(); | 
|  | if (greeting2Summary.existsSync()) greeting2Summary.deleteSync(); | 
|  | if (helloJS.existsSync()) helloJS.deleteSync(); | 
|  | }); | 
|  |  | 
|  | test('can compile in basic mode', () { | 
|  | var result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '-o', | 
|  | greetingJS.path, | 
|  | greetingDart.path, | 
|  | ]); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(greetingJS.existsSync(), isTrue); | 
|  | expect(greetingSummary.existsSync(), isTrue); | 
|  |  | 
|  | result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '--no-summarize', | 
|  | '-s', | 
|  | greetingSummary.path, | 
|  | '-o', | 
|  | helloJS.path, | 
|  | helloDart.path, | 
|  | ]); | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(helloJS.existsSync(), isTrue); | 
|  | }); | 
|  |  | 
|  | test('reports error on overlapping summaries', () { | 
|  | var result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '-o', | 
|  | greetingJS.path, | 
|  | greetingDart.path, | 
|  | ]); | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(greetingJS.existsSync(), isTrue); | 
|  | expect(greetingSummary.existsSync(), isTrue); | 
|  |  | 
|  | result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '-o', | 
|  | greeting2JS.path, | 
|  | greetingDart.path, | 
|  | ]); | 
|  | expect(result.exitCode, EXIT_CODE_OK); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(greeting2JS.existsSync(), isTrue); | 
|  | expect(greeting2Summary.existsSync(), isTrue); | 
|  |  | 
|  | result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '--no-summarize', | 
|  | '-s', | 
|  | greetingSummary.path, | 
|  | '-s', | 
|  | greeting2Summary.path, | 
|  | '-o', | 
|  | helloJS.path, | 
|  | helloDart.path, | 
|  | ]); | 
|  | // TODO(vsm): Re-enable when we turn this check back on. | 
|  | expect(result.exitCode, 0); | 
|  | // expect(result.exitCode, 65); | 
|  | // expect(result.stdout, contains("conflict")); | 
|  | // expect(result.stdout, contains('${toUri(greetingDart.path)}')); | 
|  | // expect(helloJS.existsSync(), isFalse); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | group('DDC: Error handling', () { | 
|  | final badFileDart = file('bad.dart'); | 
|  | final badFileJs = file('bad.js'); | 
|  |  | 
|  | tearDown(() { | 
|  | if (badFileDart.existsSync()) badFileDart.deleteSync(); | 
|  | if (badFileJs.existsSync()) badFileJs.deleteSync(); | 
|  | }); | 
|  |  | 
|  | test('incorrect usage', () { | 
|  | var result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | 'oops', | 
|  | ]); | 
|  | expect(result.exitCode, 64); | 
|  | expect( | 
|  | result.stdout, contains('Please specify the output file location.')); | 
|  | expect(result.stdout, isNot(contains('#0'))); | 
|  | }); | 
|  |  | 
|  | test('compile errors', () { | 
|  | badFileDart.writeAsStringSync('main() => "hello world"'); | 
|  | var result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-source-map', | 
|  | '-o', | 
|  | badFileJs.path, | 
|  | badFileDart.path, | 
|  | ]); | 
|  | expect(result.exitCode, 1); | 
|  | expect(result.stdout, contains(RegExp(r"Expected (to find )?\';\'"))); | 
|  | }); | 
|  | }); | 
|  |  | 
|  | group('DDC: Parts', () { | 
|  | final partFile = file('greeting.dart'); | 
|  | final libraryFile = file('hello.dart'); | 
|  |  | 
|  | final outJS = file('output.js'); | 
|  |  | 
|  | setUp(() { | 
|  | partFile.writeAsStringSync('part of hello;\n' | 
|  | 'String greeting = "hello";'); | 
|  | libraryFile.writeAsStringSync('library hello;\n' | 
|  | 'part "greeting.dart";\n' | 
|  | 'main() => print(greeting);\n'); | 
|  | }); | 
|  |  | 
|  | tearDown(() { | 
|  | if (partFile.existsSync()) partFile.deleteSync(); | 
|  | if (libraryFile.existsSync()) libraryFile.deleteSync(); | 
|  | if (outJS.existsSync()) outJS.deleteSync(); | 
|  | }); | 
|  |  | 
|  | test('works if part and library supplied', () { | 
|  | var result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-summarize', | 
|  | '--no-source-map', | 
|  | '-o', | 
|  | outJS.path, | 
|  | partFile.path, | 
|  | libraryFile.path, | 
|  | ]); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(result.exitCode, 0); | 
|  | expect(outJS.existsSync(), isTrue); | 
|  | }); | 
|  |  | 
|  | test('works if part is not supplied', () { | 
|  | var result = Process.runSync( | 
|  | Platform.executable, | 
|  | executableArgs + | 
|  | baseArgs + | 
|  | [ | 
|  | '--no-summarize', | 
|  | '--no-source-map', | 
|  | '-o', | 
|  | outJS.path, | 
|  | libraryFile.path, | 
|  | ]); | 
|  | expect(result.stdout, isEmpty); | 
|  | expect(result.stderr, isEmpty); | 
|  | expect(result.exitCode, 0); | 
|  | expect(outJS.existsSync(), isTrue); | 
|  | }); | 
|  | }); | 
|  | } | 
|  |  | 
|  | Future<WorkResponse> _readResponse(MessageGrouper messageGrouper) async { | 
|  | var buffer = await messageGrouper.next as List<int>; | 
|  | try { | 
|  | return WorkResponse.fromBuffer(buffer); | 
|  | } catch (_) { | 
|  | var bufferAsString = | 
|  | buffer == null ? '' : 'String: ${utf8.decode(buffer)}\n'; | 
|  | throw 'Failed to parse response:\nbytes: $buffer\n$bufferAsString'; | 
|  | } | 
|  | } |