blob: 883dbc995586146497aa3df2e15b6d110a9f38a6 [file] [log] [blame]
// Copyright (c) 2017, 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 'package:front_end/compiler_options.dart';
import 'package:front_end/memory_file_system.dart';
import 'package:front_end/src/base/processed_options.dart';
import 'package:front_end/src/fasta/fasta.dart' show ByteSink;
import 'package:front_end/src/fasta/fasta_codes.dart';
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
import 'package:kernel/kernel.dart' show Program, Library, CanonicalName;
import 'package:package_config/packages.dart' show Packages;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ProcessedOptionsTest);
});
}
@reflectiveTest
class ProcessedOptionsTest {
MemoryFileSystem fileSystem = new MemoryFileSystem(Uri.parse('file:///'));
Program _mockOutline;
Program get mockSummary => _mockOutline ??=
new Program(libraries: [new Library(Uri.parse('file:///a/b.dart'))]);
test_compileSdk_false() {
for (var value in [false, true]) {
var raw = new CompilerOptions()..compileSdk = value;
var processed = new ProcessedOptions(raw);
expect(processed.compileSdk, value);
}
}
test_sdk_summary_inferred() {
// The sdk-summary is inferred by default form sdk-root, when compile-sdk is
// false
var raw = new CompilerOptions()
..sdkRoot = Uri.parse('file:///sdk/dir/')
..compileSdk = false;
expect(new ProcessedOptions(raw).sdkSummary,
Uri.parse('file:///sdk/dir/outline.dill'));
// But it is left null when compile-sdk is true
raw = new CompilerOptions()
..sdkRoot = Uri.parse('file:///sdk/dir/')
..compileSdk = true;
expect(new ProcessedOptions(raw).sdkSummary, null);
}
test_fileSystem_noBazelRoots() {
// When no bazel roots are specified, the filesystem should be passed
// through unmodified.
var raw = new CompilerOptions()..fileSystem = fileSystem;
var processed = new ProcessedOptions(raw);
expect(processed.fileSystem, same(fileSystem));
}
test_getSdkSummary_summaryLocationProvided() async {
var uri = Uri.parse('file:///sdkSummary');
writeMockSummaryTo(uri);
checkMockSummary(new CompilerOptions()
..fileSystem = fileSystem
..sdkSummary = uri);
}
void writeMockSummaryTo(Uri uri) {
var sink = new ByteSink();
new BinaryPrinter(sink).writeProgramFile(mockSummary);
fileSystem.entityForUri(uri).writeAsBytesSync(sink.builder.takeBytes());
}
Future<Null> checkMockSummary(CompilerOptions raw) async {
var processed = new ProcessedOptions(raw);
var sdkSummary = await processed.loadSdkSummary(new CanonicalName.root());
expect(sdkSummary.libraries.single.importUri,
mockSummary.libraries.single.importUri);
}
checkPackageExpansion(
String packageName, String packageDir, Packages packages) {
var input = Uri.parse('package:$packageName/a.dart');
var expected = Uri.parse('file:///$packageDir/a.dart');
expect(packages.resolve(input), expected);
}
test_getUriTranslator_explicitPackagesFile() async {
// This .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForUri(Uri.parse('file:///explicit.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFileUri = Uri.parse('file:///explicit.packages');
var processed = new ProcessedOptions(raw);
var uriTranslator = await processed.getUriTranslator();
checkPackageExpansion('foo', 'baz', uriTranslator.packages);
}
test_getUriTranslator_explicitPackagesFile_withBaseLocation() async {
// This .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForUri(Uri.parse('file:///base/location/explicit.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFileUri = Uri.parse('file:///base/location/explicit.packages');
var processed = new ProcessedOptions(raw);
var uriTranslator = await processed.getUriTranslator();
checkPackageExpansion('foo', 'base/location/baz', uriTranslator.packages);
}
test_getUriTranslator_implicitPackagesFile_ambiguous() async {
// This .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForUri(Uri.parse('file:///explicit.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFileUri = Uri.parse('file:///explicit.packages');
var processed = new ProcessedOptions(raw);
var uriTranslator = await processed.getUriTranslator();
checkPackageExpansion('foo', 'baz', uriTranslator.packages);
}
test_getUriTranslator_implicitPackagesFile_nextToScript() async {
// Fake the existence of the base directory.
fileSystem
.entityForUri(Uri.parse('file:///base/location/'))
.writeAsStringSync('');
// Packages directory should be ignored (.packages file takes precedence).
fileSystem
.entityForUri(Uri.parse('file:///base/location/packages/'))
.writeAsStringSync('');
// This .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForUri(Uri.parse('file:///base/location/.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()..fileSystem = fileSystem;
var processed = new ProcessedOptions(
raw, false, [Uri.parse('file:///base/location/script.dart')]);
var uriTranslator = await processed.getUriTranslator();
checkPackageExpansion('foo', 'base/location/baz', uriTranslator.packages);
}
test_getUriTranslator_implicitPackagesFile_searchAbove() async {
// Fake the existence of the base directory.
fileSystem
.entityForUri(Uri.parse('file:///base/location/'))
.writeAsStringSync('');
// This .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
// This one should be used.
fileSystem
.entityForUri(Uri.parse('file:///base/.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()..fileSystem = fileSystem;
var processed = new ProcessedOptions(
raw, false, [Uri.parse('file:///base/location/script.dart')]);
var uriTranslator = await processed.getUriTranslator();
checkPackageExpansion('foo', 'base/baz', uriTranslator.packages);
}
test_getUriTranslator_implicitPackagesFile_packagesDirectory() async {
// Fake the existence of the base directory.
fileSystem
.entityForUri(Uri.parse('file:///base/location/'))
.writeAsStringSync('');
fileSystem
.entityForUri(Uri.parse('file:///base/location/packages/'))
.writeAsStringSync('');
// Both of these .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
fileSystem
.entityForUri(Uri.parse('file:///base/.packages'))
.writeAsStringSync('foo:baz\n');
var raw = new CompilerOptions()..fileSystem = fileSystem;
var processed = new ProcessedOptions(
raw, false, [Uri.parse('file:///base/location/script.dart')]);
var uriTranslator = await processed.getUriTranslator();
checkPackageExpansion(
'foo', 'base/location/packages/foo', uriTranslator.packages);
}
test_getUriTranslator_implicitPackagesFile_noPackages() async {
// Fake the existence of the base directory.
fileSystem
.entityForUri(Uri.parse('file:///base/location/'))
.writeAsStringSync('');
var errors = [];
// .packages file should be ignored.
var raw = new CompilerOptions()
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var processed = new ProcessedOptions(
raw, false, [Uri.parse('file:///base/location/script.dart')]);
var uriTranslator = await processed.getUriTranslator();
expect(errors, isEmpty);
expect(uriTranslator.packages.asMap(), isEmpty);
}
test_getUriTranslator_noPackages() async {
var errors = [];
// .packages file should be ignored.
fileSystem
.entityForUri(Uri.parse('file:///.packages'))
.writeAsStringSync('foo:bar\n');
var raw = new CompilerOptions()
..fileSystem = fileSystem
..packagesFileUri = new Uri()
..onError = (e) => errors.add(e);
var processed = new ProcessedOptions(raw);
var uriTranslator = await processed.getUriTranslator();
expect(uriTranslator.packages.asMap(), isEmpty);
expect(errors.single.message,
startsWith(_stringPrefixOf(templateCannotReadPackagesFile)));
}
test_validateOptions_noInputs() async {
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var errors = [];
var raw = new CompilerOptions()
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw);
var result = await options.validateOptions();
expect(errors.single.message, messageMissingInput.message);
expect(result, isFalse);
}
test_validateOptions_input_doesnt_exist() async {
var errors = [];
var raw = new CompilerOptions()
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
var result = await options.validateOptions();
expect(errors.single.message,
startsWith(_stringPrefixOf(templateInputFileNotFound)));
expect(result, isFalse);
}
test_validateOptions_root_exists() async {
var sdkRoot = Uri.parse('file:///sdk/root/');
fileSystem
// Note: this test is a bit hackish because the memory file system
// doesn't have the notion of directories.
.entityForUri(sdkRoot)
.writeAsStringSync('\n');
fileSystem
.entityForUri(sdkRoot.resolve('outline.dill'))
.writeAsStringSync('\n');
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var errors = [];
var raw = new CompilerOptions()
..sdkRoot = sdkRoot
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
var result = await options.validateOptions();
// Note: we check this first so test failures show the cause directly.
expect(errors, isEmpty);
expect(result, isTrue);
}
test_validateOptions_root_doesnt_exists() async {
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var sdkRoot = Uri.parse('file:///sdk/root');
var errors = [];
var raw = new CompilerOptions()
..sdkRoot = sdkRoot
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
expect(await options.validateOptions(), isFalse);
expect(errors.first.message,
startsWith(_stringPrefixOf(templateSdkRootNotFound)));
}
test_validateOptions_summary_exists() async {
var sdkSummary = Uri.parse('file:///sdk/root/outline.dill');
fileSystem.entityForUri(sdkSummary).writeAsStringSync('\n');
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var errors = [];
var raw = new CompilerOptions()
..sdkSummary = sdkSummary
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
var result = await options.validateOptions();
expect(errors, isEmpty);
expect(result, isTrue);
}
test_validateOptions_summary_doesnt_exists() async {
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var sdkSummary = Uri.parse('file:///sdk/root/outline.dill');
var errors = [];
var raw = new CompilerOptions()
..sdkSummary = sdkSummary
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
expect(await options.validateOptions(), isFalse);
expect(errors.single.message,
startsWith(_stringPrefixOf(templateSdkSummaryNotFound)));
}
test_validateOptions_inferred_summary_exists() async {
var sdkRoot = Uri.parse('file:///sdk/root/');
var sdkSummary = Uri.parse('file:///sdk/root/outline.dill');
fileSystem.entityForUri(sdkRoot).writeAsStringSync('\n');
fileSystem.entityForUri(sdkSummary).writeAsStringSync('\n');
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var errors = [];
var raw = new CompilerOptions()
..sdkRoot = sdkRoot
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
var result = await options.validateOptions();
expect(errors, isEmpty);
expect(result, isTrue);
}
test_validateOptions_inferred_summary_doesnt_exists() async {
var sdkRoot = Uri.parse('file:///sdk/root/');
var sdkSummary = Uri.parse('file:///sdk/root/outline.dill');
fileSystem.entityForUri(sdkRoot).writeAsStringSync('\n');
fileSystem
.entityForUri(Uri.parse('file:///foo.dart'))
.writeAsStringSync('main(){}\n');
var errors = [];
var raw = new CompilerOptions()
..sdkSummary = sdkSummary
..fileSystem = fileSystem
..onError = (e) => errors.add(e);
var options = new ProcessedOptions(raw, false, [Uri.parse('foo.dart')]);
expect(await options.validateOptions(), isFalse);
expect(errors.single.message,
startsWith(_stringPrefixOf(templateSdkSummaryNotFound)));
}
/// Returns the longest prefix of the text in a message template that doesn't
/// mention a template argument.
_stringPrefixOf(Template template) {
var messageTemplate = template.messageTemplate;
var index = messageTemplate.indexOf('#');
var prefix = messageTemplate.substring(0, index - 1);
// Check that the prefix is not empty and that it contains more than one
// word.
expect(prefix.length > 0, isTrue);
expect(prefix.contains(' '), isTrue);
return prefix;
}
}