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

import 'package:file/file.dart';
import 'package:file/memory.dart';
7
import 'package:flutter_tools/src/base/bot_detector.dart';
8
import 'package:flutter_tools/src/base/common.dart';
9
import 'package:flutter_tools/src/base/file_system.dart';
10
import 'package:flutter_tools/src/base/io.dart';
11
import 'package:flutter_tools/src/base/logger.dart';
12
import 'package:flutter_tools/src/base/platform.dart';
13
import 'package:flutter_tools/src/cache.dart';
14
import 'package:flutter_tools/src/dart/pub.dart';
15
import 'package:flutter_tools/src/reporting/reporting.dart';
16 17
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
18
import 'package:fake_async/fake_async.dart';
19

20 21
import '../../src/common.dart';
import '../../src/context.dart';
22
import '../../src/mocks.dart' as mocks;
23 24

void main() {
25
  setUpAll(() {
26
    Cache.flutterRoot = '';
27
  });
28

29 30 31 32
  tearDown(() {
    MockDirectory.findCache = false;
  });

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
  testWithoutContext('checkUpToDate skips pub get if the package config is newer than the pubspec '
    'and the current framework version is the same as the last version', () async {
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
    final BufferLogger logger = BufferLogger.test();
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('pubspec.lock').createSync();
    fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
    fileSystem.file('.dart_tool/version').writeAsStringSync('a');
    fileSystem.file('version').writeAsStringSync('a');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(logger.traceText, contains('Skipping pub get: version match.'));
  });

  testWithoutContext('checkUpToDate does not skip pub get if the package config is newer than the pubspec '
    'but the current framework version is not the same as the last version', () async {
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      const FakeCommand(command: <String>[
        'bin/cache/dart-sdk/bin/pub',
        '--verbosity=warning',
        'get',
        '--no-precompile',
      ])
    ]);
    final BufferLogger logger = BufferLogger.test();
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('pubspec.lock').createSync();
    fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
    fileSystem.file('.dart_tool/version').writeAsStringSync('a');
    fileSystem.file('version').writeAsStringSync('b');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(processManager.hasRemainingExpectations, false);
    expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
  });

  testWithoutContext('checkUpToDate does not skip pub get if the package config is newer than the pubspec '
    'but the current framework version does not exist yet', () async {
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      const FakeCommand(command: <String>[
        'bin/cache/dart-sdk/bin/pub',
        '--verbosity=warning',
        'get',
        '--no-precompile',
      ])
    ]);
    final BufferLogger logger = BufferLogger.test();
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('pubspec.lock').createSync();
    fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
    fileSystem.file('version').writeAsStringSync('b');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(processManager.hasRemainingExpectations, false);
    expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
  });

  testWithoutContext('checkUpToDate does not skip pub get if the package config does not exist', () async {
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      FakeCommand(command: const <String>[
        'bin/cache/dart-sdk/bin/pub',
        '--verbosity=warning',
        'get',
        '--no-precompile',
      ], onRun: () {
        fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
      })
    ]);
    final BufferLogger logger = BufferLogger.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('pubspec.lock').createSync();
    fileSystem.file('version').writeAsStringSync('b');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(processManager.hasRemainingExpectations, false);
    expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
  });

  testWithoutContext('checkUpToDate does not skip pub get if the pubspec.lock does not exist', () async {
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      const FakeCommand(command: <String>[
        'bin/cache/dart-sdk/bin/pub',
        '--verbosity=warning',
        'get',
        '--no-precompile',
      ]),
    ]);
    final BufferLogger logger = BufferLogger.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('version').writeAsStringSync('b');
    fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
    fileSystem.file('.dart_tool/version').writeAsStringSync('b');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(processManager.hasRemainingExpectations, false);
    expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
  });

  testWithoutContext('checkUpToDate does not skip pub get if the package config is older that the pubspec', () async {
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      const FakeCommand(command: <String>[
        'bin/cache/dart-sdk/bin/pub',
        '--verbosity=warning',
        'get',
        '--no-precompile',
      ])
    ]);
    final BufferLogger logger = BufferLogger.test();
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('pubspec.lock').createSync();
    fileSystem.file('.dart_tool/package_config.json')
      ..createSync(recursive: true)
      ..setLastModifiedSync(DateTime(1991));
    fileSystem.file('version').writeAsStringSync('b');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(processManager.hasRemainingExpectations, false);
    expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
  });

  testWithoutContext('checkUpToDate does not skip pub get if the pubspec.lock is older that the pubspec', () async {
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      const FakeCommand(command: <String>[
        'bin/cache/dart-sdk/bin/pub',
        '--verbosity=warning',
        'get',
        '--no-precompile',
      ])
    ]);
    final BufferLogger logger = BufferLogger.test();
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();

    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('pubspec.lock')
      ..createSync()
      ..setLastModifiedSync(DateTime(1991));
    fileSystem.file('.dart_tool/package_config.json')
      .createSync(recursive: true);
    fileSystem.file('version').writeAsStringSync('b');
    fileSystem.file('.dart_tool/version').writeAsStringSync('b');

    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );

    await pub.get(
      context: PubContext.pubGet,
      checkUpToDate: true,
    );

    expect(processManager.hasRemainingExpectations, false);
    expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
  });

296
  testWithoutContext('pub get 69', () async {
297
    String error;
298

299 300 301 302 303 304 305 306 307 308 309 310
    final MockProcessManager processMock = MockProcessManager(69);
    final BufferLogger logger = BufferLogger.test();
    final Pub pub = Pub(
      fileSystem: MockFileSystem(),
      logger: logger,
      processManager: processMock,
      usage: MockUsage(),
      platform: FakePlatform(
        environment: const <String, String>{},
      ),
      botDetector: const BotDetectorAlwaysNo(),
    );
311

312
    FakeAsync().run((FakeAsync time) {
Josh Soref's avatar
Josh Soref committed
313
      expect(processMock.lastPubEnvironment, isNull);
314
      expect(logger.statusText, '');
315
      pub.get(context: PubContext.flutterTests).then((void value) {
316
        error = 'test completed unexpectedly';
317 318
      }, onError: (dynamic thrownError) {
        error = 'test failed unexpectedly: $thrownError';
319 320
      });
      time.elapse(const Duration(milliseconds: 500));
321
      expect(logger.statusText,
322
        'Running "flutter pub get" in /...\n'
323
        'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n',
324
      );
Josh Soref's avatar
Josh Soref committed
325
      expect(processMock.lastPubEnvironment, contains('flutter_cli:flutter_tests'));
326
      expect(processMock.lastPubCache, isNull);
327
      time.elapse(const Duration(milliseconds: 500));
328
      expect(logger.statusText,
329
        'Running "flutter pub get" in /...\n'
330 331
        'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
        'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n',
332 333
      );
      time.elapse(const Duration(seconds: 1));
334
      expect(logger.statusText,
335
        'Running "flutter pub get" in /...\n'
336 337
        'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
        'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n',
338 339
      );
      time.elapse(const Duration(seconds: 100)); // from t=0 to t=100
340
      expect(logger.statusText,
341
        'Running "flutter pub get" in /...\n'
342 343 344 345 346 347 348
        'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
        'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n' // at t=1
        'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n' // at t=5
        'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n' // at t=13
        'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n' // at t=29
        'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n', // at t=61
349 350
      );
      time.elapse(const Duration(seconds: 200)); // from t=0 to t=200
351
      expect(logger.statusText,
352
        'Running "flutter pub get" in /...\n'
353 354 355 356 357 358 359 360 361 362
        'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n'
        'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n'
        'pub get failed (server unavailable) -- attempting retry 8 in 64 seconds...\n' // at t=39
        'pub get failed (server unavailable) -- attempting retry 9 in 64 seconds...\n' // at t=103
        'pub get failed (server unavailable) -- attempting retry 10 in 64 seconds...\n', // at t=167
363 364
      );
    });
365
    expect(logger.errorText, isEmpty);
366
    expect(error, isNull);
367 368
  });

369 370 371 372 373 374 375 376 377 378
  testWithoutContext('pub get 66 shows message from pub', () async {
    final BufferLogger logger = BufferLogger.test();
    final Pub pub = Pub(
      platform: FakePlatform(environment: const <String, String>{}),
      fileSystem: MockFileSystem(),
      logger: logger,
      usage: MockUsage(),
      botDetector: const BotDetectorAlwaysNo(),
      processManager: MockProcessManager(66, stderr: 'err1\nerr2\nerr3\n', stdout: 'out1\nout2\nout3\n'),
    );
379
    try {
380
      await pub.get(context: PubContext.flutterTests);
381 382 383 384
      throw AssertionError('pubGet did not fail');
    } on ToolExit catch (error) {
      expect(error.message, 'pub get failed (66; err3)');
    }
385
    expect(logger.statusText,
386 387 388 389 390
      'Running "flutter pub get" in /...\n'
      'out1\n'
      'out2\n'
      'out3\n'
    );
391
    expect(logger.errorText,
392 393 394 395 396 397
      'err1\n'
      'err2\n'
      'err3\n'
    );
  });

398
  testWithoutContext('pub cache in root is used', () async {
399
    String error;
400 401 402 403 404 405 406 407 408 409
    final MockProcessManager processMock = MockProcessManager(69);
    final MockFileSystem fsMock = MockFileSystem();
    final Pub pub = Pub(
      platform: FakePlatform(environment: const <String, String>{}),
      usage: MockUsage(),
      fileSystem: fsMock,
      logger: BufferLogger.test(),
      processManager: processMock,
      botDetector: const BotDetectorAlwaysNo(),
    );
410

411
    FakeAsync().run((FakeAsync time) {
412
      MockDirectory.findCache = true;
Josh Soref's avatar
Josh Soref committed
413
      expect(processMock.lastPubEnvironment, isNull);
414
      expect(processMock.lastPubCache, isNull);
415
      pub.get(context: PubContext.flutterTests).then((void value) {
416 417 418 419 420
        error = 'test completed unexpectedly';
      }, onError: (dynamic thrownError) {
        error = 'test failed unexpectedly: $thrownError';
      });
      time.elapse(const Duration(milliseconds: 500));
421

422
      expect(processMock.lastPubCache, equals(fsMock.path.join(Cache.flutterRoot, '.pub-cache')));
423 424 425 426
      expect(error, isNull);
    });
  });

427 428 429 430 431 432 433 434 435 436 437 438 439 440
  testWithoutContext('pub cache in environment is used', () async {
    final MockProcessManager processMock = MockProcessManager(69);
    final Pub pub = Pub(
      fileSystem: MockFileSystem(),
      logger: BufferLogger.test(),
      processManager: processMock,
      usage: MockUsage(),
      botDetector: const BotDetectorAlwaysNo(),
      platform: FakePlatform(
        environment: const <String, String>{
          'PUB_CACHE': 'custom/pub-cache/path',
        },
      ),
    );
441

442
    FakeAsync().run((FakeAsync time) {
443
      MockDirectory.findCache = true;
Josh Soref's avatar
Josh Soref committed
444
      expect(processMock.lastPubEnvironment, isNull);
445
      expect(processMock.lastPubCache, isNull);
446 447

      String error;
448
      pub.get(context: PubContext.flutterTests).then((void value) {
449 450 451 452 453
        error = 'test completed unexpectedly';
      }, onError: (dynamic thrownError) {
        error = 'test failed unexpectedly: $thrownError';
      });
      time.elapse(const Duration(milliseconds: 500));
454

455
      expect(processMock.lastPubCache, equals('custom/pub-cache/path'));
456 457
      expect(error, isNull);
    });
458
  });
459

460
  testWithoutContext('analytics sent on success', () async {
461
    final FileSystem fileSystem = MemoryFileSystem.test();
462 463
    final MockUsage usage = MockUsage();
    final Pub pub = Pub(
464
      fileSystem: fileSystem,
465 466 467 468 469 470 471 472 473 474
      logger: BufferLogger.test(),
      processManager: MockProcessManager(0),
      botDetector: const BotDetectorAlwaysNo(),
      usage: usage,
      platform: FakePlatform(
        environment: const <String, String>{
          'PUB_CACHE': 'custom/pub-cache/path',
        }
      ),
    );
475
    fileSystem.file('version').createSync();
476 477 478 479
    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('.dart_tool/package_config.json')
      ..createSync(recursive: true)
      ..writeAsStringSync('{"configVersion": 2,"packages": []}');
480

481 482 483 484
    await pub.get(
      context: PubContext.flutterTests,
      generateSyntheticPackage: true,
    );
485 486

    verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'success')).called(1);
487 488
  });

489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
  testWithoutContext('package_config_subset file is generated from packages and not timestamp', () async {
    final FileSystem fileSystem = MemoryFileSystem.test();
    final MockUsage usage = MockUsage();
    final Pub pub = Pub(
      fileSystem: fileSystem,
      logger: BufferLogger.test(),
      processManager: MockProcessManager(0),
      botDetector: const BotDetectorAlwaysNo(),
      usage: usage,
      platform: FakePlatform(
        environment: const <String, String>{
          'PUB_CACHE': 'custom/pub-cache/path',
        }
      ),
    );
504
    fileSystem.file('version').createSync();
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
    fileSystem.file('pubspec.yaml').createSync();
    fileSystem.file('.dart_tool/package_config.json')
      ..createSync(recursive: true)
      ..writeAsStringSync('''
      {"configVersion": 2,"packages": [
        {
          "name": "flutter_tools",
          "rootUri": "../",
          "packageUri": "lib/",
          "languageVersion": "2.7"
        }
      ],"generated":"some-time"}
''');

    await pub.get(
      context: PubContext.flutterTests,
      generateSyntheticPackage: true,
    );

    expect(
      fileSystem.file('.dart_tool/package_config_subset').readAsStringSync(),
      'flutter_tools\n'
      '2.7\n'
      'file:///\n'
      'file:///lib/\n'
      '2\n',
    );
  });


535
  testWithoutContext('analytics sent on failure', () async {
536
    MockDirectory.findCache = true;
537 538 539 540 541 542 543 544 545 546 547 548 549
    final MockUsage usage = MockUsage();
    final Pub pub = Pub(
      usage: usage,
      fileSystem: MockFileSystem(),
      logger: BufferLogger.test(),
      processManager: MockProcessManager(1),
      botDetector: const BotDetectorAlwaysNo(),
      platform: FakePlatform(
        environment: const <String, String>{
          'PUB_CACHE': 'custom/pub-cache/path',
        },
      ),
    );
550
    try {
551
      await pub.get(context: PubContext.flutterTests);
552 553 554
    } on ToolExit {
      // Ignore.
    }
555 556

    verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'failure')).called(1);
557 558
  });

559 560
  testWithoutContext('analytics sent on failed version solve', () async {
    final MockUsage usage = MockUsage();
561
    final FileSystem fileSystem = MemoryFileSystem.test();
562
    final Pub pub = Pub(
563
      fileSystem: fileSystem,
564 565 566 567 568 569 570 571 572 573 574 575 576
      logger: BufferLogger.test(),
      processManager: MockProcessManager(
        1,
        stderr: 'version solving failed',
      ),
      platform: FakePlatform(
        environment: <String, String>{
          'PUB_CACHE': 'custom/pub-cache/path',
        },
      ),
      usage: usage,
      botDetector: const BotDetectorAlwaysNo(),
    );
577
    fileSystem.file('pubspec.yaml').writeAsStringSync('name: foo');
578

579
    try {
580
      await pub.get(context: PubContext.flutterTests);
581 582 583
    } on ToolExit {
      // Ignore.
    }
584 585

    verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'version-solving-failed')).called(1);
586
  });
587

588 589 590
  testWithoutContext('Pub error handling', () async {
    final BufferLogger logger = BufferLogger.test();
    final MemoryFileSystem fileSystem = MemoryFileSystem.test();
591 592 593
    final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
      FakeCommand(
        command: const <String>[
594
          'bin/cache/dart-sdk/bin/pub',
595 596 597 598 599
          '--verbosity=warning',
          'get',
          '--no-precompile',
        ],
        onRun: () {
600
          fileSystem.file('.dart_tool/package_config.json')
601
            .setLastModifiedSync(DateTime(2002));
602 603 604 605
        }
      ),
      const FakeCommand(
        command: <String>[
606
          'bin/cache/dart-sdk/bin/pub',
607 608 609 610 611 612 613
          '--verbosity=warning',
          'get',
          '--no-precompile',
        ],
      ),
      FakeCommand(
        command: const <String>[
614
          'bin/cache/dart-sdk/bin/pub',
615 616 617 618 619
          '--verbosity=warning',
          'get',
          '--no-precompile',
        ],
        onRun: () {
620
          fileSystem.file('pubspec.yaml')
621
            .setLastModifiedSync(DateTime(2002));
622 623
        }
      ),
624 625
      const FakeCommand(
        command: <String>[
626
          'bin/cache/dart-sdk/bin/pub',
627 628 629 630 631
          '--verbosity=warning',
          'get',
          '--no-precompile',
        ],
      ),
632
    ]);
633 634 635 636 637 638
    final Pub pub = Pub(
      usage: MockUsage(),
      fileSystem: fileSystem,
      logger: logger,
      processManager: processManager,
      platform: FakePlatform(
639 640 641
        operatingSystem: 'linux', // so that the command executed is consistent
        environment: <String, String>{},
      ),
642 643 644
      botDetector: const BotDetectorAlwaysNo()
    );

645
    fileSystem.file('version').createSync();
646 647 648 649 650 651 652
    // the good scenario: .packages is old, pub updates the file.
    fileSystem.file('.dart_tool/package_config.json')
      ..createSync(recursive: true)
      ..setLastModifiedSync(DateTime(2000));
    fileSystem.file('pubspec.yaml')
      ..createSync()
      ..setLastModifiedSync(DateTime(2001));
653
    await pub.get(context: PubContext.flutterTests); // pub sets date of .packages to 2002
654 655 656 657 658 659 660 661 662 663 664

    expect(logger.statusText, 'Running "flutter pub get" in /...\n');
    expect(logger.errorText, isEmpty);
    expect(fileSystem.file('pubspec.yaml').lastModifiedSync(), DateTime(2001)); // because nothing should touch it
    logger.clear();

    // bad scenario 1: pub doesn't update file; doesn't matter, because we do instead
    fileSystem.file('.dart_tool/package_config.json')
      .setLastModifiedSync(DateTime(2000));
    fileSystem.file('pubspec.yaml')
      .setLastModifiedSync(DateTime(2001));
665
    await pub.get(context: PubContext.flutterTests); // pub does nothing
666 667 668 669 670

    expect(logger.statusText, 'Running "flutter pub get" in /...\n');
    expect(logger.errorText, isEmpty);
    expect(fileSystem.file('pubspec.yaml').lastModifiedSync(), DateTime(2001)); // because nothing should touch it
    logger.clear();
671 672 673 674 675
  });
}

class BotDetectorAlwaysNo implements BotDetector {
  const BotDetectorAlwaysNo();
676

677
  @override
678
  Future<bool> get isRunningOnBot async => false;
679 680
}

681
typedef StartCallback = void Function(List<dynamic> command);
682 683

class MockProcessManager implements ProcessManager {
684
  MockProcessManager(this.fakeExitCode, {
685
    this.stdout = '',
686 687
    this.stderr = '',
  });
688 689

  final int fakeExitCode;
690
  final String stdout;
691
  final String stderr;
692

Josh Soref's avatar
Josh Soref committed
693
  String lastPubEnvironment;
694
  String lastPubCache;
695

696 697 698 699 700
  @override
  Future<Process> start(
    List<dynamic> command, {
    String workingDirectory,
    Map<String, String> environment,
701 702
    bool includeParentEnvironment = true,
    bool runInShell = false,
703
    ProcessStartMode mode = ProcessStartMode.normal,
704
  }) {
Josh Soref's avatar
Josh Soref committed
705
    lastPubEnvironment = environment['PUB_ENVIRONMENT'];
706
    lastPubCache = environment['PUB_CACHE'];
707 708
    return Future<Process>.value(mocks.createMockProcess(
      exitCode: fakeExitCode,
709
      stdout: stdout,
710 711
      stderr: stderr,
    ));
712 713 714 715 716 717
  }

  @override
  dynamic noSuchMethod(Invocation invocation) => null;
}

718
class MockFileSystem extends ForwardingFileSystem {
719
  MockFileSystem() : super(MemoryFileSystem.test());
720

721 722
  @override
  File file(dynamic path) {
723
    return MockFile();
724
  }
725 726 727

  @override
  Directory directory(dynamic path) {
728
    return MockDirectory(path as String);
729
  }
730 731 732 733
}

class MockFile implements File {
  @override
734
  Future<RandomAccessFile> open({ FileMode mode = FileMode.read }) async {
735
    return MockRandomAccessFile();
736 737 738 739 740 741
  }

  @override
  bool existsSync() => true;

  @override
742
  DateTime lastModifiedSync() => DateTime(0);
743 744 745 746 747

  @override
  dynamic noSuchMethod(Invocation invocation) => null;
}

748 749 750 751 752 753
class MockDirectory implements Directory {
  MockDirectory(this.path);

  @override
  final String path;

754 755
  static bool findCache = false;

756 757 758 759 760 761 762
  @override
  bool existsSync() => findCache && path.endsWith('.pub-cache');

  @override
  dynamic noSuchMethod(Invocation invocation) => null;
}

763
class MockRandomAccessFile extends Mock implements RandomAccessFile {}
764 765

class MockUsage extends Mock implements Usage {}