blob: db4723e40e58418f3b1a2e41c52086fe1426afc2 [file]
// Copyright 2014 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/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/upgrade.dart';
import 'package:flutter_tools/src/context_runner.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/persistent_tool_state.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/fake_process_manager.dart';
import '../../src/fakes.dart';
import '../../src/test_flutter_command_runner.dart';
void main() {
setUpAll(() {
Cache.disableLocking();
Cache.flutterRoot = getFlutterRoot();
Cache.disableLocking();
});
group('UpgradeCommandRunner', () {
final jan12026 = DateTime.utc(2026);
late FakeUpgradeCommandRunner fakeCommandRunner;
late UpgradeCommandRunner realCommandRunner;
late FakeProcessManager processManager;
late FakePlatform fakePlatform;
const gitTagVersion = GitTagVersion(
x: 1,
y: 2,
z: 3,
hotfix: 4,
commits: 5,
hash: 'asd',
gitTag: '1.2.3+hotfix.5.pre.5',
);
setUp(() {
fakePlatform = FakePlatform()
..environment = Map<String, String>.unmodifiable(<String, String>{
'ENV1': 'irrelevant',
'ENV2': 'irrelevant',
});
processManager = FakeProcessManager.empty();
final toolContext = DelegatingToolContext(systemClock: SystemClock.fixed(jan12026));
fakeCommandRunner = FakeUpgradeCommandRunner(toolContext: toolContext);
realCommandRunner = UpgradeCommandRunner(toolContext: toolContext)
..workingDirectory = getFlutterRoot();
fakeCommandRunner.willHaveUncommittedChanges = false;
});
testUsingContext('throws on unknown tag, official branch, noforce', () async {
final flutterVersion = FakeFlutterVersion(branch: 'beta');
const upstreamRevision = '';
final latestVersion = FakeFlutterVersion(frameworkRevision: upstreamRevision);
fakeCommandRunner.remoteVersion = latestVersion;
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: false,
testFlow: false,
gitTagVersion: const GitTagVersion.unknown(),
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(result, throwsToolExit());
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{Platform: () => fakePlatform});
testUsingContext('throws tool exit with uncommitted changes', () async {
final flutterVersion = FakeFlutterVersion(branch: 'beta');
const upstreamRevision = '';
final latestVersion = FakeFlutterVersion(frameworkRevision: upstreamRevision);
fakeCommandRunner.remoteVersion = latestVersion;
fakeCommandRunner.willHaveUncommittedChanges = true;
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: false,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(result, throwsToolExit());
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{Platform: () => fakePlatform});
testUsingContext(
'hasUncommittedChanges ignores pubspec.lock on non-stable channel',
() async {
final flutterVersion = FakeFlutterVersion();
processManager.addCommand(
const FakeCommand(
command: <String>['git', 'status', '-s'],
stdout: ' M pubspec.lock\n M packages/flutter_tools/pubspec.lock',
),
);
final bool result = await realCommandRunner.hasUncommittedChanges(flutterVersion);
expect(result, false);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'hasUncommittedChanges does not ignore pubspec.lock on stable channel',
() async {
final flutterVersion = FakeFlutterVersion(branch: 'stable');
processManager.addCommand(
const FakeCommand(command: <String>['git', 'status', '-s'], stdout: ' M pubspec.lock'),
);
final bool result = await realCommandRunner.hasUncommittedChanges(flutterVersion);
expect(result, true);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'hasUncommittedChanges detects other changes on non-stable channel',
() async {
final flutterVersion = FakeFlutterVersion();
processManager.addCommand(
const FakeCommand(
command: <String>['git', 'status', '-s'],
stdout: ' M pubspec.lock\n M lib/src/commands/upgrade.dart',
),
);
final bool result = await realCommandRunner.hasUncommittedChanges(flutterVersion);
expect(result, true);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
"Doesn't continue on known tag, beta branch, no force, already up-to-date",
() async {
const revision = 'abc123';
final latestVersion = FakeFlutterVersion(frameworkRevision: revision);
final flutterVersion = FakeFlutterVersion(branch: 'beta', frameworkRevision: revision);
fakeCommandRunner.alreadyUpToDate = true;
fakeCommandRunner.remoteVersion = latestVersion;
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: false,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(testLogger.statusText, contains('Flutter is already up to date'));
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'starts the upgrade operation and passes now as --continue-started-at <iso-date>',
() async {
const revision = 'abc123';
const upstreamRevision = 'def456';
const version = '1.2.3';
const upstreamVersion = '4.5.6';
final flutterVersion = FakeFlutterVersion(
branch: 'beta',
frameworkRevision: revision,
frameworkRevisionShort: revision,
frameworkVersion: version,
);
final latestVersion = FakeFlutterVersion(
frameworkRevision: upstreamRevision,
frameworkRevisionShort: upstreamRevision,
frameworkVersion: upstreamVersion,
);
final DateTime now = DateTime.now().subtract(const Duration(minutes: 25));
fakeCommandRunner = FakeUpgradeCommandRunner(
toolContext: DelegatingToolContext(systemClock: SystemClock.fixed(now)),
)..remoteVersion = latestVersion;
processManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
globals.fs.path.join(Cache.flutterRoot!, 'bin', 'flutter'),
'upgrade',
'--continue',
'--continue-started-at',
now.toIso8601String(),
'--no-version-check',
],
),
]);
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: false,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(testLogger.statusText, isNot(contains('Took ')));
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'finishes the upgrade operation and prints minutes the operation took',
() async {
const revision = 'abc123';
const upstreamRevision = 'def456';
const version = '1.2.3';
const upstreamVersion = '4.5.6';
final flutterVersion = FakeFlutterVersion(
branch: 'beta',
frameworkRevision: revision,
frameworkRevisionShort: revision,
frameworkVersion: version,
);
final latestVersion = FakeFlutterVersion(
frameworkRevision: upstreamRevision,
frameworkRevisionShort: upstreamRevision,
frameworkVersion: upstreamVersion,
);
final now = DateTime.now();
final DateTime before = now.subtract(const Duration(minutes: 25));
fakeCommandRunner = FakeUpgradeCommandRunner(
toolContext: DelegatingToolContext(systemClock: SystemClock.fixed(now)),
)..remoteVersion = latestVersion;
processManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
globals.fs.path.join('bin', 'flutter'),
'--no-color',
'--no-version-check',
'precache',
],
),
]);
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
UpgradePhase.secondHalf(upgradeStartedAt: before),
force: false,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(testLogger.statusText, contains('Took 25.0m'));
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'correctly provides upgrade version on verify only',
() async {
const revision = 'abc123';
const upstreamRevision = 'def456';
const version = '1.2.3';
const upstreamVersion = '4.5.6';
final flutterVersion = FakeFlutterVersion(
branch: 'beta',
frameworkRevision: revision,
frameworkRevisionShort: revision,
frameworkVersion: version,
);
final latestVersion = FakeFlutterVersion(
frameworkRevision: upstreamRevision,
frameworkRevisionShort: upstreamRevision,
frameworkVersion: upstreamVersion,
);
fakeCommandRunner.alreadyUpToDate = false;
fakeCommandRunner.remoteVersion = latestVersion;
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: false,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: true,
);
expect(await result, FlutterCommandResult.success());
expect(testLogger.statusText, contains('A new version of Flutter is available'));
expect(testLogger.statusText, contains('The latest version: 4.5.6 (revision def456)'));
expect(testLogger.statusText, contains('Your current version: 1.2.3 (revision abc123)'));
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'fetchLatestVersion returns version if git succeeds',
() async {
const revision = 'abc123';
const version = '1.2.3';
processManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>['git', 'fetch', '--tags']),
const FakeCommand(
command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
stdout: revision,
),
const FakeCommand(
command: <String>['git', 'tag', '--points-at', revision],
stdout: version,
),
]);
final FlutterVersion updateVersion = await realCommandRunner.fetchLatestVersion(
localVersion: FakeFlutterVersion(),
);
expect(updateVersion.frameworkVersion, version);
expect(updateVersion.frameworkRevision, revision);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'fetchLatestVersion returns latest version if the upstream revision has multiple tags',
() async {
const latestVersion = '3.4.5';
const tags = <String>['1.2.3', '1.2.4', latestVersion];
const revision = 'abc123';
processManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>['git', 'fetch', '--tags']),
const FakeCommand(
command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
stdout: revision,
),
FakeCommand(
command: const <String>['git', 'tag', '--points-at', revision],
stdout: tags.join('\n'),
),
]);
final FlutterVersion updateVersion = await realCommandRunner.fetchLatestVersion(
localVersion: FakeFlutterVersion(),
);
expect(updateVersion.frameworkVersion, latestVersion);
expect(updateVersion.frameworkRevision, revision);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'fetchLatestVersion throws toolExit if HEAD is detached',
() async {
processManager.addCommands(const <FakeCommand>[
FakeCommand(command: <String>['git', 'fetch', '--tags']),
FakeCommand(
command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
exception: ProcessException('git', <String>[
'rev-parse',
'--verify',
'@{upstream}',
], 'fatal: HEAD does not point to a branch'),
),
]);
await expectLater(
() async => realCommandRunner.fetchLatestVersion(localVersion: FakeFlutterVersion()),
throwsToolExit(
message:
'Unable to upgrade Flutter: Your Flutter checkout '
'is currently not on a release branch.\n'
'Use "flutter channel" to switch to an official channel, and retry. '
'Alternatively, re-install Flutter by going to https://flutter.dev/setup.',
),
);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'fetchLatestVersion throws toolExit if no upstream configured',
() async {
processManager.addCommands(const <FakeCommand>[
FakeCommand(command: <String>['git', 'fetch', '--tags']),
FakeCommand(
command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
exception: ProcessException('git', <String>[
'rev-parse',
'--verify',
'@{upstream}',
], 'fatal: no upstream configured for branch'),
),
]);
await expectLater(
() async => realCommandRunner.fetchLatestVersion(localVersion: FakeFlutterVersion()),
throwsToolExit(
message:
'Unable to upgrade Flutter: The current Flutter '
'branch/channel is not tracking any remote repository.\n'
'Re-install Flutter by going to https://flutter.dev/setup.',
),
);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'git exception during attemptReset throwsToolExit',
() async {
const revision = 'abc123';
const errorMessage = 'fatal: Could not parse object ´$revision´';
processManager.addCommand(
const FakeCommand(
command: <String>['git', 'reset', '--hard', revision],
exception: ProcessException('git', <String>['reset', '--hard', revision], errorMessage),
),
);
await expectLater(
() async => realCommandRunner.attemptReset(revision),
throwsToolExit(message: errorMessage),
);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'flutterUpgradeContinue passes env variables to child process',
() async {
processManager.addCommand(
FakeCommand(
command: <String>[
globals.fs.path.join(
realCommandRunner.workingDirectory ?? Cache.flutterRoot!,
'bin',
'flutter',
),
'upgrade',
'--continue',
'--continue-started-at',
'2026-01-01T00:00:00.000Z',
'--no-version-check',
],
environment: <String, String>{
'FLUTTER_ALREADY_LOCKED': 'true',
...fakePlatform.environment,
},
),
);
await realCommandRunner.flutterUpgradeContinue(startedAt: jan12026);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'Show current version to the upgrade message.',
() async {
const revision = 'abc123';
const upstreamRevision = 'def456';
const version = '1.2.3';
const upstreamVersion = '4.5.6';
final flutterVersion = FakeFlutterVersion(
branch: 'beta',
frameworkRevision: revision,
frameworkVersion: version,
);
final latestVersion = FakeFlutterVersion(
frameworkRevision: upstreamRevision,
frameworkVersion: upstreamVersion,
);
fakeCommandRunner.alreadyUpToDate = false;
fakeCommandRunner.remoteVersion = latestVersion;
fakeCommandRunner.workingDirectory = 'workingDirectory/aaa/bbb';
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: true,
testFlow: true,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(
testLogger.statusText,
contains('Upgrading Flutter to 4.5.6 from 1.2.3 in workingDirectory/aaa/bbb...'),
);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'Can upgrade to a newer version with the same framework Git revision',
() async {
const revision = 'abc123';
const version = '1.2.3';
const upstreamVersion = '4.5.6';
final flutterVersion = FakeFlutterVersion(
frameworkRevision: revision,
frameworkVersion: version,
gitTagVersion: GitTagVersion.parse(version),
);
final latestVersion = FakeFlutterVersion(
frameworkRevision: revision,
frameworkVersion: upstreamVersion,
gitTagVersion: GitTagVersion.parse(upstreamVersion),
);
fakeCommandRunner.alreadyUpToDate = false;
fakeCommandRunner.remoteVersion = latestVersion;
fakeCommandRunner.workingDirectory = 'workingDirectory/aaa/bbb';
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: true,
testFlow: true,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(
testLogger.statusText,
contains('Upgrading Flutter to 4.5.6 from 1.2.3 in workingDirectory/aaa/bbb...'),
);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'always regenerates flutter.version.json',
() async {
const revision = 'abc123';
const version = '3.38.1';
const upstreamVersion = '3.38.2';
const engineRevision = 'zyxw';
final flutterVersion = FakeFlutterVersion(
frameworkRevision: revision,
frameworkVersion: version,
engineRevision: engineRevision,
gitTagVersion: GitTagVersion.parse(version),
);
final latestVersion = FakeFlutterVersion(
frameworkRevision: revision,
frameworkVersion: upstreamVersion,
engineRevision: engineRevision,
gitTagVersion: GitTagVersion.parse(upstreamVersion),
);
final CommandRunner<void> runner = createTestCommandRunner(
UpgradeCommand(
toolContext: DelegatingToolContext(),
verboseHelp: false,
commandRunner: fakeCommandRunner,
),
);
fakeCommandRunner.alreadyUpToDate = false;
fakeCommandRunner.remoteVersion = latestVersion;
fakeCommandRunner.workingDirectory = 'workingDirectory/aaa/bbb';
await runInContext(
() async {
await runner.run(<String>['upgrade', '--force']);
// Verify that flutter.version.json is deleted before running phase two of the
// upgrade command. This should cause the version file to be regenerated based on the
// current branch state.
expect(flutterVersion.didDeleteVersionFile, true);
},
overrides: <Type, Generator>{
FlutterVersion: () => flutterVersion,
ProcessManager: () => FakeProcessManager.any(),
Platform: () => fakePlatform,
},
);
await runInContext(
() async {
await runner.run(<String>['upgrade', '--continue']);
// Verify that ensureVersionFile() was invoked, which will create flutter.version.json
// if it doesn't exist.
expect(latestVersion.didEnsureVersionFile, true);
expect(latestVersion.didDeleteVersionFile, false);
},
overrides: <Type, Generator>{
FlutterVersion: () => latestVersion,
ProcessManager: () => FakeProcessManager.any(),
Platform: () => fakePlatform,
},
);
},
overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
Platform: () => fakePlatform,
},
);
testUsingContext(
'Does not confuse an older release with the same revision as an available update',
() async {
const revision = 'abc123';
const version = '4.5.6';
final GitTagVersion currentTag = GitTagVersion.parse(version);
const upstreamVersion = '1.2.3';
final GitTagVersion upstreamTag = GitTagVersion.parse(upstreamVersion);
final flutterVersion = FakeFlutterVersion(
branch: 'stable',
frameworkRevision: revision,
frameworkVersion: version,
gitTagVersion: currentTag,
);
final latestVersion = FakeFlutterVersion(
branch: 'stable',
frameworkRevision: revision,
frameworkVersion: upstreamVersion,
gitTagVersion: upstreamTag,
);
fakeCommandRunner.alreadyUpToDate = false;
fakeCommandRunner.remoteVersion = latestVersion;
fakeCommandRunner.workingDirectory = 'workingDirectory/aaa/bbb';
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: true,
testFlow: true,
gitTagVersion: currentTag,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(testLogger.statusText, contains('Flutter is already up to date on channel stable'));
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'precacheArtifacts passes env variables to child process',
() async {
processManager.addCommand(
FakeCommand(
command: <String>[
globals.fs.path.join('bin', 'flutter'),
'--no-color',
'--no-version-check',
'precache',
],
environment: <String, String>{
'FLUTTER_ALREADY_LOCKED': 'true',
...fakePlatform.environment,
},
),
);
await precacheArtifacts(
fileSystem: globals.fs,
logger: globals.logger,
platform: fakePlatform,
processUtils: globals.processUtils,
);
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
group('runs upgrade', () {
setUp(() {
processManager.addCommand(
FakeCommand(
command: <String>[
globals.fs.path.join(Cache.flutterRoot!, 'bin', 'flutter'),
'upgrade',
'--continue',
'--continue-started-at',
'2026-01-01T00:00:00.000Z',
'--no-version-check',
],
),
);
});
testUsingContext(
'does not throw on unknown tag, official branch, force',
() async {
fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: '1234');
final flutterVersion = FakeFlutterVersion(branch: 'beta');
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: true,
testFlow: false,
gitTagVersion: const GitTagVersion.unknown(),
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
'does not throw tool exit with uncommitted changes and force',
() async {
final flutterVersion = FakeFlutterVersion(branch: 'beta');
fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: '1234');
fakeCommandRunner.willHaveUncommittedChanges = true;
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: true,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
testUsingContext(
"Doesn't throw on known tag, beta branch, no force",
() async {
final flutterVersion = FakeFlutterVersion(branch: 'beta');
fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: '1234');
final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
const UpgradePhase.firstHalf(),
force: true,
testFlow: false,
gitTagVersion: gitTagVersion,
flutterVersion: flutterVersion,
verifyOnly: false,
);
expect(await result, FlutterCommandResult.success());
expect(processManager, hasNoRemainingExpectations);
},
overrides: <Type, Generator>{
ProcessManager: () => processManager,
Platform: () => fakePlatform,
},
);
group('full command', () {
late FakeProcessManager fakeProcessManager;
late Directory tempDir;
late File flutterToolState;
late FileSystem fs;
setUp(() {
fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(command: <String>['git', 'tag', '--points-at', 'HEAD']),
const FakeCommand(
command: <String>['git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD'],
stdout: 'v1.12.16-19-gb45b676af',
),
]);
fs = MemoryFileSystem.test();
tempDir = fs.systemTempDirectory.createTempSync('flutter_upgrade_test.');
flutterToolState = tempDir.childFile('.flutter_tool_state');
});
tearDown(() {
tryToDelete(tempDir);
});
testUsingContext(
'upgrade continue prints welcome message',
() async {
fakeProcessManager = FakeProcessManager.any();
final toolContext = FakeToolContext(
fs: fs,
persistentToolState: PersistentToolState.test(directory: tempDir, logger: testLogger),
processManager: fakeProcessManager,
);
final fakeRunner = FakeUpgradeCommandRunner(toolContext: toolContext);
final upgradeCommand = UpgradeCommand(
toolContext: toolContext,
verboseHelp: false,
commandRunner: fakeRunner,
);
await createTestCommandRunner(upgradeCommand).run(<String>[
'upgrade',
'--continue',
'--continue-started-at',
DateTime.now().toIso8601String(),
]);
expect(
json.decode(flutterToolState.readAsStringSync()),
containsPair('redisplay-welcome-message', true),
);
},
overrides: <Type, Generator>{
FileSystem: () => fs,
FlutterVersion: () => FakeFlutterVersion(),
ProcessManager: () => fakeProcessManager,
PersistentToolState: () =>
PersistentToolState.test(directory: tempDir, logger: testLogger),
},
);
});
});
});
}
class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
FakeUpgradeCommandRunner({required super.toolContext});
bool willHaveUncommittedChanges = false;
bool alreadyUpToDate = false;
late FlutterVersion remoteVersion;
@override
Future<FlutterVersion> fetchLatestVersion({FlutterVersion? localVersion}) async => remoteVersion;
@override
Future<bool> hasUncommittedChanges(FlutterVersion version) async => willHaveUncommittedChanges;
@override
Future<void> attemptReset(String newRevision) async {}
@override
Future<void> updatePackages(FlutterVersion flutterVersion) async {}
@override
Future<void> runDoctor() async {}
}