blob: 7345bc07663f7fd9b7057bd683a5ad5854443e2d [file] [log] [blame]
// 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 'package:compiler/src/io/source_information.dart';
import 'package:expect/async_helper.dart';
import 'package:expect/expect.dart';
import 'package:compiler/compiler_api.dart' as api;
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/common/codegen.dart';
import 'package:compiler/src/common/work.dart';
import 'package:compiler/src/compiler.dart';
import 'package:compiler/src/dart2js.dart' as entry;
import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
import 'package:compiler/src/diagnostics/invariant.dart';
import 'package:compiler/src/diagnostics/messages.dart';
import 'package:compiler/src/diagnostics/spannable.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/inferrer/abstract_value_domain.dart';
import 'package:compiler/src/js_model/js_strategy.dart';
import 'package:compiler/src/null_compiler_output.dart';
import 'package:compiler/src/serialization/serialization.dart';
import 'package:compiler/src/options.dart' show CompilerOptions;
import 'package:compiler/src/universe/world_impact.dart';
import 'package:compiler/src/util/memory_compiler.dart';
class TestCompiler extends Compiler {
final String testMarker;
final String testType;
final Function onTest;
@override
late final TestDiagnosticReporter reporter;
TestCompiler(
api.CompilerInput inputProvider,
api.CompilerOutput outputProvider,
api.CompilerDiagnostics handler,
CompilerOptions options,
String this.testMarker,
String this.testType,
Function this.onTest,
) : super(inputProvider, outputProvider, handler, options) {
reporter = TestDiagnosticReporter(this);
test('Compiler');
}
@override
JsBackendStrategy createBackendStrategy() {
return TestBackendStrategy(this);
}
@override
Future<bool> run() {
test('Compiler.run');
return super.run();
}
test(String marker) {
if (marker == testMarker) {
switch (testType) {
case 'assert':
onTest(testMarker, testType);
assert(false);
break;
case 'failedAt':
onTest(testMarker, testType);
failedAt(noLocationSpannable, marker);
case 'warning':
onTest(testMarker, testType);
reporter.reportWarningMessage(
noLocationSpannable,
MessageKind.generic,
{'text': marker},
);
break;
case 'error':
onTest(testMarker, testType);
reporter.reportErrorMessage(
noLocationSpannable,
MessageKind.generic,
{'text': marker},
);
break;
case 'internalError':
onTest(testMarker, testType);
reporter.internalError(noLocationSpannable, marker);
case 'NoSuchMethodError':
onTest(testMarker, testType);
dynamic n;
n.foo;
break;
case '':
onTest(testMarker, testType);
break;
}
}
}
}
class TestBackendStrategy extends JsBackendStrategy {
final TestCompiler compiler;
TestBackendStrategy(TestCompiler compiler)
: this.compiler = compiler,
super(compiler);
@override
WorldImpact generateCode(
WorkItem work,
AbstractValueDomain abstractValueDomain,
CodegenResults codegenResults,
ComponentLookup componentLookup,
SourceLookup sourceLookup,
) {
compiler.test('Compiler.codegen');
return super.generateCode(
work,
abstractValueDomain,
codegenResults,
componentLookup,
sourceLookup,
);
}
}
class TestDiagnosticReporter extends DiagnosticReporter {
TestCompiler compiler;
TestDiagnosticReporter(this.compiler) : super(compiler);
@override
withCurrentElement(Entity element, f()) {
return super.withCurrentElement(element, () {
compiler.test('Compiler.withCurrentElement');
return f();
});
}
}
int checkedResults = 0;
Future testExitCode(
String marker,
String type,
int expectedExitCode,
List options,
) {
bool testOccurred = false;
void onTest(String testMarker, String testType) {
if (testMarker == marker && testType == type) {
testOccurred = true;
}
}
return Future(() {
Future<api.CompilationResult> compile(
CompilerOptions compilerOptions,
api.CompilerInput compilerInput,
api.CompilerDiagnostics compilerDiagnostics,
api.CompilerOutput compilerOutput,
) {
compilerOutput = const NullCompilerOutput();
// Use this to silence the test when debugging:
// handler = (uri, begin, end, message, kind) {};
Compiler compiler = TestCompiler(
compilerInput,
compilerOutput,
compilerDiagnostics,
compilerOptions,
marker,
type,
onTest,
);
return compiler.run().then((bool success) {
return 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++;
}
// TODO(48220): Make return type `Never` when this test is migrated.
/* Never */
exit(exitCode) {
if (foundExitCode == null) {
foundExitCode = exitCode;
}
throw 'Exit';
}
entry.exitFunc = exit;
entry.compileFunc = compile;
List<String> args = List<String>.from(options)
..add("--libraries-spec=$sdkLibrariesSpecificationUri")
..add("--platform-binaries=$sdkPlatformBinariesPath")
..add("pkg/compiler/test/end_to_end/data/exit_code_helper.dart");
Future result = entry.internalMain(args);
return result
.catchError((e, s) {
// Capture crashes.
})
.whenComplete(checkResult);
});
}
Future testExitCodes(
String marker,
Map<String, int> expectedExitCodes,
List<String> options,
) {
return Future.forEach(expectedExitCodes.keys, (String type) {
return testExitCode(marker, type, expectedExitCodes[type]!, options);
});
}
void main() {
bool isCheckedMode = false;
assert((isCheckedMode = true));
entry.enableWriteString = false;
Map<String, int> _expectedExitCode({
bool beforeRun = false,
bool fatalWarnings = false,
}) {
if (beforeRun) {
return {
'': 0,
'NoSuchMethodError': 253,
'assert': isCheckedMode ? 253 : 0,
'failedAt': 253,
};
}
// duringRun:
return {
'': 0,
'NoSuchMethodError': 253,
'assert': isCheckedMode ? 253 : 0,
'failedAt': 253,
'warning': fatalWarnings ? 1 : 0,
'error': 1,
'internalError': 253,
};
}
const beforeRun = false;
const duringRun = true;
final tests = {
'Compiler': beforeRun,
'Compiler.run': beforeRun,
'Compiler.withCurrentElement': duringRun,
'Compiler.codegen': duringRun,
};
int totalExpectedErrors = 0;
asyncTest(() async {
for (String marker in tests.keys) {
var expected = _expectedExitCode(beforeRun: tests[marker]!);
totalExpectedErrors += expected.length;
await testExitCodes(marker, expected, []);
expected = _expectedExitCode(
beforeRun: tests[marker]!,
fatalWarnings: true,
);
totalExpectedErrors += expected.length;
await testExitCodes(marker, expected, [Flags.fatalWarnings]);
}
Expect.equals(totalExpectedErrors, checkedResults);
});
}