mirror of
https://github.com/flutter/packages.git
synced 2025-08-23 18:44:31 +08:00

Consolidates the code to find all changed file paths into the `PackageLoopingCommand` class that is the base of almost all of the repo tooling commands. This in a preparatory PR for a future change to allow each command to define a list of files or file patterns that definitively *don't* affect that test, so that CI can be smarter about what tests to run (e.g., not running expensive integration tests for README changes). A side effect of this change is that tests of almost all commands now need a mock `GitDir` instance. This would add a lot of copy/pasted boilerplate to the test setup, and there is already too much of that, so instead this refactors common test setup: - Creating a memory file system - Populating it with a packages directory - Creating a RecordingProcessRunner to mock out process calls - Creating a mock GitDir that forwards to a RecordingProcessRunner into a helper method (using records and destructuring to easily return multiple values). While some tests don't need all of these steps, those that don't can easily ignore parts of it, and it will make it much easier to update tests in the future if they need them, and it makes the setup much more consistent which makes it easier to reason about test setup in general. Prep for https://github.com/flutter/flutter/issues/136394
1898 lines
56 KiB
Dart
1898 lines
56 KiB
Dart
// Copyright 2013 The Flutter Authors. 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:args/command_runner.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:flutter_plugin_tools/src/common/core.dart';
|
|
import 'package:flutter_plugin_tools/src/pubspec_check_command.dart';
|
|
import 'package:git/git.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'mocks.dart';
|
|
import 'util.dart';
|
|
|
|
/// Returns the top section of a pubspec.yaml for a package named [name].
|
|
///
|
|
/// By default it will create a header that includes all of the expected
|
|
/// values, elements can be changed via arguments to create incorrect
|
|
/// entries.
|
|
///
|
|
/// If [includeRepository] is true, by default the path in the link will
|
|
/// be "packages/[name]"; a different "packages"-relative path can be
|
|
/// provided with [repositoryPackagesDirRelativePath].
|
|
String _headerSection(
|
|
String name, {
|
|
String repository = 'flutter/packages',
|
|
bool includeRepository = true,
|
|
String repositoryBranch = 'main',
|
|
String? repositoryPackagesDirRelativePath,
|
|
bool includeHomepage = false,
|
|
bool includeIssueTracker = true,
|
|
bool publishable = true,
|
|
String? description,
|
|
}) {
|
|
final String repositoryPath = repositoryPackagesDirRelativePath ?? name;
|
|
final List<String> repoLinkPathComponents = <String>[
|
|
repository,
|
|
'tree',
|
|
repositoryBranch,
|
|
'packages',
|
|
repositoryPath,
|
|
];
|
|
final String repoLink =
|
|
'https://github.com/${repoLinkPathComponents.join('/')}';
|
|
final String issueTrackerLink = 'https://github.com/flutter/flutter/issues?'
|
|
'q=is%3Aissue+is%3Aopen+label%3A%22p%3A+$name%22';
|
|
description ??= 'A test package for validating that the pubspec.yaml '
|
|
'follows repo best practices.';
|
|
return '''
|
|
name: $name
|
|
description: $description
|
|
${includeRepository ? 'repository: $repoLink' : ''}
|
|
${includeHomepage ? 'homepage: $repoLink' : ''}
|
|
${includeIssueTracker ? 'issue_tracker: $issueTrackerLink' : ''}
|
|
version: 1.0.0
|
|
${publishable ? '' : "publish_to: 'none'"}
|
|
''';
|
|
}
|
|
|
|
String _environmentSection({
|
|
String dartConstraint = '>=2.17.0 <4.0.0',
|
|
String? flutterConstraint = '>=3.0.0',
|
|
}) {
|
|
return <String>[
|
|
'environment:',
|
|
' sdk: "$dartConstraint"',
|
|
if (flutterConstraint != null) ' flutter: "$flutterConstraint"',
|
|
'',
|
|
].join('\n');
|
|
}
|
|
|
|
String _flutterSection({
|
|
bool isPlugin = false,
|
|
String? implementedPackage,
|
|
Map<String, Map<String, String>> pluginPlatformDetails =
|
|
const <String, Map<String, String>>{},
|
|
}) {
|
|
String pluginEntry = '''
|
|
plugin:
|
|
${implementedPackage == null ? '' : ' implements: $implementedPackage'}
|
|
platforms:
|
|
''';
|
|
|
|
for (final MapEntry<String, Map<String, String>> platform
|
|
in pluginPlatformDetails.entries) {
|
|
pluginEntry += '''
|
|
${platform.key}:
|
|
''';
|
|
for (final MapEntry<String, String> detail in platform.value.entries) {
|
|
pluginEntry += '''
|
|
${detail.key}: ${detail.value}
|
|
''';
|
|
}
|
|
}
|
|
|
|
return '''
|
|
flutter:
|
|
${isPlugin ? pluginEntry : ''}
|
|
''';
|
|
}
|
|
|
|
String _dependenciesSection(
|
|
[List<String> extraDependencies = const <String>[]]) {
|
|
return '''
|
|
dependencies:
|
|
flutter:
|
|
sdk: flutter
|
|
${extraDependencies.map((String dep) => ' $dep').join('\n')}
|
|
''';
|
|
}
|
|
|
|
String _devDependenciesSection(
|
|
[List<String> extraDependencies = const <String>[]]) {
|
|
return '''
|
|
dev_dependencies:
|
|
flutter_test:
|
|
sdk: flutter
|
|
${extraDependencies.map((String dep) => ' $dep').join('\n')}
|
|
''';
|
|
}
|
|
|
|
String _topicsSection([List<String> topics = const <String>['a-topic']]) {
|
|
return '''
|
|
topics:
|
|
${topics.map((String topic) => ' - $topic').join('\n')}
|
|
''';
|
|
}
|
|
|
|
String _falseSecretsSection() {
|
|
return '''
|
|
false_secrets:
|
|
- /lib/main.dart
|
|
''';
|
|
}
|
|
|
|
void main() {
|
|
group('test pubspec_check_command', () {
|
|
late CommandRunner<void> runner;
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
|
|
setUp(() {
|
|
mockPlatform = MockPlatform();
|
|
final RecordingProcessRunner processRunner;
|
|
final GitDir gitDir;
|
|
(:packagesDir, :processRunner, gitProcessRunner: _, :gitDir) =
|
|
configureBaseCommandMocks(platform: mockPlatform);
|
|
final PubspecCheckCommand command = PubspecCheckCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
);
|
|
|
|
runner = CommandRunner<void>(
|
|
'pubspec_check_command', 'Test for pubspec_check_command');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('passes for a plugin following conventions', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
${_falseSecretsSection()}
|
|
''');
|
|
|
|
plugin.getExamples().first.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin_example',
|
|
publishable: false,
|
|
includeRepository: false,
|
|
includeIssueTracker: false,
|
|
)}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_flutterSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin...'),
|
|
contains('Running for plugin/example...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes for a Flutter package following conventions', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_flutterSection()}
|
|
${_topicsSection()}
|
|
${_falseSecretsSection()}
|
|
''');
|
|
|
|
package.getExamples().first.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'a_package',
|
|
publishable: false,
|
|
includeRepository: false,
|
|
includeIssueTracker: false,
|
|
)}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_flutterSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('Running for a_package/example...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes for a minimal package following conventions', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when homepage is included', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', includeHomepage: true)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Found a "homepage" entry; only "repository" should be used.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when repository is missing', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', includeRepository: false)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Missing "repository"'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when homepage is given instead of repository', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', includeHomepage: true, includeRepository: false)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Found a "homepage" entry; only "repository" should be used.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when repository package name is incorrect', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', repositoryPackagesDirRelativePath: 'different_plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('The "repository" link should end with the package path.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when repository uses master instead of main', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', repositoryBranch: 'master')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('The "repository" link should start with the repository\'s '
|
|
'main tree: "https://github.com/flutter/packages/tree/main"'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when repository is not flutter/packages', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', repository: 'flutter/plugins')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('The "repository" link should start with the repository\'s '
|
|
'main tree: "https://github.com/flutter/packages/tree/main"'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when issue tracker is missing', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', includeIssueTracker: false)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('A package should have an "issue_tracker" link'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when description is too short', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'a_plugin', packagesDir.childDirectory('a_plugin'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', description: 'Too short')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('"description" is too short. pub.dev recommends package '
|
|
'descriptions of 60-180 characters.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'allows short descriptions for non-app-facing parts of federated plugins',
|
|
() async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', description: 'Too short')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('"description" is too short. pub.dev recommends package '
|
|
'descriptions of 60-180 characters.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when description is too long', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
const String description = 'This description is too long. It just goes '
|
|
'on and on and on and on and on. pub.dev will down-score it because '
|
|
'there is just too much here. Someone shoul really cut this down to just '
|
|
'the core description so that search results are more useful and the '
|
|
'package does not lose pub points.';
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin', description: description)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('"description" is too long. pub.dev recommends package '
|
|
'descriptions of 60-180 characters.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when topics section is missing', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('A published package should include "topics".'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when topics section is empty', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>[])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('A published package should include "topics".'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when federated plugin topics do not include plugin name',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'some_plugin_ios', packagesDir.childDirectory('some_plugin'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'A federated plugin package should include its plugin name as a topic. '
|
|
'Add "some-plugin" to the "topics" section.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when topic name contains a space', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin a'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): plugin a in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when topic a topic name contains double dash', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin--a'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): plugin--a in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when topic a topic name starts with a number', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['1plugin-a'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): 1plugin-a in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when topic a topic name contains uppercase', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin-A'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): plugin-A in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when there are more than 5 topics', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>[
|
|
'plugin-a',
|
|
'plugin-a',
|
|
'plugin-a',
|
|
'plugin-a',
|
|
'plugin-a',
|
|
'plugin-a'
|
|
])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' A published package should have maximum 5 topics. See https://dart.dev/tools/pub/pubspec#topics.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a topic name is longer than 32 characters', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['foobarfoobarfoobarfoobarfoobarfoobarfoo'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Invalid topic(s): foobarfoobarfoobarfoobarfoobarfoobarfoo in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a topic name is longer than 2 characters', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['a'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): a in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a topic name ends in a dash', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin-'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): plugin- in "topics" section. '),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Invalid topics section has expected error message', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin-A', 'Plugin-b'])}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Invalid topic(s): plugin-A, Plugin-b in "topics" section. '
|
|
'Topics must consist of lowercase alphanumerical characters or dash (but no double dash), '
|
|
'start with a-z and ending with a-z or 0-9, have a minimum of 2 characters '
|
|
'and have a maximum of 32 characters.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when environment section is out of order', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_environmentSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Major sections should follow standard repository ordering:'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when flutter section is out of order', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Major sections should follow standard repository ordering:'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when dependencies section is out of order', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_devDependenciesSection()}
|
|
${_dependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Major sections should follow standard repository ordering:'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when dev_dependencies section is out of order', () async {
|
|
final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_devDependenciesSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Major sections should follow standard repository ordering:'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when false_secrets section is out of order', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_falseSecretsSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Major sections should follow standard repository ordering:'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when an implemenation package is missing "implements"',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a_foo', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin_a_foo')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Missing "implements: plugin_a" in "plugin" section.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when an implemenation package has the wrong "implements"',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a_foo', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin_a_foo')}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true, implementedPackage: 'plugin_a_foo')}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Expecetd "implements: plugin_a"; '
|
|
'found "implements: plugin_a_foo".'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes for a correct implemenation package', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a_foo', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin_a_foo',
|
|
repositoryPackagesDirRelativePath: 'plugin_a/plugin_a_foo',
|
|
)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true, implementedPackage: 'plugin_a')}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin-a'])}
|
|
''');
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['pubspec-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin_a_foo...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when a "default_package" looks incorrect', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin_a',
|
|
repositoryPackagesDirRelativePath: 'plugin_a/plugin_a',
|
|
)}
|
|
${_environmentSection()}
|
|
${_flutterSection(
|
|
isPlugin: true,
|
|
pluginPlatformDetails: <String, Map<String, String>>{
|
|
'android': <String, String>{'default_package': 'plugin_b_android'}
|
|
},
|
|
)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'"plugin_b_android" is not an expected implementation name for "plugin_a"'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails when a "default_package" does not have a corresponding dependency',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin_a',
|
|
repositoryPackagesDirRelativePath: 'plugin_a/plugin_a',
|
|
)}
|
|
${_environmentSection()}
|
|
${_flutterSection(
|
|
isPlugin: true,
|
|
pluginPlatformDetails: <String, Map<String, String>>{
|
|
'android': <String, String>{'default_package': 'plugin_a_android'}
|
|
},
|
|
)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('The following default_packages are missing corresponding '
|
|
'dependencies:\n plugin_a_android'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes for an app-facing package without "implements"', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin_a',
|
|
repositoryPackagesDirRelativePath: 'plugin_a/plugin_a',
|
|
)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin-a'])}
|
|
''');
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['pubspec-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin_a/plugin_a...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes for a platform interface package without "implements"',
|
|
() async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a_platform_interface', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin_a_platform_interface',
|
|
repositoryPackagesDirRelativePath:
|
|
'plugin_a/plugin_a_platform_interface',
|
|
)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection(<String>['plugin-a'])}
|
|
''');
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['pubspec-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin_a_platform_interface...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('validates some properties even for unpublished packages', () async {
|
|
final RepositoryPackage plugin = createFakePlugin(
|
|
'plugin_a_foo', packagesDir.childDirectory('plugin_a'),
|
|
examples: <String>[]);
|
|
|
|
// Environment section is in the wrong location.
|
|
// Missing 'implements'.
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('plugin_a_foo', publishable: false)}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
${_environmentSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['pubspec-check'], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'Major sections should follow standard repository ordering:'),
|
|
contains('Missing "implements: plugin_a" in "plugin" section.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('ignores some checks for unpublished packages', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, examples: <String>[]);
|
|
|
|
// Missing metadata that is only useful for published packages, such as
|
|
// repository and issue tracker.
|
|
plugin.pubspecFile.writeAsStringSync('''
|
|
${_headerSection(
|
|
'plugin',
|
|
publishable: false,
|
|
includeRepository: false,
|
|
includeIssueTracker: false,
|
|
)}
|
|
${_environmentSection()}
|
|
${_flutterSection(isPlugin: true)}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection()}
|
|
''');
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['pubspec-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when a Flutter package has a too-low minimum Flutter version',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
isFlutter: true, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(flutterConstraint: '>=2.10.0')}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
'--min-min-flutter-version',
|
|
'3.0.0'
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Minimum allowed Flutter version 2.10.0 is less than 3.0.0'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when a Flutter package requires exactly the minimum Flutter version',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
isFlutter: true, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(flutterConstraint: '>=3.3.0', dartConstraint: '>=2.18.0 <4.0.0')}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['pubspec-check', '--min-min-flutter-version', '3.3.0']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when a Flutter package requires a higher minimum Flutter version',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
isFlutter: true, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(flutterConstraint: '>=3.7.0', dartConstraint: '>=2.19.0 <4.0.0')}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['pubspec-check', '--min-min-flutter-version', '3.3.0']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when a non-Flutter package has a too-low minimum Dart version',
|
|
() async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(dartConstraint: '>=2.14.0 <4.0.0', flutterConstraint: null)}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
'--min-min-flutter-version',
|
|
'3.0.0'
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Minimum allowed Dart version 2.14.0 is less than 2.17.0'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when a non-Flutter package requires exactly the minimum Dart version',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
isFlutter: true, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(dartConstraint: '>=2.18.0 <4.0.0', flutterConstraint: null)}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['pubspec-check', '--min-min-flutter-version', '3.3.0']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when a non-Flutter package requires a higher minimum Dart version',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
isFlutter: true, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(dartConstraint: '>=2.18.0 <4.0.0', flutterConstraint: null)}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['pubspec-check', '--min-min-flutter-version', '3.0.0']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when a Flutter->Dart SDK version mapping is missing', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
'--min-min-flutter-version',
|
|
'2.0.0'
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Dart SDK version for Flutter SDK version 2.0.0 is unknown'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails when a Flutter package has a too-low minimum Dart version for '
|
|
'the corresponding minimum Flutter version', () async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
isFlutter: true, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection(flutterConstraint: '>=3.3.0', dartConstraint: '>=2.16.0 <4.0.0')}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('The minimum Dart version is 2.16.0, but the '
|
|
'minimum Flutter version of 3.3.0 shipped with '
|
|
'Dart 2.18.0. Please use consistent lower SDK '
|
|
'bounds'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
group('dependency check', () {
|
|
test('passes for local dependencies', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir);
|
|
final RepositoryPackage dependencyPackage =
|
|
createFakePackage('local_dependency', packagesDir);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>['local_dependency: ^1.0.0'])}
|
|
${_topicsSection()}
|
|
''');
|
|
dependencyPackage.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('local_dependency')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['pubspec-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when an unexpected dependency is found', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>['bad_dependency: ^1.0.0'])}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' The following unexpected non-local dependencies were found:\n'
|
|
' bad_dependency\n'
|
|
' Please see https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#Dependencies\n'
|
|
' for more information and next steps.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when an unexpected dev dependency is found', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_devDependenciesSection(<String>['bad_dependency: ^1.0.0'])}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' The following unexpected non-local dependencies were found:\n'
|
|
' bad_dependency\n'
|
|
' Please see https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#Dependencies\n'
|
|
' for more information and next steps.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes when a dependency is on the allow list', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>['allowed: ^1.0.0'])}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['pubspec-check', '--allow-dependencies', 'allowed']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when an exactly-pinned dependency is on the pinned allow list',
|
|
() async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>['allow_pinned: 1.0.0'])}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
'--allow-pinned-dependencies',
|
|
'allow_pinned'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'passes when an explicit-range-pinned dependency is on the pinned allow list',
|
|
() async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>['allow_pinned: ">=1.0.0 <=1.3.1"'])}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
'--allow-pinned-dependencies',
|
|
'allow_pinned'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails when an allowed-when-pinned dependency is unpinned',
|
|
() async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>['allow_pinned: ^1.0.0'])}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
'--allow-pinned-dependencies',
|
|
'allow_pinned'
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' The following unexpected non-local dependencies were found:\n'
|
|
' allow_pinned\n'
|
|
' Please see https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#Dependencies\n'
|
|
' for more information and next steps.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
group('dev dependencies', () {
|
|
const List<String> packages = <String>[
|
|
'build_runner',
|
|
'integration_test',
|
|
'flutter_test',
|
|
'leak_tracker_flutter_testing',
|
|
'mockito',
|
|
'pigeon',
|
|
'test',
|
|
];
|
|
for (final String dependency in packages) {
|
|
test('fails when $dependency is used in non dev dependency',
|
|
() async {
|
|
final RepositoryPackage package = createFakePackage(
|
|
'a_package', packagesDir,
|
|
examples: <String>[]);
|
|
|
|
final String version =
|
|
dependency == 'integration_test' || dependency == 'flutter_test'
|
|
? '{ sdk: flutter }'
|
|
: '1.0.0';
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>[
|
|
'$dependency: $version',
|
|
])}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
Error? commandError;
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' The following dev dependencies were found in the dependencies section:\n'
|
|
' $dependency\n'
|
|
' Please move them to dev_dependencies.'),
|
|
]),
|
|
);
|
|
});
|
|
}
|
|
});
|
|
|
|
test(
|
|
'passes when integration_test or flutter_test are used in non published package',
|
|
() async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('a_package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('a_package', publishable: false)}
|
|
${_environmentSection()}
|
|
${_dependenciesSection(<String>[
|
|
'integration_test: \n sdk: flutter',
|
|
'flutter_test: \n sdk: flutter'
|
|
])}
|
|
${_devDependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'pubspec-check',
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for a_package...'),
|
|
contains('Ran for'),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
group('test pubspec_check_command on Windows', () {
|
|
late CommandRunner<void> runner;
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
|
|
setUp(() {
|
|
mockPlatform = MockPlatform(isWindows: true);
|
|
final RecordingProcessRunner processRunner;
|
|
final GitDir gitDir;
|
|
(:packagesDir, :processRunner, gitProcessRunner: _, :gitDir) =
|
|
configureBaseCommandMocks(platform: mockPlatform);
|
|
final PubspecCheckCommand command = PubspecCheckCommand(
|
|
packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
);
|
|
|
|
runner = CommandRunner<void>(
|
|
'pubspec_check_command', 'Test for pubspec_check_command');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('repository check works', () async {
|
|
final RepositoryPackage package =
|
|
createFakePackage('package', packagesDir, examples: <String>[]);
|
|
|
|
package.pubspecFile.writeAsStringSync('''
|
|
${_headerSection('package')}
|
|
${_environmentSection()}
|
|
${_dependenciesSection()}
|
|
${_topicsSection()}
|
|
''');
|
|
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['pubspec-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for package...'),
|
|
contains('No issues found!'),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
}
|