upgrade_test.dart 17.8 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:flutter_tools/src/base/file_system.dart';
8
import 'package:flutter_tools/src/base/io.dart';
9
import 'package:flutter_tools/src/base/platform.dart';
10
import 'package:flutter_tools/src/cache.dart';
11
import 'package:flutter_tools/src/commands/upgrade.dart';
12
import 'package:flutter_tools/src/convert.dart';
13
import 'package:flutter_tools/src/globals.dart' as globals;
14
import 'package:flutter_tools/src/persistent_tool_state.dart';
15 16
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
17

18 19
import '../../src/common.dart';
import '../../src/context.dart';
20
import '../../src/fake_process_manager.dart';
21
import '../../src/fakes.dart';
22
import '../../src/test_flutter_command_runner.dart';
23

24
void main() {
25 26 27
  group('UpgradeCommandRunner', () {
    FakeUpgradeCommandRunner fakeCommandRunner;
    UpgradeCommandRunner realCommandRunner;
28
    FakeProcessManager processManager;
29
    FakePlatform fakePlatform;
30 31 32 33 34 35 36 37
    const GitTagVersion gitTagVersion = GitTagVersion(
      x: 1,
      y: 2,
      z: 3,
      hotfix: 4,
      commits: 5,
      hash: 'asd',
    );
38 39 40 41

    setUp(() {
      fakeCommandRunner = FakeUpgradeCommandRunner();
      realCommandRunner = UpgradeCommandRunner();
42
      processManager = FakeProcessManager.empty();
43
      fakeCommandRunner.willHaveUncommittedChanges = false;
44
      fakePlatform = FakePlatform()..environment = Map<String, String>.unmodifiable(<String, String>{
45 46
        'ENV1': 'irrelevant',
        'ENV2': 'irrelevant',
47
      });
48 49
    });

50
    testUsingContext('throws on unknown tag, official branch,  noforce', () async {
51
      final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
52
      const String upstreamRevision = '';
53
      final FakeFlutterVersion latestVersion = FakeFlutterVersion(frameworkRevision: upstreamRevision);
54 55
      fakeCommandRunner.remoteVersion = latestVersion;

56
      final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
57 58 59 60 61
        force: false,
        continueFlow: false,
        testFlow: false,
        gitTagVersion: const GitTagVersion.unknown(),
        flutterVersion: flutterVersion,
62
        verifyOnly: false,
63
      );
Dan Field's avatar
Dan Field committed
64
      expect(result, throwsToolExit());
65
      expect(processManager, hasNoRemainingExpectations);
66 67
    }, overrides: <Type, Generator>{
      Platform: () => fakePlatform,
68 69
    });

70
    testUsingContext('throws tool exit with uncommitted changes', () async {
71
      final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
72
      const String upstreamRevision = '';
73
      final FakeFlutterVersion latestVersion = FakeFlutterVersion(frameworkRevision: upstreamRevision);
74
      fakeCommandRunner.remoteVersion = latestVersion;
75
      fakeCommandRunner.willHaveUncommittedChanges = true;
76

77
      final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
78 79 80 81 82
        force: false,
        continueFlow: false,
        testFlow: false,
        gitTagVersion: gitTagVersion,
        flutterVersion: flutterVersion,
83
        verifyOnly: false,
84
      );
Dan Field's avatar
Dan Field committed
85
      expect(result, throwsToolExit());
86
      expect(processManager, hasNoRemainingExpectations);
87 88
    }, overrides: <Type, Generator>{
      Platform: () => fakePlatform,
89 90
    });

91
    testUsingContext("Doesn't continue on known tag, beta branch, no force, already up-to-date", () async {
92
      const String revision = 'abc123';
93
      final FakeFlutterVersion latestVersion = FakeFlutterVersion(frameworkRevision: revision);
94
      final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta', frameworkRevision: revision);
95
      fakeCommandRunner.alreadyUpToDate = true;
96 97
      fakeCommandRunner.remoteVersion = latestVersion;

98
      final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
99 100 101 102 103
        force: false,
        continueFlow: false,
        testFlow: false,
        gitTagVersion: gitTagVersion,
        flutterVersion: flutterVersion,
104
        verifyOnly: false,
105
      );
106
      expect(await result, FlutterCommandResult.success());
107
      expect(testLogger.statusText, contains('Flutter is already up to date'));
108
      expect(processManager, hasNoRemainingExpectations);
109 110 111 112 113
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

114
    testUsingContext('correctly provides upgrade version on verify only', () async {
115
      const String revision = 'abc123';
116 117 118 119
      const String upstreamRevision = 'def456';
      const String version = '1.2.3';
      const String upstreamVersion = '4.5.6';

120
      final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
121
        channel: 'beta',
122 123 124 125
        frameworkRevision: revision,
        frameworkRevisionShort: revision,
        frameworkVersion: version,
      );
126

127 128 129 130 131
      final FakeFlutterVersion latestVersion = FakeFlutterVersion(
        frameworkRevision: upstreamRevision,
        frameworkRevisionShort: upstreamRevision,
        frameworkVersion: upstreamVersion,
      );
132

133
      fakeCommandRunner.alreadyUpToDate = false;
134 135
      fakeCommandRunner.remoteVersion = latestVersion;

136 137 138 139 140 141 142 143 144 145
      final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
        force: false,
        continueFlow: false,
        testFlow: false,
        gitTagVersion: gitTagVersion,
        flutterVersion: flutterVersion,
        verifyOnly: true,
      );
      expect(await result, FlutterCommandResult.success());
      expect(testLogger.statusText, contains('A new version of Flutter is available'));
146 147
      expect(testLogger.statusText, contains('The latest version: 4.5.6 (revision def456)'));
      expect(testLogger.statusText, contains('Your current version: 1.2.3 (revision abc123)'));
148
      expect(processManager, hasNoRemainingExpectations);
149 150 151 152 153
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

154
    testUsingContext('fetchLatestVersion returns version if git succeeds', () async {
155
      const String revision = 'abc123';
156
      const String version = '1.2.3';
157 158 159

      processManager.addCommands(<FakeCommand>[
        const FakeCommand(command: <String>[
160
          'git', 'fetch', '--tags',
161 162
        ]),
        const FakeCommand(command: <String>[
163
          'git', 'rev-parse', '--verify', '@{upstream}',
164 165
        ],
        stdout: revision),
166 167
        const FakeCommand(command: <String>[
          'git', 'tag', '--points-at', revision,
168
        ]),
169 170 171 172
        const FakeCommand(command: <String>[
          'git', 'describe', '--match', '*.*.*', '--long', '--tags', revision,
        ],
        stdout: version),
173 174
      ]);

175
      final FlutterVersion updateVersion = await realCommandRunner.fetchLatestVersion(localVersion: FakeFlutterVersion());
176 177 178

      expect(updateVersion.frameworkVersion, version);
      expect(updateVersion.frameworkRevision, revision);
179
      expect(processManager, hasNoRemainingExpectations);
180 181
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
182 183 184
      Platform: () => fakePlatform,
    });

185
    testUsingContext('fetchLatestVersion throws toolExit if HEAD is detached', () async {
186 187
      processManager.addCommands(const <FakeCommand>[
        FakeCommand(command: <String>[
188
          'git', 'fetch', '--tags',
189 190
        ]),
        FakeCommand(
191
          command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
192 193
          exception: ProcessException(
            'git',
194
            <String>['rev-parse', '--verify', '@{upstream}'],
195 196
            'fatal: HEAD does not point to a branch',
          ),
197 198 199
        ),
      ]);

200
      await expectLater(
201
        () async => realCommandRunner.fetchLatestVersion(localVersion: FakeFlutterVersion()),
202 203 204 205 206
        throwsToolExit(message: 'Unable to upgrade Flutter: Your Flutter checkout '
          'is currently not on a release branch.\n'
          'Use "flutter channel" to switch to an official channel, and retry. '
          'Alternatively, re-install Flutter by going to https://flutter.dev/docs/get-started/install.'
        ),
207
      );
208
      expect(processManager, hasNoRemainingExpectations);
209 210 211 212 213
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

214
    testUsingContext('fetchLatestVersion throws toolExit if no upstream configured', () async {
215 216
      processManager.addCommands(const <FakeCommand>[
        FakeCommand(command: <String>[
217
          'git', 'fetch', '--tags',
218 219
        ]),
        FakeCommand(
220
          command: <String>['git', 'rev-parse', '--verify', '@{upstream}'],
221 222
          exception: ProcessException(
            'git',
223
            <String>['rev-parse', '--verify', '@{upstream}'],
224 225
            'fatal: no upstream configured for branch',
          ),
226 227 228
        ),
      ]);

229
      await expectLater(
230
        () async => realCommandRunner.fetchLatestVersion(localVersion: FakeFlutterVersion()),
231 232 233
        throwsToolExit(message: 'Unable to upgrade Flutter: The current Flutter '
          'branch/channel is not tracking any remote repository.\n'
          'Re-install Flutter by going to https://flutter.dev/docs/get-started/install.'
234 235
        ),
      );
236
      expect(processManager, hasNoRemainingExpectations);
237 238 239 240 241
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

242 243 244
    testUsingContext('git exception during attemptReset throwsToolExit', () async {
      const String revision = 'abc123';
      const String errorMessage = 'fatal: Could not parse object ´$revision´';
245 246 247 248 249 250 251 252
      processManager.addCommand(
        const FakeCommand(
          command: <String>['git', 'reset', '--hard', revision],
          exception: ProcessException(
            'git',
            <String>['reset', '--hard', revision],
            errorMessage,
          ),
253
        ),
254
      );
255 256

      await expectLater(
257
            () async => realCommandRunner.attemptReset(revision),
258 259
        throwsToolExit(message: errorMessage),
      );
260
      expect(processManager, hasNoRemainingExpectations);
261 262 263 264 265
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

266
    testUsingContext('flutterUpgradeContinue passes env variables to child process', () async {
267 268 269 270 271 272 273 274 275 276 277
      processManager.addCommand(
        FakeCommand(
          command: <String>[
            globals.fs.path.join('bin', 'flutter'),
            'upgrade',
            '--continue',
            '--no-version-check',
          ],
          environment: <String, String>{'FLUTTER_ALREADY_LOCKED': 'true', ...fakePlatform.environment}
        ),
      );
278
      await realCommandRunner.flutterUpgradeContinue();
279
      expect(processManager, hasNoRemainingExpectations);
280 281 282 283 284
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

285 286 287 288 289 290
    testUsingContext('Show current version to the upgrade message.', () async {
      const String revision = 'abc123';
      const String upstreamRevision = 'def456';
      const String version = '1.2.3';
      const String upstreamVersion = '4.5.6';

291
      final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
292
        channel: 'beta',
293 294 295 296 297 298 299
        frameworkRevision: revision,
        frameworkVersion: version,
      );
      final FakeFlutterVersion latestVersion = FakeFlutterVersion(
        frameworkRevision: upstreamRevision,
        frameworkVersion: upstreamVersion,
      );
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

      fakeCommandRunner.alreadyUpToDate = false;
      fakeCommandRunner.remoteVersion = latestVersion;
      fakeCommandRunner.workingDirectory = 'workingDirectory/aaa/bbb';

      final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
        force: true,
        continueFlow: false,
        testFlow: true,
        gitTagVersion: gitTagVersion,
        flutterVersion: flutterVersion,
        verifyOnly: false,
      );
      expect(await result, FlutterCommandResult.success());
      expect(testLogger.statusText, contains('Upgrading Flutter to 4.5.6 from 1.2.3 in workingDirectory/aaa/bbb...'));
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
    });

320
    testUsingContext('precacheArtifacts passes env variables to child process', () async {
321 322 323 324 325 326 327 328 329 330 331
      processManager.addCommand(
        FakeCommand(
          command: <String>[
            globals.fs.path.join('bin', 'flutter'),
            '--no-color',
            '--no-version-check',
            'precache',
          ],
          environment: <String, String>{'FLUTTER_ALREADY_LOCKED': 'true', ...fakePlatform.environment}
        ),
      );
332
      await realCommandRunner.precacheArtifacts();
333
      expect(processManager, hasNoRemainingExpectations);
334 335 336
    }, overrides: <Type, Generator>{
      ProcessManager: () => processManager,
      Platform: () => fakePlatform,
337
    });
338

339
    group('runs upgrade', () {
340
      setUp(() {
341 342 343 344 345 346 347 348
        processManager.addCommand(
          FakeCommand(command: <String>[
            globals.fs.path.join('bin', 'flutter'),
            'upgrade',
            '--continue',
            '--no-version-check',
          ]),
        );
349 350
      });

351
      testUsingContext('does not throw on unknown tag, official branch, force', () async {
352
        fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: null);
353
        final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
354

355 356 357 358 359 360
        final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
          force: true,
          continueFlow: false,
          testFlow: false,
          gitTagVersion: const GitTagVersion.unknown(),
          flutterVersion: flutterVersion,
361
          verifyOnly: false,
362 363
        );
        expect(await result, FlutterCommandResult.success());
364
        expect(processManager, hasNoRemainingExpectations);
365 366 367
      }, overrides: <Type, Generator>{
        ProcessManager: () => processManager,
        Platform: () => fakePlatform,
368 369
      });

370
      testUsingContext('does not throw tool exit with uncommitted changes and force', () async {
371
        final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
372
        fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: null);
373
        fakeCommandRunner.willHaveUncommittedChanges = true;
374

375 376 377 378 379 380
        final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
          force: true,
          continueFlow: false,
          testFlow: false,
          gitTagVersion: gitTagVersion,
          flutterVersion: flutterVersion,
381
          verifyOnly: false,
382
        );
383
        expect(await result, FlutterCommandResult.success());
384
        expect(processManager, hasNoRemainingExpectations);
385 386 387 388
      }, overrides: <Type, Generator>{
        ProcessManager: () => processManager,
        Platform: () => fakePlatform,
      });
389

390 391
      testUsingContext("Doesn't throw on known tag, beta branch, no force", () async {
        final FakeFlutterVersion flutterVersion = FakeFlutterVersion(channel: 'beta');
392
        fakeCommandRunner.remoteVersion = FakeFlutterVersion(frameworkRevision: null);
393

394 395 396 397 398 399
        final Future<FlutterCommandResult> result = fakeCommandRunner.runCommand(
          force: false,
          continueFlow: false,
          testFlow: false,
          gitTagVersion: gitTagVersion,
          flutterVersion: flutterVersion,
400
          verifyOnly: false,
401
        );
402
        expect(await result, FlutterCommandResult.success());
403
        expect(processManager, hasNoRemainingExpectations);
404
      }, overrides: <Type, Generator>{
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
        ProcessManager: () => processManager,
        Platform: () => fakePlatform,
      });

      group('full command', () {
        FakeProcessManager fakeProcessManager;
        Directory tempDir;
        File flutterToolState;

        setUp(() {
          Cache.disableLocking();
          fakeProcessManager = FakeProcessManager.list(<FakeCommand>[
            const FakeCommand(
              command: <String>[
                'git', 'tag', '--points-at', 'HEAD',
              ],
            ),
            const FakeCommand(
              command: <String>[
424
                'git', 'describe', '--match', '*.*.*', '--long', '--tags', 'HEAD',
425 426 427 428 429 430 431 432 433 434 435 436 437 438
              ],
              stdout: 'v1.12.16-19-gb45b676af',
            ),
          ]);
          tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_upgrade_test.');
          flutterToolState = tempDir.childFile('.flutter_tool_state');
        });

        tearDown(() {
          Cache.enableLocking();
          tryToDelete(tempDir);
        });

        testUsingContext('upgrade continue prints welcome message', () async {
439 440 441 442
          final UpgradeCommand upgradeCommand = UpgradeCommand(
            verboseHelp: false,
            commandRunner: fakeCommandRunner,
          );
443 444 445 446 447 448 449 450 451 452 453 454 455

          await createTestCommandRunner(upgradeCommand).run(
            <String>[
              'upgrade',
              '--continue',
            ],
          );

          expect(
            json.decode(flutterToolState.readAsStringSync()),
            containsPair('redisplay-welcome-message', true),
          );
        }, overrides: <Type, Generator>{
456
          FlutterVersion: () => FakeFlutterVersion(),
457 458 459 460 461 462
          ProcessManager: () => fakeProcessManager,
          PersistentToolState: () => PersistentToolState.test(
            directory: tempDir,
            logger: testLogger,
          ),
        });
463 464
      });
    });
465

466
  });
467
}
468 469

class FakeUpgradeCommandRunner extends UpgradeCommandRunner {
470
  bool willHaveUncommittedChanges = false;
471 472
  bool alreadyUpToDate = false;

473
  FlutterVersion remoteVersion;
474

475
  @override
476
  Future<FlutterVersion> fetchLatestVersion({FlutterVersion localVersion}) async => remoteVersion;
477

478
  @override
479
  Future<bool> hasUncommittedChanges() async => willHaveUncommittedChanges;
480 481

  @override
482
  Future<void> attemptReset(String newRevision) async {}
483 484 485 486 487 488 489 490 491 492

  @override
  Future<void> precacheArtifacts() async {}

  @override
  Future<void> updatePackages(FlutterVersion flutterVersion) async {}

  @override
  Future<void> runDoctor() async {}
}