Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 33 additions & 30 deletions lib/src/tasks/gen_test_runner/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,16 @@ import 'dart:io';
import 'package:dart_dev/src/tasks/gen_test_runner/config.dart';
import 'package:dart_dev/src/tasks/task.dart';

Future<GenTestRunnerTask> genTestRunner(TestRunnerConfig currentConfig) async {
Future<GenTestRunnerTask> genTestRunner(TestRunnerConfig currentConfig,
{List<String> filesToInclude}) async {
var taskTitle = 'gen-test-runner';
var args = ['-d ${currentConfig.directory}', '-e ${currentConfig.env}'];
currentConfig.genHtml ? args.add('--genHtml') : args.add('--no-genHtml');

GenTestRunnerTask task =
new GenTestRunnerTask('$taskTitle ${args.join(' ')}');

var currentDirectory = currentConfig.directory;
if (!currentDirectory.endsWith('/')) {
currentDirectory += '/';
}

File generatedRunner =
new File('${currentDirectory}${currentConfig.filename}.dart');
File generatedRunner = new File(currentConfig.path);

List<String> existingLines;
if (currentConfig.check) {
Expand All @@ -46,29 +41,35 @@ Future<GenTestRunnerTask> genTestRunner(TestRunnerConfig currentConfig) async {
}

List<String> runnerLines = [];

Directory testDirectory = new Directory(currentDirectory);
List<File> testFiles = [];
final allFiles = testDirectory
.listSync(recursive: true, followLinks: false)
.where((entity) => entity is File)
.toList();
allFiles.sort((left, right) => left.path.compareTo(right.path));
allFiles
.where(
(entity) => !entity.path.endsWith('${currentConfig.filename}.dart'))
.forEach((entity) {
if (entity.path.endsWith('_test.dart')) {
testFiles.add(entity);
task.testFiles.add(entity.path);
} else if (entity.path.endsWith('.dart')) {
task.excludedFiles.add(entity.path);

if (filesToInclude != null) {
for (final filePath in filesToInclude) {
testFiles.add(new File(filePath));
}
});
} else {
Directory testDirectory = new Directory(currentConfig.directory);
final allFiles = testDirectory
.listSync(recursive: true, followLinks: false)
.where((entity) => entity is File)
.toList();
allFiles.sort((left, right) => left.path.compareTo(right.path));
allFiles
.where(
(entity) => !entity.path.endsWith('${currentConfig.filename}.dart'))
.forEach((entity) {
if (entity.path.endsWith('_test.dart')) {
testFiles.add(entity);
task.testFiles.add(entity.path);
} else if (entity.path.endsWith('.dart')) {
task.excludedFiles.add(entity.path);
}
});
}

if (currentConfig.genHtml && !currentConfig.check) {
await testHtmlFileGenerator(
currentDirectory, currentConfig.filename, currentConfig.htmlHeaders);
await testHtmlFileGenerator(currentConfig.directory, currentConfig.filename,
currentConfig.htmlHeaders);
}

if (currentConfig.env == Environment.browser) {
Expand All @@ -79,14 +80,15 @@ Future<GenTestRunnerTask> genTestRunner(TestRunnerConfig currentConfig) async {
runnerLines.add('@TestOn(\'browser || vm\')');
}
runnerLines.add(
'library ${currentDirectory.replaceAll('/', '.')}${currentConfig.filename};');
'library ${currentConfig.normalizedDirectory.replaceAll('/', '.')}${currentConfig.filename};');
runnerLines.add('');

runnerLines.add('// Generated by `pub run dart_dev ${task.generateCommand}`');
runnerLines.add('');

testFiles.forEach((File file) {
var testPath = file.path.replaceFirst(currentDirectory, '');
var testPath =
file.path.replaceFirst(currentConfig.normalizedDirectory, '');
runnerLines.add(
'import \'${'./' + testPath}\' as ${testPath.replaceAll('/', '_').substring(0, testPath.length - 5)};');
});
Expand All @@ -109,7 +111,8 @@ Future<GenTestRunnerTask> genTestRunner(TestRunnerConfig currentConfig) async {
}

testFiles.forEach((File file) {
var testPath = file.path.replaceFirst(currentDirectory, '');
var testPath =
file.path.replaceFirst(currentConfig.normalizedDirectory, '');
runnerLines.add(
' ${testPath.replaceAll('/', '_').substring(0, testPath.length - 5)}.main();');
});
Expand Down
4 changes: 4 additions & 0 deletions lib/src/tasks/gen_test_runner/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ class TestRunnerConfig {
List<String> dartHeaders = defaultDartHeaders;
List<String> preTestCommands = defaultPreTestCommands;
String directory = defaultDirectory;
String get normalizedDirectory =>
directory + (!directory.endsWith('/') ? '/' : '');
Environment env = defaultEnv;
String filename = defaultFilename;
bool genHtml = defaultGenHtml;
List<String> htmlHeaders = defaultHtmlHeaders;

String get path => '$normalizedDirectory$filename.dart';

TestRunnerConfig(
{List<String> this.dartHeaders: defaultDartHeaders,
List<String> this.preTestCommands: defaultPreTestCommands,
Expand Down
64 changes: 59 additions & 5 deletions lib/src/tasks/test/cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@
library dart_dev.src.tasks.test.cli;

import 'dart:async';
import 'dart:io';

import 'package:args/args.dart';
import 'package:dart_dev/dart_dev.dart';
import 'package:dart_dev/src/tasks/gen_test_runner/api.dart';
import 'package:dart_dev/src/tasks/gen_test_runner/cli.dart';
import 'package:dart_dev/src/tasks/gen_test_runner/config.dart';

import 'package:dart_dev/util.dart'
show hasImmediateDependency, isPortBound, reporter, TaskProcess;
Expand All @@ -39,13 +44,21 @@ class TestCli extends TaskCli {
..addFlag('functional',
defaultsTo: defaultFunctional,
help: 'Includes the functional test suite.')
..addFlag('delete-conflicting-outputs',
help: 'Deletes conflicting outputs during the build', negatable: false)
..addFlag('disable-serve-std-out',
defaultsTo: defaultDisableServeStdOut,
help: 'Disables standard output for pub serve task.')
..addOption('concurrency',
abbr: 'j',
defaultsTo: '$defaultConcurrency',
help: 'The number of concurrent test suites run.')
..addFlag(_hackFastBuilds,
help:
'Improves iterative build times by re-writing the generated test runners at runtime.\n'
'Use this flag and specify the test file you want to run.\nThis flag '
'is a no-op if no test files are specified.',
defaultsTo: false)
..addFlag('pub-serve',
negatable: true,
defaultsTo: defaultPubServe,
Expand All @@ -55,8 +68,6 @@ class TestCli extends TaskCli {
'Implies --concurrency=1 and --timeout=none.\n'
'Currently only supported for browser tests.',
negatable: false)
..addFlag('delete-conflicting-outputs',
help: 'Deletes conflicting outputs during the build', negatable: false)
..addFlag('release',
abbr: 'r',
negatable: true,
Expand Down Expand Up @@ -98,8 +109,8 @@ class TestCli extends TaskCli {
}

final testArgs = <String>[];
List<String> tests = [];
List<String> buildArgs = [];
final tests = <String>[];
final buildArgs = <String>[];

if (!color) {
testArgs.add('--no-color');
Expand Down Expand Up @@ -161,10 +172,44 @@ class TestCli extends TaskCli {
'files/directories');
}

final mapRunnerToContents = <File /*generated runner file*/, String /* contents */>{};
// Build the list of tests to run.
if (individualTestsSpecified) {
// Individual tests explicitly passed in should override the test suites.
tests.addAll(parsedArgs.rest);
if (dartMajorVersion == 2 && parsedArgs[_hackFastBuilds]) {
reporter.warning(
'WARNING: You\'re using `${_hackFastBuilds}`. This will re-write the generated test runners in your repo.\n'
'The test task will attempt to restore your generated runners after completion, but you may '
'have to re-run `pub run dart_dev gen-test-runner` and `pub run dart_dev format` if your runners have changed.\n\n');
final mapConfigToTestFiles = <TestRunnerConfig,
Set<String> /* tests to include in runner */>{};
// Construct mapping from config to tests which should be ran in that config
final copyOfConfigs = new List.from(config.genTestRunner.configs);
for (final _config in copyOfConfigs) {
for (final testFilePath in parsedArgs.rest) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be some issue if someone also passed the --release flag instead of or at the end of end of a list of files?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.rest includes anything which wasn't specified as a top level option/flag, so we should be good here.

if (testFilePath.contains(_config.directory)) {
mapConfigToTestFiles.putIfAbsent(_config, () => new Set.from([testFilePath]));
mapConfigToTestFiles[_config].add(testFilePath);
}
}
}

for (final _config in mapConfigToTestFiles.keys) {
copyOfConfigs.remove(_config);
final runnerFile = new File(_config.path);
mapRunnerToContents.putIfAbsent(runnerFile, () => runnerFile.readAsStringSync());
await genTestRunner(_config,
filesToInclude: mapConfigToTestFiles[_config].toList());
tests.add(_config.path);
}

// Empty all other unused generated runners
for (final _config in copyOfConfigs) {
await genTestRunner(_config, filesToInclude: []);
}
} else {
tests.addAll(parsedArgs.rest);
}
} else {
// Unit and/or integration suites should only run if individual tests
// were not specified.
Expand Down Expand Up @@ -285,8 +330,17 @@ A pub serve instance will not be started.''');
await runAll(config.test.after = config.test.afterFunctionalTests);
}

if (parsedArgs[_hackFastBuilds]) {
// Regenerate all runners:
mapRunnerToContents.forEach((file, originalContents) {
file.writeAsStringSync(originalContents);
});
}

return task.successful
? new CliResult.success(task.testSummary)
: new CliResult.fail(task.testSummary);
}

static String _hackFastBuilds = 'hack-fast-builds';
}