5ece7f3a6e
Bug: https://github.com/dart-lang/sdk/issues/44569 Change-Id: Icad2908772eb42593c0149a99b8256d470490670 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177241 Reviewed-by: Sigmund Cherem <sigmund@google.com> Reviewed-by: Nicholas Shahan <nshahan@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
390 lines
13 KiB
Dart
390 lines
13 KiB
Dart
// 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:path/path.dart' show dirname, join, joinAll;
|
|
import 'package:test/test.dart';
|
|
|
|
Directory tmp = Directory.systemTemp.createTempSync('ddc_worker_test');
|
|
File file(String path) => File(join(tmp.path, joinAll(path.split('/'))));
|
|
|
|
void main() {
|
|
var baseArgs = <String>[];
|
|
final binDir = dirname(Platform.resolvedExecutable);
|
|
// Note, the bots use the dart binary in the top-level build directory.
|
|
// On windows, this is a .bat file.
|
|
final dartdevc = 'dartdevc${Platform.isWindows ? ".bat" : ""}';
|
|
final executable = binDir.endsWith('bin')
|
|
? join(binDir, dartdevc)
|
|
: join(binDir, 'dart-sdk', 'bin', dartdevc);
|
|
final executableArgs = <String>[];
|
|
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(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(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(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(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(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(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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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';
|
|
}
|
|
}
|