blob: c654d6bdb5e0edce36d6ceb18bb93516c39601bc [file]
// Copyright (c) 2023, 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.
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/source/file_source.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:analyzer/src/pubspec/validators/missing_dependency_validator.dart';
import 'package:analyzer_testing/resource_provider_mixin.dart';
import 'package:matcher/expect.dart';
import 'package:meta/meta.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:yaml/yaml.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(MissingDependencyTest);
});
}
@reflectiveTest
class MissingDependencyTest with ResourceProviderMixin {
late Source _source;
/// Asserts that when the validator is used on the given [content], a
/// [diag.missingDependency] warning is produced.
///
/// The problem message and correction message are returned so that it can be
/// further validated if desired.
({String problemMessage, String correctionMessage}) assertErrors(
String content, {
required Set<String> usedDeps,
required Set<String> usedDevDeps,
List<String> addDeps = const [],
List<String> addDevDeps = const [],
List<String> removeDevDeps = const [],
}) {
var error = _runValidator(content, usedDeps, usedDevDeps).first;
var data = MissingDependencyData.byDiagnostic[error]!;
expect(error.diagnosticCode, diag.missingDependency);
expect(data.addDeps, addDeps);
expect(data.addDevDeps, addDevDeps);
expect(data.removeDevDeps, removeDevDeps);
return (
problemMessage: error.problemMessage.messageText(includeUrl: false),
correctionMessage: error.correctionMessage!,
);
}
/// Assert that when the validator is used on the given [content] no errors
/// are produced.
void assertNoErrors(
String content, {
Set<String> usedDeps = const {},
Set<String> usedDevDeps = const {},
}) {
List<Diagnostic> errors = _runValidator(content, usedDeps, usedDevDeps);
expect(errors.isEmpty, true);
}
@mustCallSuper
void setUp() {
var file = getFile('/sample/pubspec.yaml');
_source = FileSource(file);
}
test_missingDependency_error() {
var (:problemMessage, :correctionMessage) = assertErrors(
'''
name: sample
dependencies:
path: any
''',
usedDeps: {'path', 'matcher'},
addDeps: ['matcher'],
usedDevDeps: {},
);
expect(problemMessage, contains("package 'matcher' in 'dependencies'"));
expect(correctionMessage, contains("adding 'matcher' to 'dependencies'"));
}
test_missingDependency_move_to_dev() {
var (:problemMessage, :correctionMessage) = assertErrors(
'''
name: sample
dependencies:
path: any
dev_dependencies:
test: any
''',
usedDeps: {'path', 'test'},
addDeps: ['test'],
removeDevDeps: ['test'],
usedDevDeps: {},
);
expect(problemMessage, contains("package 'test' in 'dependencies'"));
expect(correctionMessage, contains("adding 'test' to 'dependencies'"));
}
test_missingDependency_noError() {
assertNoErrors(
'''
name: sample
dependencies:
test: any
''',
usedDeps: {'test'},
usedDevDeps: {},
);
}
test_missingDependency_package_noError() {
assertNoErrors(
'''
name: sample
dependencies:
test: any
dev_dependencies:
lints: any
''',
usedDeps: {'test', 'sample'},
usedDevDeps: {'lints'},
);
}
test_missingDevDependency_error() {
var (:problemMessage, :correctionMessage) = assertErrors(
'''
name: sample
dependencies:
path: any
dev_dependencies:
lints: any
''',
usedDeps: {'path'},
usedDevDeps: {'lints', 'test'},
addDevDeps: ['test'],
);
expect(problemMessage, contains("package 'test' in 'dev_dependencies'"));
expect(correctionMessage, contains("adding 'test' to 'dev_dependencies'"));
}
test_missingDevDependency_inDeps_noError() {
assertNoErrors(
'''
name: sample
dependencies:
test: any
path: any
dev_dependencies:
lints: any
''',
usedDeps: {'test', 'path'},
usedDevDeps: {'lints', 'path'},
);
}
test_missingDevDependency_multiple_deps() {
var (:problemMessage, :correctionMessage) = assertErrors(
'''
name: sample
dependencies:
path: any
''',
usedDeps: {'path', 'test', 'args'},
usedDevDeps: {},
addDeps: ['test', 'args'],
);
expect(
problemMessage,
contains("packages 'test', 'args' in 'dependencies'"),
);
expect(
correctionMessage,
contains("adding 'test', 'args' to 'dependencies'"),
);
}
test_missingDevDependency_multiple_depsAndDevDeps() {
var (:problemMessage, :correctionMessage) = assertErrors(
'''
name: sample
''',
usedDeps: {'path', 'args'},
usedDevDeps: {'test', 'lints'},
addDeps: ['path', 'args'],
addDevDeps: ['test', 'lints'],
);
expect(
problemMessage,
contains(
"packages 'path', 'args' in 'dependencies', and packages 'test', "
"'lints' in 'dev_dependencies'",
),
);
expect(
correctionMessage,
contains(
"adding 'path', 'args' to 'dependencies', and 'test', 'lints' to "
"'dev_dependencies'",
),
);
}
test_missingDevDependency_multiple_devDeps() {
var (:problemMessage, :correctionMessage) = assertErrors(
'''
name: sample
dev_dependencies:
path: any
''',
usedDeps: {},
usedDevDeps: {'path', 'test', 'args'},
addDevDeps: ['test', 'args'],
);
expect(
problemMessage,
contains("packages 'test', 'args' in 'dev_dependencies'"),
);
expect(
correctionMessage,
contains("adding 'test', 'args' to 'dev_dependencies'"),
);
}
test_missingDevDependency_noError() {
assertNoErrors(
'''
name: sample
dependencies:
test: any
dev_dependencies:
lints: any
''',
usedDeps: {'test'},
usedDevDeps: {'lints'},
);
}
test_missingDevDependency_package_noError() {
assertNoErrors(
'''
name: sample
dependencies:
test: any
dev_dependencies:
lints: any
''',
usedDeps: {'test'},
usedDevDeps: {'lints', 'sample'},
);
}
List<Diagnostic> _runValidator(
String content,
Set<String> usedDeps,
Set<String> usedDevDeps,
) {
YamlNode node = loadYamlNode(content);
if (node is! YamlMap) {
// The file is empty.
node = YamlMap();
}
var errors = MissingDependencyValidator(
node,
_source,
resourceProvider,
).validate(usedDeps, usedDevDeps);
return errors;
}
}