|  | // 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() { | 
|  | ensureRunFromSdkBinDart(); | 
|  |  | 
|  | group('create integration', defineCreateTests, timeout: longTimeout); | 
|  | } | 
|  |  | 
|  | void defineCreateTests() { | 
|  | // Create tests for each template. | 
|  | for (String templateId | 
|  | in CreateCommand.legalTemplateIds(includeDeprecated: true)) { | 
|  | test(templateId, () async { | 
|  | const projectName = 'template_project'; | 
|  | final p = project(); | 
|  | 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.runAnalyze( | 
|  | ['--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. | 
|  | var hasError = false; | 
|  | stdoutSub = process.stdout.transform(utf8.decoder).listen((e) { | 
|  | print('stdout: $e'); | 
|  | if (e.contains('[SEVERE]') || | 
|  | (isServerTemplate && e.contains('Server listening on port')) || | 
|  | (isWebTemplate && e.contains('Succeeded after'))) { | 
|  | if (e.contains('[SEVERE]')) { | 
|  | hasError = true; | 
|  | } | 
|  | stderrSub.cancel(); | 
|  | stdoutSub.cancel(); | 
|  | process.kill(); | 
|  | completer.complete(); | 
|  | } | 
|  | }); | 
|  | stderrSub = process.stderr.transform(utf8.decoder).listen((e) { | 
|  | print('stderr: $e'); | 
|  | hasError = true; | 
|  | stderrSub.cancel(); | 
|  | stdoutSub.cancel(); | 
|  | process.kill(); | 
|  | completer.complete(); | 
|  | }); | 
|  | await completer.future; | 
|  | expect(hasError, isFalse, reason: 'Command $command failed.'); | 
|  |  | 
|  | // 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 { | 
|  | final output = <String>[]; | 
|  | final errors = <String>[]; | 
|  | process.stdout.transform(utf8.decoder).listen(output.add); | 
|  | process.stderr.transform(utf8.decoder).listen(errors.add); | 
|  |  | 
|  | // If the sample should exit on its own, it should always result in | 
|  | // an exit code of 0. | 
|  | final duration = const Duration(seconds: 30); | 
|  | final exitCode = | 
|  | await process.exitCode.timeout(duration, onTimeout: () { | 
|  | print('Command $command timed out'); | 
|  | return -1; | 
|  | }); | 
|  | if (exitCode != 0) { | 
|  | print('Command $command exited with code $exitCode'); | 
|  | print('Output: \n${output.join('\n')}'); | 
|  | print('Errors: \n${errors.join('\n')}'); | 
|  | } | 
|  | expect(exitCode, 0); | 
|  | } | 
|  | print('[${i + 1} / ${runCommands.length}] Done "$command".'); | 
|  | } | 
|  | }); | 
|  | } | 
|  | } |