0a5604c223
Bug: https://github.com/dart-lang/sdk/issues/63318 Change-Id: Ie02aef92d9ab0e04ce864a85ed6071d5e4b5e14f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500820 Reviewed-by: Bob Nystrom <rnystrom@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
1053 lines
25 KiB
Dart
1053 lines
25 KiB
Dart
// Copyright (c) 2019, 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.
|
|
|
|
// ignore_for_file: unnecessary_string_escapes
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:expect/expect.dart';
|
|
import 'package:test_runner/src/path.dart';
|
|
import 'package:test_runner/src/static_error.dart';
|
|
import 'package:test_runner/src/test_file.dart';
|
|
|
|
import 'utils.dart';
|
|
|
|
// Note: This test file validates how some of the special markers used by the
|
|
// test runner are parsed. But this test is also run *by* that same test
|
|
// runner, and we don't want it to see the markers inside the string literals
|
|
// here as significant, so we obfuscate them using seemingly-pointless string
|
|
// escapes here like `\/`.
|
|
|
|
void main() {
|
|
testParseDill();
|
|
testParseVMOptions();
|
|
testParseOtherOptions();
|
|
testParseEnvironment();
|
|
testParsePackages();
|
|
testParseExperiments();
|
|
testParseMultitest();
|
|
testParseErrorExpectations();
|
|
testParseContextMessages();
|
|
testIsRuntimeTest();
|
|
testName();
|
|
testMultitest();
|
|
testShardHash();
|
|
}
|
|
|
|
void testParseDill() {
|
|
// Handles ".dill" files.
|
|
var file = createTestFile(source: "", path: "test.dill");
|
|
Expect.isNotNull(file.vmOptions);
|
|
Expect.equals(1, file.vmOptions.length);
|
|
Expect.listEquals(<String>[], file.vmOptions.first);
|
|
|
|
Expect.listEquals(<String>[], file.dartOptions);
|
|
Expect.listEquals(<String>[], file.sharedOptions);
|
|
Expect.listEquals(<String>[], file.dart2jsOptions);
|
|
Expect.listEquals(<String>[], file.dart2wasmOptions);
|
|
Expect.listEquals(<String>[], file.ddcOptions);
|
|
Expect.listEquals(<String>[], file.otherResources);
|
|
Expect.listEquals(<String>[], file.sharedObjects);
|
|
|
|
Expect.isEmpty(file.environment.entries);
|
|
Expect.isNull(file.packages);
|
|
|
|
Expect.isFalse(file.isMultitest);
|
|
|
|
Expect.isFalse(file.hasSyntaxError);
|
|
Expect.isFalse(file.hasCompileError);
|
|
Expect.isFalse(file.hasRuntimeError);
|
|
Expect.isFalse(file.hasStaticWarning);
|
|
Expect.isFalse(file.hasCrash);
|
|
}
|
|
|
|
void testParseVMOptions() {
|
|
expectVMOptions(String source, List<List<String>> expected) {
|
|
var file = createTestFile(source: source);
|
|
Expect.isNotNull(file.vmOptions);
|
|
Expect.equals(expected.length, file.vmOptions.length);
|
|
for (var i = 0; i < expected.length; i++) {
|
|
Expect.listEquals(expected[i], file.vmOptions[i]);
|
|
}
|
|
}
|
|
|
|
// No options.
|
|
expectVMOptions("", [[]]);
|
|
|
|
// Splits words.
|
|
expectVMOptions("/\/ VMOptions=--verbose --async", [
|
|
["--verbose", "--async"],
|
|
]);
|
|
|
|
// Allows multiple.
|
|
expectVMOptions(
|
|
"""
|
|
/\/ VMOptions=--first one
|
|
/\/ VMOptions=--second two
|
|
""",
|
|
[
|
|
["--first", "one"],
|
|
["--second", "two"],
|
|
],
|
|
);
|
|
}
|
|
|
|
void testParseOtherOptions() {
|
|
// No options.
|
|
var file = createTestFile(source: "");
|
|
Expect.listEquals(<String>[], file.dartOptions);
|
|
Expect.listEquals(<String>[], file.sharedOptions);
|
|
Expect.listEquals(<String>[], file.dart2jsOptions);
|
|
Expect.listEquals(<String>[], file.dart2wasmOptions);
|
|
Expect.listEquals(<String>[], file.ddcOptions);
|
|
Expect.listEquals(<String>[], file.otherResources);
|
|
Expect.listEquals(<String>[], file.sharedObjects);
|
|
Expect.listEquals(<String>[], file.requirements);
|
|
|
|
// Single options split into words.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ DartOptions=dart options
|
|
/\/ SharedOptions=shared options
|
|
/\/ dart2jsOptions=dart2js options
|
|
/\/ dart2wasmOptions=dart2wasm options
|
|
/\/ ddcOptions=ddc options
|
|
/\/ OtherResources=other resources
|
|
/\/ SharedObjects=shared objects
|
|
""",
|
|
);
|
|
Expect.listEquals(["dart", "options"], file.dartOptions);
|
|
Expect.listEquals(["shared", "options"], file.sharedOptions);
|
|
Expect.listEquals(["dart2js", "options"], file.dart2jsOptions);
|
|
Expect.listEquals(["dart2wasm", "options"], file.dart2wasmOptions);
|
|
Expect.listEquals(["ddc", "options"], file.ddcOptions);
|
|
Expect.listEquals(["other", "resources"], file.otherResources);
|
|
|
|
// Disallows multiple lines for some options.
|
|
expectParseThrows("""
|
|
/\/ DartOptions=first
|
|
/\/ DartOptions=second
|
|
""");
|
|
expectParseThrows("""
|
|
/\/ SharedOptions=first
|
|
/\/ SharedOptions=second
|
|
""");
|
|
expectParseThrows("""
|
|
/\/ dart2jsOptions=first
|
|
/\/ dart2jsOptions=second
|
|
""");
|
|
expectParseThrows("""
|
|
/\/ dart2wasmOptions=first
|
|
/\/ dart2wasmOptions=second
|
|
""");
|
|
expectParseThrows("""
|
|
/\/ ddcOptions=first
|
|
/\/ ddcOptions=second
|
|
""");
|
|
|
|
// Merges multiple lines for others.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ OtherResources=other resources
|
|
/\/ OtherResources=even more
|
|
/\/ SharedObjects=shared objects
|
|
/\/ SharedObjects=many more
|
|
""",
|
|
);
|
|
Expect.listEquals([
|
|
"other",
|
|
"resources",
|
|
"even",
|
|
"more",
|
|
], file.otherResources);
|
|
Expect.listEquals(["shared", "objects", "many", "more"], file.sharedObjects);
|
|
|
|
// Disallows unrecognized features in requirements.
|
|
expectParseThrows("""
|
|
/\/ Requirements=unknown-feature
|
|
""");
|
|
}
|
|
|
|
void testParseEnvironment() {
|
|
// No environment.
|
|
var file = createTestFile(source: "");
|
|
Expect.isTrue(file.environment.isEmpty);
|
|
|
|
// Without values.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ Environment=some value
|
|
/\/ Environment=another one
|
|
""",
|
|
);
|
|
Expect.mapEquals({"some value": "", "another one": ""}, file.environment);
|
|
|
|
// With values.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ Environment=some value=its value
|
|
/\/ Environment=another one = also value
|
|
""",
|
|
);
|
|
Expect.mapEquals({
|
|
"some value": "its value",
|
|
"another one ": " also value",
|
|
}, file.environment);
|
|
}
|
|
|
|
void testParsePackages() {
|
|
// No option.
|
|
var file = createTestFile(source: "");
|
|
Expect.isNull(file.packages);
|
|
|
|
// Single option is converted to a path.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ Packages=packages thing
|
|
""",
|
|
);
|
|
Expect.isTrue(
|
|
file.packages!.endsWith("${Platform.pathSeparator}packages thing"),
|
|
);
|
|
|
|
// "none" is left alone.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ Packages=none
|
|
""",
|
|
);
|
|
Expect.equals("none", file.packages);
|
|
|
|
// Cannot appear more than once.
|
|
expectParseThrows("""
|
|
/\/ Packages=first
|
|
/\/ Packages=second
|
|
""");
|
|
}
|
|
|
|
void testParseExperiments() {
|
|
// No option.
|
|
var file = createTestFile(source: "");
|
|
Expect.isTrue(file.experiments.isEmpty);
|
|
|
|
// Single non-experiment option.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ SharedOptions=not-experiment
|
|
""",
|
|
);
|
|
Expect.isTrue(file.experiments.isEmpty);
|
|
Expect.listEquals(["not-experiment"], file.sharedOptions);
|
|
|
|
// Experiments.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ SharedOptions=--enable-experiment=flubber,gloop
|
|
""",
|
|
);
|
|
Expect.listEquals(["flubber", "gloop"], file.experiments);
|
|
Expect.isTrue(file.sharedOptions.isEmpty);
|
|
|
|
// Experiment option mixed with other options.
|
|
file = createTestFile(
|
|
source: """
|
|
/\/ SharedOptions=-a --enable-experiment=flubber --other
|
|
""",
|
|
);
|
|
Expect.listEquals(["flubber"], file.experiments);
|
|
Expect.listEquals(["-a", "--other"], file.sharedOptions);
|
|
|
|
// Poorly-formatted experiment option.
|
|
expectParseThrows("""
|
|
/\/ SharedOptions=stuff--enable-experiment=flubber,gloop
|
|
""");
|
|
}
|
|
|
|
void testParseMultitest() {
|
|
// Not present.
|
|
var file = createTestFile(source: "");
|
|
Expect.isFalse(file.isMultitest);
|
|
|
|
// Present.
|
|
file = createTestFile(
|
|
source: """
|
|
main() {} /\/# 01: compile-time error
|
|
""",
|
|
);
|
|
Expect.isTrue(file.isMultitest);
|
|
}
|
|
|
|
void testParseErrorExpectations() {
|
|
// No errors.
|
|
expectParseErrorExpectations("""
|
|
main() {}
|
|
""", []);
|
|
|
|
// Empty file
|
|
expectParseErrorExpectations("", []);
|
|
|
|
// Multiple errors.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [analyzer] CompileTimeErrorCode.WRONG_TYPE
|
|
/\/ [cfe] Error: Can't assign a string to an int.
|
|
/\/ [cfe] Another CFE error.
|
|
/\/ [web] Web-specific error.
|
|
|
|
num j = "str";
|
|
/\/ ^^^^^
|
|
/\/ [analyzer] CompileTimeErrorCode.ALSO_WRONG_TYPE
|
|
/\/ [cfe] Error: Can't assign a string to a num.
|
|
/\/ [web] Another web error.
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 3,
|
|
analyzerError: "CompileTimeErrorCode.WRONG_TYPE",
|
|
),
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 3,
|
|
cfeError: "Error: Can't assign a string to an int.",
|
|
),
|
|
makeError(line: 1, column: 9, length: 3, cfeError: "Another CFE error."),
|
|
makeError(line: 1, column: 9, length: 3, webError: "Web-specific error."),
|
|
makeError(
|
|
line: 8,
|
|
column: 9,
|
|
length: 5,
|
|
analyzerError: "CompileTimeErrorCode.ALSO_WRONG_TYPE",
|
|
),
|
|
makeError(
|
|
line: 8,
|
|
column: 9,
|
|
length: 5,
|
|
cfeError: "Error: Can't assign a string to a num.",
|
|
),
|
|
makeError(line: 8, column: 9, length: 5, webError: "Another web error."),
|
|
],
|
|
);
|
|
|
|
// Explicit error location.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
/\/ [error line 123, column 45, length 678]
|
|
/\/ [analyzer] CompileTimeErrorCode.FIRST
|
|
/\/ [cfe] First error.
|
|
/\/ [ error line 23 , column 5 , length 78 ]
|
|
/\/ [analyzer] CompileTimeErrorCode.SECOND
|
|
/\/ [cfe] Second error.
|
|
/\/ [web] Second web error.
|
|
/\/[error line 9,column 8,length 7]
|
|
/\/ [cfe] Third.
|
|
/\/[error line 10,column 9]
|
|
/\/ [cfe] No length.
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 123,
|
|
column: 45,
|
|
length: 678,
|
|
analyzerError: "CompileTimeErrorCode.FIRST",
|
|
),
|
|
makeError(line: 123, column: 45, length: 678, cfeError: "First error."),
|
|
makeError(
|
|
line: 23,
|
|
column: 5,
|
|
length: 78,
|
|
analyzerError: "CompileTimeErrorCode.SECOND",
|
|
),
|
|
makeError(line: 23, column: 5, length: 78, cfeError: "Second error."),
|
|
makeError(line: 23, column: 5, length: 78, webError: "Second web error."),
|
|
makeError(line: 9, column: 8, length: 7, cfeError: "Third."),
|
|
makeError(line: 10, column: 9, cfeError: "No length."),
|
|
],
|
|
);
|
|
|
|
// Comment after error message.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [analyzer] CompileTimeErrorCode.WRONG_TYPE
|
|
/\/ [cfe] Error message.
|
|
/\/ Unrelated comment.
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 3,
|
|
analyzerError: "CompileTimeErrorCode.WRONG_TYPE",
|
|
),
|
|
makeError(line: 1, column: 9, length: 3, cfeError: "Error message."),
|
|
],
|
|
);
|
|
|
|
// Multiple errors attached to same line.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
main() {}
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe] First error.
|
|
/\/ ^
|
|
/\/ [analyzer] ErrorCode.second
|
|
/\/ ^^^^^^^
|
|
/\/ [cfe] Third error.
|
|
""",
|
|
[
|
|
makeError(line: 2, column: 9, length: 3, cfeError: "First error."),
|
|
makeError(
|
|
line: 2,
|
|
column: 7,
|
|
length: 1,
|
|
analyzerError: "ErrorCode.second",
|
|
),
|
|
makeError(line: 2, column: 5, length: 7, cfeError: "Third error."),
|
|
],
|
|
);
|
|
|
|
// Unspecified errors.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
// [analyzer] unspecified
|
|
// [cfe] unspecified
|
|
// [web] unspecified
|
|
int j = "s";
|
|
/\/ ^^^
|
|
// [analyzer] unspecified
|
|
// [cfe] Message.
|
|
int k = "s";
|
|
/\/ ^^^
|
|
// [analyzer] Error.CODE
|
|
// [cfe] unspecified
|
|
int l = "s";
|
|
/\/ ^^^
|
|
// [analyzer] unspecified
|
|
int m = "s";
|
|
/\/ ^^^
|
|
// [cfe] unspecified
|
|
int n = "s";
|
|
/\/ ^^^
|
|
// [web] unspecified
|
|
""",
|
|
[
|
|
makeError(line: 1, column: 8, length: 3, analyzerError: "unspecified"),
|
|
makeError(line: 1, column: 8, length: 3, cfeError: "unspecified"),
|
|
makeError(line: 1, column: 8, length: 3, webError: "unspecified"),
|
|
makeError(line: 6, column: 8, length: 3, analyzerError: "unspecified"),
|
|
makeError(line: 6, column: 8, length: 3, cfeError: "Message."),
|
|
makeError(line: 10, column: 8, length: 3, analyzerError: "Error.CODE"),
|
|
makeError(line: 10, column: 8, length: 3, cfeError: "unspecified"),
|
|
makeError(line: 14, column: 8, length: 3, analyzerError: "unspecified"),
|
|
makeError(line: 17, column: 8, length: 3, cfeError: "unspecified"),
|
|
makeError(line: 20, column: 8, length: 3, webError: "unspecified"),
|
|
],
|
|
);
|
|
|
|
// Ignore multitest markers.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^ /\/# 0: ok
|
|
/\/ [analyzer] ErrorCode.BAD_THING /\/# 123: continued
|
|
/\/ [cfe] Message 1. /\/# named: compile-time error
|
|
/\/ [error line 12, column 34, length 56] /\/# 3: continued
|
|
/\/ [cfe] Message 2.
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 3,
|
|
analyzerError: "ErrorCode.BAD_THING",
|
|
),
|
|
makeError(line: 1, column: 9, length: 3, cfeError: "Message 1."),
|
|
makeError(line: 12, column: 34, length: 56, cfeError: "Message 2."),
|
|
],
|
|
);
|
|
|
|
// Allow front ends in any order.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe] Error message.
|
|
/\/ [analyzer] ErrorCode.BAD_THING
|
|
""",
|
|
[
|
|
makeError(line: 1, column: 9, length: 3, cfeError: "Error message."),
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 3,
|
|
analyzerError: "ErrorCode.BAD_THING",
|
|
),
|
|
],
|
|
);
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [web] Web message.
|
|
/\/ [analyzer] ErrorCode.BAD_THING
|
|
""",
|
|
[
|
|
makeError(line: 1, column: 9, length: 3, webError: "Web message."),
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 3,
|
|
analyzerError: "ErrorCode.BAD_THING",
|
|
),
|
|
],
|
|
);
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [web] Web message.
|
|
/\/ [cfe] Error message.
|
|
""",
|
|
[
|
|
makeError(line: 1, column: 9, length: 3, webError: "Web message."),
|
|
makeError(line: 1, column: 9, length: 3, cfeError: "Error message."),
|
|
],
|
|
);
|
|
|
|
// Must have at least one error message.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
|
|
var wrong;
|
|
""");
|
|
|
|
// Location must follow some real code.
|
|
expectFormatError("""
|
|
/\/ [error line 123, column 45, length 678]
|
|
/\/ [analyzer] CompileTimeErrorCode.FIRST
|
|
/\/ ^^^
|
|
/\/ [cfe] This doesn't make sense.
|
|
""");
|
|
|
|
// Location at end without message.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
""");
|
|
|
|
// Must recognize the front end.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [wat] Error message.
|
|
""");
|
|
|
|
// Analyzer error must look like an error code.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [analyzer] Not error code.
|
|
""");
|
|
|
|
// A CFE error with length one is treated as having no length.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int i = "s";
|
|
/\/ ^
|
|
/\/ [cfe] Message.
|
|
|
|
int j = "s";
|
|
/\/ ^
|
|
/\/ [analyzer] Error.BAD
|
|
/\/ [cfe] Message.
|
|
|
|
int j = "s";
|
|
/\/ ^
|
|
/\/ [cfe] Message.
|
|
/\/ [web] Web message.
|
|
""",
|
|
[
|
|
makeError(line: 1, column: 9, length: 0, cfeError: "Message."),
|
|
makeError(line: 5, column: 9, length: 1, analyzerError: "Error.BAD"),
|
|
makeError(line: 5, column: 9, length: 0, cfeError: "Message."),
|
|
makeError(line: 10, column: 9, length: 0, cfeError: "Message."),
|
|
makeError(line: 10, column: 9, length: 1, webError: "Web message."),
|
|
],
|
|
);
|
|
}
|
|
|
|
void testParseContextMessages() {
|
|
// Multiple messages.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
var string = "str";
|
|
/\/ ^^^^^^
|
|
/\/ [context 1] Analyzer context before.
|
|
/\/ [context 2] CFE context before.
|
|
|
|
int j = string;
|
|
/\/ ^^^^^^
|
|
/\/ [analyzer 1] Error.BAD
|
|
/\/ [cfe 2] Error message.
|
|
|
|
var string = "str";
|
|
/\/ ^^^
|
|
/\/ [context 2] CFE context after.
|
|
|
|
var string = "str";
|
|
/\/ ^^^
|
|
/\/ [context 1] Analyzer context after.
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 6,
|
|
column: 9,
|
|
length: 6,
|
|
analyzerError: "Error.BAD",
|
|
context: [
|
|
makeError(
|
|
line: 1,
|
|
column: 5,
|
|
length: 6,
|
|
analyzerError: "Analyzer context before.",
|
|
),
|
|
makeError(
|
|
line: 15,
|
|
column: 15,
|
|
length: 3,
|
|
analyzerError: "Analyzer context after.",
|
|
),
|
|
],
|
|
),
|
|
makeError(
|
|
line: 6,
|
|
column: 9,
|
|
length: 6,
|
|
cfeError: "Error message.",
|
|
context: [
|
|
makeError(
|
|
line: 1,
|
|
column: 5,
|
|
length: 6,
|
|
analyzerError: "CFE context before.",
|
|
),
|
|
makeError(
|
|
line: 11,
|
|
column: 15,
|
|
length: 3,
|
|
analyzerError: "CFE context after.",
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
|
|
// Context before error.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
var string = "str";
|
|
/\/ ^^^^^^
|
|
/\/ [context 1] Context.
|
|
|
|
int j = string;
|
|
/\/ ^^^^^^
|
|
/\/ [analyzer 1] Error.BAD
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 5,
|
|
column: 9,
|
|
length: 6,
|
|
analyzerError: "Error.BAD",
|
|
context: [
|
|
makeError(line: 1, column: 5, length: 6, analyzerError: "Context."),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
|
|
// Context after error.
|
|
expectParseErrorExpectations(
|
|
"""
|
|
int j = string;
|
|
/\/ ^^^^^^
|
|
/\/ [analyzer 1] Error.BAD
|
|
|
|
var string = "str";
|
|
/\/ ^^^^^^
|
|
/\/ [context 1] Context.
|
|
""",
|
|
[
|
|
makeError(
|
|
line: 1,
|
|
column: 9,
|
|
length: 6,
|
|
analyzerError: "Error.BAD",
|
|
context: [
|
|
makeError(line: 5, column: 5, length: 6, analyzerError: "Context."),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
|
|
// Context must have a number.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [context] No number.
|
|
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe 1] Error.
|
|
""");
|
|
|
|
// Context number must match an error.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [context 2] Wrong number.
|
|
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe 1] Error.
|
|
""");
|
|
|
|
// Two errors with same number.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [context 1] Context.
|
|
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe 1] Error.
|
|
/\/ [analyzer 1] Error.CODE
|
|
""");
|
|
|
|
// Numbered error with no context.
|
|
expectFormatError("""
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe 1] Error.
|
|
""");
|
|
|
|
// Cross-file context messages.
|
|
{
|
|
late String rootPath;
|
|
late String helperPath;
|
|
var file = createTestFile(
|
|
path: "cross_file_test.dart",
|
|
source: """
|
|
import 'helper.dart';
|
|
|
|
void f(C c) {
|
|
c.value.isEven;
|
|
/\/ ^^^^^^
|
|
/\/ [analyzer 1 see helper.dart] Error.BAD
|
|
}
|
|
""",
|
|
additionalFiles: {
|
|
"helper.dart": """
|
|
class C {
|
|
int? get value => 0;
|
|
/\/ ^^^^^
|
|
/\/ [context 1 for cross_file_test.dart] Context in helper.
|
|
}
|
|
""",
|
|
},
|
|
onPaths: (root, additionalPaths) {
|
|
rootPath = root;
|
|
helperPath = additionalPaths["helper.dart"]!;
|
|
},
|
|
);
|
|
Expect.listEquals([
|
|
makeError(
|
|
path: rootPath,
|
|
line: 4,
|
|
column: 11,
|
|
length: 6,
|
|
analyzerError: "Error.BAD",
|
|
context: [
|
|
makeError(
|
|
path: helperPath,
|
|
line: 2,
|
|
column: 12,
|
|
length: 5,
|
|
contextError: "Context in helper.",
|
|
),
|
|
],
|
|
).toString(),
|
|
], file.expectedErrors.map((error) => error.toString()).toList());
|
|
}
|
|
|
|
// A cross-file context owned by another root is inert.
|
|
{
|
|
var file = createTestFile(
|
|
path: "cross_file_test.dart",
|
|
source: """
|
|
import 'helper.dart';
|
|
""",
|
|
additionalFiles: {
|
|
"helper.dart": """
|
|
class C {
|
|
int? get value => 0;
|
|
/\/ ^^^^^
|
|
/\/ [context 1 for other_test.dart] Context in helper.
|
|
}
|
|
""",
|
|
},
|
|
);
|
|
Expect.isTrue(file.expectedErrors.isEmpty);
|
|
}
|
|
|
|
// The cross-file relationship must be bidirectional.
|
|
expectThrowsFormatExceptionContains(
|
|
() => createTestFile(
|
|
path: "cross_file_test.dart",
|
|
source: """
|
|
import 'helper.dart';
|
|
|
|
void f(C c) {
|
|
c.value.isEven;
|
|
/\/ ^^^^^^
|
|
/\/ [analyzer 1] Error.BAD
|
|
}
|
|
""",
|
|
additionalFiles: {
|
|
"helper.dart": """
|
|
class C {
|
|
int? get value => 0;
|
|
/\/ ^^^^^
|
|
/\/ [context 1 for cross_file_test.dart] Context in helper.
|
|
}
|
|
""",
|
|
},
|
|
),
|
|
["Missing context for numbered error 1", "Error.BAD"],
|
|
);
|
|
|
|
// A context owned by the root must have a matching numbered error.
|
|
expectThrowsFormatExceptionContains(
|
|
() => createTestFile(
|
|
path: "cross_file_test.dart",
|
|
source: """
|
|
import 'helper.dart';
|
|
""",
|
|
additionalFiles: {
|
|
"helper.dart": """
|
|
class C {
|
|
int? get value => 0;
|
|
/\/ ^^^^^
|
|
/\/ [context 1 for cross_file_test.dart] Context in helper.
|
|
}
|
|
""",
|
|
},
|
|
),
|
|
["No error with number 1", "Context in helper."],
|
|
);
|
|
|
|
// A parsed `see` file must contain a matching context marker.
|
|
expectThrowsFormatExceptionContains(
|
|
() => createTestFile(
|
|
path: "cross_file_test.dart",
|
|
source: """
|
|
bad.use;
|
|
/\/ ^^^
|
|
/\/ [analyzer 1 see helper.dart] Error.BAD
|
|
""",
|
|
additionalFiles: {
|
|
"helper.dart": """
|
|
class C {
|
|
int? get value => 0;
|
|
}
|
|
""",
|
|
},
|
|
),
|
|
["No context with number 1", "helper.dart", "Error.BAD"],
|
|
);
|
|
|
|
// A `see` path must point to a parsed context file.
|
|
expectThrowsFormatExceptionContains(
|
|
() => createTestFile(
|
|
path: "cross_file_test.dart",
|
|
source: """
|
|
bad.use;
|
|
/\/ ^^^
|
|
/\/ [analyzer 1 see missing_helper.dart] Error.BAD
|
|
""",
|
|
),
|
|
["Context file", "missing_helper.dart", "was not parsed", "Error.BAD"],
|
|
);
|
|
}
|
|
|
|
void testIsRuntimeTest() {
|
|
// No static errors at all.
|
|
var file = createTestFile(source: "");
|
|
Expect.isTrue(file.isRuntimeTest);
|
|
|
|
// Only warnings.
|
|
file = createTestFile(
|
|
source: """
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [analyzer] STATIC_WARNING.INVALID_OPTION
|
|
/\/ ^^^
|
|
/\/ [analyzer] STATIC_WARNING.INVALID_OPTION
|
|
""",
|
|
);
|
|
Expect.isTrue(file.isRuntimeTest);
|
|
|
|
// Errors.
|
|
file = createTestFile(
|
|
source: """
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [analyzer] COMPILE_TIME_ERROR.NOT_ENOUGH_POSITIONAL_ARGUMENTS
|
|
""",
|
|
);
|
|
Expect.isFalse(file.isRuntimeTest);
|
|
|
|
file = createTestFile(
|
|
source: """
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [cfe] Error message.
|
|
""",
|
|
);
|
|
Expect.isFalse(file.isRuntimeTest);
|
|
|
|
file = createTestFile(
|
|
source: """
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [web] Error message.
|
|
""",
|
|
);
|
|
Expect.isFalse(file.isRuntimeTest);
|
|
|
|
// Mixed errors and warnings.
|
|
file = createTestFile(
|
|
source: """
|
|
int i = "s";
|
|
/\/ ^^^
|
|
/\/ [analyzer] STATIC_WARNING.INVALID_OPTION
|
|
/\/ [cfe] Error message.
|
|
""",
|
|
);
|
|
Expect.isFalse(file.isRuntimeTest);
|
|
}
|
|
|
|
void testName() {
|
|
// Immediately inside suite.
|
|
var file = createTestFile(source: "", path: "a_test.dart", suite: "suite");
|
|
Expect.equals("a_test", file.name);
|
|
|
|
// Inside subdirectory.
|
|
file = createTestFile(source: "", path: "a/b/c_test.dart", suite: "suite");
|
|
Expect.equals("a/b/c_test", file.name);
|
|
|
|
// Multitest.
|
|
file = file.split(Path("suite/a/b/c_test_00.dart").absolute, "00", "");
|
|
Expect.equals("a/b/c_test/00", file.name);
|
|
}
|
|
|
|
void testMultitest() {
|
|
var file = createTestFile(source: "", path: "origin.dart");
|
|
Expect.isFalse(file.hasSyntaxError);
|
|
Expect.isFalse(file.hasCompileError);
|
|
Expect.isFalse(file.hasRuntimeError);
|
|
Expect.isFalse(file.hasStaticWarning);
|
|
|
|
var a = file.split(Path("a.dart").absolute, "a", "", hasSyntaxError: true);
|
|
Expect.isTrue(a.originPath.toNativePath().endsWith("origin.dart"));
|
|
Expect.isTrue(a.path.toNativePath().endsWith("a.dart"));
|
|
Expect.isTrue(a.hasSyntaxError);
|
|
Expect.isFalse(a.hasCompileError);
|
|
Expect.isFalse(a.hasRuntimeError);
|
|
Expect.isFalse(a.hasStaticWarning);
|
|
|
|
var b = file.split(Path("b.dart").absolute, "b", "", hasCompileError: true);
|
|
Expect.isTrue(b.originPath.toNativePath().endsWith("origin.dart"));
|
|
Expect.isTrue(b.path.toNativePath().endsWith("b.dart"));
|
|
Expect.isFalse(b.hasSyntaxError);
|
|
Expect.isTrue(b.hasCompileError);
|
|
Expect.isFalse(b.hasRuntimeError);
|
|
Expect.isFalse(b.hasStaticWarning);
|
|
|
|
var c = file.split(Path("c.dart").absolute, "c", "", hasRuntimeError: true);
|
|
Expect.isTrue(c.originPath.toNativePath().endsWith("origin.dart"));
|
|
Expect.isTrue(c.path.toNativePath().endsWith("c.dart"));
|
|
Expect.isFalse(c.hasSyntaxError);
|
|
Expect.isFalse(c.hasCompileError);
|
|
Expect.isTrue(c.hasRuntimeError);
|
|
Expect.isFalse(c.hasStaticWarning);
|
|
|
|
var d = file.split(Path("d.dart").absolute, "d", "", hasStaticWarning: true);
|
|
Expect.isTrue(d.originPath.toNativePath().endsWith("origin.dart"));
|
|
Expect.isTrue(d.path.toNativePath().endsWith("d.dart"));
|
|
Expect.isFalse(d.hasSyntaxError);
|
|
Expect.isFalse(d.hasCompileError);
|
|
Expect.isFalse(d.hasRuntimeError);
|
|
Expect.isTrue(d.hasStaticWarning);
|
|
}
|
|
|
|
void testShardHash() {
|
|
// Test files with paths should successfully return some kind of integer. We
|
|
// don't want to depend on the hash algorithm, so we can't really be more
|
|
// specific than that.
|
|
var testFile = createTestFile(source: "", path: "a_test.dart");
|
|
Expect.type<int>(testFile.shardHash);
|
|
|
|
// VM test files are based on a fake path.
|
|
testFile = TestFile.vmUnitTest(
|
|
"ExampleTestName",
|
|
hasCompileError: false,
|
|
hasCrash: false,
|
|
hasRuntimeError: false,
|
|
);
|
|
Expect.type<int>(testFile.shardHash);
|
|
}
|
|
|
|
void expectParseErrorExpectations(String source, List<StaticError> errors) {
|
|
var file = createTestFile(source: source);
|
|
Expect.listEquals(
|
|
errors.map((error) => error.toString()).toList(),
|
|
file.expectedErrors.map((error) => error.toString()).toList(),
|
|
);
|
|
}
|
|
|
|
void expectFormatError(String source) {
|
|
Expect.throwsFormatException(() => createTestFile(source: source));
|
|
}
|
|
|
|
void expectThrowsFormatExceptionContains(
|
|
void Function() callback,
|
|
List<String> expectedSubstrings,
|
|
) {
|
|
try {
|
|
callback();
|
|
} on FormatException catch (error) {
|
|
for (var expectedSubstring in expectedSubstrings) {
|
|
Expect.contains(expectedSubstring, error.message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
Expect.fail("Expected a FormatException.");
|
|
}
|
|
|
|
void expectParseThrows(String source) {
|
|
Expect.throws(() => createTestFile(source: source));
|
|
}
|