// Copyright (c) 2013, 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. // Test the exit code of dart2js in case of exceptions, errors, warnings, etc. import 'dart:async'; import 'dart:io' show Platform; import 'package:async_helper/async_helper.dart'; import 'package:expect/expect.dart'; import 'package:compiler/compiler.dart' as api; import 'package:compiler/src/dart2js.dart' as entry; import 'package:compiler/src/dart2jslib.dart'; import 'package:compiler/src/apiimpl.dart' as apiimpl; import 'package:compiler/src/elements/elements.dart'; import 'package:compiler/src/library_loader.dart'; import 'package:compiler/src/resolution/resolution.dart'; import 'package:compiler/src/scanner/scannerlib.dart'; import 'package:compiler/src/util/util.dart'; class TestCompiler extends apiimpl.Compiler { final String testMarker; final String testType; final Function onTest; TestCompiler(api.CompilerInputProvider inputProvider, api.CompilerOutputProvider outputProvider, api.DiagnosticHandler handler, Uri libraryRoot, Uri packageRoot, List options, Map environment, String this.testMarker, String this.testType, Function this.onTest) : super(inputProvider, outputProvider, handler, libraryRoot, packageRoot, options, environment) { scanner = new TestScanner(this); resolver = new TestResolver(this, backend.constantCompilerTask); test('Compiler'); } Future run(Uri uri) { test('Compiler.run'); return super.run(uri); } Future onLibraryScanned(LibraryElement element, LibraryLoader loader) { test('Compiler.onLibraryScanned'); return super.onLibraryScanned(element, loader); } Future onLibrariesLoaded(LoadedLibraries loadedLibraries) { test('Compiler.onLibrariesLoaded'); return super.onLibrariesLoaded(loadedLibraries); } void analyzeElement(Element element) { test('Compiler.analyzeElement'); super.analyzeElement(element); } void codegen(CodegenWorkItem work, CodegenEnqueuer world) { test('Compiler.codegen'); super.codegen(work, world); } withCurrentElement(Element element, f()) { return super.withCurrentElement(element, () { test('Compiler.withCurrentElement'); return f(); }); } test(String marker) { if (marker == testMarker) { switch (testType) { case 'assert': onTest(testMarker, testType); assert(false); break; case 'invariant': onTest(testMarker, testType); invariant(NO_LOCATION_SPANNABLE, false, message: marker); break; case 'warning': onTest(testMarker, testType); reportWarning(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {'text': marker}); break; case 'error': onTest(testMarker, testType); reportError(NO_LOCATION_SPANNABLE, MessageKind.GENERIC, {'text': marker}); break; case 'internalError': onTest(testMarker, testType); internalError(NO_LOCATION_SPANNABLE, marker); break; case 'NoSuchMethodError': onTest(testMarker, testType); null.foo; break; case '': onTest(testMarker, testType); break; } } } } class TestScanner extends ScannerTask { TestScanner(TestCompiler compiler) : super(compiler); TestCompiler get compiler => super.compiler; void scanElements(CompilationUnitElement compilationUnit) { compiler.test('ScannerTask.scanElements'); super.scanElements(compilationUnit); } } class TestResolver extends ResolverTask { TestResolver(TestCompiler compiler, ConstantCompiler constantCompiler) : super(compiler, constantCompiler); TestCompiler get compiler => super.compiler; void computeClassMembers(ClassElement element) { compiler.test('ResolverTask.computeClassMembers'); super.computeClassMembers(element); } } int checkedResults = 0; Future testExitCode(String marker, String type, int expectedExitCode) { bool testOccurred = false; void onTest(String testMarker, String testType) { if (testMarker == marker && testType == type) { testOccurred = true; } } return new Future(() { Future compile( Uri script, Uri libraryRoot, Uri packageRoot, api.CompilerInputProvider inputProvider, api.DiagnosticHandler handler, [List options = const [], api.CompilerOutputProvider outputProvider, Map environment = const {}]) { libraryRoot = Platform.script.resolve('../../../sdk/'); outputProvider = NullSink.outputProvider; // Use this to silence the test when debugging: // handler = (uri, begin, end, message, kind) {}; Compiler compiler = new TestCompiler(inputProvider, outputProvider, handler, libraryRoot, packageRoot, options, environment, marker, type, onTest); return compiler.run(script).then((bool success) { return new api.CompilationResult(compiler, isSuccess: success); }); } int foundExitCode; checkResult() { Expect.isTrue(testOccurred, 'testExitCode($marker, $type) did not occur'); if (foundExitCode == null) foundExitCode = 0; print('testExitCode($marker, $type) ' 'exitCode=$foundExitCode expected=$expectedExitCode'); Expect.equals(expectedExitCode, foundExitCode, 'testExitCode($marker, $type) ' 'exitCode=$foundExitCode expected=${expectedExitCode}'); checkedResults++; } void exit(exitCode) { if (foundExitCode == null) { foundExitCode = exitCode; } }; entry.exitFunc = exit; entry.compileFunc = compile; Future result = entry.internalMain( ["tests/compiler/dart2js/exit_code_helper.dart"]); return result.catchError((e, s) { // Capture crashes. }).whenComplete(checkResult); }); } Future testExitCodes(String marker, Map expectedExitCodes) { return Future.forEach(expectedExitCodes.keys, (String type) { return testExitCode(marker, type, expectedExitCodes[type]); }); } void main() { bool isCheckedMode = false; assert((isCheckedMode = true)); final beforeRun = { '': 0, 'NoSuchMethodError': 253, 'assert': isCheckedMode ? 253 : 0, 'invariant': 253 }; final duringRun = { '': 0, 'NoSuchMethodError': 253, 'assert': isCheckedMode ? 253 : 0, 'invariant': 253, 'warning': 0, 'error': 1, 'internalError': 253, }; final tests = { 'Compiler': beforeRun, 'Compiler.run': beforeRun, 'Compiler.onLibraryScanned': beforeRun, 'Compiler.onLibrariesLoaded': beforeRun, 'ScannerTask.scanElements': duringRun, 'Compiler.withCurrentElement': duringRun, 'Compiler.analyzeElement': duringRun, 'Compiler.codegen': duringRun, 'ResolverTask.computeClassMembers': duringRun, }; asyncStart(); Future.forEach(tests.keys, (marker) { return testExitCodes(marker, tests[marker]); }).then((_) { int countResults(Map runType) { return runType.length * tests.values.where((r) => r == runType).length; } Expect.equals(countResults(beforeRun) + countResults(duringRun), checkedResults); asyncEnd(); }); }