| // 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. |
| |
| library dartdoc.model_test; |
| |
| import 'dart:io'; |
| |
| import 'package:dartdoc/src/dartdoc_options.dart'; |
| import 'package:dartdoc/src/package_meta.dart'; |
| import 'package:dartdoc/src/tool_definition.dart'; |
| import 'package:dartdoc/src/tool_runner.dart'; |
| import 'package:path/path.dart' as path; |
| import 'package:test/test.dart'; |
| import 'package:yaml/yaml.dart'; |
| |
| final Directory _testPackageDir = Directory('testing/test_package'); |
| final Directory _toolExecutableDir = Directory('testing/tool_executables'); |
| |
| void main() { |
| ToolConfiguration toolMap; |
| late Directory tempDir; |
| late File setupFile; |
| |
| late ToolRunner runner; |
| late ToolErrorCallback errorCallback; |
| final errors = <String>[]; |
| |
| setUpAll(() async { |
| ProcessResult? result; |
| tempDir = Directory.systemTemp.createTempSync('tool_runner_test_'); |
| var snapshotFile = path.join(tempDir.path, 'drill.snapshot'); |
| try { |
| result = Process.runSync( |
| Platform.resolvedExecutable, |
| [ |
| '--snapshot=$snapshotFile', |
| '--snapshot-kind=app-jit', |
| 'bin/drill.dart' |
| ], |
| workingDirectory: pubPackageMetaProvider.resourceProvider.pathContext |
| .absolute(_testPackageDir.path)); |
| } on ProcessException catch (exception) { |
| stderr.writeln('Unable to make snapshot of tool: $exception'); |
| expect(result?.exitCode, equals(0)); |
| } |
| if (result != null && result.exitCode != 0) { |
| stdout.writeln(result.stdout); |
| stderr.writeln(result.stderr); |
| } |
| expect(result?.exitCode, equals(0)); |
| setupFile = File(path.join(tempDir.path, 'setup.stamp')); |
| var nonDartName = Platform.isWindows ? 'non_dart.bat' : 'non_dart.sh'; |
| var nonDartExecutable = |
| path.join(_toolExecutableDir.absolute.path, nonDartName); |
| // Have to replace backslashes on Windows with double-backslashes, to |
| // escape them for YAML parser. |
| var yamlMap = ''' |
| drill: |
| command: ["bin/drill.dart"] |
| description: "Puts holes in things." |
| compile_args: ["--no-sound-null-safety"] |
| snapshot_drill: |
| command: ["${snapshotFile.replaceAll(r'\', r'\\')}"] |
| description: "Puts holes in things, but faster." |
| setup_drill: |
| command: ["bin/drill.dart"] |
| setup_command: ["bin/setup.dart", "${setupFile.absolute.path.replaceAll(r'\', r'\\')}"] |
| description: "Puts holes in things, with setup." |
| non_dart: |
| command: ["${nonDartExecutable.replaceAll(r'\', r'\\')}"] |
| description: "A non-dart tool" |
| echo: |
| macos: ['/bin/sh', '-c', 'echo'] |
| linux: ['/bin/sh', '-c', 'echo'] |
| windows: ['C:\\Windows\\System32\\cmd.exe', '/c', 'echo'] |
| description: 'Works on everything' |
| '''; |
| toolMap = ToolConfiguration.fromYamlMap( |
| loadYaml(yamlMap) as YamlMap, |
| pubPackageMetaProvider.resourceProvider.pathContext |
| .absolute(_testPackageDir.path), |
| pubPackageMetaProvider.resourceProvider); |
| // This shouldn't really happen, but if you didn't load the config from a |
| // yaml map (which would fail on a missing executable), or a file is deleted |
| // during execution,it might, so we test it. |
| toolMap.tools.addAll({ |
| 'missing': ToolDefinition(['/a/missing/executable'], [], 'missing'), |
| }); |
| |
| runner = ToolRunner(toolMap); |
| errorCallback = errors.add; |
| }); |
| |
| tearDownAll(() { |
| tempDir.deleteSync(recursive: true); |
| SnapshotCache.instanceFor(pubPackageMetaProvider.resourceProvider) |
| .dispose(); |
| }); |
| |
| group('ToolRunner', () { |
| setUp(errors.clear); |
| // This test must come first, to verify that the first run creates |
| // a snapshot. |
| test('Tool definition includes compile arguments.', () async { |
| var definition = |
| runner.toolConfiguration.tools['drill'] as DartToolDefinition; |
| expect(definition.compileArgs, equals(['--no-sound-null-safety'])); |
| }); |
| test('can invoke a Dart tool, and second run is a snapshot.', () async { |
| var result = await runner.run( |
| ['drill', r'--file=$INPUT'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isEmpty); |
| expect(result, contains('--file=<INPUT_FILE>')); |
| expect(result, contains('## `TEST INPUT`')); |
| expect(result, contains('Script location is in dartdoc tree.')); |
| // Compile args shouldn't be in the args passed to the tool. |
| expect(result, isNot(contains('--no-sound-null-safety'))); |
| expect(setupFile.existsSync(), isFalse); |
| result = await runner.run( |
| ['drill', r'--file=$INPUT'], |
| content: 'TEST INPUT 2', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isEmpty); |
| expect(result, contains('--file=<INPUT_FILE>')); |
| expect(result, contains('## `TEST INPUT 2`')); |
| expect(result, contains('Script location is in snapshot cache.')); |
| expect(setupFile.existsSync(), isFalse); |
| }); |
| test('can invoke a Dart tool', () async { |
| var result = await runner.run( |
| ['drill', r'--file=$INPUT'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isEmpty); |
| expect(result, contains('Script location is in snapshot cache.')); |
| expect(result, contains('--file=<INPUT_FILE>')); |
| expect(result, contains('## `TEST INPUT`')); |
| expect(setupFile.existsSync(), isFalse); |
| }); |
| test('can invoke a non-Dart tool', () async { |
| var result = await runner.run( |
| ['non_dart', '--version'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isEmpty); |
| expect(result, startsWith('this is not dart')); |
| }); |
| test('can invoke a pre-snapshotted tool', () async { |
| var result = await runner.run( |
| ['snapshot_drill', r'--file=$INPUT'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isEmpty); |
| expect(result, contains('--file=<INPUT_FILE>')); |
| expect(result, contains('## `TEST INPUT`')); |
| }); |
| test('can invoke a tool with a setup action', () async { |
| var result = await runner.run( |
| ['setup_drill', r'--file=$INPUT'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isEmpty); |
| expect(result, contains('--file=<INPUT_FILE>')); |
| expect(result, contains('## `TEST INPUT`')); |
| expect(setupFile.existsSync(), isTrue); |
| }); |
| test('fails if tool not in tool map', () async { |
| var result = await runner.run( |
| ['hammer', r'--file=$INPUT'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isNotEmpty); |
| expect( |
| errors[0], contains('Unable to find definition for tool "hammer"')); |
| expect(result, isEmpty); |
| }); |
| test('fails if tool returns non-zero status', () async { |
| var result = await runner.run( |
| ['drill', r'--file=/a/missing/file'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isNotEmpty); |
| expect(errors[0], contains('Tool "drill" returned non-zero exit code')); |
| expect(result, isEmpty); |
| }); |
| test("fails if tool in tool map doesn't exist", () async { |
| var result = await runner.run( |
| ['missing'], |
| content: 'TEST INPUT', |
| toolErrorCallback: errorCallback, |
| ); |
| expect(errors, isNotEmpty); |
| expect(errors[0], |
| contains('Failed to run tool "missing" as "/a/missing/executable"')); |
| expect(result, isEmpty); |
| }); |
| }); |
| } |