diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart index 5f09e68308..9e3cb5633f 100644 --- a/packages/flutter_tools/lib/src/version.dart +++ b/packages/flutter_tools/lib/src/version.dart @@ -207,20 +207,34 @@ class FlutterVersion { /// The amount of time we wait before pinging the server to check for the /// availability of a newer version of Flutter. @visibleForTesting - static const Duration kCheckAgeConsideredUpToDate = Duration(days: 3); + static const Duration checkAgeConsideredUpToDate = Duration(days: 3); /// We warn the user if the age of their Flutter installation is greater than - /// this duration. + /// this duration. The durations are slightly longer than the expected release + /// cadence for each channel, to give the user a grace period before they get + /// notified. /// - /// This is set to 5 weeks because releases are currently around every 4 weeks. + /// For example, for the beta channel, this is set to five weeks because + /// beta releases happen approximately every month. @visibleForTesting - static const Duration kVersionAgeConsideredUpToDate = Duration(days: 35); + static Duration versionAgeConsideredUpToDate(String channel) { + switch (channel) { + case 'stable': + return const Duration(days: 365 ~/ 2); // Six months + case 'beta': + return const Duration(days: 7 * 8); // Eight weeks + case 'dev': + return const Duration(days: 7 * 4); // Four weeks + default: + return const Duration(days: 7 * 3); // Three weeks + } + } /// The amount of time we wait between issuing a warning. /// /// This is to avoid annoying users who are unable to upgrade right away. @visibleForTesting - static const Duration kMaxTimeSinceLastWarning = Duration(days: 1); + static const Duration maxTimeSinceLastWarning = Duration(days: 1); /// The amount of time we pause for to let the user read the message about /// outdated Flutter installation. @@ -238,7 +252,7 @@ class FlutterVersion { static Future resetFlutterVersionFreshnessCheck() async { try { await Cache.instance.getStampFileFor( - VersionCheckStamp.kFlutterVersionCheckStampFile, + VersionCheckStamp.flutterVersionCheckStampFile, ).delete(); } on FileSystemException { // Ignore, since we don't mind if the file didn't exist in the first place. @@ -258,7 +272,7 @@ class FlutterVersion { final DateTime localFrameworkCommitDate = DateTime.parse(frameworkCommitDate); final Duration frameworkAge = _clock.now().difference(localFrameworkCommitDate); - final bool installationSeemsOutdated = frameworkAge > kVersionAgeConsideredUpToDate; + final bool installationSeemsOutdated = frameworkAge > versionAgeConsideredUpToDate(channel); // Get whether there's a newer version on the remote. This only goes // to the server if we haven't checked recently so won't happen on every @@ -273,8 +287,8 @@ class FlutterVersion { // Do not load the stamp before the above server check as it may modify the stamp file. final VersionCheckStamp stamp = await VersionCheckStamp.load(); - final DateTime lastTimeWarningWasPrinted = stamp.lastTimeWarningWasPrinted ?? _clock.ago(kMaxTimeSinceLastWarning * 2); - final bool beenAWhileSinceWarningWasPrinted = _clock.now().difference(lastTimeWarningWasPrinted) > kMaxTimeSinceLastWarning; + final DateTime lastTimeWarningWasPrinted = stamp.lastTimeWarningWasPrinted ?? _clock.ago(maxTimeSinceLastWarning * 2); + final bool beenAWhileSinceWarningWasPrinted = _clock.now().difference(lastTimeWarningWasPrinted) > maxTimeSinceLastWarning; // We show a warning if either we know there is a new remote version, or we couldn't tell but the local // version is outdated. @@ -327,7 +341,7 @@ class FlutterVersion { /// Gets the release date of the latest available Flutter version. /// /// This method sends a server request if it's been more than - /// [kCheckAgeConsideredUpToDate] since the last version check. + /// [checkAgeConsideredUpToDate] since the last version check. /// /// Returns null if the cached version is out-of-date or missing, and we are /// unable to reach the server to get the latest version. @@ -339,7 +353,7 @@ class FlutterVersion { final Duration timeSinceLastCheck = _clock.now().difference(versionCheckStamp.lastTimeVersionWasChecked); // Don't ping the server too often. Return cached value if it's fresh. - if (timeSinceLastCheck < kCheckAgeConsideredUpToDate) + if (timeSinceLastCheck < checkAgeConsideredUpToDate) return versionCheckStamp.lastKnownRemoteVersion; } @@ -381,10 +395,10 @@ class VersionCheckStamp { /// The prefix of the stamp file where we cache Flutter version check data. @visibleForTesting - static const String kFlutterVersionCheckStampFile = 'flutter_version_check'; + static const String flutterVersionCheckStampFile = 'flutter_version_check'; static Future load() async { - final String versionCheckStamp = Cache.instance.getStampFor(kFlutterVersionCheckStampFile); + final String versionCheckStamp = Cache.instance.getStampFor(flutterVersionCheckStampFile); if (versionCheckStamp != null) { // Attempt to parse stamp JSON. @@ -435,8 +449,8 @@ class VersionCheckStamp { if (newTimeWarningWasPrinted != null) jsonData['lastTimeWarningWasPrinted'] = '$newTimeWarningWasPrinted'; - const JsonEncoder kPrettyJsonEncoder = JsonEncoder.withIndent(' '); - Cache.instance.setStampFor(kFlutterVersionCheckStampFile, kPrettyJsonEncoder.convert(jsonData)); + const JsonEncoder prettyJsonEncoder = JsonEncoder.withIndent(' '); + Cache.instance.setStampFor(flutterVersionCheckStampFile, prettyJsonEncoder.convert(jsonData)); } Map toJson({ diff --git a/packages/flutter_tools/test/channel_test.dart b/packages/flutter_tools/test/channel_test.dart index a7bdf645f3..1e6ea10df1 100644 --- a/packages/flutter_tools/test/channel_test.dart +++ b/packages/flutter_tools/test/channel_test.dart @@ -188,7 +188,7 @@ void main() { )).thenAnswer((_) => Future.value(createMockProcess())); final File versionCheckFile = Cache.instance.getStampFileFor( - VersionCheckStamp.kFlutterVersionCheckStampFile, + VersionCheckStamp.flutterVersionCheckStampFile, ); /// Create a bogus "leftover" version check file to make sure it gets diff --git a/packages/flutter_tools/test/version_test.dart b/packages/flutter_tools/test/version_test.dart index 5a1af4308f..17963e3eab 100644 --- a/packages/flutter_tools/test/version_test.dart +++ b/packages/flutter_tools/test/version_test.dart @@ -19,10 +19,8 @@ import 'src/common.dart'; import 'src/context.dart'; final SystemClock _testClock = SystemClock.fixed(DateTime(2015, 1, 1)); -final DateTime _upToDateVersion = _testClock.ago(FlutterVersion.kVersionAgeConsideredUpToDate ~/ 2); -final DateTime _outOfDateVersion = _testClock.ago(FlutterVersion.kVersionAgeConsideredUpToDate * 2); -final DateTime _stampUpToDate = _testClock.ago(FlutterVersion.kCheckAgeConsideredUpToDate ~/ 2); -final DateTime _stampOutOfDate = _testClock.ago(FlutterVersion.kCheckAgeConsideredUpToDate * 2); +final DateTime _stampUpToDate = _testClock.ago(FlutterVersion.checkAgeConsideredUpToDate ~/ 2); +final DateTime _stampOutOfDate = _testClock.ago(FlutterVersion.checkAgeConsideredUpToDate * 2); void main() { MockProcessManager mockProcessManager; @@ -33,324 +31,363 @@ void main() { mockCache = MockCache(); }); - group('$FlutterVersion', () { - setUpAll(() { - Cache.disableLocking(); - FlutterVersion.timeToPauseToLetUserReadTheMessage = Duration.zero; - }); - - testUsingContext('prints nothing when Flutter installation looks fresh', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _upToDateVersion, - // Server will be pinged because we haven't pinged within last x days - expectServerPing: true, - remoteCommitDate: _outOfDateVersion, - expectSetStamp: true); - await FlutterVersion.instance.checkFlutterVersionFreshness(); - _expectVersionMessage(''); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('prints nothing when Flutter installation looks out-of-date by is actually up-to-date', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - stamp: VersionCheckStamp( - lastTimeVersionWasChecked: _stampOutOfDate, - lastKnownRemoteVersion: _outOfDateVersion, - ), - remoteCommitDate: _outOfDateVersion, - expectSetStamp: true, - expectServerPing: true, - ); - final FlutterVersion version = FlutterVersion.instance; - - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(''); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('does not ping server when version stamp is up-to-date', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - stamp: VersionCheckStamp( - lastTimeVersionWasChecked: _stampUpToDate, - lastKnownRemoteVersion: _upToDateVersion, - ), - expectSetStamp: true, - ); - - final FlutterVersion version = FlutterVersion.instance; - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('does not print warning if printed recently', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - stamp: VersionCheckStamp( - lastTimeVersionWasChecked: _stampUpToDate, - lastKnownRemoteVersion: _upToDateVersion, - ), - expectSetStamp: true, - ); - - final FlutterVersion version = FlutterVersion.instance; - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); - expect((await VersionCheckStamp.load()).lastTimeWarningWasPrinted, _testClock.now()); - - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(''); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('pings server when version stamp is missing then does not', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - remoteCommitDate: _upToDateVersion, - expectSetStamp: true, - expectServerPing: true, - ); - final FlutterVersion version = FlutterVersion.instance; - - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); - - // Immediate subsequent check is not expected to ping the server. - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - stamp: await VersionCheckStamp.load(), - ); - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(''); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('pings server when version stamp is out-of-date', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - stamp: VersionCheckStamp( - lastTimeVersionWasChecked: _stampOutOfDate, - lastKnownRemoteVersion: _testClock.ago(const Duration(days: 2)), - ), - remoteCommitDate: _upToDateVersion, - expectSetStamp: true, - expectServerPing: true, - ); - final FlutterVersion version = FlutterVersion.instance; - - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('does not print warning when unable to connect to server if not out of date', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _upToDateVersion, - errorOnFetch: true, - expectServerPing: true, - expectSetStamp: true, - ); - final FlutterVersion version = FlutterVersion.instance; - - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(''); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('prints warning when unable to connect to server if really out of date', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - errorOnFetch: true, - expectServerPing: true, - expectSetStamp: true - ); - final FlutterVersion version = FlutterVersion.instance; - - await version.checkFlutterVersionFreshness(); - _expectVersionMessage(FlutterVersion.versionOutOfDateMessage(_testClock.now().difference(_outOfDateVersion))); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('versions comparison', () async { - fakeData( - mockProcessManager, - mockCache, - localCommitDate: _outOfDateVersion, - errorOnFetch: true, - expectServerPing: true, - expectSetStamp: true - ); - final FlutterVersion version = FlutterVersion.instance; - - when(mockProcessManager.runSync( - ['git', 'merge-base', '--is-ancestor', 'abcdef', '123456'], - workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult(1, 0, '', '')); - - expect( - version.checkRevisionAncestry( - tentativeDescendantRevision: '123456', - tentativeAncestorRevision: 'abcdef', - ), - true - ); - - verify(mockProcessManager.runSync( - ['git', 'merge-base', '--is-ancestor', 'abcdef', '123456'], - workingDirectory: anyNamed('workingDirectory'), - )); - }, - overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - }); - }); - - group('$VersionCheckStamp', () { - void _expectDefault(VersionCheckStamp stamp) { - expect(stamp.lastKnownRemoteVersion, isNull); - expect(stamp.lastTimeVersionWasChecked, isNull); - expect(stamp.lastTimeWarningWasPrinted, isNull); + for (String channel in FlutterVersion.officialChannels) { + DateTime getChannelUpToDateVersion() { + return _testClock.ago(FlutterVersion.versionAgeConsideredUpToDate(channel) ~/ 2); } - testUsingContext('loads blank when stamp file missing', () async { - fakeData(mockProcessManager, mockCache); - _expectDefault(await VersionCheckStamp.load()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, + DateTime getChannelOutOfDateVersion() { + return _testClock.ago(FlutterVersion.versionAgeConsideredUpToDate(channel) * 2); + } + + group('$FlutterVersion for $channel', () { + setUpAll(() { + Cache.disableLocking(); + FlutterVersion.timeToPauseToLetUserReadTheMessage = Duration.zero; + }); + + testUsingContext('prints nothing when Flutter installation looks fresh', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelUpToDateVersion(), + // Server will be pinged because we haven't pinged within last x days + expectServerPing: true, + remoteCommitDate: getChannelOutOfDateVersion(), + expectSetStamp: true, + channel: channel, + ); + await FlutterVersion.instance.checkFlutterVersionFreshness(); + _expectVersionMessage(''); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('prints nothing when Flutter installation looks out-of-date but is actually up-to-date', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + stamp: VersionCheckStamp( + lastTimeVersionWasChecked: _stampOutOfDate, + lastKnownRemoteVersion: getChannelOutOfDateVersion(), + ), + remoteCommitDate: getChannelOutOfDateVersion(), + expectSetStamp: true, + expectServerPing: true, + channel: channel, + ); + final FlutterVersion version = FlutterVersion.instance; + + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(''); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('does not ping server when version stamp is up-to-date', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + stamp: VersionCheckStamp( + lastTimeVersionWasChecked: _stampUpToDate, + lastKnownRemoteVersion: getChannelUpToDateVersion(), + ), + expectSetStamp: true, + channel: channel, + ); + + final FlutterVersion version = FlutterVersion.instance; + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('does not print warning if printed recently', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + stamp: VersionCheckStamp( + lastTimeVersionWasChecked: _stampUpToDate, + lastKnownRemoteVersion: getChannelUpToDateVersion(), + ), + expectSetStamp: true, + channel: channel, + ); + + final FlutterVersion version = FlutterVersion.instance; + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); + expect((await VersionCheckStamp.load()).lastTimeWarningWasPrinted, _testClock.now()); + + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(''); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('pings server when version stamp is missing then does not', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + remoteCommitDate: getChannelUpToDateVersion(), + expectSetStamp: true, + expectServerPing: true, + channel: channel, + ); + final FlutterVersion version = FlutterVersion.instance; + + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); + + // Immediate subsequent check is not expected to ping the server. + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + stamp: await VersionCheckStamp.load(), + channel: channel, + ); + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(''); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('pings server when version stamp is out-of-date', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + stamp: VersionCheckStamp( + lastTimeVersionWasChecked: _stampOutOfDate, + lastKnownRemoteVersion: _testClock.ago(const Duration(days: 2)), + ), + remoteCommitDate: getChannelUpToDateVersion(), + expectSetStamp: true, + expectServerPing: true, + channel: channel, + ); + final FlutterVersion version = FlutterVersion.instance; + + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(FlutterVersion.newVersionAvailableMessage()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('does not print warning when unable to connect to server if not out of date', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelUpToDateVersion(), + errorOnFetch: true, + expectServerPing: true, + expectSetStamp: true, + channel: channel, + ); + final FlutterVersion version = FlutterVersion.instance; + + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(''); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('prints warning when unable to connect to server if really out of date', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + errorOnFetch: true, + expectServerPing: true, + expectSetStamp: true, + channel: channel, + ); + final FlutterVersion version = FlutterVersion.instance; + + await version.checkFlutterVersionFreshness(); + _expectVersionMessage(FlutterVersion.versionOutOfDateMessage(_testClock.now().difference(getChannelOutOfDateVersion()))); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('versions comparison', () async { + fakeData( + mockProcessManager, + mockCache, + localCommitDate: getChannelOutOfDateVersion(), + errorOnFetch: true, + expectServerPing: true, + expectSetStamp: true, + channel: channel, + ); + final FlutterVersion version = FlutterVersion.instance; + + when(mockProcessManager.runSync( + ['git', 'merge-base', '--is-ancestor', 'abcdef', '123456'], + workingDirectory: anyNamed('workingDirectory'), + )).thenReturn(ProcessResult(1, 0, '', '')); + + expect( + version.checkRevisionAncestry( + tentativeDescendantRevision: '123456', + tentativeAncestorRevision: 'abcdef', + ), + true); + + verify(mockProcessManager.runSync( + ['git', 'merge-base', '--is-ancestor', 'abcdef', '123456'], + workingDirectory: anyNamed('workingDirectory'), + )); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + }); }); - testUsingContext('loads blank when stamp file is malformed JSON', () async { - fakeData(mockProcessManager, mockCache, stampJson: '<'); - _expectDefault(await VersionCheckStamp.load()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); + group('$VersionCheckStamp for $channel', () { + void _expectDefault(VersionCheckStamp stamp) { + expect(stamp.lastKnownRemoteVersion, isNull); + expect(stamp.lastTimeVersionWasChecked, isNull); + expect(stamp.lastTimeWarningWasPrinted, isNull); + } - testUsingContext('loads blank when stamp file is well-formed but invalid JSON', () async { - fakeData(mockProcessManager, mockCache, stampJson: '[]'); - _expectDefault(await VersionCheckStamp.load()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); + testUsingContext('loads blank when stamp file missing', () async { + fakeData(mockProcessManager, mockCache, channel: channel); + _expectDefault(await VersionCheckStamp.load()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); - testUsingContext('loads valid JSON', () async { - fakeData(mockProcessManager, mockCache, stampJson: ''' + testUsingContext('loads blank when stamp file is malformed JSON', () async { + fakeData(mockProcessManager, mockCache, stampJson: '<', channel: channel); + _expectDefault(await VersionCheckStamp.load()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('loads blank when stamp file is well-formed but invalid JSON', () async { + fakeData( + mockProcessManager, + mockCache, + stampJson: '[]', + channel: channel, + ); + _expectDefault(await VersionCheckStamp.load()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('loads valid JSON', () async { + fakeData( + mockProcessManager, + mockCache, + stampJson: ''' { "lastKnownRemoteVersion": "${_testClock.ago(const Duration(days: 1))}", "lastTimeVersionWasChecked": "${_testClock.ago(const Duration(days: 2))}", "lastTimeWarningWasPrinted": "${_testClock.now()}" } - '''); + ''', + channel: channel, + ); - final VersionCheckStamp stamp = await VersionCheckStamp.load(); - expect(stamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1))); - expect(stamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2))); - expect(stamp.lastTimeWarningWasPrinted, _testClock.now()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, + final VersionCheckStamp stamp = await VersionCheckStamp.load(); + expect(stamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1))); + expect(stamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2))); + expect(stamp.lastTimeWarningWasPrinted, _testClock.now()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('stores version stamp', () async { + fakeData( + mockProcessManager, + mockCache, + expectSetStamp: true, + channel: channel, + ); + + _expectDefault(await VersionCheckStamp.load()); + + final VersionCheckStamp stamp = VersionCheckStamp( + lastKnownRemoteVersion: _testClock.ago(const Duration(days: 1)), + lastTimeVersionWasChecked: _testClock.ago(const Duration(days: 2)), + lastTimeWarningWasPrinted: _testClock.now(), + ); + await stamp.store(); + + final VersionCheckStamp storedStamp = await VersionCheckStamp.load(); + expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1))); + expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2))); + expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); + + testUsingContext('overwrites individual fields', () async { + fakeData( + mockProcessManager, + mockCache, + expectSetStamp: true, + channel: channel, + ); + + _expectDefault(await VersionCheckStamp.load()); + + final VersionCheckStamp stamp = VersionCheckStamp( + lastKnownRemoteVersion: _testClock.ago(const Duration(days: 10)), + lastTimeVersionWasChecked: _testClock.ago(const Duration(days: 9)), + lastTimeWarningWasPrinted: _testClock.ago(const Duration(days: 8)), + ); + await stamp.store( + newKnownRemoteVersion: _testClock.ago(const Duration(days: 1)), + newTimeVersionWasChecked: _testClock.ago(const Duration(days: 2)), + newTimeWarningWasPrinted: _testClock.now(), + ); + + final VersionCheckStamp storedStamp = await VersionCheckStamp.load(); + expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1))); + expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2))); + expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now()); + }, overrides: { + FlutterVersion: () => FlutterVersion(_testClock), + ProcessManager: () => mockProcessManager, + Cache: () => mockCache, + }); }); - - testUsingContext('stores version stamp', () async { - fakeData(mockProcessManager, mockCache, expectSetStamp: true); - - _expectDefault(await VersionCheckStamp.load()); - - final VersionCheckStamp stamp = VersionCheckStamp( - lastKnownRemoteVersion: _testClock.ago(const Duration(days: 1)), - lastTimeVersionWasChecked: _testClock.ago(const Duration(days: 2)), - lastTimeWarningWasPrinted: _testClock.now(), - ); - await stamp.store(); - - final VersionCheckStamp storedStamp = await VersionCheckStamp.load(); - expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1))); - expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2))); - expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - - testUsingContext('overwrites individual fields', () async { - fakeData(mockProcessManager, mockCache, expectSetStamp: true); - - _expectDefault(await VersionCheckStamp.load()); - - final VersionCheckStamp stamp = VersionCheckStamp( - lastKnownRemoteVersion: _testClock.ago(const Duration(days: 10)), - lastTimeVersionWasChecked: _testClock.ago(const Duration(days: 9)), - lastTimeWarningWasPrinted: _testClock.ago(const Duration(days: 8)), - ); - await stamp.store( - newKnownRemoteVersion: _testClock.ago(const Duration(days: 1)), - newTimeVersionWasChecked: _testClock.ago(const Duration(days: 2)), - newTimeWarningWasPrinted: _testClock.now(), - ); - - final VersionCheckStamp storedStamp = await VersionCheckStamp.load(); - expect(storedStamp.lastKnownRemoteVersion, _testClock.ago(const Duration(days: 1))); - expect(storedStamp.lastTimeVersionWasChecked, _testClock.ago(const Duration(days: 2))); - expect(storedStamp.lastTimeWarningWasPrinted, _testClock.now()); - }, overrides: { - FlutterVersion: () => FlutterVersion(_testClock), - ProcessManager: () => mockProcessManager, - Cache: () => mockCache, - }); - }); + } } void _expectVersionMessage(String message) { @@ -369,6 +406,7 @@ void fakeData( bool errorOnFetch = false, bool expectSetStamp = false, bool expectServerPing = false, + String channel = 'master', }) { ProcessResult success(String standardOutput) { return ProcessResult(1, 0, standardOutput, ''); @@ -379,19 +417,21 @@ void fakeData( } when(cache.getStampFor(any)).thenAnswer((Invocation invocation) { - expect(invocation.positionalArguments.single, VersionCheckStamp.kFlutterVersionCheckStampFile); + expect(invocation.positionalArguments.single, VersionCheckStamp.flutterVersionCheckStampFile); - if (stampJson != null) + if (stampJson != null) { return stampJson; + } - if (stamp != null) + if (stamp != null) { return json.encode(stamp.toJson()); + } return null; }); when(cache.setStampFor(any, any)).thenAnswer((Invocation invocation) { - expect(invocation.positionalArguments.first, VersionCheckStamp.kFlutterVersionCheckStampFile); + expect(invocation.positionalArguments.first, VersionCheckStamp.flutterVersionCheckStampFile); if (expectSetStamp) { stamp = VersionCheckStamp.fromJson(json.decode(invocation.positionalArguments[1])); @@ -405,10 +445,7 @@ void fakeData( bool argsAre(String a1, [String a2, String a3, String a4, String a5, String a6, String a7, String a8]) { const ListEquality equality = ListEquality(); final List args = invocation.positionalArguments.single; - final List expectedArgs = - [a1, a2, a3, a4, a5, a6, a7, a8] - .where((String arg) => arg != null) - .toList(); + final List expectedArgs = [a1, a2, a3, a4, a5, a6, a7, a8].where((String arg) => arg != null).toList(); return equality.equals(args, expectedArgs); } @@ -418,11 +455,12 @@ void fakeData( return success(''); } else if (argsAre('git', 'remote', 'add', '__flutter_version_check__', 'https://github.com/flutter/flutter.git')) { return success(''); - } else if (argsAre('git', 'fetch', '__flutter_version_check__', 'master')) { - if (!expectServerPing) + } else if (argsAre('git', 'fetch', '__flutter_version_check__', channel)) { + if (!expectServerPing) { fail('Did not expect server ping'); + } return errorOnFetch ? failure(128) : success(''); - } else if (remoteCommitDate != null && argsAre('git', 'log', '__flutter_version_check__/master', '-n', '1', '--pretty=format:%ad', '--date=iso')) { + } else if (remoteCommitDate != null && argsAre('git', 'log', '__flutter_version_check__/$channel', '-n', '1', '--pretty=format:%ad', '--date=iso')) { return success(remoteCommitDate.toString()); } @@ -438,7 +476,7 @@ void fakeData( ['git', 'rev-parse', '--abbrev-ref', '--symbolic', '@{u}'], workingDirectory: anyNamed('workingDirectory'), environment: anyNamed('environment'), - )).thenReturn(ProcessResult(101, 0, 'master', '')); + )).thenReturn(ProcessResult(101, 0, channel, '')); when(pm.runSync( ['git', 'rev-parse', '--abbrev-ref', 'HEAD'], workingDirectory: anyNamed('workingDirectory'), @@ -462,4 +500,5 @@ void fakeData( } class MockProcessManager extends Mock implements ProcessManager {} + class MockCache extends Mock implements Cache {}