| // Copyright (c) 2018, 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:io'; | |
| import 'dart:isolate'; | |
| import 'package:dwds/expression_compiler.dart'; | |
| import 'package:dwds_test_common/test_sdk_configuration.dart'; | |
| import 'package:dwds_test_common/test_sdk_layout.dart'; | |
| import 'package:path/path.dart' as p; | |
| import 'package:test/test.dart'; | |
| import 'package:test_process/test_process.dart'; | |
| final _webdevBin = p.absolute(p.join('bin', 'webdev.dart')); | |
| class TestRunner { | |
| late TestSdkConfigurationProvider sdkConfigurationProvider; | |
| late TestSdkLayout sdkLayout; | |
| final bool canaryFeatures; | |
| final ModuleFormat ddcModuleFormat; | |
| final bool webHotReload; | |
| TestRunner({ | |
| this.canaryFeatures = false, | |
| this.ddcModuleFormat = ModuleFormat.amd, | |
| this.webHotReload = false, | |
| }); | |
| Future<void> setUpAll({bool verbose = false}) async { | |
| // Generate missing SDK assets if needed. | |
| sdkConfigurationProvider = TestSdkConfigurationProvider( | |
| verbose: verbose, | |
| canaryFeatures: canaryFeatures, | |
| ddcModuleFormat: ddcModuleFormat, | |
| ); | |
| sdkLayout = sdkConfigurationProvider.sdkLayout; | |
| try { | |
| // Make sure configuration was created correctly. | |
| final configuration = await sdkConfigurationProvider.configuration; | |
| configuration.validate(); | |
| } catch (_) { | |
| tearDownAll(); | |
| rethrow; | |
| } | |
| } | |
| void tearDownAll() { | |
| sdkConfigurationProvider.dispose(); | |
| } | |
| /// Runs 'webdev' with [args] in [workingDirectory], injecting additional | |
| /// flags if necessary. | |
| /// | |
| /// Setting [raw] passes [args] without additional processing. | |
| Future<TestProcess> runWebDev( | |
| List<String> args, { | |
| String? workingDirectory, | |
| bool raw = false, | |
| }) async { | |
| final fullArgs = [_webdevBin, ...args]; | |
| // 'help' doesn't accept additional args. | |
| final isHelp = | |
| args.contains('help') || args.contains('--help') || args.contains('-h'); | |
| if (!raw && !isHelp) { | |
| if (canaryFeatures) { | |
| final dashDashIndex = fullArgs.indexOf('--'); | |
| if (dashDashIndex != -1) { | |
| fullArgs.insertAll(dashDashIndex, ['--canary']); | |
| } else { | |
| fullArgs.add('--canary'); | |
| } | |
| } | |
| if (webHotReload) { | |
| final dashDashIndex = fullArgs.indexOf('--'); | |
| if (dashDashIndex != -1) { | |
| fullArgs.insertAll(dashDashIndex, ['--web-hot-reload']); | |
| } else { | |
| fullArgs.add('--web-hot-reload'); | |
| } | |
| } | |
| final moduleFormatStr = ddcModuleFormat == ModuleFormat.ddc | |
| ? 'ddc' | |
| : 'amd'; | |
| final dashDashIndex = fullArgs.indexOf('--'); | |
| final extraArgs = ['--module-format', moduleFormatStr]; | |
| if (dashDashIndex != -1) { | |
| fullArgs.insertAll(dashDashIndex, extraArgs); | |
| } else { | |
| fullArgs.addAll(extraArgs); | |
| } | |
| } | |
| return TestProcess.start( | |
| sdkLayout.dartPath, | |
| fullArgs, | |
| workingDirectory: workingDirectory, | |
| ); | |
| } | |
| /// Copies [source] to [destination], skipping '.dart_tool' to ignore built | |
| /// output from previous runs. | |
| void _copyCleanDirectory(Directory source, Directory destination) { | |
| destination.createSync(recursive: true); | |
| for (final entity in source.listSync(recursive: false)) { | |
| if (entity is Directory) { | |
| if (p.basename(entity.path) == '.dart_tool') continue; | |
| final newDirectory = Directory( | |
| p.join(destination.absolute.path, p.basename(entity.path)), | |
| ); | |
| newDirectory.createSync(recursive: true); | |
| _copyCleanDirectory(entity.absolute, newDirectory); | |
| } else if (entity is File) { | |
| entity.copySync(p.join(destination.path, p.basename(entity.path))); | |
| } | |
| } | |
| } | |
| Future<String> prepareWorkspace() async { | |
| final originalDirectory = _webdevSmokeFixturePath(); | |
| final tempDir = Directory.systemTemp.createTempSync('webdev_smoke_'); | |
| final exampleDirectory = tempDir.path; | |
| _copyCleanDirectory(Directory(originalDirectory), tempDir); | |
| final process = await TestProcess.start( | |
| sdkLayout.dartPath, | |
| ['pub', 'upgrade'], | |
| workingDirectory: exampleDirectory, | |
| environment: getPubEnvironment(), | |
| ); | |
| await process.shouldExit(0); | |
| return exampleDirectory; | |
| } | |
| } | |
| Future checkProcessStdout(TestProcess process, List items) async { | |
| for (var item in items) { | |
| if (item is! Matcher) { | |
| item = contains(item); | |
| } | |
| await expectLater(process.stdout, emitsThrough(item)); | |
| } | |
| } | |
| /// Returns an environment map that includes `PUB_ENVIRONMENT`. | |
| /// | |
| /// Maintains any existing values for this environment var. | |
| /// Adds a new value that flags this is a bot/test and not human usage. | |
| Map<String, String> getPubEnvironment() { | |
| final pubEnvironmentKey = 'PUB_ENVIRONMENT'; | |
| var pubEnvironment = Platform.environment[pubEnvironmentKey] ?? ''; | |
| if (pubEnvironment.isNotEmpty) { | |
| pubEnvironment = '$pubEnvironment;'; | |
| } | |
| pubEnvironment = '${pubEnvironment}bot.pkg.webdev.test'; | |
| final environment = {'PUB_ENVIRONMENT': pubEnvironment}; | |
| return environment; | |
| } | |
| /// The path to the `_webdev_smoke` fixture in `package:dwds_test_common`. | |
| String _webdevSmokeFixturePath() { | |
| final utilitiesUri = Isolate.resolvePackageUriSync( | |
| Uri.parse('package:dwds_test_common/utilities.dart'), | |
| ); | |
| if (utilitiesUri == null) { | |
| throw StateError('Unable to resolve package:dwds_test_common.'); | |
| } | |
| // Strip `lib/utilities.dart`. | |
| final packageRoot = p.dirname(p.dirname(utilitiesUri.toFilePath())); | |
| return p.join(packageRoot, 'fixtures', '_webdev_smoke'); | |
| } |