Unverified Commit 3b15d6a5 authored by Nehal Patel's avatar Nehal Patel Committed by GitHub

Removes retries from "dart pub get" and un-buffers its stdout/stderr output (#115801)

* Removes retries from "pub get" and proxies its stdout output

* Fix issue where ErrorHandlingProcessManager does not forward "mode" parameter to backing ProcessManager's "start" method

* Make "pub get" use ProcessStartMode.inheritStdio instead of forwarding bytes to stdout and stderr

* Fix tests

* Remove unused env var

* Add back 'Running "flutter pub get"...' status log

* Fix indent

* Add Pub.test() constructor which lets tests mock stdio
parent 49f59809
......@@ -699,6 +699,7 @@ class ErrorHandlingProcessManager extends ProcessManager {
environment: environment,
includeParentEnvironment: includeParentEnvironment,
runInShell: runInShell,
mode: mode,
);
}, platform: _platform);
}
......
......@@ -267,7 +267,6 @@ class Stdio {
}
io.Stdout? _stdout;
@visibleForTesting
io.IOSink get stderr {
if (_stderr != null) {
return _stderr!;
......
......@@ -197,6 +197,7 @@ abstract class ProcessUtils {
String? workingDirectory,
bool allowReentrantFlutter = false,
Map<String, String>? environment,
ProcessStartMode mode = ProcessStartMode.normal,
});
/// This runs the command and streams stdout/stderr from the child process to
......@@ -422,12 +423,14 @@ class _DefaultProcessUtils implements ProcessUtils {
String? workingDirectory,
bool allowReentrantFlutter = false,
Map<String, String>? environment,
ProcessStartMode mode = ProcessStartMode.normal,
}) {
_traceCommand(cmd, workingDirectory: workingDirectory);
return _processManager.start(
cmd,
workingDirectory: workingDirectory,
environment: _environment(allowReentrantFlutter, environment),
mode: mode,
);
}
......
......@@ -311,7 +311,6 @@ Future<T> runInContext<T>(
botDetector: globals.botDetector,
platform: globals.platform,
usage: globals.flutterUsage,
stdio: globals.stdio,
),
Stdio: () => Stdio(),
SystemClock: () => const SystemClock(),
......
......@@ -4,6 +4,7 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import 'package:process/process.dart';
......@@ -31,10 +32,6 @@ const String _kPubEnvironmentKey = 'PUB_ENVIRONMENT';
/// The console environment key used by the pub tool to find the cache directory.
const String _kPubCacheEnvironmentKey = 'PUB_CACHE';
/// The UNAVAILABLE exit code returned by the pub tool.
/// (see https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart)
const int _kPubExitCodeUnavailable = 69;
typedef MessageFilter = String? Function(String message);
/// globalCachePath is the directory in which the content of the localCachePath will be moved in
......@@ -150,9 +147,20 @@ abstract class Pub {
required Platform platform,
required BotDetector botDetector,
required Usage usage,
required Stdio stdio,
}) = _DefaultPub;
/// Create a [Pub] instance with a mocked [stdio].
@visibleForTesting
factory Pub.test({
required FileSystem fileSystem,
required Logger logger,
required ProcessManager processManager,
required Platform platform,
required BotDetector botDetector,
required Usage usage,
required Stdio stdio,
}) = _DefaultPub.test;
/// Runs `pub get` or `pub upgrade` for [project].
///
/// [context] provides extra information to package server requests to
......@@ -214,6 +222,26 @@ class _DefaultPub implements Pub {
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,
),
_processManager = processManager,
_stdio = null;
@visibleForTesting
_DefaultPub.test({
required FileSystem fileSystem,
required Logger logger,
required ProcessManager processManager,
required Platform platform,
required BotDetector botDetector,
required Usage usage,
required Stdio stdio,
}) : _fileSystem = fileSystem,
_logger = logger,
......@@ -234,7 +262,7 @@ class _DefaultPub implements Pub {
final BotDetector _botDetector;
final Usage _usage;
final ProcessManager _processManager;
final Stdio _stdio;
final Stdio? _stdio;
@override
Future<void> get({
......@@ -315,7 +343,7 @@ class _DefaultPub implements Pub {
'--offline',
'--example',
];
await _runWithRetries(
await _runWithStdioInherited(
args,
command: command,
context: context,
......@@ -346,15 +374,15 @@ class _DefaultPub implements Pub {
}
}
/// Runs pub with [arguments].
/// Runs pub with [arguments] and [ProcessStartMode.inheritStdio] mode.
///
/// Retries the command as long as the exit code is
/// `_kPubExitCodeUnavailable`.
/// Uses [ProcessStartMode.normal] and [Pub._stdio] if [Pub.test] constructor
/// was used.
///
/// Prints the stderr and stdout of the last run.
/// Prints the stdout and stderr of the whole run.
///
/// Sends an analytics event
Future<void> _runWithRetries(
/// Sends an analytics event.
Future<void> _runWithStdioInherited(
List<String> arguments, {
required String command,
required bool printProgress,
......@@ -365,57 +393,47 @@ class _DefaultPub implements Pub {
String? flutterRootOverride,
}) async {
int exitCode;
int attempts = 0;
int duration = 1;
List<_OutputLine>? output;
StreamSubscription<String> recordLines(Stream<List<int>> stream, _OutputStream streamName) {
return stream
.transform<String>(utf8.decoder)
.transform<String>(const LineSplitter())
.listen((String line) => output!.add(_OutputLine(line, streamName)));
}
final Status? status = printProgress
? _logger.startProgress('Running "flutter pub $command" in ${_fileSystem.path.basename(directory)}...',)
: null;
_logger.printStatus('Running "flutter pub $command" in ${_fileSystem.path.basename(directory)}...');
final List<String> pubCommand = _pubCommand(arguments);
final Map<String, String> pubEnvironment = await _createPubEnvironment(context, flutterRootOverride);
try {
do {
output = <_OutputLine>[];
attempts += 1;
final io.Process process = await _processUtils.start(
final io.Process process;
final io.Stdio? stdio = _stdio;
if (stdio != null) {
// Omit mode parameter and direct pub output to [Pub._stdio] for tests.
process = await _processUtils.start(
pubCommand,
workingDirectory: _fileSystem.path.current,
environment: pubEnvironment,
);
final StreamSubscription<String> stdoutSubscription =
recordLines(process.stdout, _OutputStream.stdout);
final StreamSubscription<String> stderrSubscription =
recordLines(process.stderr, _OutputStream.stderr);
exitCode = await process.exitCode;
final StreamSubscription<List<int>> stdoutSubscription =
process.stdout.listen(stdio.stdout.add);
final StreamSubscription<List<int>> stderrSubscription =
process.stderr.listen(stdio.stderr.add);
await Future.wait<void>(<Future<void>>[
stdoutSubscription.asFuture<void>(),
stderrSubscription.asFuture<void>(),
]);
unawaited(stdoutSubscription.cancel());
unawaited(stderrSubscription.cancel());
} else {
// Let pub inherit stdio for normal operation.
process = await _processUtils.start(
pubCommand,
workingDirectory: _fileSystem.path.current,
environment: pubEnvironment,
mode: ProcessStartMode.inheritStdio,
);
}
if (retry && exitCode == _kPubExitCodeUnavailable) {
_logger.printStatus(
'$failureMessage (server unavailable) -- attempting retry $attempts in $duration '
'second${ duration == 1 ? "" : "s"}...',
);
await Future<void>.delayed(Duration(seconds: duration));
if (duration < 64) {
duration *= 2;
}
// This will cause a retry.
output = null;
}
} while (output == null);
status?.stop();
exitCode = await process.exitCode;
// The exception is rethrown, so don't catch only Exceptions.
} catch (exception) { // ignore: avoid_catches_without_on_clauses
status?.cancel();
if (exception is io.ProcessException) {
final StringBuffer buffer = StringBuffer('${exception.message}\n');
final String directoryExistsMessage = _fileSystem.directory(directory).existsSync()
......@@ -434,40 +452,19 @@ class _DefaultPub implements Pub {
rethrow;
}
if (printProgress) {
// Show the output of the last run.
for (final _OutputLine line in output) {
switch (line.stream) {
case _OutputStream.stdout:
_stdio.stdoutWrite('${line.line}\n');
break;
case _OutputStream.stderr:
_stdio.stderrWrite('${line.line}\n');
break;
}
}
}
final int code = exitCode;
String result = 'success';
if (output.any((_OutputLine line) => line.line.contains('version solving failed'))) {
result = 'version-solving-failed';
} else if (code != 0) {
result = 'failure';
}
final String result = code == 0 ? 'success' : 'failure';
PubResultEvent(
context: context.toAnalyticsString(),
result: result,
usage: _usage,
).send();
final String lastPubMessage = output.isEmpty ? 'no message' : output.last.line;
if (code != 0) {
final StringBuffer buffer = StringBuffer('$failureMessage\n');
buffer.writeln('command: "${pubCommand.join(' ')}"');
buffer.write(_stringifyPubEnv(pubEnvironment));
buffer.writeln('exit code: $code');
buffer.writeln('last line of pub output: "${lastPubMessage.trim()}"');
throwToolExit(
buffer.toString(),
exitCode: code,
......@@ -813,14 +810,3 @@ class _DefaultPub implements Pub {
return buffer.toString();
}
}
class _OutputLine {
_OutputLine(this.line, this.stream);
final String line;
final _OutputStream stream;
}
enum _OutputStream {
stdout,
stderr,
}
......@@ -77,7 +77,7 @@ void main() {
testUsingContext('AnalysisServer success', () async {
createSampleProject(tempDir);
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -117,7 +117,7 @@ void main() {
testUsingContext('AnalysisServer errors', () async {
createSampleProject(tempDir, brokenCode: true);
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......
......@@ -171,7 +171,7 @@ void main() {
// check that the tests run clean
return _runFlutterTest(projectDir);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: logger,
processManager: globals.processManager,
......@@ -220,7 +220,7 @@ void main() {
);
return _runFlutterTest(projectDir);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -247,7 +247,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -277,7 +277,7 @@ void main() {
]);
return _runFlutterTest(projectDir);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -303,7 +303,7 @@ void main() {
]),
throwsToolExit(message: 'Sorry, unable to detect the type of project to recreate'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -329,7 +329,7 @@ void main() {
expect(exec.exitCode, 2);
expect(exec.stderr, contains('Cannot create a project within the Flutter SDK'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -363,7 +363,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -396,7 +396,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -424,7 +424,7 @@ void main() {
'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',]
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -463,7 +463,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -493,7 +493,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -532,7 +532,7 @@ void main() {
);
return _runFlutterTest(projectDir);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -563,7 +563,7 @@ void main() {
);
return _runFlutterTest(projectDir.childDirectory('example'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -594,7 +594,7 @@ void main() {
expect(logger.errorText, isNot(contains(_kNoPlatformsMessage)));
}, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -622,7 +622,7 @@ void main() {
final PathDependency pathDependency = pubspec.dependencies[pluginName]! as PathDependency;
expect(pathDependency.path, '../');
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -744,7 +744,7 @@ void main() {
'.android/Flutter/src/main/java/io/flutter/facade/Flutter.java',
]);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -1443,7 +1443,7 @@ void main() {
final String displayName = _getStringValueFromPlist(plistFile: plistFile, key: 'CFBundleDisplayName');
expect(displayName, 'My Project');
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -1472,7 +1472,7 @@ void main() {
final String displayName = _getStringValueFromPlist(plistFile: plistFile, key: 'CFBundleDisplayName');
expect(displayName, 'My Project');
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -1730,7 +1730,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -1755,7 +1755,7 @@ void main() {
'com.bar.foo.flutterProject',
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -1906,7 +1906,7 @@ void main() {
],
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -1941,14 +1941,14 @@ void main() {
},
overrides: <Type, Generator>{
ProcessManager: () => loggingProcessManager,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: mockStdio,
stdio: mockStdio,
),
},
);
......@@ -2917,7 +2917,7 @@ void main() {
expectedFailures: expectedFailures,
);
}, overrides: <Type, Generator>{
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......
......@@ -211,7 +211,7 @@ void main() {
expect(mockStdio.stdout.writes.map(utf8.decode),
allOf(
contains(matches(RegExp(r'Resolving dependencies in .+flutter_project\.\.\.'))),
contains('+ flutter 0.0.0 from sdk flutter\n'),
contains(matches(RegExp(r'\+ flutter 0.0.0 from sdk flutter\n'))),
contains(matches(RegExp(r'Changed \d+ dependencies in .+flutter_project!'))),
),
);
......@@ -220,7 +220,7 @@ void main() {
expectZeroPluginsInjected(projectPath);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -242,7 +242,7 @@ void main() {
expectZeroPluginsInjected(projectPath);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -264,7 +264,7 @@ void main() {
expect((await getCommand.usageValues).commandPackagesNumberPlugins, 0);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -288,7 +288,7 @@ void main() {
expect((await getCommand.usageValues).commandPackagesNumberPlugins, 1);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -310,7 +310,7 @@ void main() {
expect((await getCommand.usageValues).commandPackagesProjectModule, false);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -332,7 +332,7 @@ void main() {
expect((await getCommand.usageValues).commandPackagesProjectModule, true);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -363,7 +363,7 @@ void main() {
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v1');
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -385,7 +385,7 @@ void main() {
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v2');
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -407,7 +407,7 @@ void main() {
expectZeroPluginsInjected(projectPath);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -429,7 +429,7 @@ void main() {
expectModulePluginInjected(projectPath);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -459,7 +459,7 @@ void main() {
expectPluginInjected(exampleProjectPath);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -496,7 +496,7 @@ void main() {
ProcessManager: () => processManager,
Stdio: () => mockStdio,
BotDetector: () => const FakeBotDetector(false),
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -522,7 +522,7 @@ void main() {
ProcessManager: () => processManager,
Stdio: () => mockStdio,
BotDetector: () => const FakeBotDetector(true),
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -553,7 +553,7 @@ void main() {
Platform: () => FakePlatform(environment: <String, String>{}),
ProcessManager: () => processManager,
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -584,7 +584,7 @@ void main() {
Platform: () => FakePlatform(environment: <String, String>{}),
ProcessManager: () => processManager,
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......@@ -613,7 +613,7 @@ void main() {
Platform: () => FakePlatform(environment: <String, String>{}),
ProcessManager: () => processManager,
Stdio: () => mockStdio,
Pub: () => Pub(
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
......
......@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:fake_async/fake_async.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/bot_detector.dart';
......@@ -34,7 +33,7 @@ void main() {
fileSystem.file('pubspec.yaml').createSync();
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -81,7 +80,7 @@ void main() {
}
''');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -132,7 +131,7 @@ void main() {
fileSystem.file('.dart_tool/version').writeAsStringSync('a');
fileSystem.file('version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -182,7 +181,7 @@ void main() {
fileSystem.file('.dart_tool/version').writeAsStringSync('a');
fileSystem.file('version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -214,7 +213,7 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('{"generator": "third-party"}');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -249,7 +248,7 @@ void main() {
fileSystem.file('.dart_tool/version').writeAsStringSync('a');
fileSystem.file('version').writeAsStringSync('a');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -289,7 +288,7 @@ void main() {
fileSystem.file('.dart_tool/version').writeAsStringSync('a');
fileSystem.file('version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -329,7 +328,7 @@ void main() {
fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
fileSystem.file('version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -369,7 +368,7 @@ void main() {
fileSystem.file('pubspec.lock').createSync();
fileSystem.file('version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -408,7 +407,7 @@ void main() {
fileSystem.file('.dart_tool/package_config.json').createSync(recursive: true);
fileSystem.file('.dart_tool/version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -449,7 +448,7 @@ void main() {
..setLastModifiedSync(DateTime(1991));
fileSystem.file('version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -492,7 +491,7 @@ void main() {
fileSystem.file('version').writeAsStringSync('b');
fileSystem.file('.dart_tool/version').writeAsStringSync('b');
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
......@@ -512,156 +511,6 @@ void main() {
expect(fileSystem.file('.dart_tool/version').readAsStringSync(), 'b');
});
testWithoutContext('pub get 69', () async {
String? error;
const FakeCommand pubGetCommand = FakeCommand(
command: <String>[
'bin/cache/dart-sdk/bin/dart',
'__deprecated_pub',
'--directory',
'.',
'get',
'--example',
],
exitCode: 69,
environment: <String, String>{'FLUTTER_ROOT': '', 'PUB_ENVIRONMENT': 'flutter_cli:flutter_tests'},
);
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
pubGetCommand,
]);
final BufferLogger logger = BufferLogger.test();
final FileSystem fileSystem = MemoryFileSystem.test();
final Pub pub = Pub(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
usage: TestUsage(),
platform: FakePlatform(),
botDetector: const BotDetectorAlwaysNo(),
stdio: FakeStdio(),
);
FakeAsync().run((FakeAsync time) {
expect(logger.statusText, '');
pub.get(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
context: PubContext.flutterTests,
).then((void value) {
error = 'test completed unexpectedly';
}, onError: (dynamic thrownError) {
error = 'test failed unexpectedly: $thrownError';
});
time.elapse(const Duration(milliseconds: 500));
expect(logger.statusText,
'Running "flutter pub get" in /...\n'
'pub get failed (server unavailable) -- attempting retry 1 in 1 second...\n',
);
time.elapse(const Duration(milliseconds: 500));
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(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(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'
'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n' // at t=1
'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n' // at t=5
'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n' // at t=13
'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n' // at t=29
'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n', // at t=61
);
time.elapse(const Duration(seconds: 200)); // from t=0 to t=200
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'
'pub get failed (server unavailable) -- attempting retry 3 in 4 seconds...\n'
'pub get failed (server unavailable) -- attempting retry 4 in 8 seconds...\n'
'pub get failed (server unavailable) -- attempting retry 5 in 16 seconds...\n'
'pub get failed (server unavailable) -- attempting retry 6 in 32 seconds...\n'
'pub get failed (server unavailable) -- attempting retry 7 in 64 seconds...\n'
'pub get failed (server unavailable) -- attempting retry 8 in 64 seconds...\n' // at t=39
'pub get failed (server unavailable) -- attempting retry 9 in 64 seconds...\n' // at t=103
'pub get failed (server unavailable) -- attempting retry 10 in 64 seconds...\n', // at t=167
);
});
expect(logger.errorText, isEmpty);
expect(error, isNull);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('pub get offline does not retry', () async {
String? error;
const FakeCommand pubGetCommand = FakeCommand(
command: <String>[
'bin/cache/dart-sdk/bin/dart',
'__deprecated_pub',
'--directory',
'.',
'get',
'--offline',
'--example',
],
exitCode: 69,
environment: <String, String>{'FLUTTER_ROOT': '', 'PUB_ENVIRONMENT': 'flutter_cli:flutter_tests'},
);
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
pubGetCommand,
]);
final BufferLogger logger = BufferLogger.test();
final FileSystem fileSystem = MemoryFileSystem.test();
final Pub pub = Pub(
fileSystem: fileSystem,
logger: logger,
processManager: processManager,
usage: TestUsage(),
platform: FakePlatform(),
botDetector: const BotDetectorAlwaysNo(),
stdio: FakeStdio(),
);
FakeAsync().run((FakeAsync time) {
expect(logger.statusText, '');
pub.get(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
context: PubContext.flutterTests,
offline: true
).then((void value) {
error = 'test completed unexpectedly';
}, onError: (dynamic thrownError) {
error = 'test failed unexpectedly: $thrownError';
});
time.elapse(const Duration(milliseconds: 500));
expect(logger.statusText,
'Running "flutter pub get" in /...\n'
);
});
expect(logger.errorText, isEmpty);
expect(error, contains('test failed unexpectedly: Exception: pub get failed'));
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('pub get 66 shows message from pub', () async {
final BufferLogger logger = BufferLogger.test();
final FileSystem fileSystem = MemoryFileSystem.test();
......@@ -683,7 +532,7 @@ void main() {
),
]);
final FakeStdio mockStdio = FakeStdio();
final Pub pub = Pub(
final Pub pub = Pub.test(
platform: FakePlatform(),
fileSystem: fileSystem,
logger: logger,
......@@ -700,7 +549,6 @@ pub env: {
"PUB_ENVIRONMENT": "flutter_cli:flutter_tests",
}
exit code: 66
last line of pub output: "err3"
''';
await expectLater(
() => pub.get(
......@@ -713,17 +561,13 @@ last line of pub output: "err3"
expect(
mockStdio.stdout.writes.map(utf8.decode),
<String>[
'out1\n',
'out2\n',
'out3\n',
'out1\nout2\nout3\n',
]
);
expect(
mockStdio.stderr.writes.map(utf8.decode),
<String>[
'err1\n',
'err2\n',
'err3\n',
'err1\nerr2\nerr3\n',
]
);
expect(processManager, hasNoRemainingExpectations);
......@@ -764,7 +608,7 @@ last line of pub output: "err3"
),
]);
final Pub pub = Pub(
final Pub pub = Pub.test(
platform: FakePlatform(),
fileSystem: fileSystem,
logger: logger,
......@@ -798,7 +642,6 @@ last line of pub output: "err3"
});
testWithoutContext('pub cache in flutter root is ignored', () async {
String? error;
final FileSystem fileSystem = MemoryFileSystem.test();
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
......@@ -815,36 +658,40 @@ last line of pub output: "err3"
'FLUTTER_ROOT': '',
'PUB_ENVIRONMENT': 'flutter_cli:flutter_tests',
},
stdout: "FakeCommand's env successfully matched"
),
]);
final Pub pub = Pub(
final FakeStdio mockStdio = FakeStdio();
final Pub pub = Pub.test(
platform: FakePlatform(),
usage: TestUsage(),
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: processManager,
botDetector: const BotDetectorAlwaysNo(),
stdio: FakeStdio(),
stdio: mockStdio,
);
FakeAsync().run((FakeAsync time) {
pub.get(
try {
await pub.get(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
context: PubContext.flutterTests
).then((void value) {
error = 'test completed unexpectedly';
}, onError: (dynamic thrownError) {
error = 'test failed unexpectedly: $thrownError';
});
time.elapse(const Duration(milliseconds: 500));
expect(error, isNull);
expect(processManager, hasNoRemainingExpectations);
});
);
} on ToolExit {
// Ignore.
}
expect(
mockStdio.stdout.writes.map(utf8.decode),
<String>[
"FakeCommand's env successfully matched",
]
);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('pub cache local is merge to global', () async {
String? error;
final FileSystem fileSystem = MemoryFileSystem.test();
final Directory local = fileSystem.currentDirectory.childDirectory('.pub-cache');
final Directory global = fileSystem.currentDirectory.childDirectory('/global');
......@@ -888,7 +735,7 @@ last line of pub output: "err3"
final Platform platform = FakePlatform(
environment: <String, String>{'HOME': '/global'}
);
final Pub pub = Pub(
final Pub pub = Pub.test(
platform: platform,
usage: TestUsage(),
fileSystem: fileSystem,
......@@ -898,25 +745,22 @@ last line of pub output: "err3"
stdio: FakeStdio(),
);
FakeAsync().run((FakeAsync time) {
pub.get(
try {
await pub.get(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
context: PubContext.flutterTests,
).then((void value) {
error = 'test completed unexpectedly';
}, onError: (dynamic thrownError) {
error = thrownError.toString();
});
time.elapse(const Duration(milliseconds: 500));
expect(error, isNull);
expect(processManager, hasNoRemainingExpectations);
expect(local.existsSync(), false);
expect(globalHosted.childFile('second.file').existsSync(), false);
expect(
globalHosted.childDirectory('dir').childFile('third.file').existsSync(), false
); // do not copy dependencies that are already downloaded
expect(globalHosted.childDirectory('dir_2').childFile('fourth.file').existsSync(), true);
});
context: PubContext.flutterTests
);
} on ToolExit {
// Ignore.
}
expect(processManager, hasNoRemainingExpectations);
expect(local.existsSync(), false);
expect(globalHosted.childFile('second.file').existsSync(), false);
expect(
globalHosted.childDirectory('dir').childFile('third.file').existsSync(), false
); // do not copy dependencies that are already downloaded
expect(globalHosted.childDirectory('dir_2').childFile('fourth.file').existsSync(), true);
});
testWithoutContext('pub cache in environment is used', () async {
......@@ -938,15 +782,18 @@ last line of pub output: "err3"
'PUB_ENVIRONMENT': 'flutter_cli:flutter_tests',
'PUB_CACHE': 'custom/pub-cache/path',
},
stdout: "FakeCommand's env successfully matched"
),
]);
final Pub pub = Pub(
final FakeStdio mockStdio = FakeStdio();
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: processManager,
usage: TestUsage(),
botDetector: const BotDetectorAlwaysNo(),
stdio: FakeStdio(),
stdio: mockStdio,
platform: FakePlatform(
environment: const <String, String>{
'PUB_CACHE': 'custom/pub-cache/path',
......@@ -954,25 +801,28 @@ last line of pub output: "err3"
),
);
FakeAsync().run((FakeAsync time) {
String? error;
pub.get(
try {
await pub.get(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
context: PubContext.flutterTests).then((void value) {
error = 'test completed unexpectedly';
}, onError: (dynamic thrownError) {
error = 'test failed unexpectedly: $thrownError';
});
time.elapse(const Duration(milliseconds: 500));
expect(error, isNull);
expect(processManager, hasNoRemainingExpectations);
});
context: PubContext.flutterTests
);
} on ToolExit {
// Ignore.
}
expect(
mockStdio.stdout.writes.map(utf8.decode),
<String>[
"FakeCommand's env successfully matched",
]
);
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('analytics sent on success', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final TestUsage usage = TestUsage();
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
......@@ -1003,7 +853,7 @@ last line of pub output: "err3"
testWithoutContext('package_config_subset file is generated from packages and not timestamp', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final TestUsage usage = TestUsage();
final Pub pub = Pub(
final Pub pub = Pub.test(
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
......@@ -1065,7 +915,7 @@ last line of pub output: "err3"
),
]);
final Pub pub = Pub(
final Pub pub = Pub.test(
usage: usage,
fileSystem: fileSystem,
logger: BufferLogger.test(),
......@@ -1093,56 +943,6 @@ last line of pub output: "err3"
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('analytics sent on failed version solve', () async {
final TestUsage usage = TestUsage();
final FileSystem fileSystem = MemoryFileSystem.test();
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'bin/cache/dart-sdk/bin/dart',
'__deprecated_pub',
'--directory',
'.',
'get',
'--example',
],
exitCode: 1,
stderr: 'version solving failed',
),
]);
final Pub pub = Pub(
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: processManager,
platform: FakePlatform(
environment: <String, String>{
'PUB_CACHE': 'custom/pub-cache/path',
},
),
usage: usage,
botDetector: const BotDetectorAlwaysNo(),
stdio: FakeStdio(),
);
fileSystem.file('pubspec.yaml').writeAsStringSync('name: foo');
try {
await pub.get(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
context: PubContext.flutterTests
);
} on ToolExit {
// Ignore.
}
expect(usage.events, contains(
const TestUsageEvent('pub-result', 'flutter-tests', label: 'version-solving-failed'),
));
expect(processManager, hasNoRemainingExpectations);
});
testWithoutContext('Pub error handling', () async {
final BufferLogger logger = BufferLogger.test();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
......@@ -1196,7 +996,7 @@ last line of pub output: "err3"
],
),
]);
final Pub pub = Pub(
final Pub pub = Pub.test(
usage: TestUsage(),
fileSystem: fileSystem,
logger: logger,
......
......@@ -68,15 +68,15 @@ void main() {
expect(contents, contains('SharedPreferencesPlugin.registerWith(registrar);'));
expect(contents, contains('registrar.registerMessageHandler();'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
});
testUsingContext('generated plugin registrant passes analysis without null safety', () async {
......@@ -116,15 +116,15 @@ void main() {
expect(contents, contains('SharedPreferencesPlugin.registerWith(registrar);'));
expect(contents, contains('registrar.registerMessageHandler();'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
});
......@@ -148,15 +148,15 @@ void main() {
final String contents = registrant.readAsStringSync();
expect(contents, contains('void registerPlugins() {}'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
});
// See: https://github.com/dart-lang/dart-services/pull/874
......@@ -179,15 +179,15 @@ void main() {
final Directory buildDir = projectDir.childDirectory('.dart_tool/flutter_build');
expect(buildDir, isNot(exists));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
});
testUsingContext(
......@@ -229,15 +229,15 @@ void main() {
);
await _analyzeEntity(buildDir.childFile('web_plugin_registrant.dart'));
}, overrides: <Type, Generator>{
Pub: () => Pub(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: globals.stdio,
),
});
}
......
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