blob: d9e712800088e45cf5f2fa51d5e4768a3275c7d5 [file] [log] [blame]
// Copyright (c) 2012, 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.
library test_configurations;
import "dart:async";
import 'dart:io';
import "dart:math" as math;
import 'android.dart';
import "browser_controller.dart";
import "co19_test_config.dart";
import "http_server.dart";
import "path.dart";
import "test_progress.dart";
import "test_runner.dart";
import "test_suite.dart";
import "utils.dart";
import "vm_test_config.dart";
/**
* The directories that contain test suites which follow the conventions
* required by [StandardTestSuite]'s forDirectory constructor.
* New test suites should follow this convention because it makes it much
* simpler to add them to test.dart. Existing test suites should be
* moved to here, if possible.
*/
final TEST_SUITE_DIRECTORIES = [
new Path('pkg'),
new Path('third_party/pkg_tested'),
new Path('runtime/tests/vm'),
new Path('runtime/observatory/tests/service'),
new Path('runtime/observatory/tests/observatory_ui'),
new Path('samples'),
new Path('samples-dev'),
new Path('tests/benchmark_smoke'),
new Path('tests/chrome'),
new Path('tests/compiler/dart2js'),
new Path('tests/compiler/dart2js_extra'),
new Path('tests/compiler/dart2js_native'),
new Path('tests/corelib'),
new Path('tests/html'),
new Path('tests/isolate'),
new Path('tests/kernel'),
new Path('tests/language'),
new Path('tests/lib'),
new Path('tests/standalone'),
new Path('tests/utils'),
new Path('utils/tests/css'),
new Path('utils/tests/peg'),
];
Future testConfigurations(List<Map> configurations) async {
var startTime = new DateTime.now();
// Extract global options from first configuration.
var firstConf = configurations[0];
var maxProcesses = firstConf['tasks'];
var progressIndicator = firstConf['progress'];
BuildbotProgressIndicator.stepName = firstConf['step_name'];
var verbose = firstConf['verbose'];
var printTiming = firstConf['time'];
var listTests = firstConf['list'];
var reportInJson = firstConf['report_in_json'];
var recordingPath = firstConf['record_to_file'];
var recordingOutputPath = firstConf['replay_from_file'];
Browser.resetBrowserConfiguration = firstConf['reset_browser_configuration'];
if (recordingPath != null && recordingOutputPath != null) {
print("Fatal: Can't have the '--record_to_file' and '--replay_from_file'"
"at the same time. Exiting ...");
exit(1);
}
if (!firstConf['append_logs']) {
var files = [
new File(TestUtils.flakyFileName()),
new File(TestUtils.testOutcomeFileName())
];
for (var file in files) {
if (file.existsSync()) {
file.deleteSync();
}
}
}
DebugLogger.init(
firstConf['write_debug_log'] ? TestUtils.debugLogfile() : null,
append: firstConf['append_logs']);
// Print the configurations being run by this execution of
// test.dart. However, don't do it if the silent progress indicator
// is used. This is only needed because of the junit tests.
if (progressIndicator != 'silent') {
List output_words = configurations.length > 1
? ['Test configurations:']
: ['Test configuration:'];
for (Map conf in configurations) {
List settings = ['compiler', 'runtime', 'mode', 'arch']
.map((name) => conf[name])
.toList();
if (conf['checked']) settings.add('checked');
if (conf['strong']) settings.add('strong');
if (conf['noopt']) settings.add('noopt');
output_words.add(settings.join('_'));
}
print(output_words.join(' '));
}
var runningBrowserTests = configurations.any((config) {
return TestUtils.isBrowserRuntime(config['runtime']);
});
List<Future> serverFutures = [];
var testSuites = new List<TestSuite>();
var maxBrowserProcesses = maxProcesses;
if (configurations.length > 1 &&
(configurations[0]['test_server_port'] != 0 ||
configurations[0]['test_server_cross_origin_port'] != 0)) {
print("If the http server ports are specified, only one configuration"
" may be run at a time");
exit(1);
}
for (var conf in configurations) {
Map<String, RegExp> selectors = conf['selectors'];
var useContentSecurityPolicy = conf['csp'];
if (!listTests && runningBrowserTests) {
// Start global http servers that serve the entire dart repo.
// The http server is available on window.location.port, and a second
// server for cross-domain tests can be found by calling
// getCrossOriginPortNumber().
var servers = new TestingServers(
TestUtils.buildDir(conf),
useContentSecurityPolicy,
conf['runtime'],
null,
conf['package_root'],
conf['packages']);
serverFutures.add(servers.startServers(conf['local_ip'],
port: conf['test_server_port'],
crossOriginPort: conf['test_server_cross_origin_port']));
conf['_servers_'] = servers;
if (verbose) {
serverFutures.last.then((_) {
var commandline = servers.httpServerCommandline();
print('Started HttpServers: $commandline');
});
}
}
if (conf['runtime'].startsWith('ie')) {
// NOTE: We've experienced random timeouts of tests on ie9/ie10. The
// underlying issue has not been determined yet. Our current hypothesis
// is that windows does not handle the IE processes independently.
// If we have more than one browser and kill a browser we are seeing
// issues with starting up a new browser just after killing the hanging
// browser.
maxBrowserProcesses = 1;
} else if (conf['runtime'].startsWith('safari')) {
// Safari does not allow us to run from a fresh profile, so we can only
// use one browser. Additionally, you can not start two simulators
// for mobile safari simultainiously.
maxBrowserProcesses = 1;
} else if (conf['runtime'] == 'chrome' &&
Platform.operatingSystem == 'macos') {
// Chrome on mac results in random timeouts.
// Issue: https://github.com/dart-lang/sdk/issues/23891
// This change does not fix the problem.
maxBrowserProcesses = math.max(1, maxBrowserProcesses ~/ 2);
} else if (conf['runtime'] != 'drt') {
// Even on machines with more than 16 processors, don't open more
// than 15 browser instances, to avoid overloading the machine.
// This is especially important when running locally on powerful
// desktops.
maxBrowserProcesses = math.min(maxBrowserProcesses, 15);
}
// If we specifically pass in a suite only run that.
if (conf['suite_dir'] != null) {
var suite_path = new Path(conf['suite_dir']);
testSuites.add(new PKGTestSuite(conf, suite_path));
} else {
for (final testSuiteDir in TEST_SUITE_DIRECTORIES) {
final name = testSuiteDir.filename;
if (selectors.containsKey(name)) {
testSuites
.add(new StandardTestSuite.forDirectory(conf, testSuiteDir));
}
}
for (String key in selectors.keys) {
if (key == 'co19') {
testSuites.add(new Co19TestSuite(conf));
} else if (conf['compiler'] == 'none' &&
conf['runtime'] == 'vm' &&
key == 'vm') {
// vm tests contain both cc tests (added here) and dart tests (added
// in [TEST_SUITE_DIRECTORIES]).
testSuites.add(new VMTestSuite(conf));
} else if (conf['analyzer']) {
if (key == 'analyze_library') {
testSuites.add(new AnalyzeLibraryTestSuite(conf));
}
} else if (conf['compiler'] == 'none' &&
conf['runtime'] == 'vm' &&
key == 'pkgbuild') {
if (!conf['use_repository_packages'] &&
!conf['use_public_packages']) {
print("You need to use either --use-repository-packages or "
"--use-public-packages with the pkgbuild test suite!");
exit(1);
}
if (!conf['use_sdk']) {
print("Running the 'pkgbuild' test suite requires "
"passing the '--use-sdk' to test.py");
exit(1);
}
testSuites.add(
new PkgBuildTestSuite(conf, 'pkgbuild', 'pkg/pkgbuild.status'));
}
}
}
}
void allTestsFinished() {
for (var conf in configurations) {
if (conf.containsKey('_servers_')) {
conf['_servers_'].stopServers();
}
}
DebugLogger.close();
TestUtils.deleteTempSnapshotDirectory(configurations[0]);
}
var eventListener = [];
// We don't print progress if we list tests.
if (progressIndicator != 'silent' && !listTests) {
var printFailures = true;
var formatter = new Formatter();
if (progressIndicator == 'color') {
progressIndicator = 'compact';
formatter = new ColorFormatter();
}
if (progressIndicator == 'diff') {
progressIndicator = 'compact';
formatter = new ColorFormatter();
printFailures = false;
eventListener.add(new StatusFileUpdatePrinter());
}
eventListener.add(new SummaryPrinter());
eventListener.add(new FlakyLogWriter());
if (printFailures) {
// The buildbot has it's own failure summary since it needs to wrap it
// into '@@@'-annotated sections.
var printFailureSummary = progressIndicator != 'buildbot';
eventListener.add(new TestFailurePrinter(printFailureSummary, formatter));
}
eventListener.add(
progressIndicatorFromName(progressIndicator, startTime, formatter));
if (printTiming) {
eventListener.add(new TimingPrinter(startTime));
}
eventListener.add(new SkippedCompilationsPrinter());
eventListener.add(new LeftOverTempDirPrinter());
}
if (firstConf['write_test_outcome_log']) {
eventListener.add(new TestOutcomeLogWriter());
}
if (firstConf['copy_coredumps']) {
eventListener.add(new UnexpectedCrashDumpArchiver());
}
// The only progress indicator when listing tests should be the
// the summary printer.
if (listTests) {
eventListener.add(new SummaryPrinter(jsonOnly: reportInJson));
} else {
eventListener.add(new ExitCodeSetter());
eventListener.add(new IgnoredTestMonitor());
}
// If any of the configurations need to access android devices we'll first
// make a pool of all available adb devices.
AdbDevicePool adbDevicePool;
bool needsAdbDevicePool = configurations.any((Map conf) {
return conf['runtime'] == 'dart_precompiled' &&
conf['system'] == 'android';
});
if (needsAdbDevicePool) {
adbDevicePool = await AdbDevicePool.create();
}
// Start all the HTTP servers required before starting the process queue.
if (!serverFutures.isEmpty) {
await Future.wait(serverFutures);
}
// [firstConf] is needed here, since the ProcessQueue needs to know the
// settings of 'noBatch' and 'local_ip'
new ProcessQueue(
firstConf,
maxProcesses,
maxBrowserProcesses,
startTime,
testSuites,
eventListener,
allTestsFinished,
verbose,
recordingPath,
recordingOutputPath,
adbDevicePool);
}