blob: 20fbfc1ea40f3c736b34a9dd00e93b4e05a6c2d3 [file] [log] [blame]
// Copyright (c) 2021, 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:io' show Directory, File;
import 'package:dev_compiler/dev_compiler.dart';
import 'package:dev_compiler/src/kernel/command.dart';
import 'package:dev_compiler/src/kernel/module_symbols.dart';
import 'package:kernel/ast.dart' show Component, Library;
import 'package:test/test.dart';
import '../shared_test_options.dart';
class TestCompiler {
final SetupCompilerOptions setup;
TestCompiler(this.setup);
Future<JSCode> compile({Uri input, Uri packages}) async {
// Initialize incremental compiler and create component.
setup.options.packagesFileUri = packages;
var compiler = DevelopmentIncrementalCompiler(setup.options, input);
var component = await compiler.computeDelta();
component.computeCanonicalNames();
// Initialize DDC.
var moduleName = 'foo.dart';
var classHierarchy = compiler.getClassHierarchy();
var compilerOptions = SharedCompilerOptions(
replCompile: true,
moduleName: moduleName,
soundNullSafety: setup.soundNullSafety,
moduleFormats: [setup.moduleFormat],
emitDebugSymbols: true);
var coreTypes = compiler.getCoreTypes();
final importToSummary = Map<Library, Component>.identity();
final summaryToModule = Map<Component, String>.identity();
for (var lib in component.libraries) {
importToSummary[lib] = component;
}
summaryToModule[component] = moduleName;
// Compile Kernel AST to JS AST.
var kernel2jsCompiler = ProgramCompiler(component, classHierarchy,
compilerOptions, importToSummary, summaryToModule,
coreTypes: coreTypes);
var moduleTree = kernel2jsCompiler.emitModule(component);
// Compile JS AST to code.
return jsProgramToCode(moduleTree, ModuleFormat.amd,
emitDebugSymbols: true,
compiler: kernel2jsCompiler,
component: component);
}
}
class TestDriver {
final SetupCompilerOptions options;
Directory tempDir;
final String source;
Uri input;
Uri packages;
File file;
TestDriver(this.options, this.source) {
var systemTempDir = Directory.systemTemp;
tempDir = systemTempDir.createTempSync('foo bar');
input = tempDir.uri.resolve('foo.dart');
file = File.fromUri(input)..createSync();
file.writeAsStringSync(source);
packages = tempDir.uri.resolve('package_config.json');
file = File.fromUri(packages)..createSync();
file.writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "foo",
"rootUri": "./",
"packageUri": "./"
}
]
}
''');
}
Future<JSCode> compile() async =>
await TestCompiler(options).compile(input: input, packages: packages);
void cleanUp() {
tempDir.delete(recursive: true);
}
}
class NullSafetyTestOption {
final String description;
final bool soundNullSafety;
NullSafetyTestOption(this.description, this.soundNullSafety);
}
void main() async {
for (var mode in [
NullSafetyTestOption('Sound Mode:', true),
NullSafetyTestOption('Weak Mode:', false)
]) {
group(mode.description, () {
var options = SetupCompilerOptions(soundNullSafety: mode.soundNullSafety);
group('simple class debug symbols', () {
TestDriver driver;
ClassSymbol classSymbol;
final source = '''
${options.dartLangComment}
class A {}
''';
setUpAll(() async {
driver = TestDriver(options, source);
var result = await driver.compile();
classSymbol = result.symbols.classes.single;
});
tearDownAll(() {
driver.cleanUp();
});
test('has name', () async {
expect(classSymbol.name, equals('A'));
});
test('is not abstract', () async {
expect(classSymbol.isAbstract, isFalse);
});
// TODO test isConst
test('has no superclassId', () async {
expect(classSymbol.superClassId, isNull);
});
test('empty interfacesIds', () async {
expect(classSymbol.interfaceIds, isEmpty);
});
test('empty typeParameters', () async {
expect(classSymbol.typeParameters, isEmpty);
});
test('has localId', () async {
expect(classSymbol.localId, equals('A'));
});
test('has library scopeId', () async {
expect(classSymbol.scopeId, endsWith('package:foo/foo.dart'));
});
group('location', () {
test('has scriptId', () async {
expect(classSymbol.location.scriptId, endsWith('/foo.dart'));
});
test('has start token', () async {
expect(classSymbol.location.tokenPos,
22 + options.dartLangComment.length);
});
test('has end token', () async {
expect(classSymbol.location.endTokenPos,
31 + options.dartLangComment.length);
});
});
test('no fields', () async {
expect(classSymbol.variableIds, isEmpty);
});
// TODO only has the implicit constructor in scopeIds.
});
group('abstract class debug symbols', () {
TestDriver driver;
ClassSymbol classSymbol;
final source = '''
${options.dartLangComment}
abstract class A {}
''';
setUpAll(() async {
driver = TestDriver(options, source);
var result = await driver.compile();
classSymbol = result.symbols.classes.single;
});
tearDownAll(() {
driver.cleanUp();
});
test('is abstract', () async {
expect(classSymbol.isAbstract, isTrue);
});
});
group('class extends debug symbols', () {
TestDriver driver;
ClassSymbol classSymbol;
final source = '''
${options.dartLangComment}
class A extends B {}
class B {}
''';
setUpAll(() async {
driver = TestDriver(options, source);
var result = await driver.compile();
classSymbol =
result.symbols.classes.where((c) => c.localId == 'A').single;
});
tearDownAll(() {
driver.cleanUp();
});
test('has superclass', () async {
expect(classSymbol.superClassId, 'B');
});
});
group('class implements debug symbols', () {
TestDriver driver;
List<ClassSymbol> classSymbols;
final source = '''
${options.dartLangComment}
class A implements B, C {}
class B implements C {}
class C {}
''';
setUpAll(() async {
driver = TestDriver(options, source);
var result = await driver.compile();
classSymbols = result.symbols.classes;
});
tearDownAll(() {
driver.cleanUp();
});
test('single implements', () async {
var classSymbol = classSymbols.singleWhere((c) => c.localId == 'B');
expect(classSymbol.interfaceIds, orderedEquals(['C']));
});
test('multiple implements', () async {
var classSymbol = classSymbols.singleWhere((c) => c.localId == 'A');
expect(classSymbol.interfaceIds, orderedEquals(['B', 'C']));
});
});
});
}
}