Commit 03d163ce authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Update tools to use package:process (#7590)

parent 24f1b2ee
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:process/process.dart';
import '../lib/src/base/common.dart'; import '../lib/src/base/common.dart';
import '../lib/src/base/config.dart'; import '../lib/src/base/config.dart';
...@@ -13,7 +14,6 @@ import '../lib/src/base/file_system.dart'; ...@@ -13,7 +14,6 @@ import '../lib/src/base/file_system.dart';
import '../lib/src/base/io.dart'; import '../lib/src/base/io.dart';
import '../lib/src/base/logger.dart'; import '../lib/src/base/logger.dart';
import '../lib/src/base/os.dart'; import '../lib/src/base/os.dart';
import '../lib/src/base/process_manager.dart';
import '../lib/src/cache.dart'; import '../lib/src/cache.dart';
import '../lib/src/flx.dart'; import '../lib/src/flx.dart';
import '../lib/src/globals.dart'; import '../lib/src/globals.dart';
...@@ -39,7 +39,7 @@ Future<Null> main(List<String> args) async { ...@@ -39,7 +39,7 @@ Future<Null> main(List<String> args) async {
executableContext.runInZone(() { executableContext.runInZone(() {
// Initialize the context with some defaults. // Initialize the context with some defaults.
context.putIfAbsent(FileSystem, () => new LocalFileSystem()); context.putIfAbsent(FileSystem, () => new LocalFileSystem());
context.putIfAbsent(ProcessManager, () => new ProcessManager()); context.putIfAbsent(ProcessManager, () => new LocalProcessManager());
context.putIfAbsent(Logger, () => new StdoutLogger()); context.putIfAbsent(Logger, () => new StdoutLogger());
context.putIfAbsent(Cache, () => new Cache()); context.putIfAbsent(Cache, () => new Cache());
context.putIfAbsent(Config, () => new Config()); context.putIfAbsent(Config, () => new Config());
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:process/process.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
import 'src/base/common.dart'; import 'src/base/common.dart';
...@@ -15,7 +16,6 @@ import 'src/base/io.dart'; ...@@ -15,7 +16,6 @@ import 'src/base/io.dart';
import 'src/base/logger.dart'; import 'src/base/logger.dart';
import 'src/base/os.dart'; import 'src/base/os.dart';
import 'src/base/process.dart'; import 'src/base/process.dart';
import 'src/base/process_manager.dart';
import 'src/base/utils.dart'; import 'src/base/utils.dart';
import 'src/cache.dart'; import 'src/cache.dart';
import 'src/commands/analyze.dart'; import 'src/commands/analyze.dart';
...@@ -103,7 +103,7 @@ Future<Null> main(List<String> args) async { ...@@ -103,7 +103,7 @@ Future<Null> main(List<String> args) async {
// Seed these context entries first since others depend on them // Seed these context entries first since others depend on them
context.putIfAbsent(FileSystem, () => new LocalFileSystem()); context.putIfAbsent(FileSystem, () => new LocalFileSystem());
context.putIfAbsent(ProcessManager, () => new ProcessManager()); context.putIfAbsent(ProcessManager, () => new LocalProcessManager());
context.putIfAbsent(Logger, () => new StdoutLogger()); context.putIfAbsent(Logger, () => new StdoutLogger());
// Order-independent context entries // Order-independent context entries
......
...@@ -100,16 +100,15 @@ class FlutterCommandRunner extends CommandRunner<Null> { ...@@ -100,16 +100,15 @@ class FlutterCommandRunner extends CommandRunner<Null> {
hide: !verboseHelp, hide: !verboseHelp,
help: help:
'Enables recording of process invocations (including stdout and stderr of all such invocations),\n' 'Enables recording of process invocations (including stdout and stderr of all such invocations),\n'
'and serializes that recording to the specified location. If the location is a directory, a ZIP\n' 'and serializes that recording to a directory with the path specified in this flag. If the\n'
'file named `recording.zip` will be created in that directory. Otherwise, a ZIP file will be\n' 'directory does not already exist, it will be created.');
'created with the path specified in this flag.');
argParser.addOption('replay-from', argParser.addOption('replay-from',
hide: !verboseHelp, hide: !verboseHelp,
help: help:
'Enables mocking of process invocations by replaying their stdout, stderr, and exit code from\n' 'Enables mocking of process invocations by replaying their stdout, stderr, and exit code from\n'
'the specified recording (obtained via --record-to). If the location is a file, it is assumed to\n' 'the specified recording (obtained via --record-to). The path specified in this flag must refer\n'
'be a ZIP file structured according to the output of --record-to. If the location is a directory,\n' 'to a directory that holds serialized process invocations structured according to the output of\n'
'it is assumed to be an unzipped version of such a ZIP file.'); '--record-to.');
} }
@override @override
...@@ -164,23 +163,11 @@ class FlutterCommandRunner extends CommandRunner<Null> { ...@@ -164,23 +163,11 @@ class FlutterCommandRunner extends CommandRunner<Null> {
throwToolExit('--record-to and --replay-from cannot be used together.'); throwToolExit('--record-to and --replay-from cannot be used together.');
if (globalResults['record-to'] != null) { if (globalResults['record-to'] != null) {
// Turn on recording. enableRecordingProcessManager(globalResults['record-to'].trim());
String recordTo = globalResults['record-to'].trim();
if (recordTo.isEmpty)
recordTo = null;
context.setVariable(ProcessManager,
new RecordingProcessManager(recordTo));
} }
if (globalResults['replay-from'] != null) { if (globalResults['replay-from'] != null) {
// Turn on replay-based mocking. await enableReplayProcessManager(globalResults['replay-from'].trim());
try {
context.setVariable(ProcessManager, await ReplayProcessManager.create(
globalResults['replay-from'].trim(),
));
} on ArgumentError {
throwToolExit('--replay-from must specify a valid file or directory.');
}
} }
logger.quiet = globalResults['quiet']; logger.quiet = globalResults['quiet'];
......
...@@ -22,6 +22,7 @@ dependencies: ...@@ -22,6 +22,7 @@ dependencies:
mustache: ^0.2.5 mustache: ^0.2.5
package_config: '>=0.1.5 <2.0.0' package_config: '>=0.1.5 <2.0.0'
path: ^1.4.0 path: ^1.4.0
process: ^1.0.0
pub_semver: ^1.0.0 pub_semver: ^1.0.0
stack_trace: ^1.4.0 stack_trace: ^1.4.0
usage: ^2.2.1 usage: ^2.2.1
......
...@@ -37,7 +37,6 @@ import 'install_test.dart' as install_test; ...@@ -37,7 +37,6 @@ import 'install_test.dart' as install_test;
import 'logs_test.dart' as logs_test; import 'logs_test.dart' as logs_test;
import 'os_utils_test.dart' as os_utils_test; import 'os_utils_test.dart' as os_utils_test;
import 'packages_test.dart' as packages_test; import 'packages_test.dart' as packages_test;
import 'process_manager_test.dart' as process_manager_test;
import 'protocol_discovery_test.dart' as protocol_discovery_test; import 'protocol_discovery_test.dart' as protocol_discovery_test;
import 'run_test.dart' as run_test; import 'run_test.dart' as run_test;
import 'stop_test.dart' as stop_test; import 'stop_test.dart' as stop_test;
...@@ -77,7 +76,6 @@ void main() { ...@@ -77,7 +76,6 @@ void main() {
logs_test.main(); logs_test.main();
os_utils_test.main(); os_utils_test.main();
packages_test.main(); packages_test.main();
process_manager_test.main();
protocol_discovery_test.main(); protocol_discovery_test.main();
run_test.main(); run_test.main();
stop_test.main(); stop_test.main();
......
[
{
"pid": 100,
"basename": "001.sing.100",
"executable": "sing",
"arguments": [
"ppap"
],
"mode": "ProcessStartMode.NORMAL",
"exitCode": 0
},
{
"pid": 101,
"basename": "002.dance.101",
"executable": "dance",
"arguments": [
"gangnam-style"
],
"stdoutEncoding": "system",
"stderrEncoding": "system",
"exitCode": 2
}
]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:archive/archive.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/base/os.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/process_manager.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
typedef bool Predicate<T>(T item);
/// Decodes a UTF8-encoded byte array into a list of Strings, where each list
/// entry represents a line of text.
List<String> _decode(List<int> data) =>
const LineSplitter().convert(UTF8.decode(data));
/// Consumes and returns an entire stream of bytes.
Future<List<int>> _consume(Stream<List<int>> stream) =>
stream.expand((List<int> data) => data).toList();
void main() {
group('RecordingProcessManager', () {
Directory tmp;
ProcessManager manager;
setUp(() {
tmp = fs.systemTempDirectory.createTempSync('flutter_tools_');
manager = new RecordingProcessManager(tmp.path);
});
tearDown(() {
tmp.deleteSync(recursive: true);
});
test('start', () async {
Process process = await manager.start('echo', <String>['foo']);
int pid = process.pid;
int exitCode = await process.exitCode;
List<int> stdout = await _consume(process.stdout);
List<int> stderr = await _consume(process.stderr);
expect(exitCode, 0);
expect(_decode(stdout), <String>['foo']);
expect(stderr, isEmpty);
// Force the recording to be written to disk.
await runShutdownHooks();
_Recording recording = _Recording.readFrom(tmp);
expect(recording.manifest, hasLength(1));
Map<String, dynamic> entry = recording.manifest.first;
expect(entry['pid'], pid);
expect(entry['exitCode'], exitCode);
expect(recording.stdoutForEntryAt(0), stdout);
expect(recording.stderrForEntryAt(0), stderr);
});
test('run', () async {
ProcessResult result = await manager.run('echo', <String>['bar']);
int pid = result.pid;
int exitCode = result.exitCode;
String stdout = result.stdout;
String stderr = result.stderr;
expect(exitCode, 0);
expect(stdout, 'bar\n');
expect(stderr, isEmpty);
// Force the recording to be written to disk.
await runShutdownHooks();
_Recording recording = _Recording.readFrom(tmp);
expect(recording.manifest, hasLength(1));
Map<String, dynamic> entry = recording.manifest.first;
expect(entry['pid'], pid);
expect(entry['exitCode'], exitCode);
expect(recording.stdoutForEntryAt(0), stdout);
expect(recording.stderrForEntryAt(0), stderr);
});
test('runSync', () async {
ProcessResult result = manager.runSync('echo', <String>['baz']);
int pid = result.pid;
int exitCode = result.exitCode;
String stdout = result.stdout;
String stderr = result.stderr;
expect(exitCode, 0);
expect(stdout, 'baz\n');
expect(stderr, isEmpty);
// Force the recording to be written to disk.
await runShutdownHooks();
_Recording recording = _Recording.readFrom(tmp);
expect(recording.manifest, hasLength(1));
Map<String, dynamic> entry = recording.manifest.first;
expect(entry['pid'], pid);
expect(entry['exitCode'], exitCode);
expect(recording.stdoutForEntryAt(0), stdout);
expect(recording.stderrForEntryAt(0), stderr);
});
});
group('ReplayProcessManager', () {
ProcessManager manager;
setUp(() async {
await runInMinimalContext(() async {
Directory dir = fs.directory('test/data/process_manager/replay');
manager = await ReplayProcessManager.create(dir.path);
});
});
tearDown(() async {
// Allow the replay manager to clean up
await runShutdownHooks();
});
test('start', () async {
Process process = await manager.start('sing', <String>['ppap']);
int exitCode = await process.exitCode;
List<int> stdout = await _consume(process.stdout);
List<int> stderr = await _consume(process.stderr);
expect(process.pid, 100);
expect(exitCode, 0);
expect(_decode(stdout), <String>['I have a pen', 'I have a pineapple']);
expect(_decode(stderr), <String>['Uh, pineapple pen']);
});
test('run', () async {
ProcessResult result = await manager.run('dance', <String>['gangnam-style']);
expect(result.pid, 101);
expect(result.exitCode, 2);
expect(result.stdout, '');
expect(result.stderr, 'No one can dance like Psy\n');
});
test('runSync', () {
ProcessResult result = manager.runSync('dance', <String>['gangnam-style']);
expect(result.pid, 101);
expect(result.exitCode, 2);
expect(result.stdout, '');
expect(result.stderr, 'No one can dance like Psy\n');
});
});
}
Future<Null> runInMinimalContext(Future<dynamic> method()) async {
AppContext context = new AppContext();
context.putIfAbsent(FileSystem, () => new LocalFileSystem());
context.putIfAbsent(ProcessManager, () => new ProcessManager());
context.putIfAbsent(Logger, () => new BufferLogger());
context.putIfAbsent(OperatingSystemUtils, () => new OperatingSystemUtils());
await context.runInZone(method);
}
/// A testing utility class that encapsulates a recording.
class _Recording {
final File file;
final Archive _archive;
_Recording(this.file, this._archive);
static _Recording readFrom(Directory dir) {
File file = fs.file(path.join(
dir.path, RecordingProcessManager.kDefaultRecordTo));
Archive archive = new ZipDecoder().decodeBytes(file.readAsBytesSync());
return new _Recording(file, archive);
}
List<Map<String, dynamic>> get manifest {
return JSON.decoder.convert(_getFileContent('MANIFEST.txt', UTF8));
}
dynamic stdoutForEntryAt(int index) =>
_getStdioContent(manifest[index], 'stdout');
dynamic stderrForEntryAt(int index) =>
_getStdioContent(manifest[index], 'stderr');
dynamic _getFileContent(String name, Encoding encoding) {
List<int> bytes = _fileNamed(name).content;
return encoding == null ? bytes : encoding.decode(bytes);
}
dynamic _getStdioContent(Map<String, dynamic> entry, String type) {
String basename = entry['basename'];
String encodingName = entry['${type}Encoding'];
Encoding encoding;
if (encodingName != null)
encoding = encodingName == 'system'
? SYSTEM_ENCODING
: Encoding.getByName(encodingName);
return _getFileContent('$basename.$type', encoding);
}
ArchiveFile _fileNamed(String name) => _archive.firstWhere(_hasName(name));
Predicate<ArchiveFile> _hasName(String name) =>
(ArchiveFile file) => file.name == name;
}
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:process/process.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/process_manager.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
......
...@@ -10,7 +10,6 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -10,7 +10,6 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/process_manager.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/devfs.dart';
...@@ -23,6 +22,7 @@ import 'package:flutter_tools/src/usage.dart'; ...@@ -23,6 +22,7 @@ import 'package:flutter_tools/src/usage.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:process/process.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
/// Return the test logger. This assumes that the current Logger is a BufferLogger. /// Return the test logger. This assumes that the current Logger is a BufferLogger.
...@@ -43,7 +43,7 @@ void testUsingContext(String description, dynamic testMethod(), { ...@@ -43,7 +43,7 @@ void testUsingContext(String description, dynamic testMethod(), {
// Initialize the test context with some default mocks. // Initialize the test context with some default mocks.
// Seed these context entries first since others depend on them // Seed these context entries first since others depend on them
testContext.putIfAbsent(FileSystem, () => new LocalFileSystem()); testContext.putIfAbsent(FileSystem, () => new LocalFileSystem());
testContext.putIfAbsent(ProcessManager, () => new ProcessManager()); testContext.putIfAbsent(ProcessManager, () => new LocalProcessManager());
testContext.putIfAbsent(Logger, () => new BufferLogger()); testContext.putIfAbsent(Logger, () => new BufferLogger());
// Order-independent context entries // Order-independent context entries
......
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