Ian Hickson | 449f4a6 | 2019-11-27 15:04:02 -0800 | [diff] [blame] | 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import 'dart:async'; |
| 6 | import 'dart:convert'; |
| 7 | import 'dart:io'; |
| 8 | |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 9 | import 'package:flutter_tools/src/base/common.dart'; |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 10 | import 'package:flutter_tools/src/base/io.dart'; |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 11 | import 'package:flutter_tools/src/base/terminal.dart'; |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 12 | import 'package:flutter_tools/src/cache.dart'; |
| 13 | import 'package:flutter_tools/src/commands/version.dart'; |
∂ω∂ | 4277f36 | 2019-08-21 23:55:57 +0300 | [diff] [blame] | 14 | import 'package:flutter_tools/src/version.dart'; |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 15 | import 'package:mockito/mockito.dart'; |
| 16 | import 'package:process/process.dart'; |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 17 | import 'package:flutter_tools/src/globals.dart' as globals; |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 18 | |
Ian Hickson | d919e69 | 2019-07-13 11:51:44 -0700 | [diff] [blame] | 19 | import '../../src/common.dart'; |
| 20 | import '../../src/context.dart'; |
| 21 | import '../../src/mocks.dart' show MockProcess; |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 22 | |
| 23 | void main() { |
| 24 | group('version', () { |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 25 | MockStdio mockStdio; |
| 26 | |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 27 | setUpAll(() { |
| 28 | Cache.disableLocking(); |
| 29 | }); |
| 30 | |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 31 | setUp(() { |
| 32 | mockStdio = MockStdio(); |
| 33 | when(mockStdio.stdinHasTerminal).thenReturn(false); |
| 34 | when(mockStdio.hasTerminal).thenReturn(false); |
| 35 | }); |
| 36 | |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 37 | testUsingContext('version ls', () async { |
| 38 | final VersionCommand command = VersionCommand(); |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 39 | await createTestCommandRunner(command).run(<String>[ |
| 40 | 'version', |
| 41 | '--no-pub', |
| 42 | ]); |
Alexandre Ardhuin | 1cc3868 | 2020-01-24 19:43:02 +0100 | [diff] [blame] | 43 | expect(testLogger.statusText, equals('v10.0.0\r\nv20.0.0\n')); |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 44 | }, overrides: <Type, Generator>{ |
| 45 | ProcessManager: () => MockProcessManager(), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 46 | Stdio: () => mockStdio, |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 47 | }); |
| 48 | |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 49 | testUsingContext('version switch prompt is accepted', () async { |
| 50 | when(mockStdio.stdinHasTerminal).thenReturn(true); |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 51 | const String version = '10.0.0'; |
| 52 | final VersionCommand command = VersionCommand(); |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 53 | when(globals.terminal.promptForCharInput(<String>['y', 'n'], |
| 54 | logger: anyNamed('logger'), |
| 55 | prompt: 'Are you sure you want to proceed?') |
| 56 | ).thenAnswer((Invocation invocation) async => 'y'); |
| 57 | |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 58 | await createTestCommandRunner(command).run(<String>[ |
| 59 | 'version', |
| 60 | '--no-pub', |
| 61 | version, |
| 62 | ]); |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 63 | expect(testLogger.statusText, contains('Switching Flutter to version $version')); |
| 64 | }, overrides: <Type, Generator>{ |
| 65 | ProcessManager: () => MockProcessManager(), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 66 | Stdio: () => mockStdio, |
| 67 | AnsiTerminal: () => MockTerminal(), |
| 68 | }); |
| 69 | |
| 70 | testUsingContext('version switch prompt is declined', () async { |
| 71 | when(mockStdio.stdinHasTerminal).thenReturn(true); |
| 72 | const String version = '10.0.0'; |
| 73 | final VersionCommand command = VersionCommand(); |
| 74 | when(globals.terminal.promptForCharInput(<String>['y', 'n'], |
| 75 | logger: anyNamed('logger'), |
| 76 | prompt: 'Are you sure you want to proceed?') |
| 77 | ).thenAnswer((Invocation invocation) async => 'n'); |
| 78 | |
| 79 | await createTestCommandRunner(command).run(<String>[ |
| 80 | 'version', |
| 81 | '--no-pub', |
| 82 | version, |
| 83 | ]); |
| 84 | expect(testLogger.statusText, isNot(contains('Switching Flutter to version $version'))); |
| 85 | }, overrides: <Type, Generator>{ |
| 86 | ProcessManager: () => MockProcessManager(), |
| 87 | Stdio: () => mockStdio, |
| 88 | AnsiTerminal: () => MockTerminal(), |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 89 | }); |
| 90 | |
| 91 | testUsingContext('version switch, latest commit query fails', () async { |
| 92 | const String version = '10.0.0'; |
| 93 | final VersionCommand command = VersionCommand(); |
| 94 | await createTestCommandRunner(command).run(<String>[ |
| 95 | 'version', |
| 96 | '--no-pub', |
| 97 | version, |
| 98 | ]); |
| 99 | expect(testLogger.errorText, contains('git failed')); |
| 100 | }, overrides: <Type, Generator>{ |
| 101 | ProcessManager: () => MockProcessManager(latestCommitFails: true), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 102 | Stdio: () => mockStdio, |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 103 | }); |
| 104 | |
| 105 | testUsingContext('latest commit is parsable when query fails', () { |
| 106 | final FlutterVersion flutterVersion = FlutterVersion(); |
| 107 | expect( |
| 108 | () => DateTime.parse(flutterVersion.frameworkCommitDate), |
| 109 | returnsNormally, |
| 110 | ); |
| 111 | }, overrides: <Type, Generator>{ |
| 112 | ProcessManager: () => MockProcessManager(latestCommitFails: true), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 113 | Stdio: () => mockStdio, |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 114 | }); |
| 115 | |
| 116 | testUsingContext('switch to not supported version without force', () async { |
| 117 | const String version = '1.1.5'; |
| 118 | final VersionCommand command = VersionCommand(); |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 119 | await createTestCommandRunner(command).run(<String>[ |
| 120 | 'version', |
| 121 | '--no-pub', |
| 122 | version, |
| 123 | ]); |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 124 | expect(testLogger.errorText, contains('Version command is not supported in')); |
| 125 | }, overrides: <Type, Generator>{ |
| 126 | ProcessManager: () => MockProcessManager(), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 127 | Stdio: () => mockStdio, |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 128 | }); |
| 129 | |
| 130 | testUsingContext('switch to not supported version with force', () async { |
| 131 | const String version = '1.1.5'; |
| 132 | final VersionCommand command = VersionCommand(); |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 133 | await createTestCommandRunner(command).run(<String>[ |
| 134 | 'version', |
| 135 | '--no-pub', |
| 136 | '--force', |
| 137 | version, |
| 138 | ]); |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 139 | expect(testLogger.statusText, contains('Switching Flutter to version $version with force')); |
| 140 | }, overrides: <Type, Generator>{ |
| 141 | ProcessManager: () => MockProcessManager(), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 142 | Stdio: () => mockStdio, |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 143 | }); |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 144 | |
Zachary Anderson | e9bd2ef | 2019-08-26 12:12:50 -0700 | [diff] [blame] | 145 | testUsingContext('tool exit on confusing version', () async { |
| 146 | const String version = 'master'; |
| 147 | final VersionCommand command = VersionCommand(); |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 148 | expect(() async => |
| 149 | await createTestCommandRunner(command).run(<String>[ |
| 150 | 'version', |
| 151 | '--no-pub', |
| 152 | version, |
| 153 | ]), |
Dan Field | 8b29933 | 2020-01-27 14:36:02 -0800 | [diff] [blame] | 154 | throwsToolExit(), |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 155 | ); |
Zachary Anderson | e9bd2ef | 2019-08-26 12:12:50 -0700 | [diff] [blame] | 156 | }, overrides: <Type, Generator>{ |
| 157 | ProcessManager: () => MockProcessManager(), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 158 | Stdio: () => mockStdio, |
Zachary Anderson | e9bd2ef | 2019-08-26 12:12:50 -0700 | [diff] [blame] | 159 | }); |
| 160 | |
Alexandre Ardhuin | f15c887 | 2020-02-11 20:58:27 +0100 | [diff] [blame] | 161 | testUsingContext("exit tool if can't get the tags", () async { |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 162 | final VersionCommand command = VersionCommand(); |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 163 | try { |
| 164 | await command.getTags(); |
| 165 | fail('ToolExit expected'); |
Zachary Anderson | 9de7787 | 2020-02-27 22:46:23 -0800 | [diff] [blame] | 166 | } on Exception catch (e) { |
Dan Field | 8b29933 | 2020-01-27 14:36:02 -0800 | [diff] [blame] | 167 | expect(e, isA<ToolExit>()); |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 168 | } |
| 169 | }, overrides: <Type, Generator>{ |
| 170 | ProcessManager: () => MockProcessManager(failGitTag: true), |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 171 | Stdio: () => mockStdio, |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 172 | }); |
Zachary Anderson | bc4bd7b | 2020-02-25 17:19:47 -0500 | [diff] [blame] | 173 | |
| 174 | testUsingContext('Does not run pub when outside a project', () async { |
| 175 | final VersionCommand command = VersionCommand(); |
| 176 | await createTestCommandRunner(command).run(<String>[ |
| 177 | 'version', |
| 178 | ]); |
| 179 | expect(testLogger.statusText, equals('v10.0.0\r\nv20.0.0\n')); |
| 180 | }, overrides: <Type, Generator>{ |
| 181 | ProcessManager: () => MockProcessManager(), |
| 182 | Stdio: () => mockStdio, |
| 183 | }); |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 184 | }); |
| 185 | } |
| 186 | |
Jonah Williams | e2554a9 | 2020-02-13 11:56:45 -0800 | [diff] [blame] | 187 | class MockTerminal extends Mock implements AnsiTerminal {} |
| 188 | class MockStdio extends Mock implements Stdio {} |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 189 | class MockProcessManager extends Mock implements ProcessManager { |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 190 | MockProcessManager({ |
| 191 | this.failGitTag = false, |
| 192 | this.latestCommitFails = false, |
| 193 | }); |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 194 | |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 195 | String version = ''; |
| 196 | |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 197 | final bool failGitTag; |
| 198 | final bool latestCommitFails; |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 199 | |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 200 | @override |
| 201 | Future<ProcessResult> run( |
| 202 | List<dynamic> command, { |
| 203 | String workingDirectory, |
| 204 | Map<String, String> environment, |
| 205 | bool includeParentEnvironment = true, |
| 206 | bool runInShell = false, |
| 207 | Encoding stdoutEncoding = systemEncoding, |
| 208 | Encoding stderrEncoding = systemEncoding, |
| 209 | }) async { |
| 210 | if (command[0] == 'git' && command[1] == 'tag') { |
Emmanuel Garcia | 5a34e79 | 2019-07-18 10:45:37 -0700 | [diff] [blame] | 211 | if (failGitTag) { |
| 212 | return ProcessResult(0, 1, '', ''); |
| 213 | } |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 214 | return ProcessResult(0, 0, 'v10.0.0\r\nv20.0.0', ''); |
| 215 | } |
| 216 | if (command[0] == 'git' && command[1] == 'checkout') { |
Alexandre Ardhuin | 980f14e | 2019-11-24 06:54:43 +0100 | [diff] [blame] | 217 | version = command[2] as String; |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 218 | } |
| 219 | return ProcessResult(0, 0, '', ''); |
| 220 | } |
| 221 | |
| 222 | @override |
| 223 | ProcessResult runSync( |
| 224 | List<dynamic> command, { |
| 225 | String workingDirectory, |
| 226 | Map<String, String> environment, |
| 227 | bool includeParentEnvironment = true, |
| 228 | bool runInShell = false, |
| 229 | Encoding stdoutEncoding = systemEncoding, |
| 230 | Encoding stderrEncoding = systemEncoding, |
| 231 | }) { |
| 232 | final String commandStr = command.join(' '); |
∂ω∂ | 4277f36 | 2019-08-21 23:55:57 +0300 | [diff] [blame] | 233 | if (commandStr == FlutterVersion.gitLog(<String>['-n', '1', '--pretty=format:%H']).join(' ')) { |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 234 | return ProcessResult(0, 0, '000000000000000000000', ''); |
| 235 | } |
| 236 | if (commandStr == |
| 237 | 'git describe --match v*.*.* --first-parent --long --tags') { |
| 238 | if (version.isNotEmpty) { |
| 239 | return ProcessResult(0, 0, '$version-0-g00000000', ''); |
| 240 | } |
| 241 | } |
Zachary Anderson | e4b809b | 2019-11-27 10:18:43 -0800 | [diff] [blame] | 242 | final List<String> commitDateCommand = <String>[ |
| 243 | '-n', '1', |
| 244 | '--pretty=format:%ad', |
| 245 | '--date=iso', |
| 246 | ]; |
| 247 | if (latestCommitFails && commandStr == FlutterVersion.gitLog(commitDateCommand).join(' ')) { |
| 248 | return ProcessResult(0, -9, '', 'git failed'); |
| 249 | } |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 250 | return ProcessResult(0, 0, '', ''); |
| 251 | } |
| 252 | |
| 253 | @override |
Alexandre Ardhuin | bfa1d25 | 2019-03-23 00:02:21 +0100 | [diff] [blame] | 254 | Future<Process> start( |
| 255 | List<dynamic> command, { |
| 256 | String workingDirectory, |
| 257 | Map<String, String> environment, |
| 258 | bool includeParentEnvironment = true, |
| 259 | bool runInShell = false, |
| 260 | ProcessStartMode mode = ProcessStartMode.normal, |
| 261 | }) { |
木士羽 | 9f7ab4c | 2019-02-05 03:26:06 +0800 | [diff] [blame] | 262 | final Completer<Process> completer = Completer<Process>(); |
| 263 | completer.complete(MockProcess()); |
| 264 | return completer.future; |
| 265 | } |
| 266 | } |