• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Chromium Authors. All rights reserved.
2// 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' hide File;
8
9import 'package:args/command_runner.dart';
10import 'package:file/memory.dart';
11import 'package:flutter_tools/src/base/file_system.dart';
12import 'package:flutter_tools/src/cache.dart';
13import 'package:flutter_tools/src/commands/channel.dart';
14import 'package:flutter_tools/src/version.dart';
15import 'package:mockito/mockito.dart';
16import 'package:process/process.dart';
17
18import '../src/common.dart';
19import '../src/context.dart';
20
21Process createMockProcess({ int exitCode = 0, String stdout = '', String stderr = '' }) {
22  final Stream<List<int>> stdoutStream = Stream<List<int>>.fromIterable(<List<int>>[
23    utf8.encode(stdout),
24  ]);
25  final Stream<List<int>> stderrStream = Stream<List<int>>.fromIterable(<List<int>>[
26    utf8.encode(stderr),
27  ]);
28  final Process process = MockProcess();
29
30  when(process.stdout).thenAnswer((_) => stdoutStream);
31  when(process.stderr).thenAnswer((_) => stderrStream);
32  when(process.exitCode).thenAnswer((_) => Future<int>.value(exitCode));
33  return process;
34}
35
36void main() {
37  group('channel', () {
38    final MockProcessManager mockProcessManager = MockProcessManager();
39
40    setUpAll(() {
41      Cache.disableLocking();
42    });
43
44    Future<void> simpleChannelTest(List<String> args) async {
45      final ChannelCommand command = ChannelCommand();
46      final CommandRunner<void> runner = createTestCommandRunner(command);
47      await runner.run(args);
48      expect(testLogger.errorText, hasLength(0));
49      // The bots may return an empty list of channels (network hiccup?)
50      // and when run locally the list of branches might be different
51      // so we check for the header text rather than any specific channel name.
52      expect(testLogger.statusText, contains('Flutter channels:'));
53    }
54
55    testUsingContext('list', () async {
56      await simpleChannelTest(<String>['channel']);
57    });
58
59    testUsingContext('verbose list', () async {
60      await simpleChannelTest(<String>['channel', '-v']);
61    });
62
63    testUsingContext('removes duplicates', () async {
64      final Process process = createMockProcess(
65          stdout: 'origin/dev\n'
66                  'origin/beta\n'
67                  'origin/stable\n'
68                  'upstream/dev\n'
69                  'upstream/beta\n'
70                  'upstream/stable\n');
71      when(mockProcessManager.start(
72        <String>['git', 'branch', '-r'],
73        workingDirectory: anyNamed('workingDirectory'),
74        environment: anyNamed('environment'),
75      )).thenAnswer((_) => Future<Process>.value(process));
76
77      final ChannelCommand command = ChannelCommand();
78      final CommandRunner<void> runner = createTestCommandRunner(command);
79      await runner.run(<String>['channel']);
80
81      verify(mockProcessManager.start(
82        <String>['git', 'branch', '-r'],
83        workingDirectory: anyNamed('workingDirectory'),
84        environment: anyNamed('environment'),
85      )).called(1);
86
87      expect(testLogger.errorText, hasLength(0));
88
89      // format the status text for a simpler assertion.
90      final Iterable<String> rows = testLogger.statusText
91        .split('\n')
92        .map((String line) => line.trim())
93        .where((String line) => line?.isNotEmpty == true)
94        .skip(1); // remove `Flutter channels:` line
95
96      expect(rows, <String>['dev', 'beta', 'stable']);
97    }, overrides: <Type, Generator>{
98      ProcessManager: () => mockProcessManager,
99    });
100
101    testUsingContext('can switch channels', () async {
102      when(mockProcessManager.start(
103        <String>['git', 'fetch'],
104        workingDirectory: anyNamed('workingDirectory'),
105        environment: anyNamed('environment'),
106      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
107      when(mockProcessManager.start(
108        <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
109        workingDirectory: anyNamed('workingDirectory'),
110        environment: anyNamed('environment'),
111      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
112      when(mockProcessManager.start(
113        <String>['git', 'checkout', 'beta', '--'],
114        workingDirectory: anyNamed('workingDirectory'),
115        environment: anyNamed('environment'),
116      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
117
118      final ChannelCommand command = ChannelCommand();
119      final CommandRunner<void> runner = createTestCommandRunner(command);
120      await runner.run(<String>['channel', 'beta']);
121
122      verify(mockProcessManager.start(
123        <String>['git', 'fetch'],
124        workingDirectory: anyNamed('workingDirectory'),
125        environment: anyNamed('environment'),
126      )).called(1);
127      verify(mockProcessManager.start(
128        <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
129        workingDirectory: anyNamed('workingDirectory'),
130        environment: anyNamed('environment'),
131      )).called(1);
132      verify(mockProcessManager.start(
133        <String>['git', 'checkout', 'beta', '--'],
134        workingDirectory: anyNamed('workingDirectory'),
135        environment: anyNamed('environment'),
136      )).called(1);
137
138      expect(testLogger.statusText, contains("Switching to flutter channel 'beta'..."));
139      expect(testLogger.errorText, hasLength(0));
140
141      when(mockProcessManager.start(
142        <String>['git', 'fetch'],
143        workingDirectory: anyNamed('workingDirectory'),
144        environment: anyNamed('environment'),
145      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
146      when(mockProcessManager.start(
147        <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/stable'],
148        workingDirectory: anyNamed('workingDirectory'),
149        environment: anyNamed('environment'),
150      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
151      when(mockProcessManager.start(
152        <String>['git', 'checkout', 'stable', '--'],
153        workingDirectory: anyNamed('workingDirectory'),
154        environment: anyNamed('environment'),
155      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
156
157      await runner.run(<String>['channel', 'stable']);
158
159      verify(mockProcessManager.start(
160        <String>['git', 'fetch'],
161        workingDirectory: anyNamed('workingDirectory'),
162        environment: anyNamed('environment'),
163      )).called(1);
164      verify(mockProcessManager.start(
165        <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/stable'],
166        workingDirectory: anyNamed('workingDirectory'),
167        environment: anyNamed('environment'),
168      )).called(1);
169      verify(mockProcessManager.start(
170        <String>['git', 'checkout', 'stable', '--'],
171        workingDirectory: anyNamed('workingDirectory'),
172        environment: anyNamed('environment'),
173      )).called(1);
174    }, overrides: <Type, Generator>{
175      ProcessManager: () => mockProcessManager,
176      FileSystem: () => MemoryFileSystem(),
177    });
178
179    // This verifies that bug https://github.com/flutter/flutter/issues/21134
180    // doesn't return.
181    testUsingContext('removes version stamp file when switching channels', () async {
182      when(mockProcessManager.start(
183        <String>['git', 'fetch'],
184        workingDirectory: anyNamed('workingDirectory'),
185        environment: anyNamed('environment'),
186      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
187      when(mockProcessManager.start(
188        <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
189        workingDirectory: anyNamed('workingDirectory'),
190        environment: anyNamed('environment'),
191      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
192      when(mockProcessManager.start(
193        <String>['git', 'checkout', 'beta', '--'],
194        workingDirectory: anyNamed('workingDirectory'),
195        environment: anyNamed('environment'),
196      )).thenAnswer((_) => Future<Process>.value(createMockProcess()));
197
198      final File versionCheckFile = Cache.instance.getStampFileFor(
199        VersionCheckStamp.flutterVersionCheckStampFile,
200      );
201
202      /// Create a bogus "leftover" version check file to make sure it gets
203      /// removed when the channel changes. The content doesn't matter.
204      versionCheckFile.createSync(recursive: true);
205      versionCheckFile.writeAsStringSync('''
206        {
207          "lastTimeVersionWasChecked": "2151-08-29 10:17:30.763802",
208          "lastKnownRemoteVersion": "2151-09-26 15:56:19.000Z"
209        }
210      ''');
211
212      final ChannelCommand command = ChannelCommand();
213      final CommandRunner<void> runner = createTestCommandRunner(command);
214      await runner.run(<String>['channel', 'beta']);
215
216      verify(mockProcessManager.start(
217        <String>['git', 'fetch'],
218        workingDirectory: anyNamed('workingDirectory'),
219        environment: anyNamed('environment'),
220      )).called(1);
221      verify(mockProcessManager.start(
222        <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
223        workingDirectory: anyNamed('workingDirectory'),
224        environment: anyNamed('environment'),
225      )).called(1);
226      verify(mockProcessManager.start(
227        <String>['git', 'checkout', 'beta', '--'],
228        workingDirectory: anyNamed('workingDirectory'),
229        environment: anyNamed('environment'),
230      )).called(1);
231
232      expect(testLogger.statusText, isNot(contains('A new version of Flutter')));
233      expect(testLogger.errorText, hasLength(0));
234      expect(versionCheckFile.existsSync(), isFalse);
235    }, overrides: <Type, Generator>{
236      ProcessManager: () => mockProcessManager,
237      FileSystem: () => MemoryFileSystem(),
238    });
239  });
240}
241
242class MockProcessManager extends Mock implements ProcessManager {}
243
244class MockProcess extends Mock implements Process {}
245