blob: fb41b7ad9222d3dd717c0943c96346d86c655d25 [file] [log] [blame]
// 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.options_test;
import 'dart:io';
import 'package:dartdoc/src/dartdoc_options.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:yaml/yaml.dart';
class ConvertedOption {
final String param1;
final String param2;
final String myContextPath;
ConvertedOption._(this.param1, this.param2, this.myContextPath);
static ConvertedOption fromYamlMap(YamlMap yamlMap, path.Context context) {
String p1;
String p2;
String contextPath = context.current;
for (MapEntry entry in yamlMap.entries) {
switch (entry.key.toString()) {
case 'param1':
p1 = entry.value.toString();
break;
case 'param2':
p2 = entry.value.toString();
break;
}
}
return ConvertedOption._(p1, p2, contextPath);
}
}
void main() {
DartdocOptionSet dartdocOptionSetFiles;
DartdocOptionSet dartdocOptionSetArgs;
DartdocOptionSet dartdocOptionSetAll;
DartdocOptionSet dartdocOptionSetSynthetic;
Directory tempDir;
Directory firstDir;
Directory secondDir;
Directory secondDirFirstSub;
Directory secondDirSecondSub;
File dartdocOptionsOne;
File dartdocOptionsTwo;
File dartdocOptionsTwoFirstSub;
File firstExisting;
setUpAll(() {
dartdocOptionSetSynthetic = DartdocOptionSet('dartdoc');
dartdocOptionSetSynthetic
.add(DartdocOptionArgFile<int>('mySpecialInteger', 91));
dartdocOptionSetSynthetic.add(
DartdocOptionSyntheticOnly<List<String>>('vegetableLoader',
(DartdocSyntheticOption<List<String>> option, Directory dir) {
if (option.root['mySpecialInteger'].valueAt(dir) > 20) {
return <String>['existing.dart'];
} else {
return <String>['not_existing.dart'];
}
}));
dartdocOptionSetSynthetic.add(
DartdocOptionSyntheticOnly<List<String>>('vegetableLoaderChecked',
(DartdocSyntheticOption<List<String>> option, Directory dir) {
return option.root['vegetableLoader'].valueAt(dir);
}, isFile: true, mustExist: true));
dartdocOptionSetSynthetic.add(DartdocOptionFileSynth<double>('double',
(DartdocSyntheticOption<double> option, Directory dir) {
return 3.7 + 4.1;
}));
dartdocOptionSetSynthetic.add(
DartdocOptionArgSynth<String>('nonCriticalFileOption',
(DartdocSyntheticOption<String> option, Directory dir) {
return option.root['vegetableLoader'].valueAt(dir).first;
}, isFile: true));
dartdocOptionSetFiles = DartdocOptionSet('dartdoc');
dartdocOptionSetFiles
.add(DartdocOptionFileOnly<List<String>>('categoryOrder', []));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<double>('double', 3.0));
dartdocOptionSetFiles
.add(DartdocOptionFileOnly<int>('mySpecialInteger', 42));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<Map<String, String>>(
'mapOption', {'hello': 'world'}));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<List<String>>(
'fileOptionList', [],
isFile: true, mustExist: true));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<String>('fileOption', null,
isFile: true, mustExist: true));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<String>(
'parentOverride', 'oops',
parentDirOverridesChild: true));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<String>(
'nonCriticalFileOption', null,
isFile: true));
dartdocOptionSetFiles.add(DartdocOptionSet('nestedOption')
..addAll([DartdocOptionFileOnly<bool>('flag', false)]));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<String>('dirOption', null,
isDir: true, mustExist: true));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<String>(
'nonCriticalDirOption', null,
isDir: true));
dartdocOptionSetFiles.add(DartdocOptionFileOnly<ConvertedOption>(
'convertThisMap',
null,
convertYamlToType: ConvertedOption.fromYamlMap,
));
dartdocOptionSetArgs = DartdocOptionSet('dartdoc');
dartdocOptionSetArgs
.add(DartdocOptionArgOnly<bool>('cauliflowerSystem', false));
dartdocOptionSetArgs
.add(DartdocOptionArgOnly<String>('extraSpecial', 'something'));
dartdocOptionSetArgs.add(
DartdocOptionArgOnly<List<String>>('excludeFiles', ['one', 'two']));
dartdocOptionSetArgs
.add(DartdocOptionArgOnly<int>('number_of_heads', 3, abbr: 'h'));
dartdocOptionSetArgs
.add(DartdocOptionArgOnly<double>('respawnProbability', 0.2));
dartdocOptionSetArgs.add(DartdocOptionSet('warn')
..addAll([DartdocOptionArgOnly<bool>('unrecognizedVegetable', false)]));
dartdocOptionSetArgs.add(DartdocOptionArgOnly<Map<String, String>>(
'aFancyMapVariable', {'hello': 'map world'},
splitCommas: true));
dartdocOptionSetArgs.add(DartdocOptionArgOnly<List<String>>('filesFlag', [],
isFile: true, mustExist: true));
dartdocOptionSetArgs.add(DartdocOptionArgOnly<String>('singleFile', 'hello',
isFile: true, mustExist: true));
dartdocOptionSetArgs.add(DartdocOptionArgOnly<String>(
'unimportantFile', 'whatever',
isFile: true));
dartdocOptionSetAll = DartdocOptionSet('dartdoc');
dartdocOptionSetAll
.add(DartdocOptionArgFile<List<String>>('categoryOrder', []));
dartdocOptionSetAll.add(DartdocOptionArgFile<int>('mySpecialInteger', 91));
dartdocOptionSetAll.add(DartdocOptionSet('warn')
..addAll([DartdocOptionArgFile<bool>('unrecognizedVegetable', false)]));
dartdocOptionSetAll.add(DartdocOptionArgFile<Map<String, String>>(
'mapOption', {'hi': 'there'}));
dartdocOptionSetAll
.add(DartdocOptionArgFile<String>('notInAnyFile', 'so there'));
dartdocOptionSetAll.add(DartdocOptionArgFile<String>('fileOption', null,
isFile: true, mustExist: true));
tempDir = Directory.systemTemp.createTempSync('options_test');
firstDir = Directory(path.join(tempDir.path, 'firstDir'))..createSync();
firstExisting = File(path.join(firstDir.path, 'existing.dart'))
..createSync();
secondDir = Directory(path.join(tempDir.path, 'secondDir'))..createSync();
File(path.join(secondDir.path, 'existing.dart'))..createSync();
secondDirFirstSub = Directory(path.join(secondDir.path, 'firstSub'))
..createSync();
secondDirSecondSub = Directory(path.join(secondDir.path, 'secondSub'))
..createSync();
dartdocOptionsOne = File(path.join(firstDir.path, 'dartdoc_options.yaml'));
dartdocOptionsTwo = File(path.join(secondDir.path, 'dartdoc_options.yaml'));
dartdocOptionsTwoFirstSub =
File(path.join(secondDirFirstSub.path, 'dartdoc_options.yaml'));
dartdocOptionsOne.writeAsStringSync('''
dartdoc:
categoryOrder: ['options_one']
mySpecialInteger: 30
nestedOption:
flag: true
mapOption:
firstThing: yes
secondThing: stuff
fileOption: "existing.dart"
dirOption: "notHere"
nonCriticalFileOption: "whatever"
double: 3.3
convertThisMap:
param1: value1
param2: value2
''');
dartdocOptionsTwo.writeAsStringSync('''
dartdoc:
categoryOrder: ['options_two']
parentOverride: 'parent'
dirOption: 'firstSub'
fileOptionList: ['existing.dart', 'thing/that/does/not/exist']
mySpecialInteger: 11
fileOption: "not existing"
''');
dartdocOptionsTwoFirstSub.writeAsStringSync('''
dartdoc:
categoryOrder: ['options_two_first_sub']
parentOverride: 'child'
nonCriticalDirOption: 'not_existing'
''');
});
tearDownAll(() {
tempDir.deleteSync(recursive: true);
});
group('new style synthetic option', () {
test('validate argument override changes value', () {
dartdocOptionSetSynthetic.parseArguments(['--my-special-integer', '12']);
expect(dartdocOptionSetSynthetic['vegetableLoader'].valueAt(tempDir),
orderedEquals(['not_existing.dart']));
});
test('validate default value of synthetic', () {
dartdocOptionSetSynthetic.parseArguments([]);
expect(dartdocOptionSetSynthetic['vegetableLoader'].valueAt(tempDir),
orderedEquals(['existing.dart']));
});
test('file validation of synthetic', () {
dartdocOptionSetSynthetic.parseArguments([]);
expect(
dartdocOptionSetSynthetic['vegetableLoaderChecked'].valueAt(firstDir),
orderedEquals([path.canonicalize(firstExisting.path)]));
String errorMessage;
try {
dartdocOptionSetSynthetic['vegetableLoaderChecked'].valueAt(tempDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Synthetic configuration option dartdoc from <internal>, computed as [existing.dart], resolves to missing path: "${path.canonicalize(path.join(tempDir.absolute.path, 'existing.dart'))}"'));
});
test('file can override synthetic in FileSynth', () {
dartdocOptionSetSynthetic.parseArguments([]);
expect(
dartdocOptionSetSynthetic['double'].valueAt(firstDir), equals(3.3));
expect(dartdocOptionSetSynthetic['double'].valueAt(tempDir), equals(7.8));
});
test('arg can override synthetic in ArgSynth', () {
dartdocOptionSetSynthetic
.parseArguments(['--non-critical-file-option', 'stuff.zip']);
// Since this is an ArgSynth, it ignores the yaml option and resolves to the CWD
expect(
dartdocOptionSetSynthetic['nonCriticalFileOption'].valueAt(firstDir),
equals(path
.canonicalize(path.join(Directory.current.path, 'stuff.zip'))));
});
test('ArgSynth defaults to synthetic', () {
dartdocOptionSetSynthetic.parseArguments([]);
// This option is composed of FileOptions which make use of firstDir.
expect(
dartdocOptionSetSynthetic['nonCriticalFileOption'].valueAt(firstDir),
equals(path.canonicalize(path.join(firstDir.path, 'existing.dart'))));
});
});
group('new style dartdoc both file and argument options', () {
test(
'validate argument with wrong file throws error even if dartdoc_options is right',
() {
dartdocOptionSetAll
.parseArguments(['--file-option', 'override-not-existing.dart']);
String errorMessage;
try {
dartdocOptionSetAll['fileOption'].valueAt(firstDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Argument --file-option, set to override-not-existing.dart, resolves to missing path: "${path.join(path.canonicalize(Directory.current.path), "override-not-existing.dart")}"'));
});
test('validate argument can override missing file', () {
dartdocOptionSetAll.parseArguments(
['--file-option', path.canonicalize(firstExisting.path)]);
expect(dartdocOptionSetAll['fileOption'].valueAt(secondDir),
equals(path.canonicalize(firstExisting.path)));
});
test('File errors still get passed through', () {
dartdocOptionSetAll.parseArguments([]);
String errorMessage;
try {
dartdocOptionSetAll['fileOption'].valueAt(secondDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Field dartdoc.fileOption from ${path.canonicalize(dartdocOptionsTwo.path)}, set to not existing, resolves to missing path: '
'"${path.join(path.canonicalize(secondDir.path), "not existing")}"'));
});
test('validate override behavior basic', () {
dartdocOptionSetAll.parseArguments(
['--not-in-any-file', 'aha', '--map-option', 'over::theSea']);
expect(dartdocOptionSetAll['mapOption'].valueAt(tempDir),
equals({'over': 'theSea'}));
expect(dartdocOptionSetAll['mapOption'].valueAt(firstDir),
equals({'over': 'theSea'}));
expect(
dartdocOptionSetAll['notInAnyFile'].valueAt(firstDir), equals('aha'));
expect(dartdocOptionSetAll['mySpecialInteger'].valueAt(firstDir),
equals(30));
});
test('validate override behavior for parent directories', () {
dartdocOptionSetAll.parseArguments(['--my-special-integer', '14']);
expect(dartdocOptionSetAll['mySpecialInteger'].valueAt(secondDirFirstSub),
equals(14));
});
test('validate arg defaults do not override file', () {
dartdocOptionSetAll.parseArguments([]);
expect(dartdocOptionSetAll['mySpecialInteger'].valueAt(secondDir),
equals(11));
});
test(
'validate setting the default manually in an argument overrides the file',
() {
dartdocOptionSetAll.parseArguments(['--my-special-integer', '91']);
expect(dartdocOptionSetAll['mySpecialInteger'].valueAt(secondDir),
equals(91));
});
});
group('new style dartdoc arg-only options', () {
test('DartdocOptionArgOnly loads arg defaults', () {
dartdocOptionSetArgs.parseArguments([]);
expect(
dartdocOptionSetArgs['cauliflowerSystem'].valueAt(tempDir), isFalse);
expect(dartdocOptionSetArgs['extraSpecial'].valueAt(tempDir),
equals('something'));
expect(dartdocOptionSetArgs['excludeFiles'].valueAt(tempDir),
orderedEquals(['one', 'two']));
expect(
dartdocOptionSetArgs['number_of_heads'].valueAt(tempDir), equals(3));
expect(dartdocOptionSetArgs['respawnProbability'].valueAt(tempDir),
equals(0.2));
expect(
dartdocOptionSetArgs['warn']['unrecognizedVegetable']
.valueAt(tempDir),
isFalse);
expect(dartdocOptionSetArgs['aFancyMapVariable'].valueAt(tempDir),
equals({'hello': 'map world'}));
expect(
dartdocOptionSetArgs['singleFile'].valueAt(tempDir), equals('hello'));
});
test('DartdocOptionArgOnly checks file existence', () {
String errorMessage;
dartdocOptionSetArgs.parseArguments(['--single-file', 'not_found.txt']);
try {
dartdocOptionSetArgs['singleFile'].valueAt(tempDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Argument --single-file, set to not_found.txt, resolves to missing path: '
'"${path.join(path.canonicalize(Directory.current.path), 'not_found.txt')}"'));
});
test('DartdocOptionArgOnly checks file existence on multi-options', () {
String errorMessage;
dartdocOptionSetArgs.parseArguments([
'--files-flag',
firstExisting.absolute.path,
'--files-flag',
'other_not_found.txt'
]);
try {
dartdocOptionSetArgs['filesFlag'].valueAt(tempDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Argument --files-flag, set to [${firstExisting.absolute.path}, other_not_found.txt], resolves to missing path: '
'"${path.join(path.canonicalize(Directory.current.path), "other_not_found.txt")}"'));
});
test(
'DartdocOptionArgOnly does not check for file existence when mustExist is false',
() {
dartdocOptionSetArgs
.parseArguments(['--unimportant-file', 'this-will-never-exist']);
expect(
dartdocOptionSetArgs['unimportantFile'].valueAt(tempDir),
equals(path.join(path.canonicalize(Directory.current.path),
'this-will-never-exist')));
});
test('DartdocOptionArgOnly has correct flag names', () {
dartdocOptionSetArgs.parseArguments([]);
expect(
(dartdocOptionSetArgs['cauliflowerSystem'] as DartdocOptionArgOnly)
.argName,
equals('cauliflower-system'));
expect(
(dartdocOptionSetArgs['number_of_heads'] as DartdocOptionArgOnly)
.argName,
equals('number-of-heads'));
expect(
(dartdocOptionSetArgs['warn']['unrecognizedVegetable']
as DartdocOptionArgOnly)
.argName,
equals('warn-unrecognized-vegetable'));
});
test('DartdocOptionArgOnly abbreviations work', () {
dartdocOptionSetArgs.parseArguments(['-h', '125']);
expect(dartdocOptionSetArgs['number_of_heads'].valueAt(tempDir),
equals(125));
});
test('DartdocOptionArgOnly correctly handles some arguments', () {
dartdocOptionSetArgs.parseArguments([
'--cauliflower-system',
'--exclude-files',
'one',
'--exclude-files',
'three',
'--number-of-heads',
'14',
'--warn-unrecognized-vegetable',
'--extra-special',
'whatever',
'--respawn-probability',
'0.123',
'--a-fancy-map-variable',
'aKey::aValue,another key::another value',
]);
expect(
dartdocOptionSetArgs['cauliflowerSystem'].valueAt(tempDir), isTrue);
expect(
dartdocOptionSetArgs['number_of_heads'].valueAt(tempDir), equals(14));
expect(dartdocOptionSetArgs['respawnProbability'].valueAt(tempDir),
equals(0.123));
expect(dartdocOptionSetArgs['excludeFiles'].valueAt(tempDir),
equals(['one', 'three']));
expect(
dartdocOptionSetArgs['warn']['unrecognizedVegetable']
.valueAt(tempDir),
isTrue);
expect(dartdocOptionSetArgs['extraSpecial'].valueAt(tempDir),
equals('whatever'));
expect(dartdocOptionSetArgs['aFancyMapVariable'].valueAt(tempDir),
equals({'aKey': 'aValue', 'another key': 'another value'}));
});
test('DartdocOptionArgOnly throws on double type mismatch', () {
dartdocOptionSetArgs.parseArguments(['--respawn-probability', 'unknown']);
String errorMessage;
try {
dartdocOptionSetArgs['respawnProbability'].valueAt(tempDir);
} on DartdocOptionError catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Invalid argument value: --respawn-probability, set to "unknown", must be a double. Example: --respawn-probability 0.76'));
});
test('DartdocOptionArgOnly throws on integer type mismatch', () {
dartdocOptionSetArgs.parseArguments(['--number-of-heads', '3.6']);
expect(() => dartdocOptionSetArgs['number_of_heads'].valueAt(tempDir),
throwsA(const TypeMatcher<DartdocOptionError>()));
String errorMessage;
try {
dartdocOptionSetArgs['number_of_heads'].valueAt(tempDir);
} on DartdocOptionError catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Invalid argument value: --number-of-heads, set to "3.6", must be a int. Example: --number-of-heads 32'));
});
test('DartdocOptionArgOnly throws on a map type mismatch', () {
dartdocOptionSetArgs
.parseArguments(['--a-fancy-map-variable', 'not a map']);
String errorMessage;
try {
dartdocOptionSetArgs['aFancyMapVariable'].valueAt(tempDir);
} on DartdocOptionError catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Invalid argument value: --a-fancy-map-variable, set to "not a map", must be a Map<String, String>. Example: --a-fancy-map-variable key::value'));
});
});
group('new style dartdoc file-only options', () {
test('DartdocOptionSetFile can convert YamlMaps to structured data', () {
ConvertedOption converted =
dartdocOptionSetFiles['convertThisMap'].valueAt(firstDir);
expect(converted.param1, equals('value1'));
expect(converted.param2, equals('value2'));
expect(converted.myContextPath, equals(path.canonicalize(firstDir.path)));
expect(
dartdocOptionSetFiles['convertThisMap'].valueAt(secondDir), isNull);
});
test('DartdocOptionSetFile checks file existence when appropriate', () {
String errorMessage;
try {
dartdocOptionSetFiles['fileOptionList'].valueAt(secondDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Field dartdoc.fileOptionList from ${path.canonicalize(dartdocOptionsTwo.path)}, set to [existing.dart, thing/that/does/not/exist], resolves to missing path: '
'"${path.joinAll([
path.canonicalize(secondDir.path),
'thing',
'that',
'does',
'not',
'exist'
])}"'));
// It doesn't matter that this fails.
expect(dartdocOptionSetFiles['nonCriticalFileOption'].valueAt(firstDir),
equals(path.joinAll([path.canonicalize(firstDir.path), 'whatever'])));
});
test(
'DartdocOptionSetFile resolves paths for files relative to where they are declared',
() {
String errorMessage;
try {
dartdocOptionSetFiles['fileOption'].valueAt(secondDirFirstSub);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Field dartdoc.fileOption from ${path.canonicalize(dartdocOptionsTwo.path)}, set to not existing, resolves to missing path: '
'"${path.joinAll([
path.canonicalize(secondDir.path),
"not existing"
])}"'));
});
test('DartdocOptionSetFile works for directory options', () {
expect(
dartdocOptionSetFiles['nonCriticalDirOption']
.valueAt(secondDirFirstSub),
equals(path.join(
path.canonicalize(secondDirFirstSub.path), 'not_existing')));
});
test('DartdocOptionSetFile checks errors for directory options', () {
expect(dartdocOptionSetFiles['dirOption'].valueAt(secondDir),
equals(path.canonicalize(path.join(secondDir.path, 'firstSub'))));
String errorMessage;
try {
dartdocOptionSetFiles['dirOption'].valueAt(firstDir);
} on DartdocFileMissing catch (e) {
errorMessage = e.message;
}
expect(
errorMessage,
equals(
'Field dartdoc.dirOption from ${path.canonicalize(dartdocOptionsOne.path)}, set to notHere, resolves to missing path: '
'"${path.canonicalize(path.join(firstDir.path, "notHere"))}"'));
});
test('DartdocOptionSetFile loads defaults', () {
expect(dartdocOptionSetFiles['categoryOrder'].valueAt(tempDir), isEmpty);
expect(dartdocOptionSetFiles['nestedOption']['flag'].valueAt(tempDir),
equals(false));
expect(dartdocOptionSetFiles['double'].valueAt(tempDir), equals(3.0));
expect(dartdocOptionSetFiles['parentOverride'].valueAt(tempDir),
equals('oops'));
expect(dartdocOptionSetFiles['mySpecialInteger'].valueAt(tempDir),
equals(42));
expect(dartdocOptionSetFiles['mapOption'].valueAt(tempDir),
equals({'hello': 'world'}));
});
test('DartdocOptionSetFile loads a file', () {
expect(dartdocOptionSetFiles['categoryOrder'].valueAt(firstDir),
orderedEquals(['options_one']));
expect(dartdocOptionSetFiles['nestedOption']['flag'].valueAt(firstDir),
equals(true));
expect(dartdocOptionSetFiles['double'].valueAt(firstDir), equals(3.3));
expect(dartdocOptionSetFiles['mySpecialInteger'].valueAt(firstDir),
equals(30));
expect(dartdocOptionSetFiles['mapOption'].valueAt(firstDir),
equals({'firstThing': 'yes', 'secondThing': 'stuff'}));
});
test('DartdocOptionSetFile loads a file in parent directories', () {
expect(dartdocOptionSetFiles['categoryOrder'].valueAt(secondDirSecondSub),
orderedEquals(['options_two']));
});
test('DartdocOptionSetFile loads the override file instead of parents', () {
expect(dartdocOptionSetFiles['categoryOrder'].valueAt(secondDirFirstSub),
orderedEquals(['options_two_first_sub']));
});
test('DartdocOptionSetFile lets parents override children when appropriate',
() {
expect(dartdocOptionSetFiles['parentOverride'].valueAt(secondDirFirstSub),
equals('parent'));
});
});
}