blob: 54d6296f2968a4c7c1a3de8cf77e894dd5513f33 [file] [log] [blame]
// Copyright (c) 2021, 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.
/// Resolves the source of a test for a given revision of the SDK.
import 'dart:async' show Future;
import 'dart:convert' show base64Decode, jsonDecode;
import 'package:http/http.dart' as http;
import 'dart:io' show HttpStatus;
final testDirectories = {
"observatory_ui": "runtime/observatory/tests/observatory_ui",
"observatory_ui_2": "runtime/observatory_2/tests/observatory_ui_2",
"pkg_tested": "third_party/pkg_tested",
"samples": "samples",
"samples_2": "samples_2",
"service": "runtime/observatory/tests/service",
"service_2": "runtime/observatory_2/tests/service_2"
};
const customTestRunnerSuites = {
'flutter_frontend':
'pkg/frontend_server/test/frontend_server_flutter_suite.dart',
};
// These names must be kept in sync with
// https://github.com/dart-lang/sdk/blob/master/pkg/front_end/test/unit_test_suites_impl.dart
const frontendUnitTestSuites = [
"fasta/expression",
"fasta/outline",
"fasta/incremental_dartino",
"fasta/messages",
"fasta/text_serialization",
"fasta/strong",
"incremental_bulk_compiler_smoke",
"incremental",
"lint",
"parser",
"parser_equivalence",
"parser_all",
"spelling_test_not_src",
"spelling_test_src",
"fasta/weak",
"fasta/textual_outline",
];
String findBaseName(String suite, Iterable<String> nameParts) {
var parts = nameParts.toList();
final regExp = (suite == 'co19' || suite == 'co19_2')
? RegExp(r"t[0-9]{2,3}$")
: RegExp(r"_test$");
for (var i = 0; i <= 2 && parts.isNotEmpty; i++) {
if (regExp.hasMatch(parts.last)) {
return parts.join('/');
} else {
parts.removeLast();
}
}
return null;
}
Future<Uri> guessFileName(
String suite, Uri testDirectory, Iterable<String> parts) async {
final baseName = findBaseName(suite, parts);
if (baseName != null) {
return testDirectory.resolve(baseName + ".dart");
} else {
return null;
}
}
bool isFrontEndUnitTestSuiteTest(String testName) {
for (final suite in frontendUnitTestSuites) {
if (testName.startsWith('pkg/front_end/test/$suite')) {
return true;
}
}
return false;
}
Future<Uri> findTestFile(
String testName, Uri root, String suite, Iterable<String> testParts) async {
if (isCo19(suite)) {
return await guessFileName(suite, root, testParts);
} else if (isExternalPackage(suite)) {
return root.resolve(testParts.join('/') + ".dart");
} else if (testDirectories.containsKey(suite)) {
var testDir = root.resolveUri(Uri.directory(testDirectories[suite]));
return await guessFileName(suite, testDir, testParts);
} else if (customTestRunnerSuites.containsKey(suite)) {
return root.resolve(customTestRunnerSuites[suite]);
} else if (suite == 'pkg') {
if (testParts.first == 'front_end' &&
isFrontEndUnitTestSuiteTest(testName)) {
// Some of the pkg/front_end tests are in unit test suites defined in a
// custom test runner.
return root.resolve('pkg/front_end/test/unit_test_suites.dart');
} else {
final fileName =
await guessFileName(suite, root.resolve('pkg/'), testParts);
return fileName;
}
} else if (testName.startsWith('tests/modular')) {
return root.resolve(testName);
} else if (suite == 'ffi_unit') {
// TODO(karlklose): find a way to lookup the location of the definition
return root.resolve('runtime/vm/compiler/ffi');
} else if (testName.startsWith('vm/cc')) {
// TODO(karlklose): find a way to lookup the location of the definition
return root.resolve('runtime/vm/');
} else if (testName.startsWith('vm/dart')) {
return await guessFileName(
suite, root.resolve('runtime/tests/vm/'), testParts);
} else {
// By default, map the path to the tests directory.
return await guessFileName(suite, root.resolve('tests/$suite/'), testParts);
}
}
bool isCo19(String suite) {
return suite == 'co19' || suite == 'co19_2';
}
bool isExternalPackage(String suite) {
return suite == 'pkg_tested';
}
Future<String> findDepsRevision(String revision, String package) async {
final url = "https://dart.googlesource.com/sdk/+/$revision/DEPS?format=TEXT";
final response = await http.get(url);
if (response.statusCode != HttpStatus.ok) {
throw Exception("Unable to download DEPS for revision '$revision'"
"at $url");
}
final body = String.fromCharCodes(base64Decode(response.body));
final match = RegExp('"${package}_rev": "(.*)",').firstMatch(body);
return match?.group(1);
}
const gerritDataHeader = ")]}'";
Future<String> getPatchsetRevision(int review, int patchset) async {
final url = 'https://dart-review.googlesource.com/'
'changes/$review/revisions/$patchset/commit';
final response = await http.get(url);
if (response.statusCode != HttpStatus.ok) {
throw Exception("Can't find revision for cl/$review/$patchset");
}
final body = response.body;
if (!body.startsWith(gerritDataHeader)) {
throw Exception('Got invalid response for cl/$review/$patchset');
}
final data = jsonDecode(body.substring(gerritDataHeader.length));
return data['commit'];
}
Future<Uri> computeTestSource(
String revision, String testName, bool useGob) async {
final splitName = testName.split('/');
var parts = splitName.skip(1);
final suite = splitName.first;
var root;
if (isCo19(suite)) {
revision = await findDepsRevision(revision, suite);
root = "https://github.com/dart-lang/co19/blob/$revision/";
} else if (isExternalPackage(suite)) {
final package = parts.first;
revision = await findDepsRevision(revision, package);
root = "https://dart.googlesource.com/$package/+/$revision/";
parts = parts.skip(1);
} else {
root = useGob
? "https://dart.googlesource.com/sdk/+/$revision/"
: "https://github.com/dart-lang/sdk/blob/$revision/";
}
return findTestFile(testName, Uri.parse(root), suite, parts);
}