mirror of
https://github.com/flutter/packages.git
synced 2025-05-29 12:26:24 +08:00
456 lines
16 KiB
Dart
456 lines
16 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:flutter_plugin_tools/src/common.dart';
|
|
import 'package:git/git.dart';
|
|
import 'package:mockito/mockito.dart';
|
|
import "package:test/test.dart";
|
|
import "package:flutter_plugin_tools/src/version_check_command.dart";
|
|
import 'package:pub_semver/pub_semver.dart';
|
|
import 'util.dart';
|
|
|
|
void testAllowedVersion(
|
|
String masterVersion,
|
|
String headVersion, {
|
|
bool allowed = true,
|
|
NextVersionType nextVersionType,
|
|
}) {
|
|
final Version master = Version.parse(masterVersion);
|
|
final Version head = Version.parse(headVersion);
|
|
final Map<Version, NextVersionType> allowedVersions =
|
|
getAllowedNextVersions(master, head);
|
|
if (allowed) {
|
|
expect(allowedVersions, contains(head));
|
|
if (nextVersionType != null) {
|
|
expect(allowedVersions[head], equals(nextVersionType));
|
|
}
|
|
} else {
|
|
expect(allowedVersions, isNot(contains(head)));
|
|
}
|
|
}
|
|
|
|
class MockGitDir extends Mock implements GitDir {}
|
|
|
|
class MockProcessResult extends Mock implements ProcessResult {}
|
|
|
|
void main() {
|
|
group('$VersionCheckCommand', () {
|
|
CommandRunner<VersionCheckCommand> runner;
|
|
RecordingProcessRunner processRunner;
|
|
List<List<String>> gitDirCommands;
|
|
String gitDiffResponse;
|
|
Map<String, String> gitShowResponses;
|
|
|
|
setUp(() {
|
|
gitDirCommands = <List<String>>[];
|
|
gitDiffResponse = '';
|
|
gitShowResponses = <String, String>{};
|
|
final MockGitDir gitDir = MockGitDir();
|
|
when(gitDir.runCommand(any)).thenAnswer((Invocation invocation) {
|
|
gitDirCommands.add(invocation.positionalArguments[0]);
|
|
final MockProcessResult mockProcessResult = MockProcessResult();
|
|
if (invocation.positionalArguments[0][0] == 'diff') {
|
|
when<String>(mockProcessResult.stdout).thenReturn(gitDiffResponse);
|
|
} else if (invocation.positionalArguments[0][0] == 'show') {
|
|
final String response =
|
|
gitShowResponses[invocation.positionalArguments[0][1]];
|
|
when<String>(mockProcessResult.stdout).thenReturn(response);
|
|
}
|
|
return Future<ProcessResult>.value(mockProcessResult);
|
|
});
|
|
initializeFakePackages();
|
|
processRunner = RecordingProcessRunner();
|
|
final VersionCheckCommand command = VersionCheckCommand(
|
|
mockPackagesDir, mockFileSystem,
|
|
processRunner: processRunner, gitDir: gitDir);
|
|
|
|
runner = CommandRunner<Null>(
|
|
'version_check_command', 'Test for $VersionCheckCommand');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
tearDown(() {
|
|
cleanupPackages();
|
|
});
|
|
|
|
test('allows valid version', () async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
gitDiffResponse = "packages/plugin/pubspec.yaml";
|
|
gitShowResponses = <String, String>{
|
|
'master:packages/plugin/pubspec.yaml': 'version: 1.0.0',
|
|
'HEAD:packages/plugin/pubspec.yaml': 'version: 2.0.0',
|
|
};
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<String>[
|
|
'No version check errors found!',
|
|
]),
|
|
);
|
|
expect(gitDirCommands.length, equals(3));
|
|
expect(
|
|
gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
|
|
expect(gitDirCommands[1].join(' '),
|
|
equals('show master:packages/plugin/pubspec.yaml'));
|
|
expect(gitDirCommands[2].join(' '),
|
|
equals('show HEAD:packages/plugin/pubspec.yaml'));
|
|
});
|
|
|
|
test('denies invalid version', () async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
gitDiffResponse = "packages/plugin/pubspec.yaml";
|
|
gitShowResponses = <String, String>{
|
|
'master:packages/plugin/pubspec.yaml': 'version: 0.0.1',
|
|
'HEAD:packages/plugin/pubspec.yaml': 'version: 0.2.0',
|
|
};
|
|
final Future<List<String>> result = runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
|
|
await expectLater(
|
|
result,
|
|
throwsA(const TypeMatcher<Error>()),
|
|
);
|
|
expect(gitDirCommands.length, equals(3));
|
|
expect(
|
|
gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
|
|
expect(gitDirCommands[1].join(' '),
|
|
equals('show master:packages/plugin/pubspec.yaml'));
|
|
expect(gitDirCommands[2].join(' '),
|
|
equals('show HEAD:packages/plugin/pubspec.yaml'));
|
|
});
|
|
|
|
test('gracefully handles missing pubspec.yaml', () async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
gitDiffResponse = "packages/plugin/pubspec.yaml";
|
|
mockFileSystem.currentDirectory
|
|
.childDirectory('packages')
|
|
.childDirectory('plugin')
|
|
.childFile('pubspec.yaml')
|
|
.deleteSync();
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
|
|
expect(
|
|
output,
|
|
orderedEquals(<String>[
|
|
'Determine diff with base sha: master',
|
|
'No version check errors found!',
|
|
]),
|
|
);
|
|
expect(gitDirCommands.length, equals(1));
|
|
expect(gitDirCommands.first.join(' '),
|
|
equals('diff --name-only master HEAD'));
|
|
});
|
|
|
|
test('allows minor changes to platform interfaces', () async {
|
|
createFakePlugin('plugin_platform_interface',
|
|
includeChangeLog: true, includeVersion: true);
|
|
gitDiffResponse = "packages/plugin_platform_interface/pubspec.yaml";
|
|
gitShowResponses = <String, String>{
|
|
'master:packages/plugin_platform_interface/pubspec.yaml':
|
|
'version: 1.0.0',
|
|
'HEAD:packages/plugin_platform_interface/pubspec.yaml':
|
|
'version: 1.1.0',
|
|
};
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<String>[
|
|
'No version check errors found!',
|
|
]),
|
|
);
|
|
expect(gitDirCommands.length, equals(3));
|
|
expect(
|
|
gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
|
|
expect(
|
|
gitDirCommands[1].join(' '),
|
|
equals(
|
|
'show master:packages/plugin_platform_interface/pubspec.yaml'));
|
|
expect(gitDirCommands[2].join(' '),
|
|
equals('show HEAD:packages/plugin_platform_interface/pubspec.yaml'));
|
|
});
|
|
|
|
test('disallows breaking changes to platform interfaces', () async {
|
|
createFakePlugin('plugin_platform_interface',
|
|
includeChangeLog: true, includeVersion: true);
|
|
gitDiffResponse = "packages/plugin_platform_interface/pubspec.yaml";
|
|
gitShowResponses = <String, String>{
|
|
'master:packages/plugin_platform_interface/pubspec.yaml':
|
|
'version: 1.0.0',
|
|
'HEAD:packages/plugin_platform_interface/pubspec.yaml':
|
|
'version: 2.0.0',
|
|
};
|
|
final Future<List<String>> output = runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
await expectLater(
|
|
output,
|
|
throwsA(const TypeMatcher<Error>()),
|
|
);
|
|
expect(gitDirCommands.length, equals(3));
|
|
expect(
|
|
gitDirCommands[0].join(' '), equals('diff --name-only master HEAD'));
|
|
expect(
|
|
gitDirCommands[1].join(' '),
|
|
equals(
|
|
'show master:packages/plugin_platform_interface/pubspec.yaml'));
|
|
expect(gitDirCommands[2].join(' '),
|
|
equals('show HEAD:packages/plugin_platform_interface/pubspec.yaml'));
|
|
});
|
|
|
|
test('Allow empty lines in front of the first version in CHANGELOG',
|
|
() async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
|
|
final Directory pluginDirectory =
|
|
mockPackagesDir.childDirectory('plugin');
|
|
|
|
createFakePubspec(pluginDirectory,
|
|
isFlutter: true, includeVersion: true, version: '1.0.1');
|
|
String changelog = '''
|
|
|
|
|
|
|
|
## 1.0.1
|
|
|
|
* Some changes.
|
|
''';
|
|
createFakeCHANGELOG(pluginDirectory, changelog);
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
await expect(
|
|
output,
|
|
containsAllInOrder([
|
|
'Checking the first version listed in CHANGELOG.MD matches the version in pubspec.yaml for plugin.',
|
|
'plugin passed version check',
|
|
'No version check errors found!'
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Throws if versions in changelog and pubspec do not match', () async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
|
|
final Directory pluginDirectory =
|
|
mockPackagesDir.childDirectory('plugin');
|
|
|
|
createFakePubspec(pluginDirectory,
|
|
isFlutter: true, includeVersion: true, version: '1.0.1');
|
|
String changelog = '''
|
|
## 1.0.2
|
|
|
|
* Some changes.
|
|
''';
|
|
createFakeCHANGELOG(pluginDirectory, changelog);
|
|
final Future<List<String>> output = runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
await expectLater(
|
|
output,
|
|
throwsA(const TypeMatcher<Error>()),
|
|
);
|
|
try {
|
|
List<String> outputValue = await output;
|
|
await expectLater(
|
|
outputValue,
|
|
containsAllInOrder([
|
|
'''
|
|
versions for plugin in CHANGELOG.md and pubspec.yaml do not match.
|
|
The version in pubspec.yaml is 1.0.1.
|
|
The first version listed in CHANGELOG.md is 1.0.2.
|
|
''',
|
|
]),
|
|
);
|
|
} on ToolExit catch (_) {}
|
|
});
|
|
|
|
test('Success if CHANGELOG and pubspec versions match', () async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
|
|
final Directory pluginDirectory =
|
|
mockPackagesDir.childDirectory('plugin');
|
|
|
|
createFakePubspec(pluginDirectory,
|
|
isFlutter: true, includeVersion: true, version: '1.0.1');
|
|
String changelog = '''
|
|
## 1.0.1
|
|
|
|
* Some changes.
|
|
''';
|
|
createFakeCHANGELOG(pluginDirectory, changelog);
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
await expect(
|
|
output,
|
|
containsAllInOrder([
|
|
'Checking the first version listed in CHANGELOG.MD matches the version in pubspec.yaml for plugin.',
|
|
'plugin passed version check',
|
|
'No version check errors found!'
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'Fail if pubspec version only matches an older version listed in CHANGELOG',
|
|
() async {
|
|
createFakePlugin('plugin', includeChangeLog: true, includeVersion: true);
|
|
|
|
final Directory pluginDirectory =
|
|
mockPackagesDir.childDirectory('plugin');
|
|
|
|
createFakePubspec(pluginDirectory,
|
|
isFlutter: true, includeVersion: true, version: '1.0.0');
|
|
String changelog = '''
|
|
## 1.0.1
|
|
|
|
* Some changes.
|
|
|
|
## 1.0.0
|
|
|
|
* Some other changes.
|
|
''';
|
|
createFakeCHANGELOG(pluginDirectory, changelog);
|
|
Future<List<String>> output = runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=master']);
|
|
await expectLater(
|
|
output,
|
|
throwsA(const TypeMatcher<Error>()),
|
|
);
|
|
try {
|
|
List<String> outputValue = await output;
|
|
await expectLater(
|
|
outputValue,
|
|
containsAllInOrder([
|
|
'''
|
|
versions for plugin in CHANGELOG.md and pubspec.yaml do not match.
|
|
The version in pubspec.yaml is 1.0.0.
|
|
The first version listed in CHANGELOG.md is 1.0.1.
|
|
''',
|
|
]),
|
|
);
|
|
} on ToolExit catch (_) {}
|
|
});
|
|
});
|
|
|
|
group("Pre 1.0", () {
|
|
test("nextVersion allows patch version", () {
|
|
testAllowedVersion("0.12.0", "0.12.0+1",
|
|
nextVersionType: NextVersionType.PATCH);
|
|
testAllowedVersion("0.12.0+4", "0.12.0+5",
|
|
nextVersionType: NextVersionType.PATCH);
|
|
});
|
|
|
|
test("nextVersion does not allow jumping patch", () {
|
|
testAllowedVersion("0.12.0", "0.12.0+2", allowed: false);
|
|
testAllowedVersion("0.12.0+2", "0.12.0+4", allowed: false);
|
|
});
|
|
|
|
test("nextVersion does not allow going back", () {
|
|
testAllowedVersion("0.12.0", "0.11.0", allowed: false);
|
|
testAllowedVersion("0.12.0+2", "0.12.0+1", allowed: false);
|
|
testAllowedVersion("0.12.0+1", "0.12.0", allowed: false);
|
|
});
|
|
|
|
test("nextVersion allows minor version", () {
|
|
testAllowedVersion("0.12.0", "0.12.1",
|
|
nextVersionType: NextVersionType.MINOR);
|
|
testAllowedVersion("0.12.0+4", "0.12.1",
|
|
nextVersionType: NextVersionType.MINOR);
|
|
});
|
|
|
|
test("nextVersion does not allow jumping minor", () {
|
|
testAllowedVersion("0.12.0", "0.12.2", allowed: false);
|
|
testAllowedVersion("0.12.0+2", "0.12.3", allowed: false);
|
|
});
|
|
});
|
|
|
|
group("Releasing 1.0", () {
|
|
test("nextVersion allows releasing 1.0", () {
|
|
testAllowedVersion("0.12.0", "1.0.0",
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR);
|
|
testAllowedVersion("0.12.0+4", "1.0.0",
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR);
|
|
});
|
|
|
|
test("nextVersion does not allow jumping major", () {
|
|
testAllowedVersion("0.12.0", "2.0.0", allowed: false);
|
|
testAllowedVersion("0.12.0+4", "2.0.0", allowed: false);
|
|
});
|
|
|
|
test("nextVersion does not allow un-releasing", () {
|
|
testAllowedVersion("1.0.0", "0.12.0+4", allowed: false);
|
|
testAllowedVersion("1.0.0", "0.12.0", allowed: false);
|
|
});
|
|
});
|
|
|
|
group("Post 1.0", () {
|
|
test("nextVersion allows patch jumps", () {
|
|
testAllowedVersion("1.0.1", "1.0.2",
|
|
nextVersionType: NextVersionType.PATCH);
|
|
testAllowedVersion("1.0.0", "1.0.1",
|
|
nextVersionType: NextVersionType.PATCH);
|
|
});
|
|
|
|
test("nextVersion does not allow build jumps", () {
|
|
testAllowedVersion("1.0.1", "1.0.1+1", allowed: false);
|
|
testAllowedVersion("1.0.0+5", "1.0.0+6", allowed: false);
|
|
});
|
|
|
|
test("nextVersion does not allow skipping patches", () {
|
|
testAllowedVersion("1.0.1", "1.0.3", allowed: false);
|
|
testAllowedVersion("1.0.0", "1.0.6", allowed: false);
|
|
});
|
|
|
|
test("nextVersion allows minor version jumps", () {
|
|
testAllowedVersion("1.0.1", "1.1.0",
|
|
nextVersionType: NextVersionType.MINOR);
|
|
testAllowedVersion("1.0.0", "1.1.0",
|
|
nextVersionType: NextVersionType.MINOR);
|
|
});
|
|
|
|
test("nextVersion does not allow skipping minor versions", () {
|
|
testAllowedVersion("1.0.1", "1.2.0", allowed: false);
|
|
testAllowedVersion("1.1.0", "1.3.0", allowed: false);
|
|
});
|
|
|
|
test("nextVersion allows breaking changes", () {
|
|
testAllowedVersion("1.0.1", "2.0.0",
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR);
|
|
testAllowedVersion("1.0.0", "2.0.0",
|
|
nextVersionType: NextVersionType.BREAKING_MAJOR);
|
|
});
|
|
|
|
test("nextVersion allows null safety pre prelease", () {
|
|
testAllowedVersion("1.0.1", "2.0.0-nullsafety",
|
|
nextVersionType: NextVersionType.MAJOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("1.0.0", "2.0.0-nullsafety",
|
|
nextVersionType: NextVersionType.MAJOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("1.0.0-nullsafety", "1.0.0-nullsafety.1",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("1.0.0-nullsafety.1", "1.0.0-nullsafety.2",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("0.1.0", "0.2.0-nullsafety",
|
|
nextVersionType: NextVersionType.MAJOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("0.1.0-nullsafety", "0.1.0-nullsafety.1",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("0.1.0-nullsafety.1", "0.1.0-nullsafety.2",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("1.0.0", "1.1.0-nullsafety",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("1.1.0-nullsafety", "1.1.0-nullsafety.1",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("0.1.0", "0.1.1-nullsafety",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
testAllowedVersion("0.1.1-nullsafety", "0.1.1-nullsafety.1",
|
|
nextVersionType: NextVersionType.MINOR_NULLSAFETY_PRE_RELEASE);
|
|
});
|
|
|
|
test("nextVersion does not allow skipping major versions", () {
|
|
testAllowedVersion("1.0.1", "3.0.0", allowed: false);
|
|
testAllowedVersion("1.1.0", "2.3.0", allowed: false);
|
|
});
|
|
});
|
|
}
|