Unverified Commit 8df0d686 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove globals from pub (#55412)

parent cbc121e3
......@@ -205,7 +205,7 @@ class PackagesPublishCommand extends FlutterCommand {
if (boolArg('force')) '--force',
];
Cache.releaseLockEarly();
await pub.interactively(<String>['publish', ...args]);
await pub.interactively(<String>['publish', ...args], stdio: globals.stdio);
return FlutterCommandResult.success();
}
}
......@@ -236,7 +236,7 @@ class PackagesForwardCommand extends FlutterCommand {
@override
Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly();
await pub.interactively(<String>[_commandName, ...argResults.rest]);
await pub.interactively(<String>[_commandName, ...argResults.rest], stdio: globals.stdio);
return FlutterCommandResult.success();
}
......@@ -264,7 +264,7 @@ class PackagesPassthroughCommand extends FlutterCommand {
@override
Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly();
await pub.interactively(argResults.rest);
await pub.interactively(argResults.rest, stdio: globals.stdio);
return FlutterCommandResult.success();
}
}
......@@ -167,7 +167,14 @@ Future<T> runInContext<T>(
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(),
......
......@@ -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<Pub>();
/// 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._(<String>['upgrade']);
static final PubContext pubForward = PubContext._(<String>['forward']);
static final PubContext runTest = PubContext._(<String>['run_test']);
static final PubContext flutterTests = PubContext._(<String>['flutter_tests']);
static final PubContext updatePackages = PubContext._(<String>['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<void> interactively(
List<String> 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<void> 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<String> args = <String>[
if (verbose) '--verbose' else '--verbosity=warning',
...<String>[command, '--no-precompile'],
if (offline) '--offline',
if (verbose)
'--verbose'
else
'--verbosity=warning',
...<String>[
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<void> batch(
List<String> 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<void>.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<void> interactively(
List<String> 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<dynamic>(<Future<dynamic>>[
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<String> _pubCommand(List<String> arguments) {
return <String>[sdkBinaryName('pub'), ...arguments];
// TODO(jonahwilliams): refactor to use artifacts.
final String sdkPath = _fileSystem.path.joinAll(<String>[
Cache.flutterRoot,
'bin',
'cache',
'dart-sdk',
'bin',
if (_platform.isWindows)
'pub.bat'
else
'pub'
]);
return <String>[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<Map<String, String>> _createPubEnvironment(PubContext context, [ String flutterRootOverride ]) async {
final Map<String, String> environment = <String, String>{
'FLUTTER_ROOT': flutterRootOverride ?? Cache.flutterRoot,
_pubEnvironmentKey: await _getPubEnvironmentValue(context),
};
final String pubCache = _getRootPubCacheIfAvailable();
if (pubCache != null) {
environment[_pubCacheEnvironmentKey] = pubCache;
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;
}
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<String> _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<String> values = <String>[
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];
// 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<String> _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<String> values = <String>[
if (existing != null && existing.isNotEmpty) existing,
if (await _botDetector.isRunningOnBot) 'flutter_bot',
'flutter_cli',
...pubContext._values,
];
return values.join(':');
}
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;
}
String _getRootPubCacheIfAvailable() {
if (_platform.environment.containsKey(_kPubCacheEnvironmentKey)) {
return _platform.environment[_kPubCacheEnvironmentKey];
}
// Use pub's default location by returning null.
return null;
}
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;
}
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:') {
// 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<Map<String, String>> _createPubEnvironment(PubContext context, [ String flutterRootOverride ]) async {
final Map<String, String> environment = <String, String>{
'FLUTTER_ROOT': flutterRootOverride ?? Cache.flutterRoot,
_kPubEnvironmentKey: await _getPubEnvironmentValue(context),
};
final String pubCache = _getRootPubCacheIfAvailable();
if (pubCache != null) {
environment[_kPubCacheEnvironmentKey] = pubCache;
}
return environment;
}
return message;
}
......@@ -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.
......
......@@ -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, <String>[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, <String>[tempDir.path],
fileSystem: fileSystem,
......
......@@ -86,7 +86,14 @@ void main() {
);
return _runFlutterTest(projectDir);
}, overrides: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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() {
<String>['lib/main.dart'],
);
}, overrides: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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,
),
},
);
......
......@@ -195,7 +195,14 @@ void main() {
expectDependenciesResolved(projectPath);
expectZeroPluginsInjected(projectPath);
}, overrides: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
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,
),
});
});
}
......@@ -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: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/context.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 '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 '../../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<ProcessManager>() 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 <String, String>{},
),
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: <Type, Generator>{
FileSystem: () => MockFileSystem(),
ProcessManager: () => MockProcessManager(69),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{}),
),
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 <String, String>{}),
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: <Type, Generator>{
ProcessManager: () => MockProcessManager(66, stderr: 'err1\nerr2\nerr3\n', stdout: 'out1\nout2\nout3\n'),
FileSystem: () => MockFileSystem(),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{}),
),
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<ProcessManager>() as MockProcessManager;
final MockFileSystem fsMock = context.get<FileSystem>() as MockFileSystem;
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(),
);
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: <Type, Generator>{
FileSystem: () => MockFileSystem(),
ProcessManager: () => MockProcessManager(69),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{}),
),
Pub: () => const Pub(),
});
testUsingContext('pub cache in environment is used', () async {
String error;
final MockProcessManager processMock = context.get<ProcessManager>() 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 <String, String>{
'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: <Type, Generator>{
FileSystem: () => MockFileSystem(),
ProcessManager: () => MockProcessManager(69),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{
'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 <String, String>{
'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: <Type, Generator>{
FileSystem: () => MockFileSystem(),
ProcessManager: () => MockProcessManager(0),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{
'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 <String, String>{
'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: <Type, Generator>{
FileSystem: () => MockFileSystem(),
ProcessManager: () => MockProcessManager(1),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{
'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: <String, String>{
'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: <Type, Generator>{
FileSystem: () => MockFileSystem(),
ProcessManager: () => MockProcessManager(
1,
stderr: 'version solving failed',
),
Platform: () => FakePlatform(
environment: UnmodifiableMapView<String, String>(<String, String>{
'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>[
FakeCommand(
command: const <String>[
'/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: <String>[
'/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 <String>[
'/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: <String>[
'/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: <Type, Generator>{
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: <String, String>{},
),
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<bool> 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) {
......
......@@ -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<void> interactively(List<String> arguments, {String directory}) {
Future<void> interactively(List<String> arguments, {String directory, @required Stdio stdio,}) {
throw UnsupportedError('Attempted to invoke pub during test.');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment