blob: 972b433f0a25c07ecd94a1bb5bed23b99ccf6ec0 [file] [log] [blame]
Ian Hickson449f4a62019-11-27 15:04:02 -08001// Copyright 2014 The Flutter Authors. All rights reserved.
木士羽9f7ab4c2019-02-05 03:26:06 +08002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6import 'dart:convert';
7import 'dart:io';
8
Emmanuel Garcia5a34e792019-07-18 10:45:37 -07009import 'package:flutter_tools/src/base/common.dart';
木士羽9f7ab4c2019-02-05 03:26:06 +080010import 'package:flutter_tools/src/base/io.dart';
Jonah Williamse2554a92020-02-13 11:56:45 -080011import 'package:flutter_tools/src/base/terminal.dart';
木士羽9f7ab4c2019-02-05 03:26:06 +080012import 'package:flutter_tools/src/cache.dart';
13import 'package:flutter_tools/src/commands/version.dart';
∂ω∂4277f362019-08-21 23:55:57 +030014import 'package:flutter_tools/src/version.dart';
木士羽9f7ab4c2019-02-05 03:26:06 +080015import 'package:mockito/mockito.dart';
16import 'package:process/process.dart';
Jonah Williamse2554a92020-02-13 11:56:45 -080017import 'package:flutter_tools/src/globals.dart' as globals;
木士羽9f7ab4c2019-02-05 03:26:06 +080018
Ian Hicksond919e692019-07-13 11:51:44 -070019import '../../src/common.dart';
20import '../../src/context.dart';
21import '../../src/mocks.dart' show MockProcess;
木士羽9f7ab4c2019-02-05 03:26:06 +080022
23void main() {
24 group('version', () {
Jonah Williamse2554a92020-02-13 11:56:45 -080025 MockStdio mockStdio;
26
木士羽9f7ab4c2019-02-05 03:26:06 +080027 setUpAll(() {
28 Cache.disableLocking();
29 });
30
Jonah Williamse2554a92020-02-13 11:56:45 -080031 setUp(() {
32 mockStdio = MockStdio();
33 when(mockStdio.stdinHasTerminal).thenReturn(false);
34 when(mockStdio.hasTerminal).thenReturn(false);
35 });
36
木士羽9f7ab4c2019-02-05 03:26:06 +080037 testUsingContext('version ls', () async {
38 final VersionCommand command = VersionCommand();
Zachary Andersone4b809b2019-11-27 10:18:43 -080039 await createTestCommandRunner(command).run(<String>[
40 'version',
41 '--no-pub',
42 ]);
Alexandre Ardhuin1cc38682020-01-24 19:43:02 +010043 expect(testLogger.statusText, equals('v10.0.0\r\nv20.0.0\n'));
木士羽9f7ab4c2019-02-05 03:26:06 +080044 }, overrides: <Type, Generator>{
45 ProcessManager: () => MockProcessManager(),
Jonah Williamse2554a92020-02-13 11:56:45 -080046 Stdio: () => mockStdio,
木士羽9f7ab4c2019-02-05 03:26:06 +080047 });
48
Jonah Williamse2554a92020-02-13 11:56:45 -080049 testUsingContext('version switch prompt is accepted', () async {
50 when(mockStdio.stdinHasTerminal).thenReturn(true);
木士羽9f7ab4c2019-02-05 03:26:06 +080051 const String version = '10.0.0';
52 final VersionCommand command = VersionCommand();
Jonah Williamse2554a92020-02-13 11:56:45 -080053 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 Andersone4b809b2019-11-27 10:18:43 -080058 await createTestCommandRunner(command).run(<String>[
59 'version',
60 '--no-pub',
61 version,
62 ]);
木士羽9f7ab4c2019-02-05 03:26:06 +080063 expect(testLogger.statusText, contains('Switching Flutter to version $version'));
64 }, overrides: <Type, Generator>{
65 ProcessManager: () => MockProcessManager(),
Jonah Williamse2554a92020-02-13 11:56:45 -080066 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 Andersone4b809b2019-11-27 10:18:43 -080089 });
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 Williamse2554a92020-02-13 11:56:45 -0800102 Stdio: () => mockStdio,
Zachary Andersone4b809b2019-11-27 10:18:43 -0800103 });
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 Williamse2554a92020-02-13 11:56:45 -0800113 Stdio: () => mockStdio,
木士羽9f7ab4c2019-02-05 03:26:06 +0800114 });
115
116 testUsingContext('switch to not supported version without force', () async {
117 const String version = '1.1.5';
118 final VersionCommand command = VersionCommand();
Zachary Andersone4b809b2019-11-27 10:18:43 -0800119 await createTestCommandRunner(command).run(<String>[
120 'version',
121 '--no-pub',
122 version,
123 ]);
木士羽9f7ab4c2019-02-05 03:26:06 +0800124 expect(testLogger.errorText, contains('Version command is not supported in'));
125 }, overrides: <Type, Generator>{
126 ProcessManager: () => MockProcessManager(),
Jonah Williamse2554a92020-02-13 11:56:45 -0800127 Stdio: () => mockStdio,
木士羽9f7ab4c2019-02-05 03:26:06 +0800128 });
129
130 testUsingContext('switch to not supported version with force', () async {
131 const String version = '1.1.5';
132 final VersionCommand command = VersionCommand();
Zachary Andersone4b809b2019-11-27 10:18:43 -0800133 await createTestCommandRunner(command).run(<String>[
134 'version',
135 '--no-pub',
136 '--force',
137 version,
138 ]);
木士羽9f7ab4c2019-02-05 03:26:06 +0800139 expect(testLogger.statusText, contains('Switching Flutter to version $version with force'));
140 }, overrides: <Type, Generator>{
141 ProcessManager: () => MockProcessManager(),
Jonah Williamse2554a92020-02-13 11:56:45 -0800142 Stdio: () => mockStdio,
木士羽9f7ab4c2019-02-05 03:26:06 +0800143 });
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700144
Zachary Andersone9bd2ef2019-08-26 12:12:50 -0700145 testUsingContext('tool exit on confusing version', () async {
146 const String version = 'master';
147 final VersionCommand command = VersionCommand();
Zachary Andersone4b809b2019-11-27 10:18:43 -0800148 expect(() async =>
149 await createTestCommandRunner(command).run(<String>[
150 'version',
151 '--no-pub',
152 version,
153 ]),
Dan Field8b299332020-01-27 14:36:02 -0800154 throwsToolExit(),
Zachary Andersone4b809b2019-11-27 10:18:43 -0800155 );
Zachary Andersone9bd2ef2019-08-26 12:12:50 -0700156 }, overrides: <Type, Generator>{
157 ProcessManager: () => MockProcessManager(),
Jonah Williamse2554a92020-02-13 11:56:45 -0800158 Stdio: () => mockStdio,
Zachary Andersone9bd2ef2019-08-26 12:12:50 -0700159 });
160
Alexandre Ardhuinf15c8872020-02-11 20:58:27 +0100161 testUsingContext("exit tool if can't get the tags", () async {
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700162 final VersionCommand command = VersionCommand();
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700163 try {
164 await command.getTags();
165 fail('ToolExit expected');
Zachary Anderson9de77872020-02-27 22:46:23 -0800166 } on Exception catch (e) {
Dan Field8b299332020-01-27 14:36:02 -0800167 expect(e, isA<ToolExit>());
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700168 }
169 }, overrides: <Type, Generator>{
170 ProcessManager: () => MockProcessManager(failGitTag: true),
Jonah Williamse2554a92020-02-13 11:56:45 -0800171 Stdio: () => mockStdio,
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700172 });
Zachary Andersonbc4bd7b2020-02-25 17:19:47 -0500173
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 });
木士羽9f7ab4c2019-02-05 03:26:06 +0800184 });
185}
186
Jonah Williamse2554a92020-02-13 11:56:45 -0800187class MockTerminal extends Mock implements AnsiTerminal {}
188class MockStdio extends Mock implements Stdio {}
木士羽9f7ab4c2019-02-05 03:26:06 +0800189class MockProcessManager extends Mock implements ProcessManager {
Zachary Andersone4b809b2019-11-27 10:18:43 -0800190 MockProcessManager({
191 this.failGitTag = false,
192 this.latestCommitFails = false,
193 });
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700194
木士羽9f7ab4c2019-02-05 03:26:06 +0800195 String version = '';
196
Zachary Andersone4b809b2019-11-27 10:18:43 -0800197 final bool failGitTag;
198 final bool latestCommitFails;
Emmanuel Garcia5a34e792019-07-18 10:45:37 -0700199
木士羽9f7ab4c2019-02-05 03:26:06 +0800200 @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 Garcia5a34e792019-07-18 10:45:37 -0700211 if (failGitTag) {
212 return ProcessResult(0, 1, '', '');
213 }
木士羽9f7ab4c2019-02-05 03:26:06 +0800214 return ProcessResult(0, 0, 'v10.0.0\r\nv20.0.0', '');
215 }
216 if (command[0] == 'git' && command[1] == 'checkout') {
Alexandre Ardhuin980f14e2019-11-24 06:54:43 +0100217 version = command[2] as String;
木士羽9f7ab4c2019-02-05 03:26:06 +0800218 }
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(' ');
∂ω∂4277f362019-08-21 23:55:57 +0300233 if (commandStr == FlutterVersion.gitLog(<String>['-n', '1', '--pretty=format:%H']).join(' ')) {
木士羽9f7ab4c2019-02-05 03:26:06 +0800234 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 Andersone4b809b2019-11-27 10:18:43 -0800242 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 }
木士羽9f7ab4c2019-02-05 03:26:06 +0800250 return ProcessResult(0, 0, '', '');
251 }
252
253 @override
Alexandre Ardhuinbfa1d252019-03-23 00:02:21 +0100254 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 }) {
木士羽9f7ab4c2019-02-05 03:26:06 +0800262 final Completer<Process> completer = Completer<Process>();
263 completer.complete(MockProcess());
264 return completer.future;
265 }
266}