Commit 2dbceafb authored by Devon Carew's avatar Devon Carew

introduce globals scoped to the app's context

parent b2c710dd
...@@ -8,6 +8,8 @@ import 'dart:io'; ...@@ -8,6 +8,8 @@ import 'dart:io';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:stack_trace/stack_trace.dart'; import 'package:stack_trace/stack_trace.dart';
import 'src/base/context.dart';
import 'src/base/logger.dart';
import 'src/base/process.dart'; import 'src/base/process.dart';
import 'src/commands/analyze.dart'; import 'src/commands/analyze.dart';
import 'src/commands/apk.dart'; import 'src/commands/apk.dart';
...@@ -27,6 +29,7 @@ import 'src/commands/stop.dart'; ...@@ -27,6 +29,7 @@ import 'src/commands/stop.dart';
import 'src/commands/test.dart'; import 'src/commands/test.dart';
import 'src/commands/trace.dart'; import 'src/commands/trace.dart';
import 'src/commands/upgrade.dart'; import 'src/commands/upgrade.dart';
import 'src/device.dart';
import 'src/runner/flutter_command_runner.dart'; import 'src/runner/flutter_command_runner.dart';
/// Main entry point for commands. /// Main entry point for commands.
...@@ -62,7 +65,12 @@ Future main(List<String> args) async { ...@@ -62,7 +65,12 @@ Future main(List<String> args) async {
if (args.isNotEmpty && args[0] == 'init') if (args.isNotEmpty && args[0] == 'init')
args[0] = 'create'; args[0] = 'create';
// Initialize globals.
context[Logger] = new StdoutLogger();
context[DeviceManager] = new DeviceManager();
dynamic result = await runner.run(args); dynamic result = await runner.run(args);
if (result is int) if (result is int)
exit(result); exit(result);
}, onError: (error, Chain chain) { }, onError: (error, Chain chain) {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
// https://android.googlesource.com/platform/system/core/+/android-4.4_r1/adb/OVERVIEW.TXT // https://android.googlesource.com/platform/system/core/+/android-4.4_r1/adb/OVERVIEW.TXT
......
...@@ -10,7 +10,7 @@ import 'package:path/path.dart' as path; ...@@ -10,7 +10,7 @@ import 'package:path/path.dart' as path;
import '../application_package.dart'; import '../application_package.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/os.dart'; import '../base/os.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
......
...@@ -8,7 +8,7 @@ import 'dart:io'; ...@@ -8,7 +8,7 @@ import 'dart:io';
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'base/context.dart'; import 'base/globals.dart';
import 'base/os.dart'; import 'base/os.dart';
import 'base/process.dart'; import 'base/process.dart';
import 'build_configuration.dart'; import 'build_configuration.dart';
......
...@@ -3,9 +3,8 @@ ...@@ -3,9 +3,8 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:io';
final AppContext _defaultContext = new _DefaultAppContext(); final AppContext _defaultContext = new AppContext();
/// A singleton for application functionality. This singleton can be different /// A singleton for application functionality. This singleton can be different
/// on a per-Zone basis. /// on a per-Zone basis.
...@@ -14,57 +13,42 @@ AppContext get context { ...@@ -14,57 +13,42 @@ AppContext get context {
return currentContext == null ? _defaultContext : currentContext; return currentContext == null ? _defaultContext : currentContext;
} }
/// Display an error level message to the user. Commands should use this if they class AppContext {
/// fail in some way. Map<Type, dynamic> _instances = <Type, dynamic>{};
void printError(String message, [StackTrace stackTrace]) => context.printError(message, stackTrace);
/// Display normal output of the command. This should be used for things like dynamic getVariable(Type type) {
/// progress messages, success messages, or just normal command output. if (_instances.containsKey(type))
void printStatus(String message) => context.printStatus(message); return _instances[type];
/// Use this for verbose tracing output. Users can turn this output on in order AppContext parent = _calcParent(Zone.current);
/// to help diagnose issues with the toolchain or with their setup. return parent?.getVariable(type);
void printTrace(String message) => context.printTrace(message); }
abstract class AppContext {
bool get verbose;
set verbose(bool value);
void printError(String message, [StackTrace stackTrace]);
void printStatus(String message);
void printTrace(String message);
}
class _DefaultAppContext implements AppContext {
DateTime _startTime = new DateTime.now();
bool _verbose = false; void setVariable(Type type, dynamic instance) {
_instances[type] = instance;
}
bool get verbose => _verbose; dynamic operator[](Type type) => getVariable(type);
set verbose(bool value) { void operator[]=(Type type, dynamic instance) => setVariable(type, instance);
_verbose = value;
}
void printError(String message, [StackTrace stackTrace]) { AppContext _calcParent(Zone zone) {
stderr.writeln(_prefix + message); if (this == _defaultContext)
if (stackTrace != null) return null;
stderr.writeln(stackTrace);
}
void printStatus(String message) { Zone parentZone = zone.parent;
print(_prefix + message); if (parentZone == null)
} return _defaultContext;
void printTrace(String message) { AppContext deps = parentZone['context'];
if (_verbose) if (deps == this) {
print('$_prefix- $message'); return _calcParent(parentZone);
} else {
return deps != null ? deps : _defaultContext;
}
} }
String get _prefix { dynamic runInZone(dynamic method()) {
if (!_verbose) return runZoned(method, zoneValues: {'context': this});
return '';
Duration elapsed = new DateTime.now().difference(_startTime);
return '[${elapsed.inMilliseconds.toString().padLeft(4)} ms] ';
} }
} }
// 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 '../device.dart';
import 'context.dart';
import 'logger.dart';
DeviceManager get deviceManager => context[DeviceManager];
Logger get logger => context[Logger];
/// Display an error level message to the user. Commands should use this if they
/// fail in some way.
void printError(String message, [StackTrace stackTrace]) => logger.printError(message, stackTrace);
/// Display normal output of the command. This should be used for things like
/// progress messages, success messages, or just normal command output.
void printStatus(String message) => logger.printStatus(message);
/// Use this for verbose tracing output. Users can turn this output on in order
/// to help diagnose issues with the toolchain or with their setup.
void printTrace(String message) => logger.printTrace(message);
// 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:io';
abstract class Logger {
bool verbose = false;
/// Display an error level message to the user. Commands should use this if they
/// fail in some way.
void printError(String message, [StackTrace stackTrace]);
/// Display normal output of the command. This should be used for things like
/// progress messages, success messages, or just normal command output.
void printStatus(String message);
/// Use this for verbose tracing output. Users can turn this output on in order
/// to help diagnose issues with the toolchain or with their setup.
void printTrace(String message);
}
class StdoutLogger implements Logger {
DateTime _startTime = new DateTime.now();
bool verbose = false;
void printError(String message, [StackTrace stackTrace]) {
stderr.writeln(_prefix + message);
if (stackTrace != null)
stderr.writeln(stackTrace);
}
void printStatus(String message) {
print(_prefix + message);
}
void printTrace(String message) {
if (verbose)
print('$_prefix- $message');
}
String get _prefix {
if (!verbose)
return '';
Duration elapsed = new DateTime.now().difference(_startTime);
return '[${elapsed.inMilliseconds.toString().padLeft(4)} ms] ';
}
}
class BufferLogger implements Logger {
StringBuffer _error = new StringBuffer();
StringBuffer _status = new StringBuffer();
StringBuffer _trace = new StringBuffer();
bool verbose = false;
String get errorText => _error.toString();
String get statusText => _status.toString();
String get traceText => _trace.toString();
void printError(String message, [StackTrace stackTrace]) => _error.writeln(message);
void printStatus(String message) => _status.writeln(message);
void printTrace(String message) => _trace.writeln(message);
}
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'context.dart'; import 'globals.dart';
typedef String StringConverter(String string); typedef String StringConverter(String string);
......
...@@ -6,7 +6,7 @@ import 'dart:io'; ...@@ -6,7 +6,7 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'base/context.dart'; import 'base/globals.dart';
enum BuildType { enum BuildType {
prebuilt, prebuilt,
......
...@@ -11,7 +11,7 @@ import 'package:den_api/den_api.dart'; ...@@ -11,7 +11,7 @@ import 'package:den_api/den_api.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
......
...@@ -12,8 +12,8 @@ import 'package:yaml/yaml.dart'; ...@@ -12,8 +12,8 @@ import 'package:yaml/yaml.dart';
import '../android/device_android.dart'; import '../android/device_android.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../device.dart'; import '../device.dart';
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import '../base/context.dart'; import '../base/globals.dart';
import '../flx.dart'; import '../flx.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../toolchain.dart'; import '../toolchain.dart';
......
...@@ -11,7 +11,7 @@ import 'package:path/path.dart' as path; ...@@ -11,7 +11,7 @@ import 'package:path/path.dart' as path;
import '../android/android.dart' as android; import '../android/android.dart' as android;
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
class CreateCommand extends Command { class CreateCommand extends Command {
......
...@@ -9,6 +9,8 @@ import 'dart:io'; ...@@ -9,6 +9,8 @@ import 'dart:io';
import '../android/adb.dart'; import '../android/adb.dart';
import '../android/device_android.dart'; import '../android/device_android.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/globals.dart';
import '../base/logger.dart';
import '../device.dart'; import '../device.dart';
import '../ios/device_ios.dart'; import '../ios/device_ios.dart';
import '../ios/simulator.dart'; import '../ios/simulator.dart';
...@@ -33,9 +35,11 @@ class DaemonCommand extends FlutterCommand { ...@@ -33,9 +35,11 @@ class DaemonCommand extends FlutterCommand {
Future<int> runInProject() { Future<int> runInProject() {
printStatus('Starting device daemon...'); printStatus('Starting device daemon...');
NotifyingAppContext appContext = new NotifyingAppContext(); AppContext appContext = new AppContext();
NotifyingLogger notifyingLogger = new NotifyingLogger();
appContext[Logger] = notifyingLogger;
return runZoned(() { return appContext.runInZone(() {
Stream<Map<String, dynamic>> commandStream = stdin Stream<Map<String, dynamic>> commandStream = stdin
.transform(UTF8.decoder) .transform(UTF8.decoder)
.transform(const LineSplitter()) .transform(const LineSplitter())
...@@ -47,10 +51,10 @@ class DaemonCommand extends FlutterCommand { ...@@ -47,10 +51,10 @@ class DaemonCommand extends FlutterCommand {
Daemon daemon = new Daemon(commandStream, (Map command) { Daemon daemon = new Daemon(commandStream, (Map command) {
stdout.writeln('[${JSON.encode(command, toEncodable: _jsonEncodeObject)}]'); stdout.writeln('[${JSON.encode(command, toEncodable: _jsonEncodeObject)}]');
}, daemonCommand: this, appContext: appContext); }, daemonCommand: this, notifyingLogger: notifyingLogger);
return daemon.onExit; return daemon.onExit;
}, zoneValues: {'context': appContext}); });
} }
dynamic _jsonEncodeObject(dynamic object) { dynamic _jsonEncodeObject(dynamic object) {
...@@ -67,7 +71,7 @@ typedef Future<dynamic> CommandHandler(dynamic args); ...@@ -67,7 +71,7 @@ typedef Future<dynamic> CommandHandler(dynamic args);
class Daemon { class Daemon {
Daemon(Stream<Map> commandStream, this.sendCommand, { Daemon(Stream<Map> commandStream, this.sendCommand, {
this.daemonCommand, this.daemonCommand,
this.appContext this.notifyingLogger
}) { }) {
// Set up domains. // Set up domains.
_registerDomain(new DaemonDomain(this)); _registerDomain(new DaemonDomain(this));
...@@ -83,7 +87,7 @@ class Daemon { ...@@ -83,7 +87,7 @@ class Daemon {
final DispatchComand sendCommand; final DispatchComand sendCommand;
final DaemonCommand daemonCommand; final DaemonCommand daemonCommand;
final NotifyingAppContext appContext; final NotifyingLogger notifyingLogger;
final Completer<int> _onExitCompleter = new Completer<int>(); final Completer<int> _onExitCompleter = new Completer<int>();
final Map<String, Domain> _domainMap = <String, Domain>{}; final Map<String, Domain> _domainMap = <String, Domain>{};
...@@ -185,7 +189,7 @@ class DaemonDomain extends Domain { ...@@ -185,7 +189,7 @@ class DaemonDomain extends Domain {
registerHandler('version', version); registerHandler('version', version);
registerHandler('shutdown', shutdown); registerHandler('shutdown', shutdown);
_subscription = daemon.appContext.onMessage.listen((LogMessage message) { _subscription = daemon.notifyingLogger.onMessage.listen((LogMessage message) {
if (message.stackTrace != null) { if (message.stackTrace != null) {
sendEvent('daemon.logMessage', { sendEvent('daemon.logMessage', {
'level': message.level, 'level': message.level,
...@@ -467,13 +471,11 @@ dynamic _toJsonable(dynamic obj) { ...@@ -467,13 +471,11 @@ dynamic _toJsonable(dynamic obj) {
return '$obj'; return '$obj';
} }
class NotifyingAppContext implements AppContext { class NotifyingLogger extends Logger {
StreamController<LogMessage> _messageController = new StreamController<LogMessage>.broadcast(); StreamController<LogMessage> _messageController = new StreamController<LogMessage>.broadcast();
Stream<LogMessage> get onMessage => _messageController.stream; Stream<LogMessage> get onMessage => _messageController.stream;
bool verbose = false;
void printError(String message, [StackTrace stackTrace]) { void printError(String message, [StackTrace stackTrace]) {
_messageController.add(new LogMessage('error', message, stackTrace)); _messageController.add(new LogMessage('error', message, stackTrace));
} }
......
...@@ -8,7 +8,7 @@ import "dart:io"; ...@@ -8,7 +8,7 @@ import "dart:io";
import "package:path/path.dart" as path; import "package:path/path.dart" as path;
import "../artifacts.dart"; import "../artifacts.dart";
import "../base/context.dart"; import "../base/globals.dart";
import "../base/process.dart"; import "../base/process.dart";
import "../runner/flutter_command.dart"; import "../runner/flutter_command.dart";
import "../runner/flutter_command_runner.dart"; import "../runner/flutter_command_runner.dart";
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import '../base/context.dart'; import '../base/globals.dart';
import '../device.dart'; import '../device.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
...@@ -15,9 +15,7 @@ class ListCommand extends FlutterCommand { ...@@ -15,9 +15,7 @@ class ListCommand extends FlutterCommand {
bool get requiresProjectRoot => false; bool get requiresProjectRoot => false;
Future<int> runInProject() async { Future<int> runInProject() async {
DeviceManager deviceManager = new DeviceManager(); List<Device> devices = await deviceManager.getAllConnectedDevices();
List<Device> devices = await deviceManager.getDevices();
if (devices.isEmpty) { if (devices.isEmpty) {
printStatus('No connected devices.'); printStatus('No connected devices.');
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import 'start.dart'; import 'start.dart';
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import 'dart:async'; import 'dart:async';
import '../base/context.dart'; import '../base/globals.dart';
import '../device.dart'; import '../device.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
...@@ -24,21 +24,14 @@ class LogsCommand extends FlutterCommand { ...@@ -24,21 +24,14 @@ class LogsCommand extends FlutterCommand {
Future<int> runInProject() async { Future<int> runInProject() async {
DeviceManager deviceManager = new DeviceManager(); DeviceManager deviceManager = new DeviceManager();
List<Device> devices; deviceManager.specifiedDeviceId = globalResults['device-id'];
String deviceId = globalResults['device-id']; List<Device> devices = await deviceManager.getDevices();
if (deviceId != null) {
Device device = await deviceManager.getDeviceById(deviceId);
if (device == null) {
printError("No device found with id '$deviceId'.");
return 1;
}
devices = <Device>[device];
} else {
devices = await deviceManager.getDevices();
}
if (devices.isEmpty) { if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
printError("No device found with id '${deviceManager.specifiedDeviceId}'.");
return 1;
} else if (devices.isEmpty) {
printStatus('No connected devices.'); printStatus('No connected devices.');
return 0; return 0;
} }
......
...@@ -7,7 +7,7 @@ import 'dart:io'; ...@@ -7,7 +7,7 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../base/context.dart'; import '../base/globals.dart';
import '../flx.dart'; import '../flx.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
......
...@@ -8,7 +8,7 @@ import 'dart:io'; ...@@ -8,7 +8,7 @@ import 'dart:io';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../flx.dart' as flx; import '../flx.dart' as flx;
...@@ -118,7 +118,7 @@ class RunMojoCommand extends FlutterCommand { ...@@ -118,7 +118,7 @@ class RunMojoCommand extends FlutterCommand {
if (useDevtools) { if (useDevtools) {
final String buildFlag = argResults['mojo-debug'] ? '--debug' : '--release'; final String buildFlag = argResults['mojo-debug'] ? '--debug' : '--release';
args.add(buildFlag); args.add(buildFlag);
if (context.verbose) if (logger.verbose)
args.add('--verbose'); args.add('--verbose');
} }
......
...@@ -9,7 +9,7 @@ import 'package:path/path.dart' as path; ...@@ -9,7 +9,7 @@ import 'package:path/path.dart' as path;
import '../application_package.dart'; import '../application_package.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../device.dart'; import '../device.dart';
import '../flx.dart'; import '../flx.dart';
......
...@@ -9,7 +9,7 @@ import 'package:path/path.dart' as path; ...@@ -9,7 +9,7 @@ import 'package:path/path.dart' as path;
import 'package:test/src/executable.dart' as executable; import 'package:test/src/executable.dart' as executable;
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../test/loader.dart' as loader; import '../test/loader.dart' as loader;
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import '../android/device_android.dart'; import '../android/device_android.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
class TraceCommand extends FlutterCommand { class TraceCommand extends FlutterCommand {
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import 'dart:async'; import 'dart:async';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import '../runner/version.dart'; import '../runner/version.dart';
......
...@@ -7,33 +7,48 @@ import 'dart:async'; ...@@ -7,33 +7,48 @@ import 'dart:async';
import 'android/device_android.dart'; import 'android/device_android.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'base/common.dart'; import 'base/common.dart';
import 'base/context.dart'; import 'base/globals.dart';
import 'build_configuration.dart'; import 'build_configuration.dart';
import 'ios/device_ios.dart'; import 'ios/device_ios.dart';
import 'toolchain.dart'; import 'toolchain.dart';
/// A class to get all available devices. /// A class to get all available devices.
class DeviceManager { class DeviceManager {
/// Constructing DeviceManagers is cheap; they only do expensive work if some
/// of their methods are invoked.
DeviceManager() { DeviceManager() {
// Init the known discoverers. // Register the known discoverers.
_deviceDiscoverers.add(new AndroidDeviceDiscovery()); _deviceDiscoverers.add(new AndroidDeviceDiscovery());
_deviceDiscoverers.add(new IOSDeviceDiscovery()); _deviceDiscoverers.add(new IOSDeviceDiscovery());
_deviceDiscoverers.add(new IOSSimulatorDiscovery()); _deviceDiscoverers.add(new IOSSimulatorDiscovery());
}
Future _init() {
if (_initedCompleter == null) {
_initedCompleter = new Completer();
Future.forEach(_deviceDiscoverers, (DeviceDiscovery discoverer) {
if (!discoverer.supportsPlatform)
return null;
return discoverer.init();
}).then((_) {
_initedCompleter.complete();
}).catchError((error, stackTrace) {
_initedCompleter.completeError(error, stackTrace);
});
}
Future.forEach(_deviceDiscoverers, (DeviceDiscovery discoverer) { return _initedCompleter.future;
if (!discoverer.supportsPlatform)
return null;
return discoverer.init();
}).then((_) {
_initedCompleter.complete();
}).catchError((error, stackTrace) {
_initedCompleter.completeError(error, stackTrace);
});
} }
List<DeviceDiscovery> _deviceDiscoverers = <DeviceDiscovery>[]; List<DeviceDiscovery> _deviceDiscoverers = <DeviceDiscovery>[];
Completer _initedCompleter = new Completer(); /// A user-specified device ID.
String specifiedDeviceId;
Completer _initedCompleter;
bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
/// Return the device with the matching ID; else, complete the Future with /// Return the device with the matching ID; else, complete the Future with
/// `null`. /// `null`.
...@@ -41,15 +56,26 @@ class DeviceManager { ...@@ -41,15 +56,26 @@ class DeviceManager {
/// This does a case insentitive compare with `deviceId`. /// This does a case insentitive compare with `deviceId`.
Future<Device> getDeviceById(String deviceId) async { Future<Device> getDeviceById(String deviceId) async {
deviceId = deviceId.toLowerCase(); deviceId = deviceId.toLowerCase();
List<Device> devices = await getDevices(); List<Device> devices = await getAllConnectedDevices();
return devices.firstWhere( return devices.firstWhere(
(Device device) => device.id.toLowerCase() == deviceId, (Device device) => device.id.toLowerCase() == deviceId,
orElse: () => null orElse: () => null
); );
} }
/// Return the list of connected devices, filtered by any user-specified device id.
Future<List<Device>> getDevices() async { Future<List<Device>> getDevices() async {
await _initedCompleter.future; if (specifiedDeviceId == null) {
return getAllConnectedDevices();
} else {
Device device = await getDeviceById(specifiedDeviceId);
return device == null ? <Device>[] : <Device>[device];
}
}
/// Return the list of all connected devices.
Future<List<Device>> getAllConnectedDevices() async {
await _init();
return _deviceDiscoverers return _deviceDiscoverers
.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform) .where((DeviceDiscovery discoverer) => discoverer.supportsPlatform)
......
...@@ -13,7 +13,7 @@ import 'package:flx/signing.dart'; ...@@ -13,7 +13,7 @@ import 'package:flx/signing.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
import 'base/context.dart'; import 'base/globals.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'toolchain.dart'; import 'toolchain.dart';
......
...@@ -9,7 +9,7 @@ import 'package:path/path.dart' as path; ...@@ -9,7 +9,7 @@ import 'package:path/path.dart' as path;
import '../application_package.dart'; import '../application_package.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../device.dart'; import '../device.dart';
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import 'dart:convert' show JSON; import 'dart:convert' show JSON;
import 'dart:io'; import 'dart:io';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
const String _xcrunPath = '/usr/bin/xcrun'; const String _xcrunPath = '/usr/bin/xcrun';
......
...@@ -8,9 +8,9 @@ import 'dart:io'; ...@@ -8,9 +8,9 @@ import 'dart:io';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../base/context.dart';
import '../build_configuration.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/globals.dart';
import '../build_configuration.dart';
import '../device.dart'; import '../device.dart';
import '../toolchain.dart'; import '../toolchain.dart';
import 'flutter_command_runner.dart'; import 'flutter_command_runner.dart';
......
...@@ -10,7 +10,7 @@ import 'package:args/command_runner.dart'; ...@@ -10,7 +10,7 @@ import 'package:args/command_runner.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/context.dart'; import '../base/globals.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import 'version.dart'; import 'version.dart';
...@@ -158,10 +158,15 @@ class FlutterCommandRunner extends CommandRunner { ...@@ -158,10 +158,15 @@ class FlutterCommandRunner extends CommandRunner {
} }
Future<int> runCommand(ArgResults globalResults) { Future<int> runCommand(ArgResults globalResults) {
_globalResults = globalResults;
// Check for verbose.
if (globalResults['verbose']) if (globalResults['verbose'])
context.verbose = true; logger.verbose = true;
// See if the user specified a specific device.
deviceManager.specifiedDeviceId = globalResults['device-id'];
_globalResults = globalResults;
ArtifactStore.flutterRoot = path.normalize(path.absolute(globalResults['flutter-root'])); ArtifactStore.flutterRoot = path.normalize(path.absolute(globalResults['flutter-root']));
if (globalResults.wasParsed('package-root')) if (globalResults.wasParsed('package-root'))
ArtifactStore.packageRoot = path.normalize(path.absolute(globalResults['package-root'])); ArtifactStore.packageRoot = path.normalize(path.absolute(globalResults['package-root']));
......
...@@ -5,11 +5,13 @@ ...@@ -5,11 +5,13 @@
import 'package:flutter_tools/src/android/device_android.dart'; import 'package:flutter_tools/src/android/device_android.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
group('android_device', () { group('android_device', () {
test('stores the requested id', () { testUsingContext('stores the requested id', () {
String deviceId = '1234'; String deviceId = '1234';
AndroidDevice device = new AndroidDevice(deviceId); AndroidDevice device = new AndroidDevice(deviceId);
expect(device.id, equals(deviceId)); expect(device.id, equals(deviceId));
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'package:flutter_tools/src/base/context.dart' hide context;
import 'package:flutter_tools/src/base/globals.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
main() => defineTests(); main() => defineTests();
...@@ -12,55 +12,45 @@ main() => defineTests(); ...@@ -12,55 +12,45 @@ main() => defineTests();
defineTests() { defineTests() {
group('DeviceManager', () { group('DeviceManager', () {
test('error', () async { test('error', () async {
MockContext mockContext = new MockContext(); AppContext context = new AppContext();
BufferLogger mockLogger = new BufferLogger();
context[Logger] = mockLogger;
runZoned(() { context.runInZone(() {
printError('foo bar'); printError('foo bar');
}, zoneValues: {'context': mockContext}); });
expect(mockContext.errorText, 'foo bar\n'); expect(mockLogger.errorText, 'foo bar\n');
expect(mockContext.statusText, ''); expect(mockLogger.statusText, '');
expect(mockContext.traceText, ''); expect(mockLogger.traceText, '');
}); });
test('status', () async { test('status', () async {
MockContext mockContext = new MockContext(); AppContext context = new AppContext();
BufferLogger mockLogger = new BufferLogger();
context[Logger] = mockLogger;
runZoned(() { context.runInZone(() {
printStatus('foo bar'); printStatus('foo bar');
}, zoneValues: {'context': mockContext}); });
expect(mockContext.errorText, ''); expect(mockLogger.errorText, '');
expect(mockContext.statusText, 'foo bar\n'); expect(mockLogger.statusText, 'foo bar\n');
expect(mockContext.traceText, ''); expect(mockLogger.traceText, '');
}); });
test('trace', () async { test('trace', () async {
MockContext mockContext = new MockContext(); AppContext context = new AppContext();
BufferLogger mockLogger = new BufferLogger();
context[Logger] = mockLogger;
runZoned(() { context.runInZone(() {
printTrace('foo bar'); printTrace('foo bar');
}, zoneValues: {'context': mockContext}); });
expect(mockContext.errorText, ''); expect(mockLogger.errorText, '');
expect(mockContext.statusText, ''); expect(mockLogger.statusText, '');
expect(mockContext.traceText, 'foo bar\n'); expect(mockLogger.traceText, 'foo bar\n');
}); });
}); });
} }
class MockContext implements AppContext {
bool verbose = false;
StringBuffer _error = new StringBuffer();
StringBuffer _status = new StringBuffer();
StringBuffer _trace = new StringBuffer();
String get errorText => _error.toString();
String get statusText => _status.toString();
String get traceText => _trace.toString();
void printError(String message, [StackTrace stackTrace]) => _error.writeln(message);
void printStatus(String message) => _status.writeln(message);
void printTrace(String message) => _trace.writeln(message);
}
...@@ -11,6 +11,8 @@ import 'package:flutter_tools/src/commands/create.dart'; ...@@ -11,6 +11,8 @@ import 'package:flutter_tools/src/commands/create.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
...@@ -31,7 +33,7 @@ defineTests() { ...@@ -31,7 +33,7 @@ defineTests() {
// TODO(devoncarew): https://github.com/flutter/flutter/issues/1709 // TODO(devoncarew): https://github.com/flutter/flutter/issues/1709
if (Platform.isLinux) { if (Platform.isLinux) {
// Verify that we create a project that is well-formed. // Verify that we create a project that is well-formed.
test('flutter-simple', () async { testUsingContext('flutter-simple', () async {
ArtifactStore.flutterRoot = '../..'; ArtifactStore.flutterRoot = '../..';
CreateCommand command = new CreateCommand(); CreateCommand command = new CreateCommand();
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/globals.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/commands/daemon.dart'; import 'package:flutter_tools/src/commands/daemon.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -16,10 +18,13 @@ main() => defineTests(); ...@@ -16,10 +18,13 @@ main() => defineTests();
defineTests() { defineTests() {
group('daemon', () { group('daemon', () {
Daemon daemon; Daemon daemon;
NotifyingAppContext appContext; AppContext appContext;
NotifyingLogger notifyingLogger;
setUp(() { setUp(() {
appContext = new NotifyingAppContext(); appContext = new AppContext();
notifyingLogger = new NotifyingLogger();
appContext[Logger] = notifyingLogger;
}); });
tearDown(() { tearDown(() {
...@@ -33,7 +38,7 @@ defineTests() { ...@@ -33,7 +38,7 @@ defineTests() {
daemon = new Daemon( daemon = new Daemon(
commands.stream, commands.stream,
(Map<String, dynamic> result) => responses.add(result), (Map<String, dynamic> result) => responses.add(result),
appContext: appContext notifyingLogger: notifyingLogger
); );
commands.add({'id': 0, 'method': 'daemon.version'}); commands.add({'id': 0, 'method': 'daemon.version'});
Map response = await responses.stream.where(_notEvent).first; Map response = await responses.stream.where(_notEvent).first;
...@@ -43,13 +48,13 @@ defineTests() { ...@@ -43,13 +48,13 @@ defineTests() {
}); });
test('daemon.logMessage', () { test('daemon.logMessage', () {
return runZoned(() async { return appContext.runInZone(() async {
StreamController<Map<String, dynamic>> commands = new StreamController(); StreamController<Map<String, dynamic>> commands = new StreamController();
StreamController<Map<String, dynamic>> responses = new StreamController(); StreamController<Map<String, dynamic>> responses = new StreamController();
daemon = new Daemon( daemon = new Daemon(
commands.stream, commands.stream,
(Map<String, dynamic> result) => responses.add(result), (Map<String, dynamic> result) => responses.add(result),
appContext: appContext notifyingLogger: notifyingLogger
); );
printError('daemon.logMessage test'); printError('daemon.logMessage test');
Map<String, dynamic> response = await responses.stream.where((Map<String, dynamic> map) { Map<String, dynamic> response = await responses.stream.where((Map<String, dynamic> map) {
...@@ -60,7 +65,7 @@ defineTests() { ...@@ -60,7 +65,7 @@ defineTests() {
Map<String, String> logMessage = response['params']; Map<String, String> logMessage = response['params'];
expect(logMessage['level'], 'error'); expect(logMessage['level'], 'error');
expect(logMessage['message'], 'daemon.logMessage test'); expect(logMessage['message'], 'daemon.logMessage test');
}, zoneValues: {'context': appContext}); });
}); });
test('daemon.shutdown', () async { test('daemon.shutdown', () async {
...@@ -69,7 +74,7 @@ defineTests() { ...@@ -69,7 +74,7 @@ defineTests() {
daemon = new Daemon( daemon = new Daemon(
commands.stream, commands.stream,
(Map<String, dynamic> result) => responses.add(result), (Map<String, dynamic> result) => responses.add(result),
appContext: appContext notifyingLogger: notifyingLogger
); );
commands.add({'id': 0, 'method': 'daemon.shutdown'}); commands.add({'id': 0, 'method': 'daemon.shutdown'});
return daemon.onExit.then((int code) { return daemon.onExit.then((int code) {
...@@ -87,7 +92,7 @@ defineTests() { ...@@ -87,7 +92,7 @@ defineTests() {
commands.stream, commands.stream,
(Map<String, dynamic> result) => responses.add(result), (Map<String, dynamic> result) => responses.add(result),
daemonCommand: command, daemonCommand: command,
appContext: appContext notifyingLogger: notifyingLogger
); );
MockDeviceStore mockDevices = command.devices; MockDeviceStore mockDevices = command.devices;
...@@ -113,7 +118,7 @@ defineTests() { ...@@ -113,7 +118,7 @@ defineTests() {
daemon = new Daemon( daemon = new Daemon(
commands.stream, commands.stream,
(Map<String, dynamic> result) => responses.add(result), (Map<String, dynamic> result) => responses.add(result),
appContext: appContext notifyingLogger: notifyingLogger
); );
commands.add({'id': 0, 'method': 'device.getDevices'}); commands.add({'id': 0, 'method': 'device.getDevices'});
Map response = await responses.stream.where(_notEvent).first; Map response = await responses.stream.where(_notEvent).first;
......
...@@ -5,11 +5,13 @@ ...@@ -5,11 +5,13 @@
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
group('DeviceManager', () { group('DeviceManager', () {
test('getDevices', () async { testUsingContext('getDevices', () async {
// Test that DeviceManager.getDevices() doesn't throw. // Test that DeviceManager.getDevices() doesn't throw.
DeviceManager deviceManager = new DeviceManager(); DeviceManager deviceManager = new DeviceManager();
List<Device> devices = await deviceManager.getDevices(); List<Device> devices = await deviceManager.getDevices();
......
...@@ -33,7 +33,7 @@ defineTests() { ...@@ -33,7 +33,7 @@ defineTests() {
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command); ..addCommand(command);
runner.run(['install']).then((int code) => expect(code, equals(0))); return runner.run(['install']).then((int code) => expect(code, equals(0)));
}); });
test('returns 0 when iOS is connected and ready for an install', () { test('returns 0 when iOS is connected and ready for an install', () {
...@@ -55,7 +55,7 @@ defineTests() { ...@@ -55,7 +55,7 @@ defineTests() {
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command); ..addCommand(command);
runner.run(['install']).then((int code) => expect(code, equals(0))); return runner.run(['install']).then((int code) => expect(code, equals(0)));
}); });
}); });
} }
...@@ -10,12 +10,13 @@ import 'package:mockito/mockito.dart'; ...@@ -10,12 +10,13 @@ import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/mocks.dart'; import 'src/mocks.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
group('list', () { group('list', () {
test('returns 0 when called', () { testUsingContext('returns 0 when called', () {
final String mockCommand = Platform.isWindows ? 'cmd /c echo' : 'echo'; final String mockCommand = Platform.isWindows ? 'cmd /c echo' : 'echo';
ListCommand command = new ListCommand(); ListCommand command = new ListCommand();
...@@ -36,9 +37,8 @@ defineTests() { ...@@ -36,9 +37,8 @@ defineTests() {
// Instead, cause the test to run the echo command. // Instead, cause the test to run the echo command.
when(mockDevices.iOSSimulator.xcrunPath).thenReturn(mockCommand); when(mockDevices.iOSSimulator.xcrunPath).thenReturn(mockCommand);
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')..addCommand(command);
..addCommand(command); return runner.run(['list']).then((int code) => expect(code, equals(0)));
runner.run(['list']).then((int code) => expect(code, equals(0)));
}); });
}); });
} }
...@@ -9,12 +9,13 @@ import 'package:mockito/mockito.dart'; ...@@ -9,12 +9,13 @@ import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/mocks.dart'; import 'src/mocks.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
group('listen', () { group('listen', () {
test('returns 0 when no device is connected', () { testUsingContext('returns 0 when no device is connected', () {
ListenCommand command = new ListenCommand(singleRun: true); ListenCommand command = new ListenCommand(singleRun: true);
applyMocksToCommand(command); applyMocksToCommand(command);
MockDeviceStore mockDevices = command.devices; MockDeviceStore mockDevices = command.devices;
...@@ -24,7 +25,7 @@ defineTests() { ...@@ -24,7 +25,7 @@ defineTests() {
when(mockDevices.iOSSimulator.isConnected()).thenReturn(false); when(mockDevices.iOSSimulator.isConnected()).thenReturn(false);
CommandRunner runner = new FlutterCommandRunner()..addCommand(command); CommandRunner runner = new FlutterCommandRunner()..addCommand(command);
runner.run(['listen']).then((int code) => expect(code, equals(0))); return runner.run(['listen']).then((int code) => expect(code, equals(0)));
}); });
}); });
} }
...@@ -8,16 +8,17 @@ import 'package:flutter_tools/src/runner/flutter_command_runner.dart'; ...@@ -8,16 +8,17 @@ import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/mocks.dart'; import 'src/mocks.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
group('logs', () { group('logs', () {
test('fail with a bad device id', () { testUsingContext('fail with a bad device id', () {
LogsCommand command = new LogsCommand(); LogsCommand command = new LogsCommand();
applyMocksToCommand(command); applyMocksToCommand(command);
CommandRunner runner = new FlutterCommandRunner()..addCommand(command); CommandRunner runner = new FlutterCommandRunner()..addCommand(command);
runner.run(<String>['-d', 'abc123', 'logs']).then((int code) { return runner.run(<String>['-d', 'abc123', 'logs']).then((int code) {
expect(code, equals(1)); expect(code, equals(1));
}); });
}); });
......
// 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 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:test/test.dart';
void testUsingContext(String description, dynamic testMethod(), { Timeout timeout }) {
test(description, () {
AppContext testContext = new AppContext();
testContext[Logger] = new BufferLogger();
testContext[DeviceManager] = new MockDeviceManager();
return testContext.runInZone(testMethod);
}, timeout: timeout);
}
class MockDeviceManager implements DeviceManager {
String specifiedDeviceId;
bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
Future<List<Device>> getAllConnectedDevices() => new Future.value(<Device>[]);
Future<Device> getDeviceById(String deviceId) => new Future.value(null);
Future<List<Device>> getDevices() => getAllConnectedDevices();
}
...@@ -29,7 +29,7 @@ defineTests() { ...@@ -29,7 +29,7 @@ defineTests() {
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command); ..addCommand(command);
runner.run(['stop']).then((int code) => expect(code, equals(0))); return runner.run(['stop']).then((int code) => expect(code, equals(0)));
}); });
test('returns 0 when iOS is connected and ready to be stopped', () { test('returns 0 when iOS is connected and ready to be stopped', () {
...@@ -48,7 +48,7 @@ defineTests() { ...@@ -48,7 +48,7 @@ defineTests() {
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command); ..addCommand(command);
runner.run(['stop']).then((int code) => expect(code, equals(0))); return runner.run(['stop']).then((int code) => expect(code, equals(0)));
}); });
}); });
} }
...@@ -8,12 +8,13 @@ import 'package:mockito/mockito.dart'; ...@@ -8,12 +8,13 @@ import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/mocks.dart'; import 'src/mocks.dart';
import 'src/test_context.dart';
main() => defineTests(); main() => defineTests();
defineTests() { defineTests() {
group('trace', () { group('trace', () {
test('returns 1 when no Android device is connected', () { testUsingContext('returns 1 when no Android device is connected', () {
TraceCommand command = new TraceCommand(); TraceCommand command = new TraceCommand();
applyMocksToCommand(command); applyMocksToCommand(command);
MockDeviceStore mockDevices = command.devices; MockDeviceStore mockDevices = command.devices;
...@@ -22,7 +23,7 @@ defineTests() { ...@@ -22,7 +23,7 @@ defineTests() {
CommandRunner runner = new CommandRunner('test_flutter', '') CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command); ..addCommand(command);
runner.run(['trace']).then((int code) => expect(code, equals(1))); return runner.run(['trace']).then((int code) => expect(code, equals(1)));
}); });
}); });
} }
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