Reland "[ CLI ] Attempt to execute samples generated by 'dart create' templates"
Adds missing `PUB_CACHE` entry to a couple of places where a new process
is spawned.
This reverts commit 7a7c36ab2f1e6af0746f477fd09b204f7463c817.
Change-Id: I198deeb29ab32ca325b4d918b36ab94609a17de1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/227560
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/dartdev/lib/src/templates/server_shelf.dart b/pkg/dartdev/lib/src/templates/server_shelf.dart
index ea5dc58..023ffea 100644
--- a/pkg/dartdev/lib/src/templates/server_shelf.dart
+++ b/pkg/dartdev/lib/src/templates/server_shelf.dart
@@ -127,7 +127,7 @@
}
Response _echoHandler(Request request) {
- final message = params(request, 'message');
+ final message = request.params['message'];
return Response.ok('$message\n');
}
diff --git a/pkg/dartdev/test/commands/create_integration_test.dart b/pkg/dartdev/test/commands/create_integration_test.dart
index 92e981a..19f93d8 100644
--- a/pkg/dartdev/test/commands/create_integration_test.dart
+++ b/pkg/dartdev/test/commands/create_integration_test.dart
@@ -2,9 +2,13 @@
// 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:dartdev/src/commands/create.dart';
+import 'package:dartdev/src/templates.dart';
+import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import '../utils.dart';
@@ -14,42 +18,134 @@
}
void defineCreateTests() {
- TestProject? p;
+ TestProject? proj;
- setUp(() => p = null);
+ setUp(() => proj = null);
- tearDown(() async => await p?.dispose());
+ tearDown(() async => await proj?.dispose());
// Create tests for each template.
for (String templateId
in CreateCommand.legalTemplateIds(includeDeprecated: true)) {
test(templateId, () async {
- p = project();
+ const projectName = 'template_project';
+ proj = project();
+ final p = proj!;
+ final templateGenerator = getGenerator(templateId)!;
- ProcessResult createResult = await p!.run([
+ ProcessResult createResult = await p.run([
'create',
'--force',
'--template',
templateId,
- 'template_project',
+ projectName,
]);
expect(createResult.exitCode, 0, reason: createResult.stderr);
// Validate that the project analyzes cleanly.
- // TODO: Should we use --fatal-infos here?
- ProcessResult analyzeResult =
- await p!.run(['analyze'], workingDir: p!.dir.path);
+ ProcessResult analyzeResult = await p.run(
+ ['analyze', '--fatal-infos', projectName],
+ workingDir: p.dir.path,
+ );
expect(analyzeResult.exitCode, 0, reason: analyzeResult.stdout);
// Validate that the code is well formatted.
- ProcessResult formatResult = await p!.run([
+ ProcessResult formatResult = await p.run([
'format',
'--output',
'none',
'--set-exit-if-changed',
- 'template_project',
+ projectName,
]);
expect(formatResult.exitCode, 0, reason: formatResult.stdout);
+
+ // Process the execution instructions provided by the template.
+ final runCommands = templateGenerator
+ .getInstallInstructions(
+ projectName,
+ scriptPath: projectName,
+ )
+ .split('\n')
+ // Remove directory change instructions.
+ .sublist(1)
+ .map((command) => command.trim())
+ .map((command) {
+ final commandParts = command.split(' ');
+ if (command.startsWith('dart ')) {
+ return commandParts.sublist(1);
+ }
+ return commandParts;
+ }).toList();
+
+ final isServerTemplate = templateGenerator.categories.contains('server');
+ final isWebTemplate = templateGenerator.categories.contains('web');
+ final workingDir = path.join(p.dirPath, projectName);
+
+ // Execute the templates run instructions.
+ for (int i = 0; i < runCommands.length; ++i) {
+ // The last command is always the command to execute the code generated
+ // by the template.
+ final isLastCommand = i == runCommands.length - 1;
+ final command = runCommands[i];
+ Process process;
+
+ if (isLastCommand && isWebTemplate) {
+ // The web template uses `webdev` to execute, not `dart`, so don't
+ // run the test through the project utility method.
+ process = await Process.start(
+ path.join(
+ p.pubCacheBinPath,
+ Platform.isWindows ? '${command.first}.bat' : command.first,
+ ),
+ command.sublist(1),
+ workingDirectory: workingDir,
+ environment: {
+ 'PUB_CACHE': p.pubCachePath,
+ 'PATH': path.dirname(Platform.resolvedExecutable) +
+ (Platform.isWindows ? ';' : ':') +
+ Platform.environment['PATH']!,
+ });
+ } else {
+ process = await p.start(
+ command,
+ workingDir: workingDir,
+ );
+ }
+
+ if (isLastCommand && (isServerTemplate || isWebTemplate)) {
+ final completer = Completer<void>();
+ late StreamSubscription stdoutSub;
+ late StreamSubscription stderrSub;
+ // Listen for well-known output from specific templates to determine
+ // if they've executed correctly. These templates won't exit on their
+ // own, so we'll need to terminate the process once we've verified it
+ // runs correctly.
+ stdoutSub = process.stdout.transform(utf8.decoder).listen((e) {
+ print('stdout: $e');
+ if ((isServerTemplate && e.contains('Server listening on port')) ||
+ (isWebTemplate && e.contains('Succeeded after'))) {
+ stderrSub.cancel();
+ stdoutSub.cancel();
+ process.kill();
+ completer.complete();
+ }
+ });
+ stderrSub = process.stderr
+ .transform(utf8.decoder)
+ .listen((e) => print('stderr: $e'));
+ await completer.future;
+
+ // Since we had to terminate the process manually, we aren't certain
+ // as to what the exit code will be on all platforms (should be -15
+ // for POSIX systems), so we'll just wait for the process to exit
+ // here.
+ await process.exitCode;
+ } else {
+ // If the sample should exit on its own, it should always result in
+ // an exit code of 0.
+ expect(await process.exitCode, 0);
+ }
+ }
});
}
}
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index 145d519..996b98b 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -45,6 +45,10 @@
String get dirPath => dir.path;
+ String get pubCachePath => path.join(dirPath, 'pub_cache');
+
+ String get pubCacheBinPath => path.join(pubCachePath, 'bin');
+
String get mainPath => path.join(dirPath, relativeFilePath);
final String name;
@@ -129,7 +133,10 @@
...arguments,
],
workingDirectory: workingDir ?? dir.path,
- environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'});
+ environment: {
+ if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true',
+ 'PUB_CACHE': pubCachePath,
+ });
final proc = _process!;
final stdoutContents = proc.stdout.transform(utf8.decoder).join();
final stderrContents = proc.stderr.transform(utf8.decoder).join();
@@ -153,7 +160,10 @@
...arguments,
],
workingDirectory: workingDir ?? dir.path,
- environment: {if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true'})
+ environment: {
+ if (logAnalytics) '_DARTDEV_LOG_ANALYTICS': 'true',
+ 'PUB_CACHE': pubCachePath,
+ })
..then((p) => _process = p);
}