channel_test.dart 11.5 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:args/command_runner.dart';
6 7
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
8
import 'package:flutter_tools/src/cache.dart';
9
import 'package:flutter_tools/src/commands/channel.dart';
10
import 'package:flutter_tools/src/globals.dart' as globals;
11
import 'package:flutter_tools/src/version.dart';
12

13 14
import '../src/common.dart';
import '../src/context.dart';
15
import '../src/fake_process_manager.dart';
16
import '../src/test_flutter_command_runner.dart';
17

18 19
void main() {
  group('channel', () {
20
    late FakeProcessManager fakeProcessManager;
21 22

    setUp(() {
23
      fakeProcessManager = FakeProcessManager.empty();
24
    });
25

26 27 28 29
    setUpAll(() {
      Cache.disableLocking();
    });

30
    Future<void> simpleChannelTest(List<String> args) async {
31 32 33
      fakeProcessManager.addCommands(const <FakeCommand>[
        FakeCommand(command: <String>['git', 'branch', '-r'], stdout: '  branch-1\n  branch-2'),
      ]);
34
      final ChannelCommand command = ChannelCommand();
35
      final CommandRunner<void> runner = createTestCommandRunner(command);
36
      await runner.run(args);
37
      expect(testLogger.errorText, hasLength(0));
38 39 40
      // The bots may return an empty list of channels (network hiccup?)
      // and when run locally the list of branches might be different
      // so we check for the header text rather than any specific channel name.
41 42 43 44
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace('Flutter channels:'),
      );
45 46 47 48
    }

    testUsingContext('list', () async {
      await simpleChannelTest(<String>['channel']);
49 50 51
    }, overrides: <Type, Generator>{
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
52 53 54 55
    });

    testUsingContext('verbose list', () async {
      await simpleChannelTest(<String>['channel', '-v']);
56 57 58
    }, overrides: <Type, Generator>{
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
59
    });
60

61 62 63 64
    testUsingContext('sorted by stability', () async {
      final ChannelCommand command = ChannelCommand();
      final CommandRunner<void> runner = createTestCommandRunner(command);

65 66 67 68 69
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
          stdout: 'origin/beta\n'
              'origin/master\n'
70
              'origin/main\n'
71 72 73 74
              'origin/stable\n',
        ),
      );

75
      await runner.run(<String>['channel']);
76
      expect(fakeProcessManager, hasNoRemainingExpectations);
77 78 79 80 81
      expect(testLogger.errorText, hasLength(0));
      // format the status text for a simpler assertion.
      final Iterable<String> rows = testLogger.statusText
        .split('\n')
        .map((String line) => line.substring(2)); // remove '* ' or '  ' from output
82
      expect(rows, containsAllInOrder(kOfficialChannels));
83 84 85 86

      // clear buffer for next process
      testLogger.clear();

87 88 89 90 91 92 93
      // Extra branches.
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
          stdout: 'origin/beta\n'
              'origin/master\n'
              'origin/dependabot/bundler\n'
94
              'origin/main\n'
95 96 97 98 99
              'origin/v1.4.5-hotfixes\n'
              'origin/stable\n',
        ),
      );

100
      await runner.run(<String>['channel']);
101
      expect(fakeProcessManager, hasNoRemainingExpectations);
102
      expect(rows, containsAllInOrder(kOfficialChannels));
103 104 105 106 107
      expect(testLogger.errorText, hasLength(0));
      // format the status text for a simpler assertion.
      final Iterable<String> rows2 = testLogger.statusText
        .split('\n')
        .map((String line) => line.substring(2)); // remove '* ' or '  ' from output
108
      expect(rows2, containsAllInOrder(kOfficialChannels));
109 110 111 112

      // clear buffer for next process
      testLogger.clear();

113 114 115 116 117 118 119 120 121 122 123
      // Missing branches.
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
          stdout: 'origin/beta\n'
              'origin/dependabot/bundler\n'
              'origin/v1.4.5-hotfixes\n'
              'origin/stable\n',
        ),
      );

124
      await runner.run(<String>['channel']);
125
      expect(fakeProcessManager, hasNoRemainingExpectations);
126 127 128 129
      expect(testLogger.errorText, hasLength(0));
      // check if available official channels are in order of stability
      int prev = -1;
      int next = -1;
130
      for (final String branch in kOfficialChannels) {
131 132 133 134 135 136 137 138
        next = testLogger.statusText.indexOf(branch);
        if (next != -1) {
          expect(prev < next, isTrue);
          prev = next;
        }
      }

    }, overrides: <Type, Generator>{
139 140
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
141 142
    });

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
    testUsingContext('ignores lines with unexpected output', () async {
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
          stdout: 'origin/beta\n'
              'origin/stable\n'
              'upstream/beta\n'
              'upstream/stable\n'
              'foo',
        ),
      );

      final ChannelCommand command = ChannelCommand();
      final CommandRunner<void> runner = createTestCommandRunner(command);
      await runner.run(<String>['channel']);

159
      expect(fakeProcessManager, hasNoRemainingExpectations);
160 161 162 163 164 165
      expect(testLogger.errorText, hasLength(0));

      // format the status text for a simpler assertion.
      final Iterable<String> rows = testLogger.statusText
        .split('\n')
        .map((String line) => line.trim())
166
        .where((String line) => line.isNotEmpty == true)
167 168 169 170 171 172 173 174
        .skip(1); // remove `Flutter channels:` line

      expect(rows, <String>['beta', 'stable', 'Currently not on an official channel.']);
    }, overrides: <Type, Generator>{
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
    });

175
    testUsingContext('removes duplicates', () async {
176 177 178
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
179
          stdout: 'origin/beta\n'
180 181 182 183 184
              'origin/stable\n'
              'upstream/beta\n'
              'upstream/stable\n',
        ),
      );
185

186
      final ChannelCommand command = ChannelCommand();
187
      final CommandRunner<void> runner = createTestCommandRunner(command);
188 189
      await runner.run(<String>['channel']);

190
      expect(fakeProcessManager, hasNoRemainingExpectations);
191 192 193 194 195 196
      expect(testLogger.errorText, hasLength(0));

      // format the status text for a simpler assertion.
      final Iterable<String> rows = testLogger.statusText
        .split('\n')
        .map((String line) => line.trim())
197
        .where((String line) => line.isNotEmpty == true)
198 199
        .skip(1); // remove `Flutter channels:` line

200
      expect(rows, <String>['beta', 'stable', 'Currently not on an official channel.']);
201
    }, overrides: <Type, Generator>{
202 203
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
204 205 206
    });

    testUsingContext('can switch channels', () async {
207 208 209 210 211 212 213 214 215 216 217
      fakeProcessManager.addCommands(<FakeCommand>[
        const FakeCommand(
          command: <String>['git', 'fetch'],
        ),
        const FakeCommand(
          command: <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
        ),
        const FakeCommand(
            command: <String>['git', 'checkout', 'beta', '--']
        ),
      ]);
218

219
      final ChannelCommand command = ChannelCommand();
220
      final CommandRunner<void> runner = createTestCommandRunner(command);
221 222
      await runner.run(<String>['channel', 'beta']);

223
      expect(fakeProcessManager, hasNoRemainingExpectations);
224 225 226 227
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace("Switching to flutter channel 'beta'..."),
      );
228
      expect(testLogger.errorText, hasLength(0));
229

230 231 232 233 234 235 236 237 238 239 240
      fakeProcessManager.addCommands(<FakeCommand>[
        const FakeCommand(
          command: <String>['git', 'fetch'],
        ),
        const FakeCommand(
          command: <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/stable'],
        ),
        const FakeCommand(
            command: <String>['git', 'checkout', 'stable', '--']
        ),
      ]);
241 242 243

      await runner.run(<String>['channel', 'stable']);

244
      expect(fakeProcessManager, hasNoRemainingExpectations);
245
    }, overrides: <Type, Generator>{
246
      FileSystem: () => MemoryFileSystem.test(),
247
      ProcessManager: () => fakeProcessManager,
248 249
    });

250
    testUsingContext('switching channels prompts to run flutter upgrade', () async {
251 252 253 254 255 256 257 258 259 260 261
      fakeProcessManager.addCommands(<FakeCommand>[
        const FakeCommand(
          command: <String>['git', 'fetch'],
        ),
        const FakeCommand(
          command: <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
        ),
        const FakeCommand(
            command: <String>['git', 'checkout', 'beta', '--']
        ),
      ]);
262 263 264 265 266

      final ChannelCommand command = ChannelCommand();
      final CommandRunner<void> runner = createTestCommandRunner(command);
      await runner.run(<String>['channel', 'beta']);

267 268 269 270 271 272
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace("Successfully switched to flutter channel 'beta'."),
      );
      expect(
        testLogger.statusText,
kwkr's avatar
kwkr committed
273 274
        containsIgnoringWhitespace(
          "To ensure that you're on the latest build "
275 276
          "from this channel, run 'flutter upgrade'"),
      );
277
      expect(testLogger.errorText, hasLength(0));
278
      expect(fakeProcessManager, hasNoRemainingExpectations);
279
    }, overrides: <Type, Generator>{
280
      FileSystem: () => MemoryFileSystem.test(),
281
      ProcessManager: () => fakeProcessManager,
282 283
    });

284 285 286
    // This verifies that bug https://github.com/flutter/flutter/issues/21134
    // doesn't return.
    testUsingContext('removes version stamp file when switching channels', () async {
287 288 289 290 291 292 293 294 295 296 297
      fakeProcessManager.addCommands(<FakeCommand>[
        const FakeCommand(
          command: <String>['git', 'fetch'],
        ),
        const FakeCommand(
          command: <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/beta'],
        ),
        const FakeCommand(
          command: <String>['git', 'checkout', 'beta', '--']
        ),
      ]);
298

299
      final File versionCheckFile = globals.cache.getStampFileFor(
300
        VersionCheckStamp.flutterVersionCheckStampFile,
301 302 303 304 305 306 307 308 309 310 311 312
      );

      /// 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"
        }
      ''');

313
      final ChannelCommand command = ChannelCommand();
314
      final CommandRunner<void> runner = createTestCommandRunner(command);
315 316 317 318 319
      await runner.run(<String>['channel', 'beta']);

      expect(testLogger.statusText, isNot(contains('A new version of Flutter')));
      expect(testLogger.errorText, hasLength(0));
      expect(versionCheckFile.existsSync(), isFalse);
320
      expect(fakeProcessManager, hasNoRemainingExpectations);
321
    }, overrides: <Type, Generator>{
322
      FileSystem: () => MemoryFileSystem.test(),
323
      ProcessManager: () => fakeProcessManager,
324
    });
325 326
  });
}