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

import 'dart:async';

import 'package:args/command_runner.dart';
8
import 'package:flutter_tools/src/base/bot_detector.dart';
9
import 'package:flutter_tools/src/base/file_system.dart' hide IOSink;
10
import 'package:flutter_tools/src/base/io.dart';
11
import 'package:flutter_tools/src/cache.dart';
12
import 'package:flutter_tools/src/commands/packages.dart';
13
import 'package:flutter_tools/src/dart/pub.dart';
14
import 'package:flutter_tools/src/reporting/reporting.dart';
15
import 'package:process/process.dart';
16
import 'package:flutter_tools/src/globals.dart' as globals;
17

18 19
import '../../src/common.dart';
import '../../src/context.dart';
Dan Field's avatar
Dan Field committed
20
import '../../src/mocks.dart' show MockProcessManager, MockStdio, PromptingProcess, AlwaysTrueBotDetector, AlwaysFalseBotDetector;
21
import '../../src/testbed.dart';
22 23

void main() {
24 25
  Cache.disableLocking();
  group('packages get/upgrade', () {
26
    Directory tempDir;
27 28

    setUp(() {
29
      tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
30 31 32
    });

    tearDown(() {
33
      tryToDelete(tempDir);
34 35
    });

36
    Future<String> createProjectWithPlugin(String plugin, { List<String> arguments }) async {
37
      final String projectPath = await createProject(tempDir, arguments: arguments);
38
      final File pubspec = globals.fs.file(globals.fs.path.join(projectPath, 'pubspec.yaml'));
39 40 41 42 43 44 45 46
      String content = await pubspec.readAsString();
      content = content.replaceFirst(
        '\ndependencies:\n',
        '\ndependencies:\n  $plugin:\n',
      );
      await pubspec.writeAsString(content, flush: true);
      return projectPath;
    }
47

48
    Future<PackagesCommand> runCommandIn(String projectPath, String verb, { List<String> args }) async {
49
      final PackagesCommand command = PackagesCommand();
50
      final CommandRunner<void> runner = createTestCommandRunner(command);
51 52 53 54 55 56
      await runner.run(<String>[
        'packages',
        verb,
        ...?args,
        projectPath,
      ]);
57
      return command;
58 59
    }

60
    void expectExists(String projectPath, String relPath) {
61
      expect(
62
        globals.fs.isFileSync(globals.fs.path.join(projectPath, relPath)),
63 64 65 66 67 68 69 70
        true,
        reason: '$projectPath/$relPath should exist, but does not',
      );
    }

    void expectContains(String projectPath, String relPath, String substring) {
      expectExists(projectPath, relPath);
      expect(
71
        globals.fs.file(globals.fs.path.join(projectPath, relPath)).readAsStringSync(),
72
        contains(substring),
73
        reason: '$projectPath/$relPath has unexpected content',
74 75 76 77 78
      );
    }

    void expectNotExists(String projectPath, String relPath) {
      expect(
79
        globals.fs.isFileSync(globals.fs.path.join(projectPath, relPath)),
80 81 82 83 84 85 86 87
        false,
        reason: '$projectPath/$relPath should not exist, but does',
      );
    }

    void expectNotContains(String projectPath, String relPath, String substring) {
      expectExists(projectPath, relPath);
      expect(
88
        globals.fs.file(globals.fs.path.join(projectPath, relPath)).readAsStringSync(),
89 90 91 92 93
        isNot(contains(substring)),
        reason: '$projectPath/$relPath has unexpected content',
      );
    }

94
    const List<String> pubOutput = <String>[
95 96 97 98
      '.packages',
      'pubspec.lock',
    ];

99
    const List<String> pluginRegistrants = <String>[
100 101 102 103 104
      'ios/Runner/GeneratedPluginRegistrant.h',
      'ios/Runner/GeneratedPluginRegistrant.m',
      'android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java',
    ];

105
    const List<String> modulePluginRegistrants = <String>[
106 107 108 109 110
      '.ios/Flutter/FlutterPluginRegistrant/Classes/GeneratedPluginRegistrant.h',
      '.ios/Flutter/FlutterPluginRegistrant/Classes/GeneratedPluginRegistrant.m',
      '.android/Flutter/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java',
    ];

111
    const List<String> pluginWitnesses = <String>[
112 113 114 115
      '.flutter-plugins',
      'ios/Podfile',
    ];

116
    const List<String> modulePluginWitnesses = <String>[
117 118 119 120
      '.flutter-plugins',
      '.ios/Podfile',
    ];

121
    const Map<String, String> pluginContentWitnesses = <String, String>{
122 123 124 125
      'ios/Flutter/Debug.xcconfig': '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"',
      'ios/Flutter/Release.xcconfig': '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"',
    };

126
    const Map<String, String> modulePluginContentWitnesses = <String, String>{
127 128 129 130
      '.ios/Config/Debug.xcconfig': '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"',
      '.ios/Config/Release.xcconfig': '#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"',
    };

131
    void expectDependenciesResolved(String projectPath) {
132
      for (final String output in pubOutput) {
133 134 135 136 137
        expectExists(projectPath, output);
      }
    }

    void expectZeroPluginsInjected(String projectPath) {
138
      for (final String registrant in modulePluginRegistrants) {
139 140 141 142 143
        expectExists(projectPath, registrant);
      }
      for (final String witness in pluginWitnesses) {
        expectNotExists(projectPath, witness);
      }
144
      modulePluginContentWitnesses.forEach((String witness, String content) {
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        expectNotContains(projectPath, witness, content);
      });
    }

    void expectPluginInjected(String projectPath) {
      for (final String registrant in pluginRegistrants) {
        expectExists(projectPath, registrant);
      }
      for (final String witness in pluginWitnesses) {
        expectExists(projectPath, witness);
      }
      pluginContentWitnesses.forEach((String witness, String content) {
        expectContains(projectPath, witness, content);
      });
    }

161 162
    void expectModulePluginInjected(String projectPath) {
      for (final String registrant in modulePluginRegistrants) {
163 164
        expectExists(projectPath, registrant);
      }
165
      for (final String witness in modulePluginWitnesses) {
166 167
        expectExists(projectPath, witness);
      }
168
      modulePluginContentWitnesses.forEach((String witness, String content) {
169 170 171 172
        expectContains(projectPath, witness, content);
      });
    }

173 174 175
    void removeGeneratedFiles(String projectPath) {
      final Iterable<String> allFiles = <List<String>>[
        pubOutput,
176
        modulePluginRegistrants,
177
        pluginWitnesses,
178
      ].expand<String>((List<String> list) => list);
179
      for (final String path in allFiles) {
180
        final File file = globals.fs.file(globals.fs.path.join(projectPath, path));
181
        if (file.existsSync()) {
182
          file.deleteSync();
183
        }
184
      }
185 186
    }

187
    testUsingContext('get fetches packages', () async {
188 189
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub', '--template=module']);
190 191 192 193 194 195
      removeGeneratedFiles(projectPath);

      await runCommandIn(projectPath, 'get');

      expectDependenciesResolved(projectPath);
      expectZeroPluginsInjected(projectPath);
196
    }, overrides: <Type, Generator>{
197 198 199 200 201 202 203 204
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
205
    });
206

207
    testUsingContext('get --offline fetches packages', () async {
208 209
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub', '--template=module']);
210 211 212 213 214 215
      removeGeneratedFiles(projectPath);

      await runCommandIn(projectPath, 'get', args: <String>['--offline']);

      expectDependenciesResolved(projectPath);
      expectZeroPluginsInjected(projectPath);
216
    }, overrides: <Type, Generator>{
217 218 219 220 221 222 223 224
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
225
    });
226

227 228 229 230 231 232 233 234
    testUsingContext('set the number of plugins as usage value', () async {
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub', '--template=module']);
      removeGeneratedFiles(projectPath);

      final PackagesCommand command = await runCommandIn(projectPath, 'get');
      final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;

235 236
      expect(await getCommand.usageValues,
             containsPair(CustomDimensions.commandPackagesNumberPlugins, '0'));
237
    }, overrides: <Type, Generator>{
238 239 240 241 242 243 244 245
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
246
    });
247 248 249 250 251 252 253 254 255

    testUsingContext('indicate that the project is not a module in usage value', () async {
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub']);
      removeGeneratedFiles(projectPath);

      final PackagesCommand command = await runCommandIn(projectPath, 'get');
      final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;

256 257
      expect(await getCommand.usageValues,
             containsPair(CustomDimensions.commandPackagesProjectModule, 'false'));
258
    }, overrides: <Type, Generator>{
259 260 261 262 263 264 265 266
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
267
    });
268 269 270 271 272 273 274 275 276

    testUsingContext('indicate that the project is a module in usage value', () async {
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub', '--template=module']);
      removeGeneratedFiles(projectPath);

      final PackagesCommand command = await runCommandIn(projectPath, 'get');
      final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;

277 278
      expect(await getCommand.usageValues,
             containsPair(CustomDimensions.commandPackagesProjectModule, 'true'));
279
    }, overrides: <Type, Generator>{
280 281 282 283 284 285 286 287
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
288
    });
289

290 291 292 293 294
    testUsingContext('indicate that Android project reports v1 in usage value', () async {
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub']);
      removeGeneratedFiles(projectPath);

295 296 297 298 299 300 301 302 303
      final File androidManifest = globals.fs.file(globals.fs.path.join(
        projectPath,
        'android/app/src/main/AndroidManifest.xml',
      ));
      final String updatedAndroidManifestString =
          androidManifest.readAsStringSync().replaceAll('android:value="2"', 'android:value="1"');

      androidManifest.writeAsStringSync(updatedAndroidManifestString);

304 305 306 307 308 309
      final PackagesCommand command = await runCommandIn(projectPath, 'get');
      final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;

      expect(await getCommand.usageValues,
             containsPair(CustomDimensions.commandPackagesAndroidEmbeddingVersion, 'v1'));
    }, overrides: <Type, Generator>{
310 311 312 313 314 315 316 317
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
318 319 320 321 322 323 324 325 326 327 328 329 330
    });

    testUsingContext('indicate that Android project reports v2 in usage value', () async {
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub']);
      removeGeneratedFiles(projectPath);

      final PackagesCommand command = await runCommandIn(projectPath, 'get');
      final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;

      expect(await getCommand.usageValues,
             containsPair(CustomDimensions.commandPackagesAndroidEmbeddingVersion, 'v2'));
    }, overrides: <Type, Generator>{
331 332 333 334 335 336 337 338
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
339 340
    });

341
    testUsingContext('upgrade fetches packages', () async {
342 343
      final String projectPath = await createProject(tempDir,
        arguments: <String>['--no-pub', '--template=module']);
344 345 346 347 348 349
      removeGeneratedFiles(projectPath);

      await runCommandIn(projectPath, 'upgrade');

      expectDependenciesResolved(projectPath);
      expectZeroPluginsInjected(projectPath);
350
    }, overrides: <Type, Generator>{
351 352 353 354 355 356 357 358
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
359
    });
360 361

    testUsingContext('get fetches packages and injects plugin', () async {
362 363
      final String projectPath = await createProjectWithPlugin('path_provider',
        arguments: <String>['--no-pub', '--template=module']);
364 365 366
      removeGeneratedFiles(projectPath);

      await runCommandIn(projectPath, 'get');
367

368
      expectDependenciesResolved(projectPath);
369
      expectModulePluginInjected(projectPath);
370
    }, overrides: <Type, Generator>{
371 372 373 374 375 376 377 378
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
379
    });
380

381 382
    testUsingContext('get fetches packages and injects plugin in plugin project', () async {
      final String projectPath = await createProject(
383
        tempDir,
384
        arguments: <String>['--template=plugin', '--no-pub', '--platforms=ios,android'],
385
      );
386
      final String exampleProjectPath = globals.fs.path.join(projectPath, 'example');
387 388 389 390 391 392 393 394 395 396 397
      removeGeneratedFiles(projectPath);
      removeGeneratedFiles(exampleProjectPath);

      await runCommandIn(projectPath, 'get');

      expectDependenciesResolved(projectPath);

      await runCommandIn(exampleProjectPath, 'get');

      expectDependenciesResolved(exampleProjectPath);
      expectPluginInjected(exampleProjectPath);
398
    }, overrides: <Type, Generator>{
399 400 401 402 403 404 405 406
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
407
    });
408
  });
409 410

  group('packages test/pub', () {
411 412 413 414
    MockProcessManager mockProcessManager;
    MockStdio mockStdio;

    setUp(() {
415
      mockProcessManager = MockProcessManager();
kwkr's avatar
kwkr committed
416
      mockStdio = MockStdio()..stdout.terminalColumns = 80;
417 418
    });

419
    testUsingContext('test without bot', () async {
420
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'test']);
421 422 423 424 425 426 427 428 429
      final List<String> commands = mockProcessManager.commands;
      expect(commands, hasLength(3));
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
      expect(commands[1], 'run');
      expect(commands[2], 'test');
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysFalseBotDetector(),
430 431 432 433 434 435 436 437
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
438 439 440
    });

    testUsingContext('test with bot', () async {
441
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'test']);
442
      final List<String> commands = mockProcessManager.commands;
443
      expect(commands, hasLength(4));
444
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
445 446 447
      expect(commands[1], '--trace');
      expect(commands[2], 'run');
      expect(commands[3], 'test');
448
    }, overrides: <Type, Generator>{
449 450
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
451
      BotDetector: () => const AlwaysTrueBotDetector(),
452 453 454 455 456 457 458 459
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
460
    });
461

462
    testUsingContext('run', () async {
463
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', '--verbose', 'pub', 'run', '--foo', 'bar']);
464 465 466 467 468 469 470 471 472
      final List<String> commands = mockProcessManager.commands;
      expect(commands, hasLength(4));
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
      expect(commands[1], 'run');
      expect(commands[2], '--foo');
      expect(commands[3], 'bar');
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
473 474 475 476 477 478 479 480
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
481 482
    });

483
    testUsingContext('pub publish', () async {
484
      final PromptingProcess process = PromptingProcess();
485
      mockProcessManager.processFactory = (List<String> commands) => process;
486
      final Future<void> runPackages = createTestCommandRunner(PackagesCommand()).run(<String>['pub', 'publish']);
487 488
      final Future<void> runPrompt = process.showPrompt('Proceed (y/n)? ', <String>['hello', 'world']);
      final Future<void> simulateUserInput = Future<void>(() {
489 490
        mockStdio.simulateStdin('y');
      });
491
      await Future.wait<void>(<Future<void>>[runPackages, runPrompt, simulateUserInput]);
492 493 494 495 496 497 498 499 500 501
      final List<String> commands = mockProcessManager.commands;
      expect(commands, hasLength(2));
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
      expect(commands[1], 'publish');
      final List<String> stdout = mockStdio.writtenToStdout;
      expect(stdout, hasLength(4));
      expect(stdout.sublist(0, 2), contains('Proceed (y/n)? '));
      expect(stdout.sublist(0, 2), contains('y\n'));
      expect(stdout[2], 'hello\n');
      expect(stdout[3], 'world\n');
502
    }, overrides: <Type, Generator>{
503 504
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
505 506 507 508 509 510 511 512
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
513
    });
514

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
    testUsingContext('pub publish input fails', () async {
      final PromptingProcess process = PromptingProcess(stdinError: true);
      mockProcessManager.processFactory = (List<String> commands) => process;
      final Future<void> runPackages = createTestCommandRunner(PackagesCommand()).run(<String>['pub', 'publish']);
      final Future<void> runPrompt = process.showPrompt('Proceed (y/n)? ', <String>['hello', 'world']);
      final Future<void> simulateUserInput = Future<void>(() {
        mockStdio.simulateStdin('y');
      });
      await Future.wait<void>(<Future<void>>[runPackages, runPrompt, simulateUserInput]);
      final List<String> commands = mockProcessManager.commands;
      expect(commands, hasLength(2));
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
      expect(commands[1], 'publish');
      // We get a trace message about the write to stdin failing.
      expect(testLogger.traceText, contains('Echoing stdin to the pub subprocess failed'));
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
533 534 535 536 537 538 539 540
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
541 542
    });

543
    testUsingContext('publish', () async {
544
      await createTestCommandRunner(PackagesCommand()).run(<String>['pub', 'publish']);
545
      final List<String> commands = mockProcessManager.commands;
546
      expect(commands, hasLength(2));
547
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
548 549 550 551 552
      expect(commands[1], 'publish');
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
553 554 555 556 557 558 559 560
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
561 562 563 564 565 566 567 568
    });

    testUsingContext('packages publish', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'pub', 'publish']);
      final List<String> commands = mockProcessManager.commands;
      expect(commands, hasLength(2));
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
      expect(commands[1], 'publish');
569 570 571 572
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
573 574 575 576 577 578 579 580
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
581 582 583 584 585
    });

    testUsingContext('deps', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'deps']);
      final List<String> commands = mockProcessManager.commands;
586
      expect(commands, hasLength(2));
587
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
588
      expect(commands[1], 'deps');
589 590 591 592
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
593 594 595 596 597 598 599 600
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
601 602 603 604 605
    });

    testUsingContext('cache', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'cache']);
      final List<String> commands = mockProcessManager.commands;
606
      expect(commands, hasLength(2));
607
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
608
      expect(commands[1], 'cache');
609 610 611 612
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
613 614 615 616 617 618 619 620
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
621 622 623 624 625
    });

    testUsingContext('version', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'version']);
      final List<String> commands = mockProcessManager.commands;
626
      expect(commands, hasLength(2));
627
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
628
      expect(commands[1], 'version');
629 630 631 632
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
633 634 635 636 637 638 639 640
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
641 642 643 644 645
    });

    testUsingContext('uploader', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'uploader']);
      final List<String> commands = mockProcessManager.commands;
646
      expect(commands, hasLength(2));
647
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
648
      expect(commands[1], 'uploader');
649 650 651 652
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
653 654 655 656 657 658 659 660
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
661 662 663 664 665
    });

    testUsingContext('global', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'global', 'list']);
      final List<String> commands = mockProcessManager.commands;
666
      expect(commands, hasLength(3));
667
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
668 669
      expect(commands[1], 'global');
      expect(commands[2], 'list');
670 671 672 673
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
674 675 676 677 678 679 680 681
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
682
    });
683 684 685 686 687 688 689 690 691 692 693

    testUsingContext('outdated', () async {
      await createTestCommandRunner(PackagesCommand()).run(<String>['packages', 'outdated']);
      final List<String> commands = mockProcessManager.commands;
      expect(commands, hasLength(2));
      expect(commands[0], matches(r'dart-sdk[\\/]bin[\\/]pub'));
      expect(commands[1], 'outdated');
    }, overrides: <Type, Generator>{
      ProcessManager: () => mockProcessManager,
      Stdio: () => mockStdio,
      BotDetector: () => const AlwaysTrueBotDetector(),
694 695 696 697 698 699 700 701
      Pub: () => Pub(
        fileSystem: globals.fs,
        logger: globals.logger,
        processManager: globals.processManager,
        usage: globals.flutterUsage,
        botDetector: globals.botDetector,
        platform: globals.platform,
      ),
702
    });
703 704
  });
}