blob: fe8b2036316d516fc289e9af7e1a5f2f708688ba [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: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';
void main() {
group('create integration', defineCreateTests, timeout: longTimeout);
}
void defineCreateTests() {
TestProject? proj;
setUp(() => proj = null);
tearDown(() async => await proj?.dispose());
// Create tests for each template.
for (String templateId
in CreateCommand.legalTemplateIds(includeDeprecated: true)) {
test(templateId, () async {
const projectName = 'template_project';
proj = project();
final p = proj!;
final templateGenerator = getGenerator(templateId)!;
print('$templateId: creating template');
ProcessResult createResult = await p.run([
'create',
'--force',
'--template',
templateId,
projectName,
]);
expect(createResult.exitCode, 0, reason: createResult.stderr);
// Validate that the project analyzes cleanly.
print('$templateId: analyzing generated project');
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.
print('$templateId: checking formatting');
ProcessResult formatResult = await p.run([
'format',
'--output',
'none',
'--set-exit-if-changed',
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();
print('$templateId: running the following commands:');
for (final command in runCommands) {
print(' $command');
}
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;
print('[${i + 1} / ${runCommands.length}] Running "$command"...');
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),
'web:0', // Allow for binding to a random available port.
],
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);
}
print('[${i + 1} / ${runCommands.length}] Done "$command".');
}
});
}
}