Files
sdk/pkg/dev_compiler/test/codegen_test.dart
T
Olivier Chafik 4ad758998a Emit TypeScript / Closure ES6_TYPED type annotations in --closure mode (instead of closure comments).
- Replaced ClosureType by a general JS.TypeRef, used in AST for identifier types, return types
- Convert DartType to JS.TypeRef (in mixin JsTypeRefCodegen), including type parameters (also added to AST in Fun & ClassExpression)
- Emit field declarations expected by TS
- Wrote a TypeScriptTypePrinter (mixed in by Printer) and a ClosureTypePrinter (might disappear soon)
- Simplified annotation code, called in more places (seems to gives more source info)

Example input:

  List/*<T>*/ func/*<T>*/(List/*<T>*/ items, dynamic/*=T*/ seed) {}

  class Foo<T> {
    int i;
    static var x;
    Foo(this.i, o, {String v : "?"}) {}
  }

Output:

  function func<T>(items: core.List<T>, seed: T): core.List<T> {}

  const Foo$ = dart.generic(function(T) {
    class Foo<T> extends core.Object {
      i: number;
      static x;
      Foo(i: number, o, {v = "?"}: {v?: string} = {}) {
        this.i = i;
      }
    }
    ...
    Foo.x = null;
    return Foo;
  });

Known remaining issues:
- typedefs expect a `type Callback = (...) => ...;` statement
- `exports` is a reserved keyword in TS (either we change the way we do exports, or we'll need a different temp + extra type annotations of the default-exported object).
- Generic type is currently locked inside the generic call. Might be able to solve by exporting signatures in .d.ts file, or changing the way we do generics.

BUG=
R=jmesserly@google.com

Review URL: https://codereview.chromium.org/1676463002 .
2016-02-10 18:22:21 +00:00

271 lines
9.6 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 destructureNamedParams: false,
bool closure: false,
ModuleFormat moduleFormat: ModuleFormat.legacy}) {
// 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,
destructureNamedParams: destructureNamedParams,
forceCompile: checkSdk,
moduleFormat: moduleFormat),
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 testDirs = <String>['language', path.join('lib', 'typed_data')];
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.
for (var testDir in testDirs) {
var fullDir = path.join(inputDir, testDir);
var testFiles = _findTests(fullDir, 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(fullDir, '${filename}_${name}_multi.dart'))
.writeAsStringSync(contents);
});
}
}
}
}
var batchCompiler = createCompiler(realSdkContext);
var allDirs = [null];
allDirs.addAll(testDirs);
for (var dir in allDirs) {
if (codeCoverage && dir != null) 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 destructureNamedParams = filename == 'destructuring' || closure;
var moduleFormat = filename == 'es6_modules' || closure
? ModuleFormat.es6
: filename == 'node_modules'
? ModuleFormat.node
: ModuleFormat.legacy;
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 ||
destructureNamedParams ||
moduleFormat != ModuleFormat.legacy)
? createCompiler(realSdkContext,
sourceMaps: sourceMaps,
destructureNamedParams: destructureNamedParams,
closure: closure,
moduleFormat: moduleFormat)
: 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');
}
void logInformation(String message, [CaughtException exception]) {}
void logInformation2(String message, Object exception) {}
}