Unverified Commit 7cebaac9 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Makes switching channels remove version freshness stamp. (#21182)

When switching between channels, we were leaving around the version freshness stamp file (bin/cache/flutter_version_check.stamp), which meant that the flutter tool would read from that file to see what the cached date of the most recent commit to the current channel (branch) was. The problem was that since the file was created while on the previous channel, the cached date was for the wrong channel, so if you switch from master to beta, flutter would think that the channel was out of date, and a new version was available, at least for three days after the first time it checked (after three days since the last time the freshness was checked, the cached date would get updated).

This PR modifies the channel command to remove that stamp file whenever the user switches channels, so that the cached date will be from the right channel when it is recreated.

Fixes #21134
parent 8f8af999
...@@ -126,7 +126,12 @@ class ChannelCommand extends FlutterCommand { ...@@ -126,7 +126,12 @@ class ChannelCommand extends FlutterCommand {
); );
} }
} }
if (result != 0) if (result != 0) {
throwToolExit('Switching channels failed with error code $result.', exitCode: result); throwToolExit('Switching channels failed with error code $result.', exitCode: result);
} else {
// Remove the version check stamp, since it could contain out-of-date
// information that pertains to the previous channel.
await FlutterVersion.resetFlutterVersionFreshnessCheck();
}
} }
} }
...@@ -228,6 +228,22 @@ class FlutterVersion { ...@@ -228,6 +228,22 @@ class FlutterVersion {
@visibleForTesting @visibleForTesting
static Duration timeToPauseToLetUserReadTheMessage = const Duration(seconds: 2); static Duration timeToPauseToLetUserReadTheMessage = const Duration(seconds: 2);
/// Reset the version freshness information by removing the stamp file.
///
/// New version freshness information will be regenerated when
/// [checkFlutterVersionFreshness] is called after this. This is typically
/// used when switching channels so that stale information from another
/// channel doesn't linger.
static Future<Null> resetFlutterVersionFreshnessCheck() async {
try {
await Cache.instance.getStampFileFor(
VersionCheckStamp.kFlutterVersionCheckStampFile,
).delete();
} on FileSystemException {
// Ignore, since we don't mind if the file didn't exist in the first place.
}
}
/// Checks if the currently installed version of Flutter is up-to-date, and /// Checks if the currently installed version of Flutter is up-to-date, and
/// warns the user if it isn't. /// warns the user if it isn't.
/// ///
......
...@@ -4,17 +4,35 @@ ...@@ -4,17 +4,35 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io' hide File;
import 'package:args/command_runner.dart'; 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/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/channel.dart'; import 'package:flutter_tools/src/commands/channel.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'src/common.dart'; import 'src/common.dart';
import 'src/context.dart'; import 'src/context.dart';
Process createMockProcess({int exitCode = 0, String stdout = '', String stderr = ''}) {
final Stream<List<int>> stdoutStream = new Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(stdout),
]);
final Stream<List<int>> stderrStream = new Stream<List<int>>.fromIterable(<List<int>>[
utf8.encode(stderr),
]);
final Process process = new MockProcess();
when(process.stdout).thenAnswer((_) => stdoutStream);
when(process.stderr).thenAnswer((_) => stderrStream);
when(process.exitCode).thenAnswer((_) => new Future<int>.value(exitCode));
return process;
}
void main() { void main() {
group('channel', () { group('channel', () {
final MockProcessManager mockProcessManager = new MockProcessManager(); final MockProcessManager mockProcessManager = new MockProcessManager();
...@@ -35,32 +53,26 @@ void main() { ...@@ -35,32 +53,26 @@ void main() {
}); });
testUsingContext('removes duplicates', () async { testUsingContext('removes duplicates', () async {
final Stream<List<int>> stdout = new Stream<List<int>>.fromIterable(<List<int>>[ final Process process = createMockProcess(
utf8.encode( stdout: 'origin/dev\n'
'origin/dev\n' 'origin/beta\n'
'origin/beta\n' 'upstream/dev\n'
'upstream/dev\n' 'upstream/beta\n');
'upstream/beta\n'
),
]);
final Process process = new MockProcess();
when(process.stdout).thenAnswer((_) => stdout);
when(process.stderr).thenAnswer((_) => const Stream<List<int>>.empty());
when(process.exitCode).thenAnswer((_) => new Future<int>.value(0));
when(mockProcessManager.start( when(mockProcessManager.start(
<String>['git', 'branch', '-r'], <String>['git', 'branch', '-r'],
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'))) environment: anyNamed('environment'),
.thenAnswer((_) => new Future<Process>.value(process)); )).thenAnswer((_) => new Future<Process>.value(process));
final ChannelCommand command = new ChannelCommand(); final ChannelCommand command = new ChannelCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command); final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['channel']); await runner.run(<String>['channel']);
verify(mockProcessManager.start(<String>['git', 'branch', '-r'], verify(mockProcessManager.start(
workingDirectory: anyNamed('workingDirectory'), <String>['git', 'branch', '-r'],
environment: anyNamed('environment'))).called(1); workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
expect(testLogger.errorText, hasLength(0)); expect(testLogger.errorText, hasLength(0));
...@@ -73,7 +85,112 @@ void main() { ...@@ -73,7 +85,112 @@ void main() {
expect(rows, <String>['dev', 'beta']); expect(rows, <String>['dev', 'beta']);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
});
testUsingContext('can switch channels', () async {
when(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
final ChannelCommand command = new ChannelCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['channel', 'beta']);
verify(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
expect(testLogger.statusText, contains("Switching to flutter channel 'beta'..."));
expect(testLogger.errorText, hasLength(0));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
FileSystem: () => new MemoryFileSystem(),
});
// This verifies that bug https://github.com/flutter/flutter/issues/21134
// doesn't return.
testUsingContext('removes version stamp file when switching channels', () async {
when(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
when(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) => new Future<Process>.value(createMockProcess()));
final File versionCheckFile = Cache.instance.getStampFileFor(
VersionCheckStamp.kFlutterVersionCheckStampFile,
);
/// Create a bogus "leftover" version check file to make sure it gets
/// removed when the channel changes. The content doesn't matter.
versionCheckFile.createSync(recursive: true);
versionCheckFile.writeAsStringSync('''
{
"lastTimeVersionWasChecked": "2151-08-29 10:17:30.763802",
"lastKnownRemoteVersion": "2151-09-26 15:56:19.000Z"
}
''');
final ChannelCommand command = new ChannelCommand();
final CommandRunner<Null> runner = createTestCommandRunner(command);
await runner.run(<String>['channel', 'beta']);
verify(mockProcessManager.start(
<String>['git', 'fetch'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
verify(mockProcessManager.start(
<String>['git', 'checkout', 'beta', '--'],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).called(1);
expect(testLogger.statusText, isNot(contains('A new version of Flutter')));
expect(testLogger.errorText, hasLength(0));
expect(versionCheckFile.existsSync(), isFalse);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
FileSystem: () => new MemoryFileSystem(),
}); });
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment