blob: bf7ba0cfcb5b5ce1c202f39b7c2dee54d880ee08 [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.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dart_style/dart_style.dart';
import 'package:path/path.dart' as path;
import 'generate_stress_test.dart';
final sdkRoot = path.canonicalize(path.join(thisDirectory, '../../../'));
final tempFile = path.join(thisDirectory, 'temp.dart');
final dartDirectories = [
'runtime/tests/vm/dart',
'tests/corelib',
'tests/language',
'tests/lib',
'tests/standalone',
];
final dart2Directories = [
'runtime/tests/vm/dart_2',
'tests/corelib_2',
'tests/language_2',
'tests/lib_2',
'tests/standalone_2',
];
main(List<String> args) async {
final testFiles = await findValidTests(dart2Directories, false);
final nnbdTestFiles = await findValidTests(dartDirectories, true);
File(stressTestListJson)
.writeAsStringSync(const JsonEncoder.withIndent(' ').convert({
'nnbd': nnbdTestFiles,
'non-nnbd': testFiles,
}));
}
Future<List<String>> findValidTests(List<String> directories, bool nnbd) async {
print('Using SDK root: $sdkRoot');
final testFiles = <String>[];
final failedOrTimedOut = <String>[];
final filteredTests = <String>[];
await for (final testFile
in listTestFiles(sdkRoot, directories, filteredTests)) {
print(testFile);
final duration = await run(sdkRoot, testFile, tempFile, nnbd);
if (duration != null && duration.inSeconds < 6) {
print('-> good');
testFiles.add(testFile);
} else {
print('-> failed or timed out');
failedOrTimedOut.add(testFile);
}
}
testFiles.sort();
failedOrTimedOut.sort();
filteredTests.sort();
dumpTestList(testFiles, 'The following tests will be included:');
dumpTestList(failedOrTimedOut,
'The following tests will be excluded due to timeout or test failure:');
dumpTestList(filteredTests,
'The following tests were filtered due to using blacklisted things:');
for (int i = 0; i < testFiles.length; ++i) {
testFiles[i] = path.relative(testFiles[i], from: thisDir);
}
if (File(tempFile).existsSync()) {
File(tempFile).deleteSync();
}
return testFiles;
}
void dumpTestList(List<String> testFiles, String message) {
if (testFiles.isEmpty) return;
print(message);
for (final testFile in testFiles) {
print(' ${path.basename(testFile)}');
}
}
Stream<String> listTestFiles(String sdkRoot, List<String> directories,
List<String> filteredTests) async* {
for (final dir in directories) {
await for (final file
in Directory(path.join(sdkRoot, dir)).list(recursive: true)) {
if (file is File && file.path.endsWith('_test.dart')) {
final contents = file.readAsStringSync();
if (contents.contains(RegExp('//# .* compile-time error')) ||
contents.contains('DynamicLibrary.process') ||
contents.contains('DynamicLibrary.executable') ||
contents.contains('\npart') ||
contents.contains('mirror') ||
contents.contains('Directory.current =') ||
contents.contains('dart:isolate') ||
file.path.contains('wait_for') ||
file.path.contains('mirror') ||
file.path.contains('non_utf8')) {
filteredTests.add(file.path);
continue;
}
yield file.path;
}
}
}
}
Future<Duration?> run(
String sdkRoot, String testFile, String wrapFile, bool nnbd) async {
final env = Map<String, String>.from(Platform.environment);
env['LD_LIBRARY_PATH'] = path.join(sdkRoot, 'out/ReleaseX64');
final sw = Stopwatch()..start();
final f = File(wrapFile);
f.writeAsStringSync('''
import 'dart:isolate';
import 'dart:io' as io;
import '$testFile' as m;
wrapper(dynamic arg) {
m.main();
}
main() async {
final exit = ReceivePort();
final errors = ReceivePort();
await Isolate.spawn(wrapper, null, onExit: exit.sendPort, onError: errors.sendPort);
errors.listen((_) {
io.exit(123);
});
await exit.first;
// We delay it a bit in case the real test exit()s, in which case we
// wouldn't print the line below, which would exclude the test from
// being used in the big fuzzing test.
await Future.delayed(const Duration(milliseconds: 100));
print('Success: WORKED!');
errors.close();
}
''');
final Process process = await Process.start(Platform.executable,
<String>[nnbd ? '--sound-null-safety' : '--no-sound-null-safety', f.path],
environment: env);
final timer = Timer(const Duration(seconds: 3), () => process.kill());
bool good = false;
final stdoutF = process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((line) {
if (line.contains('Success: WORKED!')) {
good = true;
}
}, onError: (e, s) {})
.asFuture()
.catchError((e, s) {});
process.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((line) {}, onError: (e, s) {});
if (await process.exitCode != 0) return null;
await stdoutF;
f.deleteSync();
if (!good) {
return null;
}
timer.cancel();
return sw.elapsed;
}