mirror of
https://github.com/flutter/packages.git
synced 2025-05-23 03:36:45 +08:00

Updates all links to the Flutter wiki to point to their new location in the flutter/flutter repository. (The sole exception is a link to a doc that doesn't have a final home yet, and is linked from legacy code anyway so doesn't really need to be updated.) While touching the PR template, makes a few minor improvements: - Removes the breaking change discussion that doesn't apply to this repository, as breaking changes are handled totally differently for packages (and is covered by the link to docs about Dart versioning). - Adds text and a link to reflect the fact that some PRs can be changelog-exempt.
1615 lines
52 KiB
Dart
1615 lines
52 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 'dart:convert';
|
|
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:file/file.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_plugin_tools/src/common/core.dart';
|
|
import 'package:flutter_plugin_tools/src/version_check_command.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:http/testing.dart';
|
|
import 'package:mockito/mockito.dart';
|
|
import 'package:pub_semver/pub_semver.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import 'common/package_command_test.mocks.dart';
|
|
import 'mocks.dart';
|
|
import 'util.dart';
|
|
|
|
void testAllowedVersion(
|
|
String mainVersion,
|
|
String headVersion, {
|
|
bool allowed = true,
|
|
NextVersionType? nextVersionType,
|
|
}) {
|
|
final Version main = Version.parse(mainVersion);
|
|
final Version head = Version.parse(headVersion);
|
|
final Map<Version, NextVersionType> allowedVersions =
|
|
getAllowedNextVersions(main, newVersion: head);
|
|
if (allowed) {
|
|
expect(allowedVersions, contains(head));
|
|
if (nextVersionType != null) {
|
|
expect(allowedVersions[head], equals(nextVersionType));
|
|
}
|
|
} else {
|
|
expect(allowedVersions, isNot(contains(head)));
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
const String indentation = ' ';
|
|
group('VersionCheckCommand', () {
|
|
late FileSystem fileSystem;
|
|
late MockPlatform mockPlatform;
|
|
late Directory packagesDir;
|
|
late CommandRunner<void> runner;
|
|
late RecordingProcessRunner processRunner;
|
|
late MockGitDir gitDir;
|
|
// Ignored if mockHttpResponse is set.
|
|
int mockHttpStatus;
|
|
Map<String, dynamic>? mockHttpResponse;
|
|
|
|
setUp(() {
|
|
fileSystem = MemoryFileSystem();
|
|
mockPlatform = MockPlatform();
|
|
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
|
|
|
|
gitDir = MockGitDir();
|
|
when(gitDir.path).thenReturn(packagesDir.parent.path);
|
|
when(gitDir.runCommand(any, throwOnError: anyNamed('throwOnError')))
|
|
.thenAnswer((Invocation invocation) {
|
|
final List<String> arguments =
|
|
invocation.positionalArguments[0]! as List<String>;
|
|
// Route git calls through the process runner, to make mock output
|
|
// consistent with other processes. Attach the first argument to the
|
|
// command to make targeting the mock results easier.
|
|
final String gitCommand = arguments.removeAt(0);
|
|
return processRunner.run('git-$gitCommand', arguments);
|
|
});
|
|
|
|
// Default to simulating the plugin never having been published.
|
|
mockHttpStatus = 404;
|
|
mockHttpResponse = null;
|
|
final MockClient mockClient = MockClient((http.Request request) async {
|
|
return http.Response(json.encode(mockHttpResponse),
|
|
mockHttpResponse == null ? mockHttpStatus : 200);
|
|
});
|
|
|
|
processRunner = RecordingProcessRunner();
|
|
final VersionCheckCommand command = VersionCheckCommand(packagesDir,
|
|
processRunner: processRunner,
|
|
platform: mockPlatform,
|
|
gitDir: gitDir,
|
|
httpClient: mockClient);
|
|
|
|
runner = CommandRunner<void>(
|
|
'version_check_command', 'Test for $VersionCheckCommand');
|
|
runner.addCommand(command);
|
|
});
|
|
|
|
test('allows valid version', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 2.0.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show', <String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('denies invalid version', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]));
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show', <String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('uses merge-base without explicit base-sha', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-merge-base'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'abc123')),
|
|
FakeProcessInfo(MockProcess(stdout: 'abc123')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['version-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 2.0.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-merge-base',
|
|
<String>['--fork-point', 'FETCH_HEAD', 'HEAD'], null),
|
|
ProcessCall('git-show',
|
|
<String>['abc123:packages/plugin/pubspec.yaml'], null),
|
|
]));
|
|
});
|
|
|
|
test('allows valid version for new package.', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
final List<String> output =
|
|
await runCapturingPrint(runner, <String>['version-check']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Unable to find previous version at git base.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows likely reverts.', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.6.1');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.6.2')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('New version is lower than previous version. '
|
|
'This is assumed to be a revert.'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show', <String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('denies lower version that could not be a simple revert', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.5.1');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.6.2')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]));
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show', <String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('allows minor changes to platform interfaces', () async {
|
|
createFakePlugin('plugin_platform_interface', packagesDir,
|
|
version: '1.1.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 1.1.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show',
|
|
<String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml'
|
|
],
|
|
null)
|
|
]));
|
|
});
|
|
|
|
test('disallows breaking changes to platform interfaces by default',
|
|
() async {
|
|
createFakePlugin('plugin_platform_interface', packagesDir,
|
|
version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
' Breaking changes to platform interfaces are not allowed '
|
|
'without explicit justification.\n'
|
|
' See https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md '
|
|
'for more information.'),
|
|
]));
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show',
|
|
<String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml'
|
|
],
|
|
null)
|
|
]));
|
|
});
|
|
|
|
test('allows breaking changes to platform interfaces with override label',
|
|
() async {
|
|
createFakePlugin('plugin_platform_interface', packagesDir,
|
|
version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--pr-labels=some label,override: allow breaking change,another-label'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Allowing breaking change to plugin_platform_interface '
|
|
'due to the "override: allow breaking change" label.'),
|
|
contains('Ran for 1 package(s) (1 with warnings)'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show',
|
|
<String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml'
|
|
],
|
|
null)
|
|
]));
|
|
});
|
|
|
|
test('allows breaking changes to platform interfaces with bypass flag',
|
|
() async {
|
|
createFakePlugin('plugin_platform_interface', packagesDir,
|
|
version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--ignore-platform-interface-breaks'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Allowing breaking change to plugin_platform_interface due '
|
|
'to --ignore-platform-interface-breaks'),
|
|
contains('Ran for 1 package(s) (1 with warnings)'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall(
|
|
'git-show',
|
|
<String>[
|
|
'main:packages/plugin_platform_interface/pubspec.yaml'
|
|
],
|
|
null)
|
|
]));
|
|
});
|
|
|
|
test('Allow empty lines in front of the first version in CHANGELOG',
|
|
() async {
|
|
const String version = '1.0.1';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: version);
|
|
const String changelog = '''
|
|
|
|
## $version
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Throws if versions in changelog and pubspec do not match', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.1');
|
|
const String changelog = '''
|
|
## 1.0.2
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Versions in CHANGELOG.md and pubspec.yaml do not match.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Success if CHANGELOG and pubspec versions match', () async {
|
|
const String version = '1.0.1';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: version);
|
|
|
|
const String changelog = '''
|
|
## $version
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'Fail if pubspec version only matches an older version listed in CHANGELOG',
|
|
() async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.1
|
|
* Some changes.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
bool hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
});
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Versions in CHANGELOG.md and pubspec.yaml do not match.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Allow NEXT as a placeholder for gathering CHANGELOG entries',
|
|
() async {
|
|
const String version = '1.0.0';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: version);
|
|
|
|
const String changelog = '''
|
|
## NEXT
|
|
* Some changes that won't be published until the next time there's a release.
|
|
## $version
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('Found NEXT; validating next version in the CHANGELOG.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Fail if NEXT appears after a version', () async {
|
|
const String version = '1.0.1';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: version);
|
|
|
|
const String changelog = '''
|
|
## $version
|
|
* Some changes.
|
|
## NEXT
|
|
* Some changes that should have been folded in 1.0.1.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
bool hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
});
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('When bumping the version for release, the NEXT section '
|
|
"should be incorporated into the new version's release notes.")
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('Fail if NEXT is left in the CHANGELOG when adding a version bump',
|
|
() async {
|
|
const String version = '1.0.1';
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: version);
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
const String changelog = '''
|
|
## NEXT
|
|
* Some changes that should have been folded in 1.0.1.
|
|
## $version
|
|
* Some changes.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
|
|
bool hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
});
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('When bumping the version for release, the NEXT section '
|
|
"should be incorporated into the new version's release notes."),
|
|
contains('plugin:\n'
|
|
' CHANGELOG.md failed validation.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if the version increases without replacing NEXT', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.1');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
const String changelog = '''
|
|
## NEXT
|
|
* Some changes that should be listed as part of 1.0.1.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
|
|
bool hasError = false;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
});
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('When bumping the version for release, the NEXT section '
|
|
"should be incorporated into the new version's release notes.")
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows NEXT for a revert', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## NEXT
|
|
* Some changes that should be listed as part of 1.0.1.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.1')),
|
|
];
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('New version is lower than previous version. '
|
|
'This is assumed to be a revert.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
// This handles imports of a package with a NEXT section.
|
|
test('allows NEXT for a new package', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePackage('a_package', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## NEXT
|
|
* Some changes that should be listed in the next release.
|
|
## 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find previous version at git base'),
|
|
contains('Found NEXT; validating next version in the CHANGELOG'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails gracefully if the version headers are not found due to using the wrong style',
|
|
() async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## NEXT
|
|
* Some changes for a later release.
|
|
# 1.0.0
|
|
* Some other changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find a version in CHANGELOG.md'),
|
|
contains('The current version should be on a line starting with '
|
|
'"## ", either on the first non-empty line or after a "## NEXT" '
|
|
'section.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails gracefully if the version is unparseable', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## Alpha
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(runner, <String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('"Alpha" could not be parsed as a version.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
group('missing change detection', () {
|
|
Future<List<String>> runWithMissingChangeDetection(List<String> extraArgs,
|
|
{void Function(Error error)? errorHandler}) async {
|
|
return runCapturingPrint(
|
|
runner,
|
|
<String>[
|
|
'version-check',
|
|
'--base-sha=main',
|
|
'--check-for-missing-changes',
|
|
...extraArgs,
|
|
],
|
|
errorHandler: errorHandler);
|
|
}
|
|
|
|
test('passes for unchanged packages', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'fails if a version change is missing from a change that does not '
|
|
'pass the exemption check', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
''')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No version change found'),
|
|
contains('plugin:\n'
|
|
' Missing version change'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes version change requirement when version changes', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.1');
|
|
|
|
const String changelog = '''
|
|
## 1.0.1
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
packages/plugin/CHANGELOG.md
|
|
packages/plugin/pubspec.yaml
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('version change check ignores files outside the package', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin_a/lib/plugin.dart
|
|
tool/plugin/lib/plugin.dart
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows missing version change for exempt changes', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/example/android/.pluginToolsConfig.yaml
|
|
packages/plugin/example/android/lint-baseline.xml
|
|
packages/plugin/example/android/src/androidTest/foo/bar/FooTest.java
|
|
packages/plugin/example/ios/RunnerTests/Foo.m
|
|
packages/plugin/example/ios/RunnerUITests/info.plist
|
|
packages/plugin/darwin/Tests/Foo.swift
|
|
packages/plugin/analysis_options.yaml
|
|
packages/plugin/CHANGELOG.md
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows missing version change with override label', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
packages/plugin/CHANGELOG.md
|
|
packages/plugin/pubspec.yaml
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[
|
|
'--pr-labels=some label,override: no versioning needed,another-label'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Ignoring lack of version change due to the '
|
|
'"override: no versioning needed" label.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails if a CHANGELOG change is missing', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
''')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No CHANGELOG change found.\nIf'),
|
|
contains('plugin:\n'
|
|
' Missing CHANGELOG change'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('passes CHANGELOG check when the CHANGELOG is changed', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
packages/plugin/CHANGELOG.md
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('fails CHANGELOG check if only another package CHANGELOG chages',
|
|
() async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
packages/another_plugin/CHANGELOG.md
|
|
''')),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('No CHANGELOG change found'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('allows missing CHANGELOG change with justification', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/example/lib/foo.dart
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[
|
|
'--pr-labels=some label,override: no changelog needed,another-label'
|
|
]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Ignoring lack of CHANGELOG update due to the '
|
|
'"override: no changelog needed" label.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
// This test ensures that Dependabot Gradle changes to test-only files
|
|
// aren't flagged by the version check.
|
|
test(
|
|
'allows missing CHANGELOG and version change for test-only Gradle changes',
|
|
() async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
// File list.
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/android/build.gradle
|
|
''')),
|
|
// build.gradle diff
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
|
- testImplementation 'junit:junit:4.10.0'
|
|
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
|
+ testImplementation 'junit:junit:4.13.2'
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'allows missing CHANGELOG and version change for dev-only-file changes',
|
|
() async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
// File list.
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/tool/run_tests.dart
|
|
packages/plugin/run_tests.sh
|
|
''')),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'allows missing CHANGELOG and version change for dev-only line-level '
|
|
'changes in production files', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
// File list.
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
''')),
|
|
// Dart file diff.
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
+ // TODO(someone): Fix this.
|
|
+ // ignore: some_lint
|
|
'''), <String>['main', 'HEAD', '--', 'packages/plugin/lib/plugin.dart']),
|
|
];
|
|
|
|
final List<String> output =
|
|
await runWithMissingChangeDetection(<String>[]);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('documentation comments are not exempt', () async {
|
|
final RepositoryPackage plugin =
|
|
createFakePlugin('plugin', packagesDir, version: '1.0.0');
|
|
|
|
const String changelog = '''
|
|
## 1.0.0
|
|
* Some changes.
|
|
''';
|
|
plugin.changelogFile.writeAsStringSync(changelog);
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
processRunner.mockProcessesForExecutable['git-diff'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
packages/plugin/lib/plugin.dart
|
|
''')),
|
|
// Dart file diff.
|
|
FakeProcessInfo(MockProcess(stdout: '''
|
|
+ /// Important new information for API clients!
|
|
'''), <String>['main', 'HEAD', '--', 'packages/plugin/lib/plugin.dart']),
|
|
];
|
|
|
|
Error? commandError;
|
|
final List<String> output = await runWithMissingChangeDetection(
|
|
<String>[], errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains(
|
|
'No version change found, but the change to this package could '
|
|
'not be verified to be exempt\n',
|
|
),
|
|
contains('plugin:\n'
|
|
' Missing version change'),
|
|
]),
|
|
);
|
|
});
|
|
});
|
|
|
|
test('allows valid against pub', () async {
|
|
mockHttpResponse = <String, dynamic>{
|
|
'name': 'some_package',
|
|
'versions': <String>[
|
|
'0.0.1',
|
|
'0.0.2',
|
|
'1.0.0',
|
|
],
|
|
};
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
final List<String> output = await runCapturingPrint(runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('plugin: Current largest version on pub: 1.0.0'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('denies invalid against pub', () async {
|
|
mockHttpResponse = <String, dynamic>{
|
|
'name': 'some_package',
|
|
'versions': <String>[
|
|
'0.0.1',
|
|
'0.0.2',
|
|
],
|
|
};
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
|
|
bool hasError = false;
|
|
final List<String> result = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
});
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
result,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('''
|
|
${indentation}Incorrectly updated version.
|
|
${indentation}HEAD: 2.0.0, pub: 0.0.2.
|
|
${indentation}Allowed versions: {1.0.0: NextVersionType.BREAKING_MAJOR, 0.1.0: NextVersionType.MINOR, 0.0.3: NextVersionType.PATCH}''')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test(
|
|
'throw and print error message if http request failed when checking against pub',
|
|
() async {
|
|
mockHttpStatus = 400;
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
bool hasError = false;
|
|
final List<String> result = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main', '--against-pub'],
|
|
errorHandler: (Error e) {
|
|
expect(e, isA<ToolExit>());
|
|
hasError = true;
|
|
});
|
|
expect(hasError, isTrue);
|
|
|
|
expect(
|
|
result,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('''
|
|
${indentation}Error fetching version on pub for plugin.
|
|
${indentation}HTTP Status 400
|
|
${indentation}HTTP response: null
|
|
''')
|
|
]),
|
|
);
|
|
});
|
|
|
|
test('when checking against pub, allow any version if http status is 404.',
|
|
() async {
|
|
mockHttpStatus = 404;
|
|
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] = <FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> result = await runCapturingPrint(runner,
|
|
<String>['version-check', '--base-sha=main', '--against-pub']);
|
|
|
|
expect(
|
|
result,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Unable to find previous version on pub server.'),
|
|
]),
|
|
);
|
|
});
|
|
|
|
group('prelease versions', () {
|
|
test(
|
|
'allow an otherwise-valid transition that also adds a pre-release component',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0-dev');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.0.0')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.0.0 -> 2.0.0-dev'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('allow releasing a pre-release', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '1.2.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.2.0-dev')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.2.0-dev -> 1.2.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
// Allow abandoning a pre-release version in favor of a different version
|
|
// change type.
|
|
test(
|
|
'allow an otherwise-valid transition that also removes a pre-release component',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '2.0.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.2.0-dev')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.2.0-dev -> 2.0.0'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('allow changing only the pre-release version', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '1.2.0-dev.2');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 1.2.0-dev.1')),
|
|
];
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main']);
|
|
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Running for plugin'),
|
|
contains('1.2.0-dev.1 -> 1.2.0-dev.2'),
|
|
]),
|
|
);
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('denies invalid version change that also adds a pre-release',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0-dev');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]));
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('denies invalid version change that also removes a pre-release',
|
|
() async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1-dev')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]));
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
|
|
test('denies invalid version change between pre-releases', () async {
|
|
createFakePlugin('plugin', packagesDir, version: '0.2.0-dev');
|
|
processRunner.mockProcessesForExecutable['git-show'] =
|
|
<FakeProcessInfo>[
|
|
FakeProcessInfo(MockProcess(stdout: 'version: 0.0.1-dev')),
|
|
];
|
|
Error? commandError;
|
|
final List<String> output = await runCapturingPrint(
|
|
runner, <String>['version-check', '--base-sha=main'],
|
|
errorHandler: (Error e) {
|
|
commandError = e;
|
|
});
|
|
|
|
expect(commandError, isA<ToolExit>());
|
|
expect(
|
|
output,
|
|
containsAllInOrder(<Matcher>[
|
|
contains('Incorrectly updated version.'),
|
|
]));
|
|
expect(
|
|
processRunner.recordedCalls,
|
|
containsAllInOrder(const <ProcessCall>[
|
|
ProcessCall('git-show',
|
|
<String>['main:packages/plugin/pubspec.yaml'], null)
|
|
]));
|
|
});
|
|
});
|
|
});
|
|
|
|
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 does not allow skipping major versions', () {
|
|
testAllowedVersion('1.0.1', '3.0.0', allowed: false);
|
|
testAllowedVersion('1.1.0', '2.3.0', allowed: false);
|
|
});
|
|
});
|
|
}
|