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

5 6
// @dart = 2.8

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

15 16
import '../src/common.dart';
import '../src/context.dart';
17
import '../src/test_flutter_command_runner.dart';
18

19 20
void main() {
  group('channel', () {
21 22 23 24 25
    FakeProcessManager fakeProcessManager;

    setUp(() {
      fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
    });
26

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

31
    Future<void> simpleChannelTest(List<String> args) async {
32
      final ChannelCommand command = ChannelCommand();
33
      final CommandRunner<void> runner = createTestCommandRunner(command);
34
      await runner.run(args);
35
      expect(testLogger.errorText, hasLength(0));
36 37 38
      // 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.
39 40 41 42
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace('Flutter channels:'),
      );
43 44 45 46 47 48 49 50
    }

    testUsingContext('list', () async {
      await simpleChannelTest(<String>['channel']);
    });

    testUsingContext('verbose list', () async {
      await simpleChannelTest(<String>['channel', '-v']);
51
    });
52

53 54 55 56
    testUsingContext('sorted by stability', () async {
      final ChannelCommand command = ChannelCommand();
      final CommandRunner<void> runner = createTestCommandRunner(command);

57 58 59 60 61 62 63 64 65 66
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
          stdout: 'origin/beta\n'
              'origin/master\n'
              'origin/dev\n'
              'origin/stable\n',
        ),
      );

67
      await runner.run(<String>['channel']);
68
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
69 70 71 72 73
      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
74
      expect(rows, containsAllInOrder(kOfficialChannels));
75 76 77 78

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

79 80 81 82 83 84 85 86 87 88 89 90 91
      // Extra branches.
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
          stdout: 'origin/beta\n'
              'origin/master\n'
              'origin/dependabot/bundler\n'
              'origin/dev\n'
              'origin/v1.4.5-hotfixes\n'
              'origin/stable\n',
        ),
      );

92
      await runner.run(<String>['channel']);
93 94
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
      expect(rows, containsAllInOrder(kOfficialChannels));
95 96 97 98 99
      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
100
      expect(rows2, containsAllInOrder(kOfficialChannels));
101 102 103 104

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

105 106 107 108 109 110 111 112 113 114 115
      // 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',
        ),
      );

116
      await runner.run(<String>['channel']);
117
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
118 119 120 121
      expect(testLogger.errorText, hasLength(0));
      // check if available official channels are in order of stability
      int prev = -1;
      int next = -1;
122
      for (final String branch in kOfficialChannels) {
123 124 125 126 127 128 129 130
        next = testLogger.statusText.indexOf(branch);
        if (next != -1) {
          expect(prev < next, isTrue);
          prev = next;
        }
      }

    }, overrides: <Type, Generator>{
131 132
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
133 134
    });

135
    testUsingContext('removes duplicates', () async {
136 137 138
      fakeProcessManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'branch', '-r'],
139
          stdout: 'origin/dev\n'
140 141 142 143 144 145 146
              'origin/beta\n'
              'origin/stable\n'
              'upstream/dev\n'
              'upstream/beta\n'
              'upstream/stable\n',
        ),
      );
147

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

152
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
153 154 155 156 157 158 159 160 161
      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())
        .where((String line) => line?.isNotEmpty == true)
        .skip(1); // remove `Flutter channels:` line

162
      expect(rows, <String>['dev', 'beta', 'stable']);
163
    }, overrides: <Type, Generator>{
164 165
      ProcessManager: () => fakeProcessManager,
      FileSystem: () => MemoryFileSystem.test(),
166 167 168
    });

    testUsingContext('can switch channels', () async {
169 170 171 172 173 174 175 176 177 178 179
      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', '--']
        ),
      ]);
180

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

185
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
186 187 188 189
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace("Switching to flutter channel 'beta'..."),
      );
190
      expect(testLogger.errorText, hasLength(0));
191

192 193 194 195 196 197 198 199 200 201 202
      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', '--']
        ),
      ]);
203 204 205

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

206
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
207
    }, overrides: <Type, Generator>{
208
      FileSystem: () => MemoryFileSystem.test(),
209
      ProcessManager: () => fakeProcessManager,
210 211
    });

212
    testUsingContext('switching channels prompts to run flutter upgrade', () async {
213 214 215 216 217 218 219 220 221 222 223
      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', '--']
        ),
      ]);
224 225 226 227 228

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

229 230 231 232 233 234
      expect(
        testLogger.statusText,
        containsIgnoringWhitespace("Successfully switched to flutter channel 'beta'."),
      );
      expect(
        testLogger.statusText,
kwkr's avatar
kwkr committed
235 236
        containsIgnoringWhitespace(
          "To ensure that you're on the latest build "
237 238
          "from this channel, run 'flutter upgrade'"),
      );
239
      expect(testLogger.errorText, hasLength(0));
240
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
241
    }, overrides: <Type, Generator>{
242
      FileSystem: () => MemoryFileSystem.test(),
243
      ProcessManager: () => fakeProcessManager,
244 245
    });

246 247 248
    // This verifies that bug https://github.com/flutter/flutter/issues/21134
    // doesn't return.
    testUsingContext('removes version stamp file when switching channels', () async {
249 250 251 252 253 254 255 256 257 258 259
      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', '--']
        ),
      ]);
260

261
      final File versionCheckFile = globals.cache.getStampFileFor(
262
        VersionCheckStamp.flutterVersionCheckStampFile,
263 264 265 266 267 268 269 270 271 272 273 274
      );

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

275
      final ChannelCommand command = ChannelCommand();
276
      final CommandRunner<void> runner = createTestCommandRunner(command);
277 278 279 280 281
      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);
282
      expect(fakeProcessManager.hasRemainingExpectations, isFalse);
283
    }, overrides: <Type, Generator>{
284
      FileSystem: () => MemoryFileSystem.test(),
285
      ProcessManager: () => fakeProcessManager,
286
    });
287 288
  });
}