| // Copyright (c) 2012, 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. |
| |
| /// Unit tests for doc. |
| library dartdocTests; |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| import 'package:path/path.dart' as path; |
| import 'package:unittest/unittest.dart'; |
| |
| // TODO(rnystrom): Use "package:" URL (#4968). |
| import '../lib/dartdoc.dart' as dd; |
| import '../lib/markdown.dart'; |
| import 'markdown_test.dart'; |
| |
| main() { |
| // Some tests take more than the default 20 second unittest timeout. |
| unittestConfiguration.timeout = null; |
| group('isAbsolute', () { |
| final doc = new dd.Dartdoc(); |
| |
| test('returns false if there is no scheme', () { |
| expect(doc.isAbsolute('index.html'), isFalse); |
| expect(doc.isAbsolute('foo/index.html'), isFalse); |
| expect(doc.isAbsolute('foo/bar/index.html'), isFalse); |
| }); |
| |
| test('returns true if there is a scheme', () { |
| expect(doc.isAbsolute('http://google.com'), isTrue); |
| expect(doc.isAbsolute('hTtPs://google.com'), isTrue); |
| expect(doc.isAbsolute('mailto:fake@email.com'), isTrue); |
| }); |
| }); |
| |
| group('relativePath', () { |
| final doc = new dd.Dartdoc(); |
| |
| test('absolute path is unchanged', () { |
| doc.startFile('dir/sub/file.html'); |
| expect(doc.relativePath('http://foo.com'), equals('http://foo.com')); |
| }); |
| |
| test('from root to root', () { |
| doc.startFile('root.html'); |
| expect(doc.relativePath('other.html'), equals('other.html')); |
| }); |
| |
| test('from root to directory', () { |
| doc.startFile('root.html'); |
| expect(doc.relativePath('dir/file.html'), equals('dir/file.html')); |
| }); |
| |
| test('from root to nested', () { |
| doc.startFile('root.html'); |
| expect(doc.relativePath('dir/sub/file.html'), equals( |
| 'dir/sub/file.html')); |
| }); |
| |
| test('from directory to root', () { |
| doc.startFile('dir/file.html'); |
| expect(doc.relativePath('root.html'), equals('../root.html')); |
| }); |
| |
| test('from nested to root', () { |
| doc.startFile('dir/sub/file.html'); |
| expect(doc.relativePath('root.html'), equals('../../root.html')); |
| }); |
| |
| test('from dir to dir with different path', () { |
| doc.startFile('dir/file.html'); |
| expect(doc.relativePath('other/file.html'), equals('../other/file.html')); |
| }); |
| |
| test('from nested to nested with different path', () { |
| doc.startFile('dir/sub/file.html'); |
| expect(doc.relativePath('other/sub/file.html'), equals( |
| '../../other/sub/file.html')); |
| }); |
| |
| test('from nested to directory with different path', () { |
| doc.startFile('dir/sub/file.html'); |
| expect(doc.relativePath('other/file.html'), equals( |
| '../../other/file.html')); |
| }); |
| }); |
| |
| group('dartdoc markdown', () { |
| group('[::] blocks', () { |
| |
| validateDartdocMarkdown('simple case', ''' |
| before [:source:] after |
| ''', ''' |
| <p>before <code>source</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('unmatched [:', ''' |
| before [: after |
| ''', ''' |
| <p>before [: after</p> |
| '''); |
| validateDartdocMarkdown('multiple spans in one text', ''' |
| a [:one:] b [:two:] c |
| ''', ''' |
| <p>a <code>one</code> b <code>two</code> c</p> |
| '''); |
| |
| validateDartdocMarkdown('multi-line', ''' |
| before [:first |
| second:] after |
| ''', ''' |
| <p>before <code>first |
| second</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('contain backticks', ''' |
| before [:can `contain` backticks:] after |
| ''', ''' |
| <p>before <code>can `contain` backticks</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('contain double backticks', ''' |
| before [:can ``contain`` backticks:] after |
| ''', ''' |
| <p>before <code>can ``contain`` backticks</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('contain backticks with spaces', ''' |
| before [: `tick` :] after |
| ''', ''' |
| <p>before <code>`tick`</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('multiline with spaces', ''' |
| before [:in `tick` |
| another:] after |
| ''', ''' |
| <p>before <code>in `tick` |
| another</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('ignore markup inside code', ''' |
| before [:*b* _c_:] after |
| ''', ''' |
| <p>before <code>*b* _c_</code> after</p> |
| '''); |
| |
| validateDartdocMarkdown('escape HTML characters', ''' |
| [:<&>:] |
| ''', ''' |
| <p><code><&></code></p> |
| '''); |
| |
| validateDartdocMarkdown('escape HTML tags', ''' |
| '*' [:<em>:] |
| ''', ''' |
| <p>'*' <code><em></code></p> |
| '''); |
| }); |
| }); |
| |
| group('integration tests', () { |
| test('no entrypoints', () { |
| _testRunDartDoc([], (result) { |
| expect(result.exitCode, 1); |
| }); |
| }); |
| |
| test('entrypoint in lib', () { |
| _testRunDartDoc(['test_files/lib/no_package_test_file.dart'], (result) { |
| expect(result.exitCode, 0); |
| _expectDocumented(result.stdout, libCount: 1, typeCount: 1, memberCount: 0); |
| }); |
| }); |
| |
| test('entrypoint somewhere with packages locally', () { |
| _testRunDartDoc(['test_files/package_test_file.dart'], (result) { |
| expect(result.exitCode, 0); |
| _expectDocumented(result.stdout, libCount: 1, typeCount: 1, memberCount: 0); |
| }); |
| }); |
| |
| test('file does not exist', () { |
| _testRunDartDoc(['test_files/this_file_does_not_exist.dart'], (result) { |
| expect(result.exitCode, 1); |
| }); |
| }); |
| }); |
| } |
| |
| void _testRunDartDoc(List<String> libraryPaths, void eval(ProcessResult)) { |
| expect(_runDartdoc(libraryPaths).then(eval), completes); |
| } |
| |
| /// The path to the root directory of the dartdoc entrypoint. |
| String get _dartdocDir { |
| var dir = path.absolute(Platform.script); |
| while (path.basename(dir) != 'dartdoc') { |
| if (!path.absolute(dir).contains('dartdoc') || dir == path.dirname(dir)) { |
| fail('Unable to find root dartdoc directory.'); |
| } |
| dir = path.dirname(dir); |
| } |
| return path.absolute(dir); |
| } |
| |
| /// The path to use for the package root for subprocesses. |
| String get _packageRoot { |
| var sdkVersionPath = path.join(_dartdocDir, '..', '..', '..', 'version'); |
| if (new File(sdkVersionPath).existsSync()) { |
| // It looks like dartdoc is being run from the SDK, so we should set the |
| // package root to the SDK's packages directory. |
| return path.absolute(path.join(_dartdocDir, '..', '..', '..', 'packages')); |
| } |
| |
| // It looks like Dartdoc is being run from the Dart repo, so the package root |
| // is in the build output directory. We can find that directory relative to |
| // the Dart executable, but that could be in one of two places: in |
| // "$BUILD/dart" or "$BUILD/dart-sdk/bin/dart". |
| var executableDir = path.dirname(Platform.executable); |
| if (new Directory(path.join(executableDir, 'dart-sdk')).existsSync()) { |
| // The executable is in "$BUILD/dart". |
| return path.absolute(path.join(executableDir, 'packages')); |
| } else { |
| // The executable is in "$BUILD/dart-sdk/bin/dart". |
| return path.absolute(path.join(executableDir, '..', '..', 'packages')); |
| } |
| } |
| |
| /// Runs dartdoc with the libraryPaths provided, and completes to dartdoc's |
| /// ProcessResult. |
| Future<ProcessResult> _runDartdoc(List<String> libraryPaths) { |
| var dartBin = Platform.executable; |
| |
| var dartdoc = path.join(_dartdocDir, 'bin/dartdoc.dart'); |
| |
| final runArgs = ['--package-root=$_packageRoot/', dartdoc]; |
| |
| // Turn relative libraryPaths to absolute ones. |
| runArgs.addAll(libraryPaths |
| .map((e) => path.join(path.absolute(dd.scriptDir), e))); |
| |
| return Process.run(dartBin, runArgs); |
| } |
| |
| final _dartdocCompletionRegExp = |
| new RegExp(r'Documentation complete -- documented (\d+) libraries, (\d+) types, and (\d+) members\.'); |
| |
| void _expectDocumented(String output, { int libCount, int typeCount, |
| int memberCount}) { |
| |
| final completionMatches = _dartdocCompletionRegExp.allMatches(output) |
| .toList(); |
| |
| expect(completionMatches, hasLength(1), |
| reason: 'dartdoc output should contain one summary'); |
| |
| final completionMatch = completionMatches.single; |
| |
| if(libCount != null) { |
| expect(int.parse(completionMatch[1]), libCount, |
| reason: 'expected library count'); |
| } |
| |
| if(typeCount != null) { |
| expect(int.parse(completionMatch[2]), typeCount, |
| reason: 'expected type count'); |
| } |
| |
| if(memberCount != null) { |
| expect(int.parse(completionMatch[3]), memberCount, |
| reason: 'expected member count'); |
| } |
| } |
| |
| |
| validateDartdocMarkdown(String description, String markdown, |
| String html) { |
| var dartdoc = new dd.Dartdoc(); |
| validate(description, markdown, html, linkResolver: dartdoc.dartdocResolver, |
| inlineSyntaxes: dartdoc.dartdocSyntaxes); |
| } |