|
| 1 | +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| 2 | +// for details. All rights reserved. Use of this source code is governed by a |
| 3 | +// BSD-style license that can be found in the LICENSE file. |
| 4 | + |
| 5 | +library dartdoc.dartdoc_integration_test; |
| 6 | + |
| 7 | +import 'dart:async'; |
| 8 | +import 'dart:io'; |
| 9 | +import 'dart:mirrors'; |
| 10 | + |
| 11 | +import 'package:dartdoc/dartdoc.dart'; |
| 12 | +import 'package:path/path.dart' as pathLib; |
| 13 | +import 'package:test/test.dart'; |
| 14 | + |
| 15 | +import 'src/utils.dart'; |
| 16 | + |
| 17 | +Uri get _currentFileUri => |
| 18 | + (reflect(main) as ClosureMirror).function.location.sourceUri; |
| 19 | +String get _testPackagePath => |
| 20 | + pathLib.fromUri(_currentFileUri.resolve('../testing/test_package')); |
| 21 | +String get _testPackageFlutterPluginPath => pathLib |
| 22 | + .fromUri(_currentFileUri.resolve('../testing/test_package_flutter_plugin')); |
| 23 | + |
| 24 | +void main() { |
| 25 | + group('Invoking command-line dartdoc', () { |
| 26 | + String dartdocPath = |
| 27 | + pathLib.canonicalize(pathLib.join('bin', 'dartdoc.dart')); |
| 28 | + CoverageSubprocessLauncher subprocessLauncher; |
| 29 | + Directory tempDir; |
| 30 | + |
| 31 | + setUpAll(() async { |
| 32 | + tempDir = |
| 33 | + Directory.systemTemp.createTempSync('dartdoc_integration_test.'); |
| 34 | + subprocessLauncher = new CoverageSubprocessLauncher( |
| 35 | + 'dartdoc_integration_test-subprocesses'); |
| 36 | + }); |
| 37 | + |
| 38 | + tearDown(() async { |
| 39 | + tempDir.listSync().forEach((FileSystemEntity f) { |
| 40 | + f.deleteSync(recursive: true); |
| 41 | + }); |
| 42 | + }); |
| 43 | + |
| 44 | + tearDownAll(() async { |
| 45 | + await Future.wait(CoverageSubprocessLauncher.coverageResults); |
| 46 | + }); |
| 47 | + |
| 48 | + test('running --no-generate-docs is quiet and does not generate docs', |
| 49 | + () async { |
| 50 | + Directory outputDir = |
| 51 | + await Directory.systemTemp.createTemp('dartdoc.testEmpty.'); |
| 52 | + List<String> outputLines = []; |
| 53 | + await subprocessLauncher.runStreamed(Platform.resolvedExecutable, |
| 54 | + [dartdocPath, '--output', outputDir.path, '--no-generate-docs'], |
| 55 | + perLine: outputLines.add, workingDirectory: _testPackagePath); |
| 56 | + expect(outputLines, isNot(contains(matches('^parsing')))); |
| 57 | + expect(outputLines, contains(matches('^ warning:'))); |
| 58 | + expect(outputLines.last, matches(r'^found \d+ warnings and \d+ errors')); |
| 59 | + expect(outputDir.listSync(), isEmpty); |
| 60 | + }); |
| 61 | + |
| 62 | + test('running --quiet is quiet and does generate docs', () async { |
| 63 | + Directory outputDir = |
| 64 | + await Directory.systemTemp.createTemp('dartdoc.testEmpty.'); |
| 65 | + List<String> outputLines = []; |
| 66 | + await subprocessLauncher.runStreamed(Platform.resolvedExecutable, |
| 67 | + [dartdocPath, '--output', outputDir.path, '--quiet'], |
| 68 | + perLine: outputLines.add, workingDirectory: _testPackagePath); |
| 69 | + expect(outputLines, isNot(contains(matches('^parsing')))); |
| 70 | + expect(outputLines, contains(matches('^ warning:'))); |
| 71 | + expect(outputLines.last, matches(r'^found \d+ warnings and \d+ errors')); |
| 72 | + expect(outputDir.listSync(), isNotEmpty); |
| 73 | + }); |
| 74 | + |
| 75 | + test('invalid parameters return non-zero and print a fatal-error', |
| 76 | + () async { |
| 77 | + List outputLines = []; |
| 78 | + await expectLater( |
| 79 | + () => subprocessLauncher.runStreamed( |
| 80 | + Platform.resolvedExecutable, |
| 81 | + [ |
| 82 | + dartdocPath, |
| 83 | + '--nonexisting', |
| 84 | + ], |
| 85 | + perLine: outputLines.add), |
| 86 | + throwsA(const TypeMatcher<ProcessException>())); |
| 87 | + expect( |
| 88 | + outputLines.firstWhere((l) => l.startsWith(' fatal')), |
| 89 | + equals( |
| 90 | + ' fatal error: Could not find an option named "nonexisting".')); |
| 91 | + }); |
| 92 | + |
| 93 | + test('missing a required file path prints a fatal-error', () async { |
| 94 | + List outputLines = []; |
| 95 | + String impossiblePath = pathLib.join(dartdocPath, 'impossible'); |
| 96 | + await expectLater( |
| 97 | + () => subprocessLauncher.runStreamed( |
| 98 | + Platform.resolvedExecutable, |
| 99 | + [ |
| 100 | + dartdocPath, |
| 101 | + '--input', |
| 102 | + impossiblePath, |
| 103 | + ], |
| 104 | + perLine: outputLines.add), |
| 105 | + throwsA(const TypeMatcher<ProcessException>())); |
| 106 | + expect( |
| 107 | + outputLines.firstWhere((l) => l.startsWith(' fatal')), |
| 108 | + startsWith( |
| 109 | + ' fatal error: Argument --input, set to ${impossiblePath}, resolves to missing path: ')); |
| 110 | + }); |
| 111 | + |
| 112 | + test('errors cause non-zero exit when warnings are off', () async { |
| 113 | + expect( |
| 114 | + () => subprocessLauncher.runStreamed(Platform.resolvedExecutable, [ |
| 115 | + dartdocPath, |
| 116 | + '--input=${testPackageToolError.path}', |
| 117 | + '--output=${pathLib.join(tempDir.absolute.path, 'test_package_tool_error')}' |
| 118 | + ]), |
| 119 | + throwsA(const TypeMatcher<ProcessException>())); |
| 120 | + }); |
| 121 | + |
| 122 | + test('help prints command line args', () async { |
| 123 | + List<String> outputLines = []; |
| 124 | + await subprocessLauncher.runStreamed( |
| 125 | + Platform.resolvedExecutable, [dartdocPath, '--help'], |
| 126 | + perLine: outputLines.add); |
| 127 | + expect(outputLines, |
| 128 | + contains('Generate HTML documentation for Dart libraries.')); |
| 129 | + expect( |
| 130 | + outputLines.join('\n'), |
| 131 | + contains(new RegExp('^-h, --help[ ]+Show command help.', |
| 132 | + multiLine: true))); |
| 133 | + }); |
| 134 | + |
| 135 | + test('Validate missing FLUTTER_ROOT exception is clean', () async { |
| 136 | + StringBuffer output = new StringBuffer(); |
| 137 | + var args = <String>[dartdocPath]; |
| 138 | + Future run = subprocessLauncher.runStreamed( |
| 139 | + Platform.resolvedExecutable, args, |
| 140 | + environment: new Map.from(Platform.environment) |
| 141 | + ..remove('FLUTTER_ROOT'), |
| 142 | + includeParentEnvironment: false, |
| 143 | + workingDirectory: _testPackageFlutterPluginPath, perLine: (s) { |
| 144 | + output.writeln(s); |
| 145 | + }); |
| 146 | + // Asynchronous exception, but we still need the output, too. |
| 147 | + expect(run, throwsA(new TypeMatcher<ProcessException>())); |
| 148 | + try { |
| 149 | + await run; |
| 150 | + } on ProcessException catch (_) {} |
| 151 | + |
| 152 | + expect( |
| 153 | + output.toString(), |
| 154 | + contains(new RegExp( |
| 155 | + 'Top level package requires Flutter but FLUTTER_ROOT environment variable not set|test_package_flutter_plugin requires the Flutter SDK, version solving failed'))); |
| 156 | + expect(output.toString(), isNot(contains('asynchronous gap'))); |
| 157 | + }); |
| 158 | + |
| 159 | + test("Validate --version works", () async { |
| 160 | + StringBuffer output = new StringBuffer(); |
| 161 | + var args = <String>[dartdocPath, '--version']; |
| 162 | + await subprocessLauncher.runStreamed(Platform.resolvedExecutable, args, |
| 163 | + workingDirectory: _testPackagePath, |
| 164 | + perLine: (s) => output.writeln(s)); |
| 165 | + PackageMeta dartdocMeta = new PackageMeta.fromFilename(dartdocPath); |
| 166 | + expect(output.toString(), |
| 167 | + endsWith('dartdoc version: ${dartdocMeta.version}\n')); |
| 168 | + }); |
| 169 | + |
| 170 | + test('Check for sample code in examples', () async { |
| 171 | + StringBuffer output = new StringBuffer(); |
| 172 | + var args = <String>[ |
| 173 | + dartdocPath, |
| 174 | + '--include', |
| 175 | + 'ex', |
| 176 | + '--no-include-source', |
| 177 | + '--output', |
| 178 | + tempDir.path |
| 179 | + ]; |
| 180 | + |
| 181 | + await subprocessLauncher.runStreamed(Platform.resolvedExecutable, args, |
| 182 | + workingDirectory: _testPackagePath, |
| 183 | + perLine: (s) => output.writeln(s)); |
| 184 | + |
| 185 | + // Examples are reported as unfound because we (purposefully) |
| 186 | + // did not use --example-path-prefix above. |
| 187 | + final sep = '.'; // We don't care what the path separator character is |
| 188 | + final firstUnfoundExample = new RegExp('warning: lib${sep}example.dart: ' |
| 189 | + '@example file not found.*test_package${sep}dog${sep}food.md'); |
| 190 | + if (!output.toString().contains(firstUnfoundExample)) { |
| 191 | + fail('Should warn about unfound @example files'); |
| 192 | + } |
| 193 | + }); |
| 194 | + |
| 195 | + test('Validate JSON output', () async { |
| 196 | + var args = <String>[ |
| 197 | + dartdocPath, |
| 198 | + '--include', |
| 199 | + 'ex', |
| 200 | + '--no-include-source', |
| 201 | + '--output', |
| 202 | + tempDir.path, |
| 203 | + '--json' |
| 204 | + ]; |
| 205 | + |
| 206 | + Iterable<Map> jsonValues = await subprocessLauncher.runStreamed( |
| 207 | + Platform.resolvedExecutable, args, |
| 208 | + workingDirectory: _testPackagePath); |
| 209 | + |
| 210 | + expect(jsonValues, isNotEmpty, |
| 211 | + reason: 'All STDOUT lines should be JSON-encoded maps.'); |
| 212 | + }, timeout: new Timeout.factor(2)); |
| 213 | + |
| 214 | + test('--footer-text includes text', () async { |
| 215 | + String footerTextPath = |
| 216 | + pathLib.join(Directory.systemTemp.path, 'footer.txt'); |
| 217 | + new File(footerTextPath).writeAsStringSync(' footer text include '); |
| 218 | + |
| 219 | + var args = <String>[ |
| 220 | + dartdocPath, |
| 221 | + '--footer-text=${footerTextPath}', |
| 222 | + '--include', |
| 223 | + 'ex', |
| 224 | + '--output', |
| 225 | + tempDir.path |
| 226 | + ]; |
| 227 | + |
| 228 | + await subprocessLauncher.runStreamed(Platform.resolvedExecutable, args, |
| 229 | + workingDirectory: _testPackagePath); |
| 230 | + |
| 231 | + File outFile = new File(pathLib.join(tempDir.path, 'index.html')); |
| 232 | + expect(outFile.readAsStringSync(), contains('footer text include')); |
| 233 | + }); |
| 234 | + }, timeout: new Timeout.factor(4)); |
| 235 | +} |
0 commit comments