Skip to content

Commit 15d1e25

Browse files
authored
Display an error message instead of crashing on wrong data type in dartdoc_options.yaml (#3372)
1 parent a094d22 commit 15d1e25

File tree

2 files changed

+89
-61
lines changed

2 files changed

+89
-61
lines changed

lib/src/dartdoc_options.dart

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,17 @@ abstract class DartdocOption<T extends Object?> {
395395

396396
bool get _isDouble => _kDoubleVal is T;
397397

398+
String get _expectedTypeForDisplay {
399+
if (_isString) return 'String';
400+
if (_isListString) return 'list of Strings';
401+
if (_isMapString) return 'map of String to String';
402+
if (_isBool) return 'boolean';
403+
if (_isInt) return 'int';
404+
if (_isDouble) return 'double';
405+
assert(false, 'Expecting an unknown type');
406+
return '<<unknown>>';
407+
}
408+
398409
final Map<String, _YamlFileData> __yamlAtCanonicalPathCache = {};
399410

400411
/// Implementation detail for [DartdocOptionFileOnly]. Make sure we use
@@ -945,9 +956,8 @@ abstract class _DartdocFileOption<T> implements DartdocOption<T> {
945956
};
946957
}
947958
if (convertYamlToType == null) {
948-
throw DartdocOptionError(
949-
'Unable to convert yaml to type for option: $fieldName, method not '
950-
'defined');
959+
throw UnsupportedError(
960+
'$fieldName: convertYamlToType method not defined');
951961
}
952962
var canonicalDirectoryPath =
953963
resourceProvider.pathContext.canonicalize(contextPath);
@@ -964,6 +974,10 @@ abstract class _DartdocFileOption<T> implements DartdocOption<T> {
964974
} else {
965975
throw UnsupportedError('Type $T is not supported');
966976
}
977+
if (returnData == null) {
978+
throw DartdocOptionError('Error in dartdoc_options.yaml, $fieldName: '
979+
'expecting a $_expectedTypeForDisplay, got `$yamlData`');
980+
}
967981
return _OptionValueWithContext(returnData as T, contextPath,
968982
definingFile: 'dartdoc_options.yaml');
969983
}

test/dartdoc_options_test.dart

Lines changed: 72 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ConvertedOption {
4040

4141
void main() {
4242
var resourceProvider = pubPackageMetaProvider.resourceProvider;
43+
var path = pubPackageMetaProvider.resourceProvider.pathContext;
4344

4445
late final DartdocOptionRoot dartdocOptionSetFiles;
4546
late final DartdocOptionRoot dartdocOptionSetArgs;
@@ -174,34 +175,30 @@ void main() {
174175
));
175176

176177
tempDir = resourceProvider.createSystemTemp('options_test');
177-
firstDir = resourceProvider
178-
.getFolder(resourceProvider.pathContext.join(tempDir.path, 'firstDir'))
178+
firstDir = resourceProvider.getFolder(path.join(tempDir.path, 'firstDir'))
179179
..create();
180-
firstExisting = resourceProvider.getFile(
181-
resourceProvider.pathContext.join(firstDir.path, 'existing.dart'))
180+
firstExisting = resourceProvider
181+
.getFile(path.join(firstDir.path, 'existing.dart'))
182182
..writeAsStringSync('');
183-
secondDir = resourceProvider
184-
.getFolder(resourceProvider.pathContext.join(tempDir.path, 'secondDir'))
183+
secondDir = resourceProvider.getFolder(path.join(tempDir.path, 'secondDir'))
185184
..create();
186185
resourceProvider
187-
.getFile(
188-
resourceProvider.pathContext.join(secondDir.path, 'existing.dart'))
186+
.getFile(path.join(secondDir.path, 'existing.dart'))
189187
.writeAsStringSync('');
190188

191-
secondDirFirstSub = resourceProvider.getFolder(
192-
resourceProvider.pathContext.join(secondDir.path, 'firstSub'))
189+
secondDirFirstSub = resourceProvider
190+
.getFolder(path.join(secondDir.path, 'firstSub'))
193191
..create();
194-
secondDirSecondSub = resourceProvider.getFolder(
195-
resourceProvider.pathContext.join(secondDir.path, 'secondSub'))
192+
secondDirSecondSub = resourceProvider
193+
.getFolder(path.join(secondDir.path, 'secondSub'))
196194
..create();
197195

198-
dartdocOptionsOne = resourceProvider.getFile(resourceProvider.pathContext
199-
.join(firstDir.path, 'dartdoc_options.yaml'));
200-
dartdocOptionsTwo = resourceProvider.getFile(resourceProvider.pathContext
201-
.join(secondDir.path, 'dartdoc_options.yaml'));
202-
var dartdocOptionsTwoFirstSub = resourceProvider.getFile(resourceProvider
203-
.pathContext
204-
.join(secondDirFirstSub.path, 'dartdoc_options.yaml'));
196+
dartdocOptionsOne = resourceProvider
197+
.getFile(path.join(firstDir.path, 'dartdoc_options.yaml'));
198+
dartdocOptionsTwo = resourceProvider
199+
.getFile(path.join(secondDir.path, 'dartdoc_options.yaml'));
200+
var dartdocOptionsTwoFirstSub = resourceProvider
201+
.getFile(path.join(secondDirFirstSub.path, 'dartdoc_options.yaml'));
205202

206203
dartdocOptionsOne.writeAsStringSync('''
207204
dartdoc:
@@ -247,6 +244,42 @@ dartdoc:
247244
}
248245
});
249246

247+
group('invalid data in yaml', () {
248+
late Folder invalid;
249+
late File dartdocOptionsInvalid;
250+
251+
setUp(() {
252+
invalid = resourceProvider.getFolder(path.join(tempDir.path, 'invalid'))
253+
..create();
254+
dartdocOptionsInvalid = resourceProvider
255+
.getFile(path.join(invalid.path, 'dartdoc_options.yaml'));
256+
dartdocOptionsInvalid.writeAsStringSync('''
257+
dartdoc:
258+
categoryOrder: 'options_one'
259+
''');
260+
});
261+
262+
tearDown(() {
263+
try {
264+
invalid.delete();
265+
} catch (e) {
266+
print('Ignoring error trying to delete temp: $e');
267+
}
268+
});
269+
270+
test('validate correct throw for wrong data type', () {
271+
dartdocOptionSetFiles.parseArguments([]);
272+
expect(() {
273+
dartdocOptionSetFiles['categoryOrder'].valueAt(invalid);
274+
},
275+
throwsA(const TypeMatcher<DartdocOptionError>().having(
276+
(e) => e.message,
277+
'message',
278+
equals('Error in dartdoc_options.yaml, dartdoc.categoryOrder: '
279+
'expecting a list of Strings, got `options_one`'))));
280+
});
281+
});
282+
250283
group('new style synthetic option', () {
251284
test('validate argument override changes value', () {
252285
dartdocOptionSetSynthetic.parseArguments(['--my-special-integer', '12']);
@@ -272,10 +305,8 @@ dartdoc:
272305
} on DartdocFileMissing catch (e) {
273306
errorMessage = e.message;
274307
}
275-
var missingPath = resourceProvider.pathContext.canonicalize(
276-
resourceProvider.pathContext.join(
277-
resourceProvider.pathContext.absolute(tempDir.path),
278-
'existing.dart'));
308+
var missingPath = path.canonicalize(
309+
path.join(path.absolute(tempDir.path), 'existing.dart'));
279310
expect(
280311
errorMessage,
281312
equals('Synthetic configuration option vegetableLoaderChecked from '
@@ -296,7 +327,7 @@ dartdoc:
296327
// Since this is an ArgSynth, it ignores the yaml option and resolves to the CWD
297328
expect(
298329
dartdocOptionSetSynthetic['nonCriticalFileOption'].valueAt(firstDir),
299-
equals(resourceProvider.pathContext.canonicalize('stuff.zip')));
330+
equals(path.canonicalize('stuff.zip')));
300331
});
301332

302333
test('ArgSynth defaults to synthetic', () {
@@ -320,10 +351,8 @@ dartdoc:
320351
} on DartdocFileMissing catch (e) {
321352
errorMessage = e.message;
322353
}
323-
var missingPath = resourceProvider.pathContext.join(
324-
resourceProvider.pathContext
325-
.canonicalize(resourceProvider.pathContext.current),
326-
'override-not-existing.dart');
354+
var missingPath = path.join(
355+
path.canonicalize(path.current), 'override-not-existing.dart');
327356
expect(
328357
errorMessage,
329358
equals('Argument --file-option, set to override-not-existing.dart, '
@@ -386,20 +415,15 @@ dartdoc:
386415
});
387416

388417
group('glob options', () {
389-
String canonicalize(String path) =>
390-
resourceProvider.pathContext.canonicalize(path);
418+
String canonicalize(String inputPath) => path.canonicalize(inputPath);
391419

392420
test('work via the command line', () {
393421
dartdocOptionSetAll
394422
.parseArguments(['--glob-option', p.join('foo', '**')]);
395423
expect(
396424
dartdocOptionSetAll['globOption'].valueAtCurrent(),
397425
equals([
398-
resourceProvider.pathContext.joinAll([
399-
canonicalize(resourceProvider.pathContext.current),
400-
'foo',
401-
'**'
402-
])
426+
path.joinAll([canonicalize(path.current), 'foo', '**'])
403427
]));
404428
});
405429

@@ -408,25 +432,21 @@ dartdoc:
408432
expect(
409433
dartdocOptionSetAll['globOption'].valueAt(secondDir),
410434
equals([
411-
canonicalize(
412-
resourceProvider.pathContext.join(secondDir.path, 'q*.html')),
413-
canonicalize(
414-
resourceProvider.pathContext.join(secondDir.path, 'e*.dart')),
435+
canonicalize(path.join(secondDir.path, 'q*.html')),
436+
canonicalize(path.join(secondDir.path, 'e*.dart')),
415437
]));
416438
// No child override, should be the same as parent
417439
expect(
418440
dartdocOptionSetAll['globOption'].valueAt(secondDirSecondSub),
419441
equals([
420-
canonicalize(
421-
resourceProvider.pathContext.join(secondDir.path, 'q*.html')),
422-
canonicalize(
423-
resourceProvider.pathContext.join(secondDir.path, 'e*.dart')),
442+
canonicalize(path.join(secondDir.path, 'q*.html')),
443+
canonicalize(path.join(secondDir.path, 'e*.dart')),
424444
]));
425445
// Child directory overrides
426446
expect(
427447
dartdocOptionSetAll['globOption'].valueAt(secondDirFirstSub),
428448
equals([
429-
resourceProvider.pathContext.joinAll(
449+
path.joinAll(
430450
[canonicalize(secondDirFirstSub.path), '**', '*.dart'])
431451
]));
432452
});
@@ -464,10 +484,8 @@ dartdoc:
464484
} on DartdocFileMissing catch (e) {
465485
errorMessage = e.message;
466486
}
467-
var missingPath = resourceProvider.pathContext.join(
468-
resourceProvider.pathContext
469-
.canonicalize(resourceProvider.pathContext.current),
470-
'not_found.txt');
487+
var missingPath =
488+
path.join(path.canonicalize(path.current), 'not_found.txt');
471489
expect(
472490
errorMessage,
473491
equals('Argument --single-file, set to not_found.txt, resolves to '
@@ -478,7 +496,7 @@ dartdoc:
478496
String? errorMessage;
479497
dartdocOptionSetArgs.parseArguments([
480498
'--files-flag',
481-
resourceProvider.pathContext.absolute(firstExisting.path),
499+
path.absolute(firstExisting.path),
482500
'--files-flag',
483501
'other_not_found.txt'
484502
]);
@@ -487,14 +505,12 @@ dartdoc:
487505
} on DartdocFileMissing catch (e) {
488506
errorMessage = e.message;
489507
}
490-
var missingPath = resourceProvider.pathContext.join(
491-
resourceProvider.pathContext
492-
.canonicalize(resourceProvider.pathContext.current),
493-
'other_not_found.txt');
508+
var missingPath =
509+
path.join(path.canonicalize(path.current), 'other_not_found.txt');
494510
expect(
495511
errorMessage,
496512
equals('Argument --files-flag, set to '
497-
'[${resourceProvider.pathContext.absolute(firstExisting.path)}, '
513+
'[${path.absolute(firstExisting.path)}, '
498514
'other_not_found.txt], resolves to missing path: '
499515
'"$missingPath"'));
500516
});
@@ -506,10 +522,8 @@ dartdoc:
506522
.parseArguments(['--unimportant-file', 'this-will-never-exist']);
507523
expect(
508524
dartdocOptionSetArgs['unimportantFile'].valueAt(tempDir),
509-
equals(resourceProvider.pathContext.join(
510-
resourceProvider.pathContext
511-
.canonicalize(resourceProvider.pathContext.current),
512-
'this-will-never-exist')));
525+
equals(path.join(
526+
path.canonicalize(path.current), 'this-will-never-exist')));
513527
});
514528

515529
test('DartdocOptionArgOnly has correct flag names', () {

0 commit comments

Comments
 (0)