diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d54ee..1764726 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.0.2 + +- Return non-zero exit code from executable when incorrect args are used + # 2.0.1 - Fix a path issue on Windows. diff --git a/bin/dependency_validator.dart b/bin/dependency_validator.dart index 8091f1e..b173b4c 100644 --- a/bin/dependency_validator.dart +++ b/bin/dependency_validator.dart @@ -16,11 +16,13 @@ import 'dart:io' show exit, stderr, stdout; import 'package:args/args.dart'; import 'package:dependency_validator/dependency_validator.dart'; +import 'package:io/io.dart'; import 'package:logging/logging.dart'; const String helpArg = 'help'; const String verboseArg = 'verbose'; -const String helpMessage = '''Dependency Validator 2.0 is configured statically via the pubspec.yaml +const String helpMessage = + '''Dependency Validator 2.0 is configured statically via the pubspec.yaml example: # in pubspec.yaml dependency_validator: @@ -45,10 +47,10 @@ final ArgParser argParser = ArgParser() help: 'Display extra information for debugging.', ); -void showHelpAndExit() { +void showHelpAndExit({ExitCode exitCode = ExitCode.success}) { Logger.root.shout(helpMessage); Logger.root.shout(argParser.usage); - exit(0); + exit(exitCode.code); } void main(List args) async { @@ -65,7 +67,7 @@ void main(List args) async { try { argResults = argParser.parse(args); } on FormatException catch (_) { - showHelpAndExit(); + showHelpAndExit(exitCode: ExitCode.usage); } if (argResults.wasParsed(helpArg) && argResults[helpArg]) { diff --git a/lib/dependency_validator.dart b/lib/dependency_validator.dart index 1f35e89..943c9d2 100644 --- a/lib/dependency_validator.dart +++ b/lib/dependency_validator.dart @@ -32,11 +32,14 @@ Future run() async { exit(1); } if (!File('.dart_tool/package_config.json').existsSync()) { - logger.shout('No .dart_tool/package_config.json file found, please run "pub get" first.'); + logger.shout( + 'No .dart_tool/package_config.json file found, please run "pub get" first.'); exit(1); } - final config = PubspecDepValidatorConfig.fromYaml(File('pubspec.yaml').readAsStringSync()).dependencyValidator; + final config = PubspecDepValidatorConfig.fromYaml( + File('pubspec.yaml').readAsStringSync()) + .dependencyValidator; final configExcludes = config?.exclude ?.map((s) { try { @@ -57,7 +60,8 @@ Future run() async { final optionsIncludePackage = getAnalysisOptionsIncludePackage(); // Read and parse the pubspec.yaml in the current working directory. - final pubspec = Pubspec.parse(File('pubspec.yaml').readAsStringSync(), sourceUrl: 'pubspec.yaml'); + final pubspec = Pubspec.parse(File('pubspec.yaml').readAsStringSync(), + sourceUrl: 'pubspec.yaml'); logger.info('Validating dependencies for ${pubspec.name}\n'); @@ -95,7 +99,8 @@ Future run() async { // export directive. final packagesUsedInPublicFiles = {}; for (final file in publicDartFiles) { - final matches = importExportDartPackageRegex.allMatches(file.readAsStringSync()); + final matches = + importExportDartPackageRegex.allMatches(file.readAsStringSync()); for (final match in matches) { packagesUsedInPublicFiles.add(match.group(2)); } @@ -117,9 +122,12 @@ Future run() async { final publicDirGlobs = [for (final dir in publicDirs) Glob('$dir**')]; - final nonPublicDartFiles = listDartFilesIn('./', [...excludes, ...publicDirGlobs]); - final nonPublicScssFiles = listScssFilesIn('./', [...excludes, ...publicDirGlobs]); - final nonPublicLessFiles = listLessFilesIn('./', [...excludes, ...publicDirGlobs]); + final nonPublicDartFiles = + listDartFilesIn('./', [...excludes, ...publicDirGlobs]); + final nonPublicScssFiles = + listScssFilesIn('./', [...excludes, ...publicDirGlobs]); + final nonPublicLessFiles = + listLessFilesIn('./', [...excludes, ...publicDirGlobs]); logger ..fine('non-public dart files:\n' @@ -137,7 +145,8 @@ Future run() async { if (optionsIncludePackage != null) optionsIncludePackage, }; for (final file in nonPublicDartFiles) { - final matches = importExportDartPackageRegex.allMatches(file.readAsStringSync()); + final matches = + importExportDartPackageRegex.allMatches(file.readAsStringSync()); for (final match in matches) { packagesUsedOutsidePublicDirs.add(match.group(2)); } @@ -165,10 +174,10 @@ Future run() async { // Remove all explicitly declared dependencies .difference(deps) .difference(devDeps) - // Ignore self-imports - packages have implicit access to themselves. - ..remove(pubspec.name) - // Ignore known missing packages. - ..removeAll(ignoredPackages); + // Ignore self-imports - packages have implicit access to themselves. + ..remove(pubspec.name) + // Ignore known missing packages. + ..removeAll(ignoredPackages); if (missingDependencies.isNotEmpty) { log( @@ -187,10 +196,10 @@ Future run() async { // Remove all explicitly declared dependencies .difference(devDeps) .difference(deps) - // Ignore self-imports - packages have implicit access to themselves. - ..remove(pubspec.name) - // Ignore known missing packages. - ..removeAll(ignoredPackages); + // Ignore self-imports - packages have implicit access to themselves. + ..remove(pubspec.name) + // Ignore known missing packages. + ..removeAll(ignoredPackages); if (missingDevDependencies.isNotEmpty) { log( @@ -245,16 +254,20 @@ Future run() async { // Remove all deps that were used in Dart code somewhere in this package .difference(packagesUsedInPublicFiles) .difference(packagesUsedOutsidePublicDirs) - // Remove this package, since we know they're using our executable - ..remove(dependencyValidatorPackageName); + // Remove this package, since we know they're using our executable + ..remove(dependencyValidatorPackageName); // Remove deps that provide builders that will be applied final packageConfig = await findPackageConfig(Directory.current); - final rootBuildConfig = await BuildConfig.fromBuildConfigDir(pubspec.name, pubspec.dependencies.keys, '.'); + final rootBuildConfig = await BuildConfig.fromBuildConfigDir( + pubspec.name, pubspec.dependencies.keys, '.'); bool rootPackageReferencesDependencyInBuildYaml(String dependencyName) => [ ...rootBuildConfig.globalOptions.keys, - for (final target in rootBuildConfig.buildTargets.values) ...target.builders.keys, - ].map((key) => normalizeBuilderKeyUsage(key, pubspec.name)).any((key) => key.startsWith('$dependencyName:')); + for (final target in rootBuildConfig.buildTargets.values) + ...target.builders.keys, + ] + .map((key) => normalizeBuilderKeyUsage(key, pubspec.name)) + .any((key) => key.startsWith('$dependencyName:')); final packagesWithConsumedBuilders = Set(); for (final package in unusedDependencies.map((name) => packageConfig[name])) { @@ -277,14 +290,18 @@ Future run() async { for (final package in unusedDependencies.map((name) => packageConfig[name])) { // Search for executables, if found we assume they are used final binDir = Directory(p.join(p.fromUri(package.root), 'bin')); - hasDartFiles() => binDir.listSync().any((entity) => entity.path.endsWith('.dart')); + hasDartFiles() => + binDir.listSync().any((entity) => entity.path.endsWith('.dart')); if (binDir.existsSync() && hasDartFiles()) { packagesWithExecutables.add(package.name); } } - logIntersection(Level.INFO, 'The following packages contain executables, they are assumed to be used:', - unusedDependencies, packagesWithExecutables); + logIntersection( + Level.INFO, + 'The following packages contain executables, they are assumed to be used:', + unusedDependencies, + packagesWithExecutables); unusedDependencies.removeAll(packagesWithExecutables); if (unusedDependencies.contains('analyzer')) { @@ -313,7 +330,10 @@ Future run() async { /// Whether a dependency at [path] defines an auto applied builder. Future dependencyDefinesAutoAppliedBuilder(String path) async => - (await BuildConfig.fromPackageDir(path)).builderDefinitions.values.any((def) => def.autoApply != AutoApply.none); + (await BuildConfig.fromPackageDir(path)) + .builderDefinitions + .values + .any((def) => def.autoApply != AutoApply.none); /// Checks for dependency pins. /// @@ -338,12 +358,15 @@ void checkPubspecForPins( List ignoredPackages = const [], }) { final List infractions = []; - infractions.addAll(getDependenciesWithPins(pubspec.dependencies, ignoredPackages: ignoredPackages)); + infractions.addAll(getDependenciesWithPins(pubspec.dependencies, + ignoredPackages: ignoredPackages)); - infractions.addAll(getDependenciesWithPins(pubspec.devDependencies, ignoredPackages: ignoredPackages)); + infractions.addAll(getDependenciesWithPins(pubspec.devDependencies, + ignoredPackages: ignoredPackages)); if (infractions.isNotEmpty) { - log(Level.WARNING, 'These packages are pinned in pubspec.yaml:', infractions); + log(Level.WARNING, 'These packages are pinned in pubspec.yaml:', + infractions); exitCode = 1; } } diff --git a/lib/src/constants.dart b/lib/src/constants.dart index 57d32a0..458d634 100644 --- a/lib/src/constants.dart +++ b/lib/src/constants.dart @@ -1,12 +1,15 @@ /// Regex used to detect all Dart import and export directives. -final RegExp importExportDartPackageRegex = - RegExp(r'''\b(import|export)\s+['"]{1,3}package:([a-zA-Z0-9_]+)\/[^;]+''', multiLine: true); +final RegExp importExportDartPackageRegex = RegExp( + r'''\b(import|export)\s+['"]{1,3}package:([a-zA-Z0-9_]+)\/[^;]+''', + multiLine: true); /// Regex used to detect all Sass import directives. -final RegExp importScssPackageRegex = RegExp(r'''\@import\s+['"]{1,3}package:\s*([a-zA-Z0-9_]+)\/[^;]+'''); +final RegExp importScssPackageRegex = + RegExp(r'''\@import\s+['"]{1,3}package:\s*([a-zA-Z0-9_]+)\/[^;]+'''); /// Regex used to detect all Less import directives. -final RegExp importLessPackageRegex = RegExp(r'@import\s+(?:\(.*\)\s+)?"(?:packages\/|package:\/\/)([a-zA-Z1-9_-]+)\/'); +final RegExp importLessPackageRegex = RegExp( + r'@import\s+(?:\(.*\)\s+)?"(?:packages\/|package:\/\/)([a-zA-Z1-9_-]+)\/'); /// String key in pubspec.yaml for the dependencies map. const String dependenciesKey = 'dependencies'; @@ -34,7 +37,8 @@ class DependencyPinEvaluation { String toString() => message; /// <1.2.0 - static const DependencyPinEvaluation blocksMinorBumps = DependencyPinEvaluation._('This pin blocks minor bumps.'); + static const DependencyPinEvaluation blocksMinorBumps = + DependencyPinEvaluation._('This pin blocks minor bumps.'); /// <1.2.3 static const DependencyPinEvaluation blocksPatchReleases = @@ -45,18 +49,20 @@ class DependencyPinEvaluation { /// Note that <1.0.0-0 is legal because the exclusive bounds ignore the first /// possible prerelease. static const DependencyPinEvaluation buildOrPrerelease = - DependencyPinEvaluation._('Builds or preleases as max bounds block minor bumps and patches.'); + DependencyPinEvaluation._( + 'Builds or preleases as max bounds block minor bumps and patches.'); /// 1.2.3 - static const DependencyPinEvaluation directPin = DependencyPinEvaluation._('This is a direct pin.'); + static const DependencyPinEvaluation directPin = + DependencyPinEvaluation._('This is a direct pin.'); /// >1.2.3 <1.2.3 - static const DependencyPinEvaluation emptyPin = - DependencyPinEvaluation._('Empty dependency versions cannot be resolved.'); + static const DependencyPinEvaluation emptyPin = DependencyPinEvaluation._( + 'Empty dependency versions cannot be resolved.'); /// <=1.2.3 - static const DependencyPinEvaluation inclusiveMax = - DependencyPinEvaluation._('Inclusive max bounds restrict minor bumps and patches.'); + static const DependencyPinEvaluation inclusiveMax = DependencyPinEvaluation._( + 'Inclusive max bounds restrict minor bumps and patches.'); /// :) static const DependencyPinEvaluation notAPin = diff --git a/lib/src/pubspec_config.dart b/lib/src/pubspec_config.dart index d78b079..846aa3c 100644 --- a/lib/src/pubspec_config.dart +++ b/lib/src/pubspec_config.dart @@ -3,19 +3,30 @@ import 'package:json_annotation/json_annotation.dart'; part 'pubspec_config.g.dart'; -@JsonSerializable(anyMap: true, checked: true, createToJson: false, fieldRename: FieldRename.snake) +@JsonSerializable( + anyMap: true, + checked: true, + createToJson: false, + fieldRename: FieldRename.snake) class PubspecDepValidatorConfig { final DepValidatorConfig dependencyValidator; PubspecDepValidatorConfig({this.dependencyValidator}); - factory PubspecDepValidatorConfig.fromJson(Map json) => _$PubspecDepValidatorConfigFromJson(json); + factory PubspecDepValidatorConfig.fromJson(Map json) => + _$PubspecDepValidatorConfigFromJson(json); factory PubspecDepValidatorConfig.fromYaml(String yamlContent, {sourceUrl}) => - checkedYamlDecode(yamlContent, (m) => PubspecDepValidatorConfig.fromJson(m), sourceUrl: sourceUrl); + checkedYamlDecode( + yamlContent, (m) => PubspecDepValidatorConfig.fromJson(m), + sourceUrl: sourceUrl); } -@JsonSerializable(anyMap: true, checked: true, createToJson: false, fieldRename: FieldRename.snake) +@JsonSerializable( + anyMap: true, + checked: true, + createToJson: false, + fieldRename: FieldRename.snake) class DepValidatorConfig { final List exclude; @@ -23,5 +34,6 @@ class DepValidatorConfig { DepValidatorConfig({this.exclude, this.ignore}); - factory DepValidatorConfig.fromJson(Map json) => _$DepValidatorConfigFromJson(json); + factory DepValidatorConfig.fromJson(Map json) => + _$DepValidatorConfigFromJson(json); } diff --git a/lib/src/pubspec_config.g.dart b/lib/src/pubspec_config.g.dart index 8d382df..e4461c3 100644 --- a/lib/src/pubspec_config.g.dart +++ b/lib/src/pubspec_config.g.dart @@ -9,8 +9,8 @@ part of 'pubspec_config.dart'; PubspecDepValidatorConfig _$PubspecDepValidatorConfigFromJson(Map json) { return $checkedNew('PubspecDepValidatorConfig', json, () { final val = PubspecDepValidatorConfig( - dependencyValidator: $checkedConvert( - json, 'dependency_validator', (v) => v == null ? null : DepValidatorConfig.fromJson(v as Map)), + dependencyValidator: $checkedConvert(json, 'dependency_validator', + (v) => v == null ? null : DepValidatorConfig.fromJson(v as Map)), ); return val; }, fieldKeyMap: const {'dependencyValidator': 'dependency_validator'}); @@ -19,8 +19,10 @@ PubspecDepValidatorConfig _$PubspecDepValidatorConfigFromJson(Map json) { DepValidatorConfig _$DepValidatorConfigFromJson(Map json) { return $checkedNew('DepValidatorConfig', json, () { final val = DepValidatorConfig( - exclude: $checkedConvert(json, 'exclude', (v) => (v as List)?.map((e) => e as String)?.toList()), - ignore: $checkedConvert(json, 'ignore', (v) => (v as List)?.map((e) => e as String)?.toList()), + exclude: $checkedConvert(json, 'exclude', + (v) => (v as List)?.map((e) => e as String)?.toList()), + ignore: $checkedConvert(json, 'ignore', + (v) => (v as List)?.map((e) => e as String)?.toList()), ); return val; }); diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 2fc6fa0..5601805 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -27,7 +27,8 @@ import 'constants.dart'; final Logger logger = Logger('dependency_validator'); /// Returns a multi-line string with all [items] in a bulleted list format. -String bulletItems(Iterable items) => items.map((l) => ' * $l').join('\n'); +String bulletItems(Iterable items) => + items.map((l) => ' * $l').join('\n'); /// Returns the name of the package referenced in the `include:` directive in an /// analysis_options.yaml file, or null if there is not one. @@ -72,14 +73,16 @@ Iterable listLessFilesIn(String dirPath, List excludedDirs) => /// /// This also excludes Dart files that are in a hidden directory, like /// `.dart_tool`. -Iterable listFilesWithExtensionIn(String dirPath, List excludes, String ext) { +Iterable listFilesWithExtensionIn( + String dirPath, List excludes, String ext) { if (!FileSystemEntity.isDirectorySync(dirPath)) return []; return Directory(dirPath) .listSync(recursive: true) .whereType() // Skip files in hidden directories (e.g. `.dart_tool/`) - .where((file) => !p.split(file.path).any((d) => d != '.' && d.startsWith('.'))) + .where((file) => + !p.split(file.path).any((d) => d != '.' && d.startsWith('.'))) // Filter by the given file extension .where((file) => p.extension(file.path) == '.$ext') // Skip any files that match one of the given exclude globs @@ -94,7 +97,8 @@ void log(Level level, String message, Iterable dependencies) { /// Logs the given [message] at [level] and lists the intersection of [dependenciesA] /// and [dependenciesB] if there is one. -void logIntersection(Level level, String message, Set dependenciesA, Set dependenciesB) { +void logIntersection(Level level, String message, Set dependenciesA, + Set dependenciesB) { final intersection = dependenciesA.intersection(dependenciesB); if (intersection.isNotEmpty) { log(level, message, intersection); @@ -102,7 +106,8 @@ void logIntersection(Level level, String message, Set dependenciesA, Set } /// Lists the packages with infractions -List getDependenciesWithPins(Map dependencies, {List ignoredPackages = const []}) { +List getDependenciesWithPins(Map dependencies, + {List ignoredPackages = const []}) { final List infractions = []; for (String packageName in dependencies.keys) { if (ignoredPackages.contains(packageName)) { @@ -113,7 +118,8 @@ List getDependenciesWithPins(Map dependencies, {List final packageMeta = dependencies[packageName]; if (packageMeta is HostedDependency) { - final DependencyPinEvaluation evaluation = inspectVersionForPins(packageMeta.version); + final DependencyPinEvaluation evaluation = + inspectVersionForPins(packageMeta.version); if (evaluation.isPin) { infractions.add('$packageName: $version -- ${evaluation.message}'); diff --git a/pubspec.yaml b/pubspec.yaml index 0fa5c1a..10aad0b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,7 @@ dependencies: build_config: ^0.4.2 checked_yaml: ^1.0.0 glob: ^1.2.0 + io: ^0.3.5 json_annotation: ^3.0.0 logging: ^0.11.3+1 package_config: ^1.9.3 diff --git a/test/executable_test.dart b/test/executable_test.dart index ae64979..445eae0 100644 --- a/test/executable_test.dart +++ b/test/executable_test.dart @@ -15,10 +15,12 @@ @TestOn('vm') import 'dart:io'; +import 'package:io/io.dart'; import 'package:test/test.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; -ProcessResult checkProject(String projectPath) { +ProcessResult checkProject(String projectPath, + {List optionalArgs = const []}) { Process.runSync('pub', ['get'], workingDirectory: projectPath); final args = [ @@ -26,6 +28,7 @@ ProcessResult checkProject(String projectPath) { 'dependency_validator', // This makes it easier to print(result.stdout) for debugging tests '--verbose', + ...optionalArgs, ]; return Process.runSync('pub', args, workingDirectory: projectPath); @@ -73,6 +76,31 @@ void main() { ]).create(); }); + test('fails with incorrect usage', () async { + final pubspec = unindent(''' + name: common_binaries + version: 0.0.0 + private: true + environment: + sdk: '>=2.4.0 <3.0.0' + dev_dependencies: + dependency_validator: + path: ${Directory.current.path} + '''); + + await d.dir('common_binaries', [ + d.dir('lib', [ + d.file('fake.dart', 'bool fake = true;'), + ]), + d.file('pubspec.yaml', pubspec), + ]).create(); + + final result = checkProject('${d.sandbox}/common_binaries', + optionalArgs: ['-x', 'tool/wdesk_sdk']); + + expect(result.exitCode, ExitCode.usage.code); + }); + group('fails when there are packages missing from the pubspec', () { setUp(() async { final pubspec = unindent(''' @@ -99,7 +127,10 @@ void main() { final result = checkProject('${d.sandbox}/missing'); expect(result.exitCode, equals(1)); - expect(result.stderr, contains('These packages are used in lib/ but are not dependencies:')); + expect( + result.stderr, + contains( + 'These packages are used in lib/ but are not dependencies:')); expect(result.stderr, contains('yaml')); expect(result.stderr, contains('somescsspackage')); }); @@ -111,8 +142,10 @@ void main() { - 'lib/**' '''); - File('${d.sandbox}/missing/pubspec.yaml') - .writeAsStringSync(dependencyValidatorSection, mode: FileMode.append, flush: true); + File('${d.sandbox}/missing/pubspec.yaml').writeAsStringSync( + dependencyValidatorSection, + mode: FileMode.append, + flush: true); final result = checkProject('${d.sandbox}/missing'); @@ -128,7 +161,9 @@ void main() { - somescsspackage '''); - File('${d.sandbox}/missing/pubspec.yaml').writeAsStringSync(dependencyValidatorSection, mode: FileMode.append); + File('${d.sandbox}/missing/pubspec.yaml').writeAsStringSync( + dependencyValidatorSection, + mode: FileMode.append); final result = checkProject('${d.sandbox}/missing'); expect(result.exitCode, 0); @@ -164,8 +199,10 @@ void main() { final result = checkProject('${d.sandbox}/over_promoted'); expect(result.exitCode, 1); - expect(result.stderr, - contains('These packages are only used outside lib/ and should be downgraded to dev_dependencies:')); + expect( + result.stderr, + contains( + 'These packages are only used outside lib/ and should be downgraded to dev_dependencies:')); expect(result.stderr, contains('path')); expect(result.stderr, contains('yaml')); }); @@ -178,8 +215,9 @@ void main() { - yaml '''); - File('${d.sandbox}/over_promoted/pubspec.yaml') - .writeAsStringSync(dependencyValidatorSection, mode: FileMode.append); + File('${d.sandbox}/over_promoted/pubspec.yaml').writeAsStringSync( + dependencyValidatorSection, + mode: FileMode.append); final result = checkProject('${d.sandbox}/over_promoted'); expect(result.exitCode, 0); @@ -203,7 +241,8 @@ void main() { await d.dir('under_promoted', [ d.dir('lib', [ - d.file('under_promoted.dart', 'import \'package:logging/logging.dart\';'), + d.file('under_promoted.dart', + 'import \'package:logging/logging.dart\';'), d.file('under_promoted.scss', '@import \'package:yaml/foo\';'), ]), d.file('pubspec.yaml', pubspec), @@ -215,7 +254,9 @@ void main() { expect(result.exitCode, 1); expect( - result.stderr, contains('These packages are used in lib/ and should be promoted to actual dependencies:')); + result.stderr, + contains( + 'These packages are used in lib/ and should be promoted to actual dependencies:')); expect(result.stderr, contains('logging')); expect(result.stderr, contains('yaml')); }); @@ -228,8 +269,9 @@ void main() { - yaml '''); - File('${d.sandbox}/under_promoted/pubspec.yaml') - .writeAsStringSync(dependencyValidatorSection, mode: FileMode.append); + File('${d.sandbox}/under_promoted/pubspec.yaml').writeAsStringSync( + dependencyValidatorSection, + mode: FileMode.append); final result = checkProject('${d.sandbox}/under_promoted'); expect(result.exitCode, 0); @@ -261,7 +303,9 @@ void main() { expect(result.exitCode, 1); expect( - result.stderr, contains('These packages may be unused, or you may be using assets from these packages:')); + result.stderr, + contains( + 'These packages may be unused, or you may be using assets from these packages:')); expect(result.stderr, contains('fake_project')); }); @@ -273,15 +317,19 @@ void main() { - yaml '''); - File('${d.sandbox}/unused/pubspec.yaml').writeAsStringSync(dependencyValidatorSection, mode: FileMode.append); + File('${d.sandbox}/unused/pubspec.yaml').writeAsStringSync( + dependencyValidatorSection, + mode: FileMode.append); final result = checkProject('${d.sandbox}/unused'); expect(result.exitCode, 0); - expect(result.stdout, contains('No fatal infractions found, unused is good to go!')); + expect(result.stdout, + contains('No fatal infractions found, unused is good to go!')); }); }); - test('warns when the analyzer package is depended on but not used', () async { + test('warns when the analyzer package is depended on but not used', + () async { final pubspec = unindent(''' name: analyzer_dep version: 0.0.0 @@ -308,7 +356,10 @@ void main() { final result = checkProject('${d.sandbox}/project'); expect(result.exitCode, 0); - expect(result.stderr, contains('You do not need to depend on `analyzer` to run the Dart analyzer.')); + expect( + result.stderr, + contains( + 'You do not need to depend on `analyzer` to run the Dart analyzer.')); }); test('passes when all dependencies are used and valid', () async { @@ -339,13 +390,15 @@ void main() { d.file('valid.scss', '@import \'package:yaml/foo\';'), ]), d.file('pubspec.yaml', pubspec), - d.file('analysis_options.yaml', 'include: package:pedantic/analysis_options.1.8.0.yaml'), + d.file('analysis_options.yaml', + 'include: package:pedantic/analysis_options.1.8.0.yaml'), ]).create(); final result = checkProject('${d.sandbox}/valid'); expect(result.exitCode, 0); - expect(result.stdout, contains('No fatal infractions found, valid is good to go!')); + expect(result.stdout, + contains('No fatal infractions found, valid is good to go!')); }); test('passes when dependencies not used provide executables', () async { @@ -374,10 +427,15 @@ void main() { final result = checkProject('${d.sandbox}/common_binaries'); expect(result.exitCode, 0); - expect(result.stdout, contains('No fatal infractions found, common_binaries is good to go!')); + expect( + result.stdout, + contains( + 'No fatal infractions found, common_binaries is good to go!')); }); - test('passes when dependencies are not imported but provide auto applied builders', () async { + test( + 'passes when dependencies are not imported but provide auto applied builders', + () async { final pubspec = unindent(''' name: common_binaries version: 0.0.0 @@ -403,10 +461,14 @@ void main() { final result = checkProject('${d.sandbox}/common_binaries'); expect(result.exitCode, 0); - expect(result.stdout, contains('No fatal infractions found, common_binaries is good to go!')); + expect( + result.stdout, + contains( + 'No fatal infractions found, common_binaries is good to go!')); }); - test('passes when dependencies are not imported but provide used builders', () async { + test('passes when dependencies are not imported but provide used builders', + () async { final pubspec = unindent(''' name: common_binaries version: 0.0.0 @@ -438,7 +500,10 @@ void main() { final result = checkProject('${d.sandbox}/common_binaries'); expect(result.exitCode, 0); - expect(result.stdout, contains('No fatal infractions found, common_binaries is good to go!')); + expect( + result.stdout, + contains( + 'No fatal infractions found, common_binaries is good to go!')); }); group('when a dependency is pinned', () { @@ -464,7 +529,10 @@ void main() { final result = checkProject('${d.sandbox}/dependency_pins'); expect(result.exitCode, 1); - expect(result.stderr, contains('These packages are pinned in pubspec.yaml:\n * logging')); + expect( + result.stderr, + contains( + 'These packages are pinned in pubspec.yaml:\n * logging')); }); test('ignores infractions if the package is ignored', () { @@ -474,13 +542,17 @@ void main() { - logging '''); - File('${d.sandbox}/dependency_pins/pubspec.yaml') - .writeAsStringSync(dependencyValidatorSection, mode: FileMode.append); + File('${d.sandbox}/dependency_pins/pubspec.yaml').writeAsStringSync( + dependencyValidatorSection, + mode: FileMode.append); final result = checkProject('${d.sandbox}/dependency_pins'); expect(result.exitCode, 0); - expect(result.stdout, contains('No fatal infractions found, dependency_pins is good to go')); + expect( + result.stdout, + contains( + 'No fatal infractions found, dependency_pins is good to go')); }); }); }); diff --git a/test/utils_test.dart b/test/utils_test.dart index 7069dae..40c2ce5 100644 --- a/test/utils_test.dart +++ b/test/utils_test.dart @@ -49,53 +49,65 @@ include: package:pedantic/analysis_options.1.8.0.yaml }); group('importExportDartPackageRegex matches correctly for', () { - void sharedTest(String input, String expectedGroup1, String expectedGroup2) { + void sharedTest( + String input, String expectedGroup1, String expectedGroup2) { expect(input, matches(importExportDartPackageRegex)); - expect(importExportDartPackageRegex.firstMatch(input).groups([1, 2]), [expectedGroup1, expectedGroup2]); + expect(importExportDartPackageRegex.firstMatch(input).groups([1, 2]), + [expectedGroup1, expectedGroup2]); } for (var importOrExport in ['import', 'export']) { group('an $importOrExport line', () { test('with double-quotes', () { - sharedTest('$importOrExport "package:foo/bar.dart";', importOrExport, 'foo'); + sharedTest( + '$importOrExport "package:foo/bar.dart";', importOrExport, 'foo'); }); test('with single-quotes', () { - sharedTest('$importOrExport \'package:foo/bar.dart\';', importOrExport, 'foo'); + sharedTest('$importOrExport \'package:foo/bar.dart\';', + importOrExport, 'foo'); }); test('with triple double-quotes', () { - sharedTest('$importOrExport """package:foo/bar.dart""";', importOrExport, 'foo'); + sharedTest('$importOrExport """package:foo/bar.dart""";', + importOrExport, 'foo'); }); test('with triple single-quotes', () { - sharedTest('$importOrExport \'\'\'package:foo/bar.dart\'\'\';', importOrExport, 'foo'); + sharedTest('$importOrExport \'\'\'package:foo/bar.dart\'\'\';', + importOrExport, 'foo'); }); group('with a package name that', () { test('contains underscores', () { - sharedTest('$importOrExport "package:foo_foo/bar.dart";', importOrExport, 'foo_foo'); + sharedTest('$importOrExport "package:foo_foo/bar.dart";', + importOrExport, 'foo_foo'); }); test('contains numbers', () { - sharedTest('$importOrExport "package:foo1/bar.dart";', importOrExport, 'foo1'); + sharedTest('$importOrExport "package:foo1/bar.dart";', + importOrExport, 'foo1'); }); test('starts with an underscore', () { - sharedTest('$importOrExport "package:_foo/bar.dart";', importOrExport, '_foo'); + sharedTest('$importOrExport "package:_foo/bar.dart";', + importOrExport, '_foo'); }); }); test('with extra whitespace in the line', () { - sharedTest(' $importOrExport "package:foo/bar.dart" ; ', importOrExport, 'foo'); + sharedTest(' $importOrExport "package:foo/bar.dart" ; ', + importOrExport, 'foo'); }); test('with multiple ${importOrExport}s in the same line', () { - final input = '$importOrExport "package:foo/bar.dart"; $importOrExport "package:bar/foo.dart";'; + final input = + '$importOrExport "package:foo/bar.dart"; $importOrExport "package:bar/foo.dart";'; expect(input, matches(importExportDartPackageRegex)); - final allMatches = importExportDartPackageRegex.allMatches(input).toList(); + final allMatches = + importExportDartPackageRegex.allMatches(input).toList(); expect(allMatches, hasLength(2)); expect(allMatches[0].groups([1, 2]), [importOrExport, 'foo']); @@ -206,106 +218,136 @@ include: package:pedantic/analysis_options.1.8.0.yaml group('inspectVersionForPins classifies', () { test('any', () { - expect(inspectVersionForPins(VersionConstraint.parse('any')), DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('any')), + DependencyPinEvaluation.notAPin); }); test('empty', () { - expect(inspectVersionForPins(VersionConstraint.parse('>0.0.0 <0.0.0')), DependencyPinEvaluation.emptyPin); + expect(inspectVersionForPins(VersionConstraint.parse('>0.0.0 <0.0.0')), + DependencyPinEvaluation.emptyPin); }); test('caret notation', () { - expect(inspectVersionForPins(VersionConstraint.parse('^0.0.1')), DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('^0.2.4')), DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('^1.2.4')), DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('^0.0.1')), + DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('^0.2.4')), + DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('^1.2.4')), + DependencyPinEvaluation.notAPin); }); test('1.2.3', () { - expect(inspectVersionForPins(VersionConstraint.parse('1.23.456')), DependencyPinEvaluation.directPin); + expect(inspectVersionForPins(VersionConstraint.parse('1.23.456')), + DependencyPinEvaluation.directPin); }); test('explicit upper bound <=', () { - expect(inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <=4.0.0')), DependencyPinEvaluation.inclusiveMax); - expect(inspectVersionForPins(VersionConstraint.parse('<=4.0.0')), DependencyPinEvaluation.inclusiveMax); + expect(inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <=4.0.0')), + DependencyPinEvaluation.inclusiveMax); + expect(inspectVersionForPins(VersionConstraint.parse('<=4.0.0')), + DependencyPinEvaluation.inclusiveMax); }); group('when upper bound blocks patch or minor updates', () { test('when version starts with 0', () { - expect( - inspectVersionForPins(VersionConstraint.parse('>=0.2.3 <0.5.6')), DependencyPinEvaluation.blocksMinorBumps); - expect(inspectVersionForPins(VersionConstraint.parse('<0.5.6')), DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.3 <0.5.6')), + DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('<0.5.6')), + DependencyPinEvaluation.blocksMinorBumps); }); test('when version starts with nonzero', () { - expect( - inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <4.5.0')), DependencyPinEvaluation.blocksMinorBumps); - expect(inspectVersionForPins(VersionConstraint.parse('<4.5.0')), DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <4.5.0')), + DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('<4.5.0')), + DependencyPinEvaluation.blocksMinorBumps); expect(inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <4.0.6')), DependencyPinEvaluation.blocksPatchReleases); - expect(inspectVersionForPins(VersionConstraint.parse('<4.0.6')), DependencyPinEvaluation.blocksPatchReleases); + expect(inspectVersionForPins(VersionConstraint.parse('<4.0.6')), + DependencyPinEvaluation.blocksPatchReleases); expect(inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <1.2.4')), DependencyPinEvaluation.blocksPatchReleases); - expect( - inspectVersionForPins(VersionConstraint.parse('>=1.3.0 <1.4.0')), DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('>=1.3.0 <1.4.0')), + DependencyPinEvaluation.blocksMinorBumps); }); }); test('when upper bound does not allow either patch or minor updates', () { expect(inspectVersionForPins(VersionConstraint.parse('>=1.2.3 <4.5.6')), DependencyPinEvaluation.blocksPatchReleases); - expect(inspectVersionForPins(VersionConstraint.parse('<4.5.6')), DependencyPinEvaluation.blocksPatchReleases); + expect(inspectVersionForPins(VersionConstraint.parse('<4.5.6')), + DependencyPinEvaluation.blocksPatchReleases); }); test('when the maximum version is 0.0.X', () { - expect( - inspectVersionForPins(VersionConstraint.parse('>=0.0.1 <0.0.2')), DependencyPinEvaluation.blocksMinorBumps); - expect(inspectVersionForPins(VersionConstraint.parse('<0.0.2')), DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('>=0.0.1 <0.0.2')), + DependencyPinEvaluation.blocksMinorBumps); + expect(inspectVersionForPins(VersionConstraint.parse('<0.0.2')), + DependencyPinEvaluation.blocksMinorBumps); }); test('when the maximum bound contains build', () { expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0+1')), DependencyPinEvaluation.buildOrPrerelease); - expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0+1')), DependencyPinEvaluation.buildOrPrerelease); + expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0+1')), + DependencyPinEvaluation.buildOrPrerelease); expect(inspectVersionForPins(VersionConstraint.parse('>=1.0.0 <2.0.0+1')), DependencyPinEvaluation.buildOrPrerelease); - expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0+1')), DependencyPinEvaluation.buildOrPrerelease); + expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0+1')), + DependencyPinEvaluation.buildOrPrerelease); }); group('when the maximum bound contains prerelease', () { test('', () { - expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0-1')), + expect( + inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0-1')), + DependencyPinEvaluation.buildOrPrerelease); + expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0-1')), DependencyPinEvaluation.buildOrPrerelease); - expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0-1')), DependencyPinEvaluation.buildOrPrerelease); - expect(inspectVersionForPins(VersionConstraint.parse('>=1.0.0 <2.0.0-1')), + expect( + inspectVersionForPins(VersionConstraint.parse('>=1.0.0 <2.0.0-1')), + DependencyPinEvaluation.buildOrPrerelease); + expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0-1')), DependencyPinEvaluation.buildOrPrerelease); - expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0-1')), DependencyPinEvaluation.buildOrPrerelease); }); test('but determines not a pin for prerelease=0', () { - expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0-0')), DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0-0')), DependencyPinEvaluation.notAPin); + expect( + inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0-0')), + DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0-0')), + DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('>=1.0.0 <2.0.0-0')), DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0-0')), DependencyPinEvaluation.notAPin); + expect( + inspectVersionForPins(VersionConstraint.parse('>=1.0.0 <2.0.0-0')), + DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0-0')), + DependencyPinEvaluation.notAPin); }); }); group('not a pin when maximum version is', () { test('=1.0.0 <2.0.0')), DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0')), DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('>=1.0.0 <2.0.0')), + DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('<2.0.0')), + DependencyPinEvaluation.notAPin); }); test('<0.X.0', () { - expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0')), DependencyPinEvaluation.notAPin); - expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0')), DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0 <0.3.0')), + DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('<0.2.0')), + DependencyPinEvaluation.notAPin); }); test('unset', () { - expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0')), DependencyPinEvaluation.notAPin); + expect(inspectVersionForPins(VersionConstraint.parse('>=0.2.0')), + DependencyPinEvaluation.notAPin); }); }); });