blob: fc8bee97edb31ee196064ab822c03c75889870d8 [file] [log] [blame]
// Copyright (c) 2020, 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.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import '../utils.dart';
const String soundNullSafetyMessage = 'Info: Compiling with sound null safety';
const devToolsMessagePrefix =
'The Dart DevTools debugger and profiler is available at: http://127.0.0.1:';
const dartVMServiceMessagePrefix =
'The Dart VM service is listening on http://127.0.0.1:';
void main() {
group('run', run, timeout: longTimeout);
}
void run() {
late TestProject p;
tearDown(() async => await p.dispose());
test('--help', () async {
p = project();
var result = await p.run(['run', '--help']);
expect(result.stdout, contains('Run a Dart program.'));
expect(result.stdout, contains('Debugging options:'));
expect(
result.stdout,
contains(
'Usage: dart run [arguments] [<dart-file|package-target> [args]]',
),
);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
test('--help --verbose', () async {
p = project();
var result = await p.run(['run', '--help', '--verbose']);
expect(result.stdout, contains('Run a Dart program.'));
expect(result.stdout, contains('Debugging options:'));
expect(
result.stdout,
contains(
'Usage: dart [vm-options] run [arguments] [<dart-file|package-target> [args]]',
),
);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
test("'Hello World'", () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run(['run', p.relativeFilePath]);
expect(result.stdout, contains('Hello World'));
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
test('no such file', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result =
await p.run(['run', 'no/such/file/${p.relativeFilePath}']);
expect(result.stderr, isNotEmpty);
expect(result.exitCode, isNot(0));
});
test('implicit packageName.dart', () async {
// TODO(jwren) circle back to reimplement this test if possible, the file
// name (package name) will be the name of the temporary directory on disk
p = project(mainSrc: "void main() { print('Hello World'); }");
p.file('bin/main.dart', "void main() { print('Hello main.dart'); }");
ProcessResult result = await p.run(['run']);
expect(result.stdout, contains('Hello main.dart'));
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
}, skip: true);
// Could not find the implicit file to run: bin
test('missing implicit packageName.dart', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
p.file('bin/foo.dart', "void main() { print('Hello main.dart'); }");
ProcessResult result = await p.run(['run']);
expect(result.stdout, isEmpty);
expect(
result.stderr,
contains('Could not find `bin${path.separator}dartdev_temp.dart` in '
'package `dartdev_temp`.'));
expect(result.exitCode, 255);
});
test('arguments are properly passed', () async {
p = project();
p.file('main.dart', 'void main(args) { print(args); }');
ProcessResult result = await p.run([
'run',
'--enable-experiment=test-experiment',
'main.dart',
'argument1',
'argument2',
]);
// --enable-experiment and main.dart should not be passed.
expect(result.stdout, equals('[argument1, argument2]\n'));
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
test('from path-dependency with cyclic dependency', () async {
p = project(name: 'foo');
final bar = TestProject(name: 'bar');
p.file('pubspec.yaml', '''
name: foo
environment:
sdk: '>=2.9.0<3.0.0'
dependencies: { 'bar': {'path': '${bar.dir.path}'}}
''');
p.file('lib/foo.dart', r'''
import 'package:bar/bar.dart';
final b = "FOO $bar";
''');
try {
bar.file('lib/bar.dart', 'final bar = "BAR";');
bar.file('bin/main.dart', r'''
import 'package:foo/foo.dart';
void main(List<String> args) => print("$b $args");
''');
ProcessResult result = await p.run(['run', 'bar:main', '--arg1', 'arg2']);
expect(result.stderr, isEmpty);
expect(result.stdout, contains('FOO BAR [--arg1, arg2]'));
expect(result.exitCode, 0);
} finally {
await bar.dispose();
}
});
test('with absolute file path', () async {
p = project();
p.file('main.dart', 'void main(args) { print(args); }');
// Test with absolute path
final name = path.join(p.dirPath, 'main.dart');
final result = await p.run([
'run',
'--enable-experiment=test-experiment',
name,
'--argument1',
'argument2',
]);
// --enable-experiment and main.dart should not be passed.
expect(result.stderr, isEmpty);
expect(result.stdout, equals('[--argument1, argument2]\n'));
expect(result.exitCode, 0);
});
test('with file uri', () async {
p = project();
p.file('main.dart', 'void main(args) { print(args); }');
// Test with File uri
final name = path.join(p.dirPath, 'main.dart');
final result = await p.run([
'run',
Uri.file(name).toString(),
'--argument1',
'argument2',
]);
// --enable-experiment and main.dart should not be passed.
expect(result.stderr, isEmpty);
expect(result.stdout, equals('[--argument1, argument2]\n'));
expect(result.exitCode, 0);
});
test('with accepted VM flags', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
// --observe sets the following flags by default:
// --enable-vm-service
// --pause-isolate-on-exit
// --pause-isolate-on-unhandled-exception
// --warn-on-pause-with-no-debugger
//
// This test ensures that allowed arguments for dart run which are valid VM
// arguments are properly handled by the VM.
ProcessResult result = await p.run([
'run',
'--observe',
'--pause-isolates-on-start',
// This should negate the above flag.
'--no-pause-isolates-on-start',
'--no-pause-isolates-on-exit',
'--no-pause-isolates-on-unhandled-exceptions',
'-Dfoo=bar',
'--define=bar=foo',
p.relativeFilePath,
]);
expect(
result.stdout,
matches(
r'The Dart VM service is listening on http:\/\/127.0.0.1:8181\/[a-zA-Z0-9_-]+=\/\n.*'),
);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
// Again, with --disable-service-auth-codes.
result = await p.run([
'run',
'--observe',
'--pause-isolates-on-start',
// This should negate the above flag.
'--no-pause-isolates-on-start',
'--no-pause-isolates-on-exit',
'--no-pause-isolates-on-unhandled-exceptions',
'--disable-service-auth-codes',
'-Dfoo=bar',
'--define=bar=foo',
p.relativeFilePath,
]);
expect(
result.stdout,
contains('The Dart VM service is listening on http://127.0.0.1:8181/\n'),
);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
// Again, with IPv6.
result = await p.run([
'run',
'--observe=8181/::1',
'--pause-isolates-on-start',
// This should negate the above flag.
'--no-pause-isolates-on-start',
'--no-pause-isolates-on-exit',
'--no-pause-isolates-on-unhandled-exceptions',
'-Dfoo=bar',
'--define=bar=foo',
p.relativeFilePath,
]);
expect(
result.stdout,
matches(
r'The Dart VM service is listening on http:\/\/\[::1\]:8181\/[a-zA-Z0-9_-]+=\/\n.*'),
);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
test('fails when provided verbose VM flags', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
// Any VM flags not listed under 'dart run help --verbose' should be passed
// before a dartdev command.
ProcessResult result = await p.run([
'run',
'--vm-name=foo',
p.relativeFilePath,
]);
expect(result.stdout, isEmpty);
expect(
result.stderr,
contains('Could not find an option named "vm-name".'),
);
expect(result.exitCode, 64);
});
test('fails when provided unlisted VM flags', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
// Any VM flags not listed under 'dart run help --verbose' should be passed
// before a dartdev command.
ProcessResult result = await p.run([
'run',
'--verbose_gc',
p.relativeFilePath,
]);
expect(result.stdout, isEmpty);
expect(
result.stderr,
contains('Could not find an option named "verbose_gc".'),
);
expect(result.exitCode, 64);
});
test('--enable-asserts', () async {
p = project(mainSrc: 'void main() { assert(false); }');
// Ensure --enable-asserts doesn't cause the dartdev isolate to fail to
// load. Regression test for: https://github.com/dart-lang/sdk/issues/42831
ProcessResult result = await p.run([
'run',
'--enable-asserts',
p.relativeFilePath,
]);
expect(result.stdout, isEmpty);
expect(result.stderr, contains('Unhandled exception'));
expect(result.exitCode, 255);
});
test('does not interpret VM flags provided after script', () async {
p = project(mainSrc: 'void main() { assert(false); }');
// Any VM flags passed after the script shouldn't be interpreted by the VM.
ProcessResult result = await p.run([
'run',
p.relativeFilePath,
'--enable-asserts',
]);
expect(result.stdout, isEmpty);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
test('--enable-service-port-fallback', () async {
final p = project(mainSrc: '''void main() {}''');
final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
final result = await p.run(
[
'run',
'--enable-vm-service=${server.port}',
'--enable-service-port-fallback',
p.relativeFilePath,
],
);
final regexp = RegExp(
r'The Dart VM service is listening on http:\/\/127.0.0.1:(\d*)\/[a-zA-Z0-9_-]+=\/\n.*',
);
final vmServicePort =
int.parse(regexp.firstMatch(result.stdout)!.group(1)!);
expect(server.port != vmServicePort, isTrue);
await server.close();
});
test('without verbose CFE info', () async {
final p = project(mainSrc: '''void main() {}''');
var result = await p.run(
[
'run',
'--verbosity=warning',
p.relativeFilePath,
],
);
expect(result.stdout,
predicate((dynamic o) => !'$o'.contains(soundNullSafetyMessage)));
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
});
group('DDS', () {
group('disable', () {
test('dart run simple', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'run',
'--no-dds',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, isNot(contains(devToolsMessagePrefix)));
expect(result.stdout, contains(dartVMServiceMessagePrefix));
});
test('dart simple', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'--no-dds',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, isNot(contains(devToolsMessagePrefix)));
expect(result.stdout, contains(dartVMServiceMessagePrefix));
});
});
group('explicit enable', () {
test('dart run simple', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'run',
'--dds',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, contains(devToolsMessagePrefix));
expect(result.stdout, contains(dartVMServiceMessagePrefix));
});
test('dart simple', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'--dds',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, contains(devToolsMessagePrefix));
expect(result.stdout, contains(dartVMServiceMessagePrefix));
});
});
});
group('DevTools', () {
test('dart run simple', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'run',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, contains(devToolsMessagePrefix));
});
test('dart simple', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, contains(devToolsMessagePrefix));
});
test('dart run explicit', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'run',
'--serve-devtools',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, contains(devToolsMessagePrefix));
});
test('dart explicit', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'--serve-devtools',
'--enable-vm-service',
p.relativeFilePath,
]);
expect(result.stdout, contains(devToolsMessagePrefix));
});
test('dart run disabled', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'run',
'--enable-vm-service',
'--no-serve-devtools',
p.relativeFilePath,
]);
expect(result.stdout, isNot(contains(devToolsMessagePrefix)));
});
test('dart disabled', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'--enable-vm-service',
'--no-serve-devtools',
p.relativeFilePath,
]);
expect(result.stdout, isNot(contains(devToolsMessagePrefix)));
});
test('dart run VM service not enabled', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'run',
'--serve-devtools',
p.relativeFilePath,
]);
expect(result.stdout, isNot(contains(devToolsMessagePrefix)));
});
test('dart VM service not enabled', () async {
p = project(mainSrc: "void main() { print('Hello World'); }");
ProcessResult result = await p.run([
'--serve-devtools',
p.relativeFilePath,
]);
expect(result.stdout, isNot(contains(devToolsMessagePrefix)));
});
test(
'spawn via SIGQUIT',
() async {
p = project(
mainSrc:
'void main() { print("ready"); int i = 0; while(true) { i++; } }',
);
Process process = await p.start([
p.relativeFilePath,
]);
final readyCompleter = Completer<void>();
final completer = Completer<void>();
late StreamSubscription sub;
sub = process.stdout.transform(utf8.decoder).listen((event) async {
if (event.contains('ready')) {
readyCompleter.complete();
} else if (event.contains(devToolsMessagePrefix)) {
await sub.cancel();
completer.complete();
}
});
// Wait for process to start.
await readyCompleter.future;
process.kill(ProcessSignal.sigquit);
await completer.future;
process.kill();
},
// No support for SIGQUIT on Windows.
skip: Platform.isWindows,
);
});
}