From 8df0d68633eff47099e60e7f3d3991c235982455 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 22 Apr 2020 16:34:12 -0700 Subject: [PATCH] [flutter_tools] remove globals from pub (#55412) --- .../lib/src/commands/packages.dart | 6 +- .../flutter_tools/lib/src/context_runner.dart | 9 +- packages/flutter_tools/lib/src/dart/pub.dart | 280 +++++++------ .../lib/src/reporting/events.dart | 3 +- .../hermetic/analyze_continuously_test.dart | 21 +- .../commands.shard/permeable/create_test.dart | 171 +++++++- .../permeable/packages_test.dart | 207 ++++++++-- .../test/general.shard/dart/pub_get_test.dart | 380 ++++++++++-------- .../flutter_tools/test/src/throwing_pub.dart | 4 +- 9 files changed, 735 insertions(+), 346 deletions(-) diff --git a/packages/flutter_tools/lib/src/commands/packages.dart b/packages/flutter_tools/lib/src/commands/packages.dart index 38ef7e496a..7ff7e0b44c 100644 --- a/packages/flutter_tools/lib/src/commands/packages.dart +++ b/packages/flutter_tools/lib/src/commands/packages.dart @@ -205,7 +205,7 @@ class PackagesPublishCommand extends FlutterCommand { if (boolArg('force')) '--force', ]; Cache.releaseLockEarly(); - await pub.interactively(['publish', ...args]); + await pub.interactively(['publish', ...args], stdio: globals.stdio); return FlutterCommandResult.success(); } } @@ -236,7 +236,7 @@ class PackagesForwardCommand extends FlutterCommand { @override Future runCommand() async { Cache.releaseLockEarly(); - await pub.interactively([_commandName, ...argResults.rest]); + await pub.interactively([_commandName, ...argResults.rest], stdio: globals.stdio); return FlutterCommandResult.success(); } @@ -264,7 +264,7 @@ class PackagesPassthroughCommand extends FlutterCommand { @override Future runCommand() async { Cache.releaseLockEarly(); - await pub.interactively(argResults.rest); + await pub.interactively(argResults.rest, stdio: globals.stdio); return FlutterCommandResult.success(); } } diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index bfcee3e231..bc6d94e5a4 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart @@ -167,7 +167,14 @@ Future runInContext( processManager: globals.processManager, logger: globals.logger, ), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + botDetector: globals.botDetector, + platform: globals.platform, + usage: globals.flutterUsage, + ), ShutdownHooks: () => ShutdownHooks(logger: globals.logger), Signals: () => Signals(), Stdio: () => Stdio(), diff --git a/packages/flutter_tools/lib/src/dart/pub.dart b/packages/flutter_tools/lib/src/dart/pub.dart index aa5e8732e4..366334f5b5 100644 --- a/packages/flutter_tools/lib/src/dart/pub.dart +++ b/packages/flutter_tools/lib/src/dart/pub.dart @@ -5,7 +5,10 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:platform/platform.dart'; +import 'package:process/process.dart'; +import '../base/bot_detector.dart'; import '../base/common.dart'; import '../base/context.dart'; import '../base/file_system.dart'; @@ -13,14 +16,19 @@ import '../base/io.dart' as io; import '../base/logger.dart'; import '../base/process.dart'; import '../cache.dart'; -import '../globals.dart' as globals; import '../reporting/reporting.dart'; -import '../runner/flutter_command.dart'; -import 'sdk.dart'; /// The [Pub] instance. Pub get pub => context.get(); +/// The console environment key used by the pub tool. +const String _kPubEnvironmentKey = 'PUB_ENVIRONMENT'; + +/// The console environment key used by the pub tool to find the cache directory. +const String _kPubCacheEnvironmentKey = 'PUB_CACHE'; + +typedef MessageFilter = String Function(String message); + /// Represents Flutter-specific data that is added to the `PUB_ENVIRONMENT` /// environment variable and allows understanding the type of requests made to /// the package site on Flutter's behalf. @@ -47,7 +55,6 @@ class PubContext { static final PubContext pubUpgrade = PubContext._(['upgrade']); static final PubContext pubForward = PubContext._(['forward']); static final PubContext runTest = PubContext._(['run_test']); - static final PubContext flutterTests = PubContext._(['flutter_tests']); static final PubContext updatePackages = PubContext._(['update_packages']); @@ -63,26 +70,17 @@ class PubContext { } } -bool _shouldRunPubGet({ File pubSpecYaml, File dotPackages }) { - if (!dotPackages.existsSync()) { - return true; - } - final DateTime dotPackagesLastModified = dotPackages.lastModifiedSync(); - if (pubSpecYaml.lastModifiedSync().isAfter(dotPackagesLastModified)) { - return true; - } - final File flutterToolsStamp = globals.cache.getStampFileFor('flutter_tools'); - if (flutterToolsStamp.existsSync() && - flutterToolsStamp.lastModifiedSync().isAfter(dotPackagesLastModified)) { - return true; - } - return false; -} - /// A handle for interacting with the pub tool. abstract class Pub { /// Create a default [Pub] instance. - const factory Pub() = _DefaultPub; + factory Pub({ + @required FileSystem fileSystem, + @required Logger logger, + @required ProcessManager processManager, + @required Platform platform, + @required BotDetector botDetector, + @required Usage usage, + }) = _DefaultPub; /// Runs `pub get`. /// @@ -129,11 +127,34 @@ abstract class Pub { Future interactively( List arguments, { String directory, + @required io.Stdio stdio, }); } class _DefaultPub implements Pub { - const _DefaultPub(); + _DefaultPub({ + @required FileSystem fileSystem, + @required Logger logger, + @required ProcessManager processManager, + @required Platform platform, + @required BotDetector botDetector, + @required Usage usage, + }) : _fileSystem = fileSystem, + _logger = logger, + _platform = platform, + _botDetector = botDetector, + _usage = usage, + _processUtils = ProcessUtils( + logger: logger, + processManager: processManager, + ); + + final FileSystem _fileSystem; + final Logger _logger; + final ProcessUtils _processUtils; + final Platform _platform; + final BotDetector _botDetector; + final Usage _usage; @override Future get({ @@ -146,10 +167,12 @@ class _DefaultPub implements Pub { bool skipPubspecYamlCheck = false, String flutterRootOverride, }) async { - directory ??= globals.fs.currentDirectory.path; + directory ??= _fileSystem.currentDirectory.path; - final File pubSpecYaml = globals.fs.file(globals.fs.path.join(directory, 'pubspec.yaml')); - final File dotPackages = globals.fs.file(globals.fs.path.join(directory, '.packages')); + final File pubSpecYaml = _fileSystem.file( + _fileSystem.path.join(directory, 'pubspec.yaml')); + final File packageConfigFile = _fileSystem.file( + _fileSystem.path.join(directory, '.dart_tool', 'package_config.json')); if (!skipPubspecYamlCheck && !pubSpecYaml.existsSync()) { if (!skipIfAbsent) { @@ -160,24 +183,33 @@ class _DefaultPub implements Pub { final DateTime originalPubspecYamlModificationTime = pubSpecYaml.lastModifiedSync(); - if (!checkLastModified || _shouldRunPubGet(pubSpecYaml: pubSpecYaml, dotPackages: dotPackages)) { + if (!checkLastModified || _shouldRunPubGet( + pubSpecYaml: pubSpecYaml, + packageConfigFile: packageConfigFile, + )) { final String command = upgrade ? 'upgrade' : 'get'; - final Status status = globals.logger.startProgress( - 'Running "flutter pub $command" in ${globals.fs.path.basename(directory)}...', - timeout: timeoutConfiguration.slowOperation, + final Status status = _logger.startProgress( + 'Running "flutter pub $command" in ${_fileSystem.path.basename(directory)}...', + timeout: const TimeoutConfiguration().slowOperation, ); - final bool verbose = FlutterCommand.current != null && FlutterCommand.current.globalResults['verbose'] as bool; + final bool verbose = _logger.isVerbose; final List args = [ - if (verbose) '--verbose' else '--verbosity=warning', - ...[command, '--no-precompile'], - if (offline) '--offline', + if (verbose) + '--verbose' + else + '--verbosity=warning', + ...[ + command, + '--no-precompile', + ], + if (offline) + '--offline', ]; try { await batch( args, context: context, directory: directory, - filter: _filterOverrideWarnings, failureMessage: 'pub $command failed', retry: true, flutterRootOverride: flutterRootOverride, @@ -190,11 +222,13 @@ class _DefaultPub implements Pub { } } - if (!dotPackages.existsSync()) { + if (!packageConfigFile.existsSync()) { throwToolExit('$directory: pub did not create .packages file.'); } if (pubSpecYaml.lastModifiedSync() != originalPubspecYamlModificationTime) { - throwToolExit('$directory: unexpected concurrent modification of pubspec.yaml while running pub.'); + throwToolExit( + '$directory: unexpected concurrent modification of ' + 'pubspec.yaml while running pub.'); } // We don't check if dotPackages was actually modified, because as far as we can tell sometimes // pub will decide it does not need to actually modify it. @@ -202,29 +236,28 @@ class _DefaultPub implements Pub { // file to be more recently modified. final DateTime now = DateTime.now(); if (now.isBefore(originalPubspecYamlModificationTime)) { - globals.printError( - 'Warning: File "${globals.fs.path.absolute(pubSpecYaml.path)}" was created in the future. ' + _logger.printError( + 'Warning: File "${_fileSystem.path.absolute(pubSpecYaml.path)}" was created in the future. ' 'Optimizations that rely on comparing time stamps will be unreliable. Check your ' 'system clock for accuracy.\n' 'The timestamp was: $originalPubspecYamlModificationTime\n' 'The time now is: $now' ); } else { - dotPackages.setLastModifiedSync(now); - final DateTime newDotPackagesTimestamp = dotPackages.lastModifiedSync(); + packageConfigFile.setLastModifiedSync(now); + final DateTime newDotPackagesTimestamp = packageConfigFile.lastModifiedSync(); if (newDotPackagesTimestamp.isBefore(originalPubspecYamlModificationTime)) { - globals.printError( - 'Warning: Failed to set timestamp of "${globals.fs.path.absolute(dotPackages.path)}". ' + _logger.printError( + 'Warning: Failed to set timestamp of "${_fileSystem.path.absolute(packageConfigFile.path)}". ' 'Tried to set timestamp to $now, but new timestamp is $newDotPackagesTimestamp.' ); if (newDotPackagesTimestamp.isAfter(now)) { - globals.printError('Maybe the file was concurrently modified?'); + _logger.printError('Maybe the file was concurrently modified?'); } } } } - @override Future batch( List arguments, { @@ -236,7 +269,7 @@ class _DefaultPub implements Pub { bool showTraceForErrors, String flutterRootOverride, }) async { - showTraceForErrors ??= await globals.isRunningOnBot; + showTraceForErrors ??= await _botDetector.isRunningOnBot; String lastPubMessage = 'no message'; bool versionSolvingFailed = false; @@ -259,7 +292,7 @@ class _DefaultPub implements Pub { int code; loop: while (true) { attempts += 1; - code = await processUtils.stream( + code = await _processUtils.stream( _pubCommand(arguments), workingDirectory: directory, mapFunction: filterWrapper, // may set versionSolvingFailed, lastPubMessage @@ -275,7 +308,10 @@ class _DefaultPub implements Pub { } assert(message != null); versionSolvingFailed = false; - globals.printStatus('$failureMessage ($message) -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...'); + _logger.printStatus( + '$failureMessage ($message) -- attempting retry $attempts in $duration ' + 'second${ duration == 1 ? "" : "s"}...', + ); await Future.delayed(Duration(seconds: duration)); if (duration < 64) { duration *= 2; @@ -292,6 +328,7 @@ class _DefaultPub implements Pub { PubResultEvent( context: context.toAnalyticsString(), result: result, + usage: _usage, ).send(); if (code != 0) { @@ -303,33 +340,34 @@ class _DefaultPub implements Pub { Future interactively( List arguments, { String directory, + @required io.Stdio stdio, }) async { Cache.releaseLockEarly(); - final io.Process process = await processUtils.start( + final io.Process process = await _processUtils.start( _pubCommand(arguments), workingDirectory: directory, environment: await _createPubEnvironment(PubContext.interactive), ); // Pipe the Flutter tool stdin to the pub stdin. - unawaited(process.stdin.addStream(globals.stdio.stdin) + unawaited(process.stdin.addStream(stdio.stdin) // If pub exits unexpectedly with an error, that will be reported below // by the tool exit after the exit code check. .catchError((dynamic err, StackTrace stack) { - globals.printTrace('Echoing stdin to the pub subprocess failed:'); - globals.printTrace('$err\n$stack'); + _logger.printTrace('Echoing stdin to the pub subprocess failed:'); + _logger.printTrace('$err\n$stack'); } )); // Pipe the pub stdout and stderr to the tool stdout and stderr. try { await Future.wait(>[ - globals.stdio.addStdoutStream(process.stdout), - globals.stdio.addStderrStream(process.stderr), + stdio.addStdoutStream(process.stdout), + stdio.addStderrStream(process.stderr), ]); } on Exception catch (err, stack) { - globals.printTrace('Echoing stdout or stderr from the pub subprocess failed:'); - globals.printTrace('$err\n$stack'); + _logger.printTrace('Echoing stdout or stderr from the pub subprocess failed:'); + _logger.printTrace('$err\n$stack'); } // Wait for pub to exit. @@ -341,81 +379,79 @@ class _DefaultPub implements Pub { /// The command used for running pub. List _pubCommand(List arguments) { - return [sdkBinaryName('pub'), ...arguments]; + // TODO(jonahwilliams): refactor to use artifacts. + final String sdkPath = _fileSystem.path.joinAll([ + Cache.flutterRoot, + 'bin', + 'cache', + 'dart-sdk', + 'bin', + if (_platform.isWindows) + 'pub.bat' + else + 'pub' + ]); + return [sdkPath, ...arguments]; } -} - -typedef MessageFilter = String Function(String message); - -/// The full environment used when running pub. -/// -/// [context] provides extra information to package server requests to -/// understand usage. -Future> _createPubEnvironment(PubContext context, [ String flutterRootOverride ]) async { - final Map environment = { - 'FLUTTER_ROOT': flutterRootOverride ?? Cache.flutterRoot, - _pubEnvironmentKey: await _getPubEnvironmentValue(context), - }; - final String pubCache = _getRootPubCacheIfAvailable(); - if (pubCache != null) { - environment[_pubCacheEnvironmentKey] = pubCache; - } - return environment; -} - -final RegExp _analyzerWarning = RegExp(r'^! \w+ [^ ]+ from path \.\./\.\./bin/cache/dart-sdk/lib/\w+$'); - -/// The console environment key used by the pub tool. -const String _pubEnvironmentKey = 'PUB_ENVIRONMENT'; - -/// The console environment key used by the pub tool to find the cache directory. -const String _pubCacheEnvironmentKey = 'PUB_CACHE'; - -/// Returns the environment value that should be used when running pub. -/// -/// Includes any existing environment variable, if one exists. -/// -/// [context] provides extra information to package server requests to -/// understand usage. -Future _getPubEnvironmentValue(PubContext pubContext) async { - // DO NOT update this function without contacting kevmoo. - // We have server-side tooling that assumes the values are consistent. - final String existing = globals.platform.environment[_pubEnvironmentKey]; - final List values = [ - if (existing != null && existing.isNotEmpty) existing, - if (await globals.isRunningOnBot) 'flutter_bot', - 'flutter_cli', - ...pubContext._values, - ]; - return values.join(':'); -} - -String _getRootPubCacheIfAvailable() { - if (globals.platform.environment.containsKey(_pubCacheEnvironmentKey)) { - return globals.platform.environment[_pubCacheEnvironmentKey]; + bool _shouldRunPubGet({ @required File pubSpecYaml, @required File packageConfigFile }) { + if (!packageConfigFile.existsSync()) { + return true; + } + final DateTime dotPackagesLastModified = packageConfigFile.lastModifiedSync(); + if (pubSpecYaml.lastModifiedSync().isAfter(dotPackagesLastModified)) { + return true; + } + return false; } - final String cachePath = globals.fs.path.join(Cache.flutterRoot, '.pub-cache'); - if (globals.fs.directory(cachePath).existsSync()) { - globals.printTrace('Using $cachePath for the pub cache.'); - return cachePath; + // Returns the environment value that should be used when running pub. + // + // Includes any existing environment variable, if one exists. + // + // [context] provides extra information to package server requests to + // understand usage. + Future _getPubEnvironmentValue(PubContext pubContext) async { + // DO NOT update this function without contacting kevmoo. + // We have server-side tooling that assumes the values are consistent. + final String existing = _platform.environment[_kPubEnvironmentKey]; + final List values = [ + if (existing != null && existing.isNotEmpty) existing, + if (await _botDetector.isRunningOnBot) 'flutter_bot', + 'flutter_cli', + ...pubContext._values, + ]; + return values.join(':'); } - // Use pub's default location by returning null. - return null; -} + String _getRootPubCacheIfAvailable() { + if (_platform.environment.containsKey(_kPubCacheEnvironmentKey)) { + return _platform.environment[_kPubCacheEnvironmentKey]; + } -String _filterOverrideWarnings(String message) { - // This function filters out these three messages: - // Warning: You are using these overridden dependencies: - // ! analyzer 0.29.0-alpha.0 from path ../../bin/cache/dart-sdk/lib/analyzer - // ! front_end 0.1.0-alpha.0 from path ../../bin/cache/dart-sdk/lib/front_end - if (message == 'Warning: You are using these overridden dependencies:') { + final String cachePath = _fileSystem.path.join(Cache.flutterRoot, '.pub-cache'); + if (_fileSystem.directory(cachePath).existsSync()) { + _logger.printTrace('Using $cachePath for the pub cache.'); + return cachePath; + } + + // Use pub's default location by returning null. return null; } - if (message.contains(_analyzerWarning)) { - return null; + + /// The full environment used when running pub. + /// + /// [context] provides extra information to package server requests to + /// understand usage. + Future> _createPubEnvironment(PubContext context, [ String flutterRootOverride ]) async { + final Map environment = { + 'FLUTTER_ROOT': flutterRootOverride ?? Cache.flutterRoot, + _kPubEnvironmentKey: await _getPubEnvironmentValue(context), + }; + final String pubCache = _getRootPubCacheIfAvailable(); + if (pubCache != null) { + environment[_kPubCacheEnvironmentKey] = pubCache; + } + return environment; } - return message; } diff --git a/packages/flutter_tools/lib/src/reporting/events.dart b/packages/flutter_tools/lib/src/reporting/events.dart index 8b0274bef5..9fdf7c28ca 100644 --- a/packages/flutter_tools/lib/src/reporting/events.dart +++ b/packages/flutter_tools/lib/src/reporting/events.dart @@ -129,7 +129,8 @@ class PubResultEvent extends UsageEvent { PubResultEvent({ @required String context, @required String result, - }) : super('pub-result', context, label: result, flutterUsage: globals.flutterUsage); + @required Usage usage, + }) : super('pub-result', context, label: result, flutterUsage: usage); } /// An event that reports something about a build. diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart index bdb9f22cdc..8ca9c183ff 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart @@ -15,6 +15,7 @@ import 'package:flutter_tools/src/dart/sdk.dart'; import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; +import 'package:flutter_tools/src/globals.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; @@ -65,7 +66,15 @@ void main() { testUsingContext('AnalysisServer success', () async { _createSampleProject(tempDir); - await const Pub().get(context: PubContext.flutterTests, directory: tempDir.path); + final Pub pub = Pub( + fileSystem: fileSystem, + logger: logger, + processManager: processManager, + platform: const LocalPlatform(), + botDetector: globals.botDetector, + usage: globals.flutterUsage, + ); + await pub.get(context: PubContext.flutterTests, directory: tempDir.path); server = AnalysisServer(dartSdkPath, [tempDir.path], fileSystem: fileSystem, @@ -90,7 +99,15 @@ void main() { testUsingContext('AnalysisServer errors', () async { _createSampleProject(tempDir, brokenCode: true); - await const Pub().get(context: PubContext.flutterTests, directory: tempDir.path); + final Pub pub = Pub( + fileSystem: fileSystem, + logger: logger, + processManager: processManager, + platform: const LocalPlatform(), + usage: globals.flutterUsage, + botDetector: globals.botDetector, + ); + await pub.get(context: PubContext.flutterTests, directory: tempDir.path); server = AnalysisServer(dartSdkPath, [tempDir.path], fileSystem: fileSystem, diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 4b048aeced..7c4ebb17ec 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -86,7 +86,14 @@ void main() { ); return _runFlutterTest(projectDir); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('can create a default project if empty directory exists', () async { @@ -104,7 +111,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('creates a module project correctly', () async { @@ -126,7 +140,14 @@ void main() { ]); return _runFlutterTest(projectDir); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('cannot create a project if non-empty non-project directory exists with .metadata', () async { @@ -144,7 +165,14 @@ void main() { ]), throwsToolExit(message: 'Sorry, unable to detect the type of project to recreate')); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), ...noColorTerminalOverride, }); @@ -170,7 +198,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('detects and recreates an app project correctly', () async { @@ -195,7 +230,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('detects and recreates a plugin project correctly', () async { @@ -220,7 +262,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('detects and recreates a package project correctly', () async { @@ -251,7 +300,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('kotlin/swift legacy app project', () async { @@ -273,7 +329,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('can create a package project', () async { @@ -303,7 +366,14 @@ void main() { ); return _runFlutterTest(projectDir); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('can create a plugin project', () async { @@ -325,7 +395,14 @@ void main() { ); return _runFlutterTest(projectDir.childDirectory('example')); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('plugin example app depends on plugin', () async { @@ -344,7 +421,14 @@ void main() { final PathDependency pathDependency = pubspec.dependencies[pluginName] as PathDependency; expect(pathDependency.path, '../'); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('kotlin/swift plugin project', () async { @@ -429,7 +513,14 @@ void main() { ['lib/main.dart'], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('module project with pub', () async { @@ -464,7 +555,14 @@ void main() { '.android/Flutter/src/main/java/io/flutter/facade/Flutter.java', ]); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); @@ -1038,7 +1136,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('can re-gen module .ios/ folder, reusing custom org', () async { @@ -1055,7 +1160,14 @@ void main() { 'com.bar.foo.flutterProject', ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('can re-gen app android/ folder, reusing custom org', () async { @@ -1210,7 +1322,14 @@ void main() { ], ); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext( @@ -1227,7 +1346,14 @@ void main() { }, overrides: { ProcessManager: () => loggingProcessManager, - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }, ); @@ -1245,7 +1371,14 @@ void main() { }, overrides: { ProcessManager: () => loggingProcessManager, - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }, ); diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart index 97210055ab..edd3eb9d9f 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart @@ -195,7 +195,14 @@ void main() { expectDependenciesResolved(projectPath); expectZeroPluginsInjected(projectPath); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('get --offline fetches packages', () async { @@ -208,7 +215,14 @@ void main() { expectDependenciesResolved(projectPath); expectZeroPluginsInjected(projectPath); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('set the number of plugins as usage value', () async { @@ -222,7 +236,14 @@ void main() { expect(await getCommand.usageValues, containsPair(CustomDimensions.commandPackagesNumberPlugins, '0')); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('indicate that the project is not a module in usage value', () async { @@ -236,7 +257,14 @@ void main() { expect(await getCommand.usageValues, containsPair(CustomDimensions.commandPackagesProjectModule, 'false')); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('indicate that the project is a module in usage value', () async { @@ -250,7 +278,14 @@ void main() { expect(await getCommand.usageValues, containsPair(CustomDimensions.commandPackagesProjectModule, 'true')); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('indicate that Android project reports v1 in usage value', () async { @@ -265,7 +300,14 @@ void main() { containsPair(CustomDimensions.commandPackagesAndroidEmbeddingVersion, 'v1')); }, overrides: { FeatureFlags: () => TestFeatureFlags(isAndroidEmbeddingV2Enabled: false), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('indicate that Android project reports v2 in usage value', () async { @@ -280,7 +322,14 @@ void main() { containsPair(CustomDimensions.commandPackagesAndroidEmbeddingVersion, 'v2')); }, overrides: { FeatureFlags: () => TestFeatureFlags(isAndroidEmbeddingV2Enabled: true), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('upgrade fetches packages', () async { @@ -293,7 +342,14 @@ void main() { expectDependenciesResolved(projectPath); expectZeroPluginsInjected(projectPath); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('get fetches packages and injects plugin', () async { @@ -306,7 +362,14 @@ void main() { expectDependenciesResolved(projectPath); expectModulePluginInjected(projectPath); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('get fetches packages and injects plugin in plugin project', () async { @@ -327,7 +390,14 @@ void main() { expectDependenciesResolved(exampleProjectPath); expectPluginInjected(exampleProjectPath); }, overrides: { - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); }); @@ -351,7 +421,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysFalseBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('test with bot', () async { @@ -366,7 +443,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('run', () async { @@ -380,7 +464,14 @@ void main() { }, overrides: { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('pub publish', () async { @@ -405,7 +496,14 @@ void main() { }, overrides: { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('pub publish input fails', () async { @@ -426,7 +524,14 @@ void main() { }, overrides: { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('publish', () async { @@ -439,7 +544,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('packages publish', () async { @@ -452,7 +564,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('deps', () async { @@ -465,7 +584,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('cache', () async { @@ -478,7 +604,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('version', () async { @@ -491,7 +624,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('uploader', () async { @@ -504,7 +644,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('global', () async { @@ -518,7 +665,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); testUsingContext('outdated', () async { @@ -531,7 +685,14 @@ void main() { ProcessManager: () => mockProcessManager, Stdio: () => mockStdio, BotDetector: () => const AlwaysTrueBotDetector(), - Pub: () => const Pub(), + Pub: () => Pub( + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, + usage: globals.flutterUsage, + botDetector: globals.botDetector, + platform: globals.platform, + ), }); }); } diff --git a/packages/flutter_tools/test/general.shard/dart/pub_get_test.dart b/packages/flutter_tools/test/general.shard/dart/pub_get_test.dart index cdc7fb88ab..798e5d8c11 100644 --- a/packages/flutter_tools/test/general.shard/dart/pub_get_test.dart +++ b/packages/flutter_tools/test/general.shard/dart/pub_get_test.dart @@ -3,73 +3,81 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:collection'; import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:flutter_tools/src/base/bot_detector.dart'; -import 'package:flutter_tools/src/base/common.dart'; -import 'package:flutter_tools/src/base/context.dart'; -import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/base/io.dart'; -import 'package:flutter_tools/src/cache.dart'; -import 'package:flutter_tools/src/dart/pub.dart'; -import 'package:flutter_tools/src/reporting/reporting.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; - import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import 'package:quiver/testing/async.dart'; import 'package:platform/platform.dart'; +import 'package:flutter_tools/src/base/bot_detector.dart'; +import 'package:flutter_tools/src/base/common.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/cache.dart'; +import 'package:flutter_tools/src/dart/pub.dart'; +import 'package:flutter_tools/src/reporting/reporting.dart'; + import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart' as mocks; -import '../../src/testbed.dart'; void main() { setUpAll(() { - Cache.flutterRoot = getFlutterRoot(); + Cache.flutterRoot = ''; }); tearDown(() { MockDirectory.findCache = false; }); - testUsingContext('pub get 69', () async { + testWithoutContext('pub get 69', () async { String error; - final MockProcessManager processMock = context.get() as MockProcessManager; + 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 {}, + ), + botDetector: const BotDetectorAlwaysNo(), + ); FakeAsync().run((FakeAsync time) { expect(processMock.lastPubEnvironment, isNull); - expect(testLogger.statusText, ''); + expect(logger.statusText, ''); pub.get(context: PubContext.flutterTests, checkLastModified: false).then((void value) { error = 'test completed unexpectedly'; }, onError: (dynamic thrownError) { error = 'test failed unexpectedly: $thrownError'; }); time.elapse(const Duration(milliseconds: 500)); - expect(testLogger.statusText, + expect(logger.statusText, 'Running "flutter pub get" in /...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n', ); expect(processMock.lastPubEnvironment, contains('flutter_cli:flutter_tests')); expect(processMock.lastPubCache, isNull); time.elapse(const Duration(milliseconds: 500)); - expect(testLogger.statusText, + expect(logger.statusText, 'Running "flutter pub get" in /...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n', ); time.elapse(const Duration(seconds: 1)); - expect(testLogger.statusText, + expect(logger.statusText, 'Running "flutter pub get" in /...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n', ); time.elapse(const Duration(seconds: 100)); // from t=0 to t=100 - expect(testLogger.statusText, + expect(logger.statusText, 'Running "flutter pub get" in /...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n' @@ -80,7 +88,7 @@ void main() { 'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n', // at t=61 ); time.elapse(const Duration(seconds: 200)); // from t=0 to t=200 - expect(testLogger.statusText, + expect(logger.statusText, 'Running "flutter pub get" in /...\n' 'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n' 'pub get failed (server unavailable) -- attempting retry 2 in 2 seconds...\n' @@ -94,49 +102,51 @@ void main() { 'pub get failed (server unavailable) -- attempting retry 10 in 64 seconds...\n', // at t=167 ); }); - expect(testLogger.errorText, isEmpty); + expect(logger.errorText, isEmpty); expect(error, isNull); - }, overrides: { - FileSystem: () => MockFileSystem(), - ProcessManager: () => MockProcessManager(69), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({}), - ), - Pub: () => const Pub(), }); - testUsingContext('pub get 66 shows message from pub', () async { + testWithoutContext('pub get 66 shows message from pub', () async { + final BufferLogger logger = BufferLogger.test(); + final Pub pub = Pub( + platform: FakePlatform(environment: const {}), + fileSystem: MockFileSystem(), + logger: logger, + usage: MockUsage(), + botDetector: const BotDetectorAlwaysNo(), + processManager: MockProcessManager(66, stderr: 'err1\nerr2\nerr3\n', stdout: 'out1\nout2\nout3\n'), + ); try { await pub.get(context: PubContext.flutterTests, checkLastModified: false); throw AssertionError('pubGet did not fail'); } on ToolExit catch (error) { expect(error.message, 'pub get failed (66; err3)'); } - expect(testLogger.statusText, + expect(logger.statusText, 'Running "flutter pub get" in /...\n' 'out1\n' 'out2\n' 'out3\n' ); - expect(testLogger.errorText, + expect(logger.errorText, 'err1\n' 'err2\n' 'err3\n' ); - }, overrides: { - ProcessManager: () => MockProcessManager(66, stderr: 'err1\nerr2\nerr3\n', stdout: 'out1\nout2\nout3\n'), - FileSystem: () => MockFileSystem(), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({}), - ), - Pub: () => const Pub(), }); - testUsingContext('pub cache in root is used', () async { + testWithoutContext('pub cache in root is used', () async { String error; - - final MockProcessManager processMock = context.get() as MockProcessManager; - final MockFileSystem fsMock = context.get() as MockFileSystem; + final MockProcessManager processMock = MockProcessManager(69); + final MockFileSystem fsMock = MockFileSystem(); + final Pub pub = Pub( + platform: FakePlatform(environment: const {}), + usage: MockUsage(), + fileSystem: fsMock, + logger: BufferLogger.test(), + processManager: processMock, + botDetector: const BotDetectorAlwaysNo(), + ); FakeAsync().run((FakeAsync time) { MockDirectory.findCache = true; @@ -148,124 +158,137 @@ void main() { error = 'test failed unexpectedly: $thrownError'; }); time.elapse(const Duration(milliseconds: 500)); + expect(processMock.lastPubCache, equals(fsMock.path.join(Cache.flutterRoot, '.pub-cache'))); expect(error, isNull); }); - }, overrides: { - FileSystem: () => MockFileSystem(), - ProcessManager: () => MockProcessManager(69), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({}), - ), - Pub: () => const Pub(), }); - testUsingContext('pub cache in environment is used', () async { - String error; - - final MockProcessManager processMock = context.get() as MockProcessManager; + 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 { + 'PUB_CACHE': 'custom/pub-cache/path', + }, + ), + ); FakeAsync().run((FakeAsync time) { MockDirectory.findCache = true; expect(processMock.lastPubEnvironment, isNull); expect(processMock.lastPubCache, isNull); + + String error; pub.get(context: PubContext.flutterTests, checkLastModified: false).then((void value) { error = 'test completed unexpectedly'; }, onError: (dynamic thrownError) { error = 'test failed unexpectedly: $thrownError'; }); time.elapse(const Duration(milliseconds: 500)); + expect(processMock.lastPubCache, equals('custom/pub-cache/path')); expect(error, isNull); }); - }, overrides: { - FileSystem: () => MockFileSystem(), - ProcessManager: () => MockProcessManager(69), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({ - 'PUB_CACHE': 'custom/pub-cache/path', - }), - ), - Pub: () => const Pub(), }); - testUsingContext('analytics sent on success', () async { + testWithoutContext('analytics sent on success', () async { MockDirectory.findCache = true; + final MockUsage usage = MockUsage(); + final Pub pub = Pub( + fileSystem: MockFileSystem(), + logger: BufferLogger.test(), + processManager: MockProcessManager(0), + botDetector: const BotDetectorAlwaysNo(), + usage: usage, + platform: FakePlatform( + environment: const { + 'PUB_CACHE': 'custom/pub-cache/path', + } + ), + ); + await pub.get(context: PubContext.flutterTests, checkLastModified: false); - verify(globals.flutterUsage.sendEvent('pub-result', 'flutter-tests', label: 'success')).called(1); - }, overrides: { - FileSystem: () => MockFileSystem(), - ProcessManager: () => MockProcessManager(0), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({ - 'PUB_CACHE': 'custom/pub-cache/path', - }), - ), - Usage: () => MockUsage(), - Pub: () => const Pub(), + + verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'success')).called(1); }); - testUsingContext('analytics sent on failure', () async { + testWithoutContext('analytics sent on failure', () async { MockDirectory.findCache = true; + 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 { + 'PUB_CACHE': 'custom/pub-cache/path', + }, + ), + ); try { await pub.get(context: PubContext.flutterTests, checkLastModified: false); } on ToolExit { // Ignore. } - verify(globals.flutterUsage.sendEvent('pub-result', 'flutter-tests', label: 'failure')).called(1); - }, overrides: { - FileSystem: () => MockFileSystem(), - ProcessManager: () => MockProcessManager(1), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({ - 'PUB_CACHE': 'custom/pub-cache/path', - }), - ), - Usage: () => MockUsage(), - Pub: () => const Pub(), + + verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'failure')).called(1); }); - testUsingContext('analytics sent on failed version solve', () async { + testWithoutContext('analytics sent on failed version solve', () async { MockDirectory.findCache = true; + final MockUsage usage = MockUsage(); + final Pub pub = Pub( + fileSystem: MockFileSystem(), + logger: BufferLogger.test(), + processManager: MockProcessManager( + 1, + stderr: 'version solving failed', + ), + platform: FakePlatform( + environment: { + 'PUB_CACHE': 'custom/pub-cache/path', + }, + ), + usage: usage, + botDetector: const BotDetectorAlwaysNo(), + ); + try { await pub.get(context: PubContext.flutterTests, checkLastModified: false); } on ToolExit { // Ignore. } - verify(globals.flutterUsage.sendEvent('pub-result', 'flutter-tests', label: 'version-solving-failed')).called(1); - }, overrides: { - FileSystem: () => MockFileSystem(), - ProcessManager: () => MockProcessManager( - 1, - stderr: 'version solving failed', - ), - Platform: () => FakePlatform( - environment: UnmodifiableMapView({ - 'PUB_CACHE': 'custom/pub-cache/path', - }), - ), - Usage: () => MockUsage(), - Pub: () => const Pub(), + + verify(usage.sendEvent('pub-result', 'flutter-tests', label: 'version-solving-failed')).called(1); }); - test('Pub error handling', () async { - final MemoryFileSystem fileSystem = MemoryFileSystem(); + testWithoutContext('Pub error handling', () async { + final BufferLogger logger = BufferLogger.test(); + final MemoryFileSystem fileSystem = MemoryFileSystem.test(); final FakeProcessManager processManager = FakeProcessManager.list([ FakeCommand( command: const [ - '/bin/cache/dart-sdk/bin/pub', + 'bin/cache/dart-sdk/bin/pub', '--verbosity=warning', 'get', '--no-precompile', ], onRun: () { - globals.fs.file('.packages') + fileSystem.file('.dart_tool/package_config.json') .setLastModifiedSync(DateTime(2002)); } ), const FakeCommand( command: [ - '/bin/cache/dart-sdk/bin/pub', + 'bin/cache/dart-sdk/bin/pub', '--verbosity=warning', 'get', '--no-precompile', @@ -273,96 +296,105 @@ void main() { ), FakeCommand( command: const [ - '/bin/cache/dart-sdk/bin/pub', + 'bin/cache/dart-sdk/bin/pub', '--verbosity=warning', 'get', '--no-precompile', ], onRun: () { - globals.fs.file('pubspec.yaml') + fileSystem.file('pubspec.yaml') .setLastModifiedSync(DateTime(2002)); } ), const FakeCommand( command: [ - '/bin/cache/dart-sdk/bin/pub', + 'bin/cache/dart-sdk/bin/pub', '--verbosity=warning', 'get', '--no-precompile', ], ), ]); - await Testbed().run(() async { - // the good scenario: .packages is old, pub updates the file. - globals.fs.file('.packages') - ..createSync() - ..setLastModifiedSync(DateTime(2000)); - globals.fs.file('pubspec.yaml') - ..createSync() - ..setLastModifiedSync(DateTime(2001)); - await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub sets date of .packages to 2002 - expect(testLogger.statusText, 'Running "flutter pub get" in /...\n'); - expect(testLogger.errorText, isEmpty); - expect(globals.fs.file('pubspec.yaml').lastModifiedSync(), DateTime(2001)); // because nothing should touch it - expect(globals.fs.file('.packages').lastModifiedSync(), isNot(DateTime(2000))); // because pub changes it to 2002 - expect(globals.fs.file('.packages').lastModifiedSync(), isNot(DateTime(2002))); // because we set the timestamp again after pub - testLogger.clear(); - // bad scenario 1: pub doesn't update file; doesn't matter, because we do instead - globals.fs.file('.packages') - .setLastModifiedSync(DateTime(2000)); - globals.fs.file('pubspec.yaml') - .setLastModifiedSync(DateTime(2001)); - await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub does nothing - expect(testLogger.statusText, 'Running "flutter pub get" in /...\n'); - expect(testLogger.errorText, isEmpty); - expect(globals.fs.file('pubspec.yaml').lastModifiedSync(), DateTime(2001)); // because nothing should touch it - expect(globals.fs.file('.packages').lastModifiedSync(), isNot(DateTime(2000))); // because we set the timestamp - expect(globals.fs.file('.packages').lastModifiedSync(), isNot(DateTime(2002))); // just in case FakeProcessManager is buggy - testLogger.clear(); - // bad scenario 2: pub changes pubspec.yaml instead - globals.fs.file('.packages') - .setLastModifiedSync(DateTime(2000)); - globals.fs.file('pubspec.yaml') - .setLastModifiedSync(DateTime(2001)); - try { - await pub.get(context: PubContext.flutterTests, checkLastModified: true); - expect(true, isFalse, reason: 'pub.get did not throw'); - } on ToolExit catch (error) { - expect(error.message, '/: unexpected concurrent modification of pubspec.yaml while running pub.'); - } - expect(testLogger.statusText, 'Running "flutter pub get" in /...\n'); - expect(testLogger.errorText, isEmpty); - expect(globals.fs.file('pubspec.yaml').lastModifiedSync(), DateTime(2002)); // because fake pub above touched it - expect(globals.fs.file('.packages').lastModifiedSync(), DateTime(2000)); // because nothing touched it - // bad scenario 3: pubspec.yaml was created in the future - globals.fs.file('.packages') - .setLastModifiedSync(DateTime(2000)); - globals.fs.file('pubspec.yaml') - .setLastModifiedSync(DateTime(9999)); - assert(DateTime(9999).isAfter(DateTime.now())); - await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub does nothing - expect(testLogger.statusText, contains('Running "flutter pub get" in /...\n')); - expect(testLogger.errorText, startsWith( - 'Warning: File "/pubspec.yaml" was created in the future. Optimizations that rely on ' - 'comparing time stamps will be unreliable. Check your system clock for accuracy.\n' - 'The timestamp was:' - )); - testLogger.clear(); - }, overrides: { - FileSystem: () => fileSystem, - ProcessManager: () => processManager, - Pub: () => const Pub(), - Platform: () => FakePlatform( + final Pub pub = Pub( + usage: MockUsage(), + fileSystem: fileSystem, + logger: logger, + processManager: processManager, + platform: FakePlatform( operatingSystem: 'linux', // so that the command executed is consistent environment: {}, ), - BotDetector: () => const BotDetectorAlwaysNo(), // so that the test never adds --trace to the pub command - }); + botDetector: const BotDetectorAlwaysNo() + ); + + // 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)); + await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub sets date of .packages to 2002 + + 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 + expect(fileSystem.file('.dart_tool/package_config.json').lastModifiedSync(), isNot(DateTime(2000))); // because pub changes it to 2002 + expect(fileSystem.file('.dart_tool/package_config.json').lastModifiedSync(), isNot(DateTime(2002))); // because we set the timestamp again after pub + 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)); + await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub does nothing + + 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 + expect(fileSystem.file('.dart_tool/package_config.json').lastModifiedSync(), isNot(DateTime(2000))); // because we set the timestamp + expect(fileSystem.file('.dart_tool/package_config.json').lastModifiedSync(), isNot(DateTime(2002))); // just in case FakeProcessManager is buggy + logger.clear(); + + // bad scenario 2: pub changes pubspec.yaml instead + fileSystem.file('.dart_tool/package_config.json') + .setLastModifiedSync(DateTime(2000)); + fileSystem.file('pubspec.yaml') + .setLastModifiedSync(DateTime(2001)); + try { + await pub.get(context: PubContext.flutterTests, checkLastModified: true); + expect(true, isFalse, reason: 'pub.get did not throw'); + } on ToolExit catch (error) { + expect(error.message, '/: unexpected concurrent modification of pubspec.yaml while running pub.'); + } + expect(logger.statusText, 'Running "flutter pub get" in /...\n'); + expect(logger.errorText, isEmpty); + expect(fileSystem.file('pubspec.yaml').lastModifiedSync(), DateTime(2002)); // because fake pub above touched it + expect(fileSystem.file('.dart_tool/package_config.json').lastModifiedSync(), DateTime(2000)); // because nothing touched it + + // bad scenario 3: pubspec.yaml was created in the future + fileSystem.file('.dart_tool/package_config.json') + .setLastModifiedSync(DateTime(2000)); + fileSystem.file('pubspec.yaml') + .setLastModifiedSync(DateTime(9999)); + assert(DateTime(9999).isAfter(DateTime.now())); + + await pub.get(context: PubContext.flutterTests, checkLastModified: true); // pub does nothing + + expect(logger.statusText, contains('Running "flutter pub get" in /...\n')); + expect(logger.errorText, startsWith( + 'Warning: File "/pubspec.yaml" was created in the future. Optimizations that rely on ' + 'comparing time stamps will be unreliable. Check your system clock for accuracy.\n' + 'The timestamp was:' + )); + logger.clear(); }); } class BotDetectorAlwaysNo implements BotDetector { const BotDetectorAlwaysNo(); + @override Future get isRunningOnBot async => false; } @@ -405,7 +437,7 @@ class MockProcessManager implements ProcessManager { } class MockFileSystem extends ForwardingFileSystem { - MockFileSystem() : super(MemoryFileSystem()); + MockFileSystem() : super(MemoryFileSystem.test()); @override File file(dynamic path) { diff --git a/packages/flutter_tools/test/src/throwing_pub.dart b/packages/flutter_tools/test/src/throwing_pub.dart index e2d4c61da5..0e535cbacd 100644 --- a/packages/flutter_tools/test/src/throwing_pub.dart +++ b/packages/flutter_tools/test/src/throwing_pub.dart @@ -4,7 +4,9 @@ import 'dart:async'; +import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/dart/pub.dart'; +import 'package:meta/meta.dart'; class ThrowingPub implements Pub { @override @@ -34,7 +36,7 @@ class ThrowingPub implements Pub { } @override - Future interactively(List arguments, {String directory}) { + Future interactively(List arguments, {String directory, @required Stdio stdio,}) { throw UnsupportedError('Attempted to invoke pub during test.'); } }