Commit 00bed774 authored by Ian Fischer's avatar Ian Fischer

Add listen command and basic test, and don’t do unnecessary repeated work when...

Add listen command and basic test, and don’t do unnecessary repeated work when listening or poking the android server.
parent 2bc289de
......@@ -13,6 +13,7 @@ import 'package:sky_tools/src/build.dart';
import 'package:sky_tools/src/cache.dart';
import 'package:sky_tools/src/init.dart';
import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/listen.dart';
import 'package:sky_tools/src/logs.dart';
import 'package:sky_tools/src/run_mojo.dart';
import 'package:sky_tools/src/start.dart';
......@@ -149,6 +150,7 @@ void main(List<String> args) {
..addCommand(new CacheCommand())
..addCommand(new InitCommand())
..addCommand(new InstallCommand())
..addCommand(new ListenCommand())
..addCommand(new LogsCommand())
..addCommand(new RunMojoCommand())
..addCommand(new StartCommand())
......
......@@ -256,19 +256,21 @@ class AndroidDevice extends _Device {
return false;
}
// Set up port forwarding for observatory.
String observatoryPortString = 'tcp:$_observatoryPort';
runCheckedSync(
[adbPath, 'forward', observatoryPortString, observatoryPortString]);
// Actually start the server.
await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort],
workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED);
// Set up reverse port-forwarding so that the Android app can reach the
// server running on localhost.
String serverPortString = 'tcp:$_serverPort';
runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]);
if (!poke) {
// Set up port forwarding for observatory.
String observatoryPortString = 'tcp:$_observatoryPort';
runCheckedSync(
[adbPath, 'forward', observatoryPortString, observatoryPortString]);
// Actually start the server.
await Process.start('pub', ['run', 'sky_tools:sky_server', _serverPort],
workingDirectory: serverRoot, mode: ProcessStartMode.DETACHED);
// Set up reverse port-forwarding so that the Android app can reach the
// server running on localhost.
String serverPortString = 'tcp:$_serverPort';
runCheckedSync([adbPath, 'reverse', serverPortString, serverPortString]);
}
String relativeDartMain = path.relative(mainDart, from: serverRoot);
String url = 'http://localhost:$_serverPort/$relativeDartMain';
......
......@@ -15,7 +15,11 @@ class InstallCommand extends Command {
final name = 'install';
final description = 'Install your Flutter app on attached devices.';
AndroidDevice android = null;
InstallCommand([this.android]);
InstallCommand([this.android]) {
if (android == null) {
android = new AndroidDevice();
}
}
@override
Future<int> run() async {
......@@ -31,9 +35,6 @@ class InstallCommand extends Command {
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
if (android == null) {
android = new AndroidDevice();
}
ApplicationPackage androidApp = packages[BuildPlatform.android];
if (androidApp != null && android.isConnected()) {
installedSomewhere = android.installApp(androidApp) || installedSomewhere;
......
// Copyright 2015 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.
library sky_tools.listen;
import 'dart:async';
import 'dart:io';
import 'package:args/command_runner.dart';
import 'package:logging/logging.dart';
import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/device.dart';
import 'package:sky_tools/src/process.dart';
final Logger _logging = new Logger('sky_tools.listen');
class ListenCommand extends Command {
final name = 'listen';
final description = 'Listen for changes to files and reload the running app '
'on all connected devices.';
AndroidDevice android = null;
List<String> watchCommand;
/// Only run once. Used for testing.
bool singleRun;
ListenCommand({this.android, this.singleRun: false}) {
argParser.addFlag('checked',
negatable: true,
defaultsTo: true,
help: 'Toggle Dart\'s checked mode.');
argParser.addOption('target',
defaultsTo: '.',
abbr: 't',
help: 'Target app path or filename to start.');
if (android == null) {
android = new AndroidDevice();
}
}
@override
Future<int> run() async {
if (argResults.rest.length > 0) {
watchCommand = _initWatchCommand(argResults.rest);
} else {
watchCommand = _initWatchCommand(['.']);
}
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
while (true) {
_logging.info('Updating running Sky apps...');
if (android.isConnected()) {
await android.startServer(
argResults['target'], true, argResults['checked'], androidApp);
}
if (singleRun || !watchDirectory()) {
break;
}
}
return 0;
}
List<String> _initWatchCommand(List<String> directories) {
if (Platform.isMacOS) {
try {
runCheckedSync(['which', 'fswatch']);
} catch (e) {
_logging.severe('"listen" command is only useful if you have installed '
'fswatch on Mac. Run "brew install fswatch" to install it with '
'homebrew.');
return null;
}
return ['fswatch', '-r', '-v', '-1']..addAll(directories);
} else if (Platform.isLinux) {
try {
runCheckedSync(['which', 'inotifywait']);
} catch (e) {
_logging.severe('"listen" command is only useful if you have installed '
'inotifywait on Linux. Run "apt-get install inotify-tools" or '
'equivalent to install it.');
return null;
}
return [
'inotifywait',
'-r',
'-e',
// Only listen for events that matter, to avoid triggering constantly
// from the editor watching files
'modify,close_write,move,create,delete',
]..addAll(directories);
} else {
_logging.severe('"listen" command is only available on Mac and Linux.');
}
return null;
}
bool watchDirectory() {
if (watchCommand == null) {
return false;
}
try {
runCheckedSync(watchCommand);
} catch (e) {
_logging.warning('Watching directories failed.', e);
return false;
}
return true;
}
}
// Copyright 2015 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.
library stop_test;
import 'package:args/command_runner.dart';
import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/listen.dart';
import 'package:test/test.dart';
import 'src/common.dart';
main() => defineTests();
defineTests() {
group('listen', () {
test('returns 0 when no device is connected', () {
ApplicationPackageFactory.srcPath = './';
ApplicationPackageFactory.setBuildPath(
BuildType.prebuilt, BuildPlatform.android, './');
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(false);
ListenCommand command =
new ListenCommand(android: android, singleRun: true);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
runner.run(['listen']).then((int code) => expect(code, equals(0)));
});
});
}
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