ab19094dc8
This is all logic now in the analyzer. R=jmesserly@google.com Review URL: https://codereview.chromium.org/1406983003 .
251 lines
8.8 KiB
Dart
251 lines
8.8 KiB
Dart
// Copyright (c) 2015, 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.
|
|
|
|
/// Tests code generation.
|
|
/// Runs Dart Dev Compiler on all input in the `codegen` directory and checks
|
|
/// that the output is what we expected.
|
|
library dev_compiler.test.codegen_test;
|
|
|
|
import 'dart:io';
|
|
import 'package:analyzer/src/generated/engine.dart'
|
|
show AnalysisContext, AnalysisEngine, Logger;
|
|
import 'package:analyzer/src/generated/java_engine.dart' show CaughtException;
|
|
import 'package:args/args.dart';
|
|
import 'package:logging/logging.dart' show Level;
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:test/test.dart';
|
|
|
|
import 'package:dev_compiler/devc.dart';
|
|
import 'package:dev_compiler/src/compiler.dart' show defaultRuntimeFiles;
|
|
import 'package:dev_compiler/src/options.dart';
|
|
|
|
import 'testing.dart' show realSdkContext, testDirectory;
|
|
import 'multitest.dart';
|
|
|
|
final ArgParser argParser = new ArgParser()
|
|
..addOption('dart-sdk', help: 'Dart SDK Path', defaultsTo: null);
|
|
|
|
final inputDir = path.join(testDirectory, 'codegen');
|
|
|
|
Iterable<String> _findTests(String dir, RegExp filePattern) {
|
|
var files = new Directory(dir)
|
|
.listSync()
|
|
.where((f) => f is File)
|
|
.map((f) => f.path)
|
|
.where((p) => p.endsWith('.dart') && filePattern.hasMatch(p));
|
|
if (dir != inputDir) {
|
|
files = files
|
|
.where((p) => p.endsWith('_test.dart') || p.endsWith('_multi.dart'));
|
|
}
|
|
return files;
|
|
}
|
|
|
|
main(arguments) {
|
|
if (arguments == null) arguments = [];
|
|
ArgResults args = argParser.parse(arguments);
|
|
var filePattern = new RegExp(args.rest.length > 0 ? args.rest[0] : '.');
|
|
var compilerMessages = new StringBuffer();
|
|
var loggerSub;
|
|
|
|
bool codeCoverage = Platform.environment.containsKey('COVERALLS_TOKEN');
|
|
|
|
setUp(() {
|
|
compilerMessages.clear();
|
|
loggerSub = setupLogger(Level.CONFIG, compilerMessages.writeln);
|
|
});
|
|
|
|
tearDown(() {
|
|
if (loggerSub != null) {
|
|
loggerSub.cancel();
|
|
loggerSub = null;
|
|
}
|
|
});
|
|
|
|
var expectDir = path.join(inputDir, 'expect');
|
|
|
|
BatchCompiler createCompiler(AnalysisContext context,
|
|
{bool checkSdk: false, bool sourceMaps: false, bool closure: false}) {
|
|
// TODO(jmesserly): add a way to specify flags in the test file, so
|
|
// they're more self-contained.
|
|
var runtimeDir = path.join(path.dirname(testDirectory), 'lib', 'runtime');
|
|
var options = new CompilerOptions(
|
|
codegenOptions: new CodegenOptions(
|
|
outputDir: expectDir,
|
|
emitSourceMaps: sourceMaps,
|
|
closure: closure,
|
|
forceCompile: checkSdk),
|
|
useColors: false,
|
|
checkSdk: checkSdk,
|
|
runtimeDir: runtimeDir,
|
|
inputBaseDir: inputDir);
|
|
var reporter = createErrorReporter(context, options);
|
|
return new BatchCompiler(context, options, reporter: reporter);
|
|
}
|
|
|
|
bool compile(BatchCompiler compiler, String filePath) {
|
|
compiler.compileFromUriString(filePath, (String url) {
|
|
// Write compiler messages to disk.
|
|
var messagePath = '${path.withoutExtension(url)}.txt';
|
|
var file = new File(messagePath);
|
|
var message = '''
|
|
// Messages from compiling ${path.basenameWithoutExtension(url)}.dart
|
|
$compilerMessages''';
|
|
var dir = file.parent;
|
|
if (!dir.existsSync()) dir.createSync(recursive: true);
|
|
file.writeAsStringSync(message);
|
|
compilerMessages.clear();
|
|
});
|
|
return !compiler.failure;
|
|
}
|
|
|
|
var multitests = new Set<String>();
|
|
{
|
|
// Expand wacky multitests into a bunch of test files.
|
|
// We'll compile each one as if it was an input.
|
|
var languageDir = path.join(inputDir, 'language');
|
|
var testFiles = _findTests(languageDir, filePattern);
|
|
|
|
for (var filePath in testFiles) {
|
|
if (filePath.endsWith('_multi.dart')) continue;
|
|
|
|
var contents = new File(filePath).readAsStringSync();
|
|
if (isMultiTest(contents)) {
|
|
multitests.add(filePath);
|
|
|
|
var tests = new Map<String, String>();
|
|
var outcomes = new Map<String, Set<String>>();
|
|
extractTestsFromMultitest(filePath, contents, tests, outcomes);
|
|
|
|
var filename = path.basenameWithoutExtension(filePath);
|
|
tests.forEach((name, contents) {
|
|
new File(path.join(languageDir, '${filename}_${name}_multi.dart'))
|
|
.writeAsStringSync(contents);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
var batchCompiler = createCompiler(realSdkContext);
|
|
|
|
for (var dir in [null, 'language']) {
|
|
if (codeCoverage && dir == 'language') continue;
|
|
|
|
group('dartdevc ' + path.join('test', 'codegen', dir), () {
|
|
var outDir = new Directory(path.join(expectDir, dir));
|
|
if (!outDir.existsSync()) outDir.createSync(recursive: true);
|
|
|
|
var testFiles = _findTests(path.join(inputDir, dir), filePattern);
|
|
for (var filePath in testFiles) {
|
|
if (multitests.contains(filePath)) continue;
|
|
|
|
var filename = path.basenameWithoutExtension(filePath);
|
|
|
|
test('$filename.dart', () {
|
|
// TODO(jmesserly): this was added to get some coverage of source maps
|
|
// and closure annotations.
|
|
// We need a more comprehensive strategy to test them.
|
|
var sourceMaps = filename == 'map_keys';
|
|
var closure = filename == 'closure';
|
|
var success;
|
|
// TODO(vsm): Is it okay to reuse the same context here? If there is
|
|
// overlap between test files, we may need separate ones for each
|
|
// compiler.
|
|
var compiler = (sourceMaps || closure)
|
|
? createCompiler(realSdkContext,
|
|
sourceMaps: sourceMaps, closure: closure)
|
|
: batchCompiler;
|
|
success = compile(compiler, filePath);
|
|
|
|
var outFile = new File(path.join(outDir.path, '$filename.js'));
|
|
expect(!success || outFile.existsSync(), true,
|
|
reason: '${outFile.path} was created if compilation succeeds');
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
if (codeCoverage) {
|
|
group('sdk', () {
|
|
// The analyzer does not bubble exception messages for certain internal
|
|
// dart:* library failures, such as failing to find
|
|
// "_internal/libraries.dart". Instead it produces an opaque "failed to
|
|
// instantiate dart:core" message. To remedy this we hook up an analysis
|
|
// logger that prints these messages.
|
|
var savedLogger;
|
|
setUp(() {
|
|
savedLogger = AnalysisEngine.instance.logger;
|
|
AnalysisEngine.instance.logger = new PrintLogger();
|
|
});
|
|
tearDown(() {
|
|
AnalysisEngine.instance.logger = savedLogger;
|
|
});
|
|
|
|
test('devc dart:core', () {
|
|
var testSdkContext = createAnalysisContextWithSources(
|
|
new SourceResolverOptions(
|
|
dartSdkPath:
|
|
path.join(testDirectory, '..', 'tool', 'generated_sdk')));
|
|
|
|
// Get the test SDK. We use a checked in copy so test expectations can
|
|
// be generated against a specific SDK version.
|
|
var compiler = createCompiler(testSdkContext, checkSdk: true);
|
|
compile(compiler, 'dart:core');
|
|
var outFile = new File(path.join(expectDir, 'dart/core.js'));
|
|
expect(outFile.existsSync(), true,
|
|
reason: '${outFile.path} was created for dart:core');
|
|
});
|
|
});
|
|
}
|
|
|
|
var expectedRuntime =
|
|
defaultRuntimeFiles.map((f) => 'dev_compiler/runtime/$f');
|
|
|
|
test('devc jscodegen sunflower.html', () {
|
|
var filePath = path.join(inputDir, 'sunflower', 'sunflower.html');
|
|
var success = compile(batchCompiler, filePath);
|
|
|
|
var expectedFiles = ['sunflower.html', 'sunflower.js',];
|
|
|
|
for (var filepath in expectedFiles) {
|
|
var outFile = new File(path.join(expectDir, 'sunflower', filepath));
|
|
expect(outFile.existsSync(), success,
|
|
reason: '${outFile.path} was created iff compilation succeeds');
|
|
}
|
|
});
|
|
|
|
test('devc jscodegen html_input.html', () {
|
|
var filePath = path.join(inputDir, 'html_input.html');
|
|
var success = compile(batchCompiler, filePath);
|
|
|
|
var expectedFiles = [
|
|
'html_input.html',
|
|
'dir/html_input_a.js',
|
|
'dir/html_input_b.js',
|
|
'dir/html_input_c.js',
|
|
'dir/html_input_d.js',
|
|
'dir/html_input_e.js'
|
|
]..addAll(expectedRuntime);
|
|
|
|
for (var filepath in expectedFiles) {
|
|
var outFile = new File(path.join(expectDir, filepath));
|
|
expect(outFile.existsSync(), success,
|
|
reason: '${outFile.path} was created iff compilation succeeds');
|
|
}
|
|
});
|
|
}
|
|
|
|
/// An implementation of analysis engine's [Logger] that prints.
|
|
class PrintLogger implements Logger {
|
|
@override void logError(String message, [CaughtException exception]) {
|
|
print('[AnalysisEngine] error $message $exception');
|
|
}
|
|
|
|
@override void logError2(String message, Object exception) {
|
|
print('[AnalysisEngine] error $message $exception');
|
|
}
|
|
|
|
void logInformation(String message, [CaughtException exception]) {}
|
|
void logInformation2(String message, Object exception) {}
|
|
}
|