Skip to content

Commit b484972

Browse files
authored
Add more tests for @example, @youtube, and @animation. (#2298)
* Add more tests for @example, @youtube, and @animation. Additionally, make the ArgParsers for those directives static. Reduce crashes by adding proper returns. Move the error about missing @example file to the Package.warn system.
1 parent bf552aa commit b484972

File tree

6 files changed

+469
-243
lines changed

6 files changed

+469
-243
lines changed

lib/src/model/comment_processable.dart

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ import 'dart:io';
22

33
import 'package:args/args.dart';
44
import 'package:crypto/crypto.dart' as crypto;
5-
import 'package:dartdoc/src/logging.dart';
65
import 'package:dartdoc/src/model/model.dart';
76
import 'package:dartdoc/src/render/model_element_renderer.dart';
87
import 'package:dartdoc/src/utils.dart';
98
import 'package:dartdoc/src/warnings.dart';
10-
9+
import 'package:meta/meta.dart';
1110
import 'package:path/path.dart' as path;
1211

1312
final _templatePattern = RegExp(
@@ -75,7 +74,8 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
7574
// Remember, periods are legal in library names.
7675
fullyQualifiedName.replaceFirst('${library.fullyQualifiedName}.', '');
7776

78-
ModelElementRenderer get _modelElementRenderer =>
77+
@visibleForTesting
78+
ModelElementRenderer get modelElementRenderer =>
7979
packageGraph.rendererFactory.modelElementRenderer;
8080

8181
/// Replace {@tool ...&#125{@end-tool} in API comments with the
@@ -205,26 +205,28 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
205205
replacement = replacement.replaceFirst('```', '```$lang');
206206
}
207207
} else {
208-
// TODO(jcollins-g): move this to Package.warn system
209208
var filePath = element.source.fullName.substring(dirPath.length + 1);
210209

211-
logWarning(
212-
'warning: ${filePath}: @example file not found, ${fragmentFile.path}');
210+
warn(PackageWarning.missingExampleFile,
211+
message: '${fragmentFile.path}; path listed at $filePath');
213212
}
214213
return replacement;
215214
});
216215
}
217216

217+
/// An argument parser used in [_getExampleArgs] to parse an `{@example}`
218+
/// directive.
219+
static final ArgParser _exampleArgParser = ArgParser()
220+
..addOption('lang')
221+
..addOption('region');
222+
218223
/// Helper for [_injectExamples] used to process `@example` arguments.
219224
///
220225
/// Returns a map of arguments. The first unnamed argument will have key
221226
/// 'src'. The computed file path, constructed from 'src' and 'region' will
222227
/// have key 'file'.
223228
Map<String, String> _getExampleArgs(String argsAsString) {
224-
var parser = ArgParser();
225-
parser.addOption('lang');
226-
parser.addOption('region');
227-
var results = _parseArgs(argsAsString, parser, 'example');
229+
var results = _parseArgs(argsAsString, _exampleArgParser, 'example');
228230
if (results == null) {
229231
return null;
230232
}
@@ -264,6 +266,10 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
264266
static final _validYouTubeUrlPattern =
265267
RegExp('https://www\.youtube\.com/watch\\?v=([^&]+)\$');
266268

269+
/// An argument parser used in [_injectYouTube] to parse a `{@youtube}`
270+
/// directive.
271+
static final _youTubeArgParser = ArgParser();
272+
267273
/// Replace &#123;@youtube ...&#125; in API comments with some HTML to embed
268274
/// a YouTube video.
269275
///
@@ -289,8 +295,7 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
289295
/// found in the address bar of the browser when viewing a YouTube video.
290296
String _injectYouTube(String rawDocs) {
291297
return rawDocs.replaceAllMapped(_basicYouTubePattern, (basicMatch) {
292-
var parser = ArgParser();
293-
var args = _parseArgs(basicMatch[1], parser, 'youtube');
298+
var args = _parseArgs(basicMatch[1], _youTubeArgParser, 'youtube');
294299
if (args == null) {
295300
// Already warned about an invalid parameter if this happens.
296301
return '';
@@ -310,6 +315,7 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
310315
message: 'A @youtube directive has an invalid width, '
311316
'"${positionalArgs[0]}". The width must be a positive '
312317
'integer.');
318+
return '';
313319
}
314320

315321
var height = int.tryParse(positionalArgs[1]);
@@ -318,6 +324,7 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
318324
message: 'A @youtube directive has an invalid height, '
319325
'"${positionalArgs[1]}". The height must be a positive '
320326
'integer.');
327+
return '';
321328
}
322329

323330
var url = _validYouTubeUrlPattern.firstMatch(positionalArgs[2]);
@@ -332,7 +339,7 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
332339
var youTubeId = url.group(url.groupCount);
333340
var aspectRatio = (height / width * 100).toStringAsFixed(2);
334341

335-
return _modelElementRenderer.renderYoutubeUrl(youTubeId, aspectRatio);
342+
return modelElementRenderer.renderYoutubeUrl(youTubeId, aspectRatio);
336343
});
337344
}
338345

@@ -344,6 +351,10 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
344351
/// Matches valid javascript identifiers.
345352
final _validIdPattern = RegExp(r'^[a-zA-Z_]\w*$');
346353

354+
/// An argument parser used in [_injectAnimations] to parse a `{@animation}`
355+
/// directive.
356+
static final _animationArgParser = ArgParser()..addOption('id');
357+
347358
/// Replace &#123;@animation ...&#125; in API comments with some HTML to
348359
/// manage an MPEG 4 video as an animation.
349360
///
@@ -382,9 +393,7 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
382393
// Make sure we have a set to keep track of used IDs for this href.
383394
package.usedAnimationIdsByHref[href] ??= {};
384395

385-
var parser = ArgParser();
386-
parser.addOption('id');
387-
var args = _parseArgs(basicMatch[1], parser, 'animation');
396+
var args = _parseArgs(basicMatch[1], _animationArgParser, 'animation');
388397
if (args == null) {
389398
// Already warned about an invalid parameter if this happens.
390399
return '';
@@ -463,7 +472,7 @@ mixin CommentProcessable on Documentable, Warnable, Locatable, SourceCodeMixin {
463472
'parameter)');
464473
}
465474

466-
return _modelElementRenderer.renderAnimation(
475+
return modelElementRenderer.renderAnimation(
467476
uniqueId, width, height, movieUrl, overlayId);
468477
});
469478
}

lib/src/model/package_graph.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,9 @@ class PackageGraph {
420420
case PackageWarning.missingConstantConstructor:
421421
warningMessage = 'constant constructor missing: ${message}';
422422
break;
423+
case PackageWarning.missingExampleFile:
424+
warningMessage = 'example file not found: ${message}';
425+
break;
423426
}
424427

425428
var messageParts = <String>[warningMessage];

lib/src/warnings.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ enum PackageWarning {
263263
deprecated,
264264
unresolvedExport,
265265
missingConstantConstructor,
266+
missingExampleFile,
266267
}
267268

268269
/// Used to declare defaults for a particular package warning.

test/dartdoc_integration_test.dart

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -169,31 +169,6 @@ void main() {
169169
endsWith('dartdoc version: ${dartdocMeta.version}\n'));
170170
});
171171

172-
test('Check for sample code in examples', () async {
173-
var output = StringBuffer();
174-
var args = <String>[
175-
dartdocPath,
176-
'--include',
177-
'ex',
178-
'--no-include-source',
179-
'--output',
180-
tempDir.path
181-
];
182-
183-
await subprocessLauncher.runStreamed(Platform.resolvedExecutable, args,
184-
workingDirectory: _testPackagePath,
185-
perLine: (s) => output.writeln(s));
186-
187-
// Examples are reported as unfound because we (purposefully)
188-
// did not use --example-path-prefix above.
189-
final sep = '.'; // We don't care what the path separator character is
190-
final firstUnfoundExample = RegExp('warning: lib${sep}example.dart: '
191-
'@example file not found.*test_package${sep}dog${sep}food.md');
192-
if (!output.toString().contains(firstUnfoundExample)) {
193-
fail('Should warn about unfound @example files');
194-
}
195-
});
196-
197172
test('Validate JSON output', () async {
198173
var args = <String>[
199174
dartdocPath,

test/model_special_cases_test.dart

Lines changed: 0 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import 'dart:io';
1313
import 'package:dartdoc/dartdoc.dart';
1414
import 'package:dartdoc/src/model/model.dart';
1515
import 'package:dartdoc/src/special_elements.dart';
16-
import 'package:dartdoc/src/warnings.dart';
1716
import 'package:pub_semver/pub_semver.dart';
1817
import 'package:test/test.dart';
1918

@@ -486,199 +485,4 @@ void main() {
486485
'${HTMLBASE_PLACEHOLDER}dart-async/dart-async-library.html');
487486
});
488487
});
489-
490-
group('YouTube Errors', () {
491-
PackageGraph packageGraphErrors;
492-
Class documentationErrors;
493-
Method withYouTubeWrongParams;
494-
Method withYouTubeBadWidth;
495-
Method withYouTubeBadHeight;
496-
Method withYouTubeInvalidUrl;
497-
Method withYouTubeUrlWithAdditionalParameters;
498-
499-
setUpAll(() async {
500-
packageGraphErrors = await utils.testPackageGraphErrors;
501-
documentationErrors = packageGraphErrors.libraries
502-
.firstWhere((lib) => lib.name == 'doc_errors')
503-
.classes
504-
.firstWhere((c) => c.name == 'DocumentationErrors')
505-
..documentation;
506-
withYouTubeWrongParams = documentationErrors.instanceMethods
507-
.firstWhere((m) => m.name == 'withYouTubeWrongParams')
508-
..documentation;
509-
withYouTubeBadWidth = documentationErrors.instanceMethods
510-
.firstWhere((m) => m.name == 'withYouTubeBadWidth')
511-
..documentation;
512-
withYouTubeBadHeight = documentationErrors.instanceMethods
513-
.firstWhere((m) => m.name == 'withYouTubeBadHeight')
514-
..documentation;
515-
withYouTubeInvalidUrl = documentationErrors.instanceMethods
516-
.firstWhere((m) => m.name == 'withYouTubeInvalidUrl')
517-
..documentation;
518-
withYouTubeUrlWithAdditionalParameters = documentationErrors
519-
.instanceMethods
520-
.firstWhere((m) => m.name == 'withYouTubeUrlWithAdditionalParameters')
521-
..documentation;
522-
});
523-
524-
test('warns on youtube video with missing parameters', () {
525-
expect(
526-
packageGraphErrors.packageWarningCounter.hasWarning(
527-
withYouTubeWrongParams,
528-
PackageWarning.invalidParameter,
529-
'Invalid @youtube directive, "{@youtube https://youtu.be/oHg5SJYRHA0}"\n'
530-
'YouTube directives must be of the form "{@youtube WIDTH HEIGHT URL}"'),
531-
isTrue);
532-
});
533-
test('warns on youtube video with non-integer width', () {
534-
expect(
535-
packageGraphErrors.packageWarningCounter.hasWarning(
536-
withYouTubeBadWidth,
537-
PackageWarning.invalidParameter,
538-
'A @youtube directive has an invalid width, "100px". The width '
539-
'must be a positive integer.'),
540-
isTrue);
541-
});
542-
test('warns on youtube video with non-integer height', () {
543-
expect(
544-
packageGraphErrors.packageWarningCounter.hasWarning(
545-
withYouTubeBadHeight,
546-
PackageWarning.invalidParameter,
547-
'A @youtube directive has an invalid height, "100px". The height '
548-
'must be a positive integer.'),
549-
isTrue);
550-
});
551-
test('warns on youtube video with invalid video URL', () {
552-
expect(
553-
packageGraphErrors.packageWarningCounter.hasWarning(
554-
withYouTubeInvalidUrl,
555-
PackageWarning.invalidParameter,
556-
'A @youtube directive has an invalid URL: '
557-
'"http://host/path/to/video.mp4". Supported YouTube URLs have '
558-
'the following format: '
559-
'https://www.youtube.com/watch?v=oHg5SJYRHA0.'),
560-
isTrue);
561-
});
562-
test('warns on youtube video with extra parameters in URL', () {
563-
expect(
564-
packageGraphErrors.packageWarningCounter.hasWarning(
565-
withYouTubeUrlWithAdditionalParameters,
566-
PackageWarning.invalidParameter,
567-
'A @youtube directive has an invalid URL: '
568-
'"https://www.youtube.com/watch?v=yI-8QHpGIP4&list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&index=5". '
569-
'Supported YouTube URLs have the following format: '
570-
'https://www.youtube.com/watch?v=oHg5SJYRHA0.'),
571-
isTrue);
572-
});
573-
});
574-
575-
group('Animation Errors', () {
576-
PackageGraph packageGraphErrors;
577-
Class documentationErrors;
578-
Method withInvalidNamedAnimation;
579-
Method withAnimationNonUnique;
580-
Method withAnimationNonUniqueDeprecated;
581-
Method withAnimationWrongParams;
582-
Method withAnimationBadWidth;
583-
Method withAnimationBadHeight;
584-
Method withAnimationUnknownArg;
585-
586-
setUpAll(() async {
587-
packageGraphErrors = await utils.testPackageGraphErrors;
588-
documentationErrors = packageGraphErrors.libraries
589-
.firstWhere((lib) => lib.name == 'doc_errors')
590-
.classes
591-
.firstWhere((c) => c.name == 'DocumentationErrors')
592-
..documentation;
593-
withInvalidNamedAnimation = documentationErrors.instanceMethods
594-
.firstWhere((m) => m.name == 'withInvalidNamedAnimation')
595-
..documentation;
596-
withAnimationNonUnique = documentationErrors.instanceMethods
597-
.firstWhere((m) => m.name == 'withAnimationNonUnique')
598-
..documentation;
599-
withAnimationNonUniqueDeprecated = documentationErrors.instanceMethods
600-
.firstWhere((m) => m.name == 'withAnimationNonUniqueDeprecated')
601-
..documentation;
602-
withAnimationWrongParams = documentationErrors.instanceMethods
603-
.firstWhere((m) => m.name == 'withAnimationWrongParams')
604-
..documentation;
605-
withAnimationBadWidth = documentationErrors.instanceMethods
606-
.firstWhere((m) => m.name == 'withAnimationBadWidth')
607-
..documentation;
608-
withAnimationBadHeight = documentationErrors.instanceMethods
609-
.firstWhere((m) => m.name == 'withAnimationBadHeight')
610-
..documentation;
611-
withAnimationUnknownArg = documentationErrors.instanceMethods
612-
.firstWhere((m) => m.name == 'withAnimationUnknownArg')
613-
..documentation;
614-
});
615-
616-
test('warns with invalidly-named animation within the method documentation',
617-
() async {
618-
expect(
619-
packageGraphErrors.packageWarningCounter.hasWarning(
620-
withInvalidNamedAnimation,
621-
PackageWarning.invalidParameter,
622-
'An animation has an invalid identifier, "2isNot-A-ValidName". '
623-
'The identifier can only contain letters, numbers and '
624-
'underscores, and must not begin with a number.'),
625-
isTrue);
626-
});
627-
test('warns on a non-unique animation name within a method', () {
628-
expect(
629-
packageGraphErrors.packageWarningCounter.hasWarning(
630-
withAnimationNonUnique,
631-
PackageWarning.invalidParameter,
632-
'An animation has a non-unique identifier, "barHerderAnimation". '
633-
'Animation identifiers must be unique.'),
634-
isTrue);
635-
});
636-
test('warns on a non-unique animation name within a deprecated-form method',
637-
() {
638-
expect(
639-
packageGraphErrors.packageWarningCounter.hasWarning(
640-
withAnimationNonUniqueDeprecated,
641-
PackageWarning.invalidParameter,
642-
'An animation has a non-unique identifier, "fooHerderAnimation". '
643-
'Animation identifiers must be unique.'),
644-
isTrue);
645-
});
646-
test('warns on animation with missing parameters', () {
647-
expect(
648-
packageGraphErrors.packageWarningCounter.hasWarning(
649-
withAnimationWrongParams,
650-
PackageWarning.invalidParameter,
651-
'Invalid @animation directive, "{@animation http://host/path/to/video.mp4}"\n'
652-
'Animation directives must be of the form "{@animation WIDTH '
653-
'HEIGHT URL [id=ID]}"'),
654-
isTrue);
655-
});
656-
test('warns on animation with non-integer width', () {
657-
expect(
658-
packageGraphErrors.packageWarningCounter.hasWarning(
659-
withAnimationBadWidth,
660-
PackageWarning.invalidParameter,
661-
'An animation has an invalid width (badWidthAnimation), "100px". '
662-
'The width must be an integer.'),
663-
isTrue);
664-
});
665-
test('warns on animation with non-integer height', () {
666-
expect(
667-
packageGraphErrors.packageWarningCounter.hasWarning(
668-
withAnimationBadHeight,
669-
PackageWarning.invalidParameter,
670-
'An animation has an invalid height (badHeightAnimation), '
671-
'"100px". The height must be an integer.'),
672-
isTrue);
673-
});
674-
test('Unknown arguments generate an error.', () {
675-
expect(
676-
packageGraphErrors.packageWarningCounter.hasWarning(
677-
withAnimationUnknownArg,
678-
PackageWarning.invalidParameter,
679-
'The {@animation ...} directive was called with invalid '
680-
'parameters. FormatException: Could not find an option named "name".'),
681-
isTrue);
682-
});
683-
}, timeout: Timeout.factor(2));
684488
}

0 commit comments

Comments
 (0)