Port test logic to Dart.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 1f1766e..09a29cd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -87,25 +87,27 @@
 support testing the `flutter` package with pre-built binaries. To test the
 package, you'll need to follow the [instructions below](#working-on-the-engine-and-the-framework-at-the-same-time)
 for working with this repository and the Flutter engine repository
-simultaneously and then run the following command:
+simultaneously and then run the following command (assuming the `flutter/bin`
+directory is in your path):
 
- * `./dev/run_tests --debug`
+ * `flutter test --debug`
 
 Creating a workflow for running the test with a prebuilt binary is
-[Issue #56](https://github.com/flutter/flutter/issues/56). If you want to run
-a single test individually:
+[Issue #56](https://github.com/flutter/flutter/issues/56). If you want
+to run a single test individually:
 
- * `./dev/run_tests --debug test/harness/trivial_test.dart`
+ * `flutter test --debug trivial_test.dart`
 
-Note: The tests are headless, you won't see any UI. You can use `print` to
-generate console output or you can interact with the DartVM via observatory at [http://localhost:8181/](http://localhost:8181/).
+Note: The tests are headless, you won't see any UI. You can use
+`print` to generate console output or you can interact with the DartVM
+via observatory at [http://localhost:8181/](http://localhost:8181/).
 
 Adding a test
 -------------
 
-To add a test, simply create a file whose name ends with `_test.dart` in the
-`packags/unit/test` directory. The test should have a `main` function and use
-the `test` package.
+To add a test, simply create a file whose name ends with `_test.dart`
+in the `packages/unit/test` directory. The test should have a `main`
+function and use the `test` package.
 
 Contributing code
 -----------------
@@ -120,7 +122,7 @@
  and [design principles](https://github.com/flutter/engine/blob/master/sky/specs/design.md)
  before working on anything non-trivial. These guidelines are intended to keep
  the code consistent and avoid common pitfalls.
- * `git commit -a -m "<your brief but informative commit message>"`
+ * `git commit -a -m "<your informative commit message>"`
  * `git push origin name_of_your_branch`
 
 To send us a pull request:
@@ -163,15 +165,15 @@
    (e.g., `out/Debug`). To run examples on Android, build one of the Android
    configurations (e.g., `out/android_Debug`).
 
-You should now be able to run the tests against your locally built engine using
-the `./dev/run_tests` script. To run one of the examples on your device using
-your locally built engine, use the `--engine-src-path` option to the `flutter`
-tool:
+You should now be able to run the tests against your locally built
+engine using the `flutter test --debug` command. To run one of the
+examples on your device using your locally built engine, use the
+`--debug` option to the `flutter` tool:
 
- * `flutter start --engine-src-path /foo/bar/engine/src`
+ * `flutter start --debug`
 
-Eventually, the `--local-build` flag for the `flutter` command will
-automatically set the correct engine src path (see [Issue #57](https://github.com/flutter/flutter/issues/57)).
+If you want to test the release version instead of the debug version,
+use `--release` instead of `--debug`.
 
 Making a breaking change to the engine
 --------------------------------------
diff --git a/dev/run_tests b/dev/run_tests
deleted file mode 100755
index ff0ff36..0000000
--- a/dev/run_tests
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env python
-# 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.
-
-import os
-import sys
-import subprocess
-import argparse
-
-FLUTTER_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-FLUTTER = os.path.join(FLUTTER_ROOT, 'bin', 'flutter')
-
-UNIT_DIR = os.path.join(FLUTTER_ROOT, 'packages', 'unit')
-TESTS_DIR = os.path.join(UNIT_DIR, 'test')
-DEFAULT_ENGINE_DIR = os.path.abspath(os.path.join(FLUTTER_ROOT, '..', 'engine', 'src'))
-
-def main():
-    parser = argparse.ArgumentParser(description='Runs Flutter unit tests')
-    parser.add_argument('--engine-src-path', default=DEFAULT_ENGINE_DIR)
-    parser.add_argument('--config', default='Debug')
-    parser.add_argument('--debug', dest='config', action='store_const', const='Debug')
-    parser.add_argument('--release', dest='config', action='store_const', const='Release')
-    args, remaining = parser.parse_known_args()
-
-    build_dir = os.path.join(os.path.abspath(args.engine_src_path), 'out', args.config)
-
-    if not remaining:
-        for root, dirs, files in os.walk(TESTS_DIR):
-            remaining.extend(os.path.join(root, f)
-                             for f in files if f.endswith("_test.dart"))
-
-    if os.environ['TERM'] == 'dumb':
-        remaining = [ '--no-color' ] + remaining
-
-    return subprocess.call([
-        FLUTTER,
-        'test',
-        '--build-dir=%s' % build_dir
-    ] + remaining, cwd=UNIT_DIR)
-
-if __name__ == '__main__':
-    sys.exit(main())
diff --git a/packages/flutter_tools/bin/sky_test.dart b/packages/flutter_tools/bin/sky_test.dart
deleted file mode 100644
index a5e08f6..0000000
--- a/packages/flutter_tools/bin/sky_test.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// 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.
-
-import 'package:sky_tools/src/test/loader.dart' as loader;
-import 'package:test/src/executable.dart' as executable;
-
-main(List<String> args) {
-  loader.installHook();
-  return executable.main(args);
-}
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index f705622..fbe7e53 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -74,6 +74,7 @@
         return iOS;
       case TargetPlatform.iOSSimulator:
         return iOSSimulator;
+      case TargetPlatform.mac:
       case TargetPlatform.linux:
         return null;
     }
@@ -110,6 +111,7 @@
           iOSSimulator = new IOSApp(localPath: path.join(config.buildDir, IOSApp._defaultName));
           break;
 
+        case TargetPlatform.mac:
         case TargetPlatform.linux:
           break;
       }
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 17da362..0fb7c35 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -34,6 +34,8 @@
       return 'ios-arm';
     case TargetPlatform.iOSSimulator:
       return 'ios-x64';
+    case TargetPlatform.mac:
+      return 'darwin-x64';
     case TargetPlatform.linux:
       return 'linux-x64';
   }
@@ -158,12 +160,31 @@
 
   // These values are initialized by FlutterCommandRunner on startup.
   static String flutterRoot;
-  static String packageRoot;
+  static String packageRoot = 'packages';
+
+  static bool get isPackageRootValid {
+    return FileSystemEntity.isDirectorySync(packageRoot);
+  }
+
+  static void ensurePackageRootIsValid() {
+    if (!isPackageRootValid) {
+      String message = '$packageRoot is not a valid directory.';
+      if (packageRoot == 'packages') {
+        if (FileSystemEntity.isFileSync('pubspec.yaml'))
+          message += '\nDid you run `pub get` in this directory?';
+        else
+          message += '\nDid you run this command from the same directory as your pubspec.yaml file?';
+      }
+      stderr.writeln(message);
+      throw new ProcessExit(2);
+    }
+  }
 
   static String _engineRevision;
 
   static String get engineRevision {
     if (_engineRevision == null) {
+      ensurePackageRootIsValid();
       File revisionFile = new File(path.join(packageRoot, 'sky_engine', 'REVISION'));
       if (revisionFile.existsSync())
         _engineRevision = revisionFile.readAsStringSync();
diff --git a/packages/flutter_tools/lib/src/build_configuration.dart b/packages/flutter_tools/lib/src/build_configuration.dart
index 1d74bd2..5ac291c 100644
--- a/packages/flutter_tools/lib/src/build_configuration.dart
+++ b/packages/flutter_tools/lib/src/build_configuration.dart
@@ -24,6 +24,7 @@
   android,
   iOS,
   iOSSimulator,
+  mac,
   linux,
 }
 
@@ -36,12 +37,21 @@
   return HostPlatform.linux;
 }
 
+TargetPlatform getCurrentHostPlatformAsTarget() {
+  if (Platform.isMacOS)
+    return TargetPlatform.mac;
+  if (Platform.isLinux)
+    return TargetPlatform.linux;
+  _logging.warning('Unsupported host platform, defaulting to Linux');
+  return TargetPlatform.linux;
+}
+
 class BuildConfiguration {
   BuildConfiguration.prebuilt({
     this.hostPlatform,
     this.targetPlatform,
     this.deviceId
-  }) : type = BuildType.prebuilt, buildDir = null;
+  }) : type = BuildType.prebuilt, buildDir = null, testable = false;
 
   BuildConfiguration.local({
     this.type,
@@ -49,7 +59,8 @@
     this.targetPlatform,
     String enginePath,
     String buildPath,
-    this.deviceId
+    this.deviceId,
+    this.testable: false
   }) : buildDir = path.normalize(path.join(enginePath, buildPath)) {
     assert(type == BuildType.debug || type == BuildType.release);
   }
@@ -59,4 +70,5 @@
   final TargetPlatform targetPlatform;
   final String buildDir;
   final String deviceId;
+  final bool testable;
 }
diff --git a/packages/flutter_tools/lib/src/commands/flutter_command.dart b/packages/flutter_tools/lib/src/commands/flutter_command.dart
index 917ece3..f31ad56 100644
--- a/packages/flutter_tools/lib/src/commands/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/commands/flutter_command.dart
@@ -8,6 +8,7 @@
 import 'package:args/command_runner.dart';
 
 import '../application_package.dart';
+import '../build_configuration.dart';
 import '../device.dart';
 import '../toolchain.dart';
 import 'flutter_command_runner.dart';
@@ -18,19 +19,21 @@
   /// Whether this command needs to be run from the root of a project.
   bool get requiresProjectRoot => true;
 
+  List<BuildConfiguration> get buildConfigurations => runner.buildConfigurations;
+
   Future downloadApplicationPackages() async {
     if (applicationPackages == null)
-      applicationPackages = await ApplicationPackageStore.forConfigs(runner.buildConfigurations);
+      applicationPackages = await ApplicationPackageStore.forConfigs(buildConfigurations);
   }
 
   Future downloadToolchain() async {
     if (toolchain == null)
-      toolchain = await Toolchain.forConfigs(runner.buildConfigurations);
+      toolchain = await Toolchain.forConfigs(buildConfigurations);
   }
 
   void connectToDevices() {
     if (devices == null)
-      devices = new DeviceStore.forConfigs(runner.buildConfigurations);
+      devices = new DeviceStore.forConfigs(buildConfigurations);
   }
 
   Future downloadApplicationPackagesAndConnectToDevices() async {
diff --git a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart
index 4895fb0..ee17608 100644
--- a/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/commands/flutter_command_runner.dart
@@ -16,6 +16,12 @@
 
 final Logger _logging = new Logger('sky_tools.flutter_command_runner');
 
+const String kFlutterRootEnvironmentVariableName = 'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
+const String kFlutterEngineEnvironmentVariableName = 'FLUTTER_ENGINE'; // should point to //engine/src/ (root of flutter/engine repo)
+const String kSnapshotFileName = 'flutter_tools.snapshot'; // in //flutter/bin/cache/
+const String kFlutterToolsScriptFileName = 'sky_tools.dart'; // in //flutter/packages/flutter_tools/bin/
+const String kFlutterEnginePackageName = 'sky_engine';
+
 class FlutterCommandRunner extends CommandRunner {
   FlutterCommandRunner()
       : super('flutter', 'Manage your Flutter app development.') {
@@ -27,67 +33,76 @@
         negatable: false,
         help: 'Very noisy logging, including the output of all '
             'shell commands executed.');
+    String packagesHelp;
+    if (ArtifactStore.isPackageRootValid)
+      packagesHelp = '\n(defaults to "${ArtifactStore.packageRoot}")';
+    else
+      packagesHelp = '\n(required, since the current directory does not contain a "packages" subdirectory)';
     argParser.addOption('package-root',
-        help: 'Path to your packages directory.', defaultsTo: 'packages');
-    argParser.addOption('flutter-root', hide: true,
-        help: 'The root directory of the Flutter repository.');
+        help: 'Path to your packages directory.$packagesHelp');
+    argParser.addOption('flutter-root',
+        help: 'The root directory of the Flutter repository. Defaults to \$$kFlutterRootEnvironmentVariableName if set,\n'
+              'otherwise defaults to a value derived from the location of this tool.', defaultsTo: defaultFlutterRoot);
+
     argParser.addOption('android-device-id',
         help: 'Serial number of the target Android device.');
 
-    argParser.addSeparator('Local build selection options:');
+    argParser.addSeparator('Local build selection options (not normally required):');
     argParser.addFlag('debug',
         negatable: false,
         help:
-            'Set this if you are building Flutter locally and want to use the debug build products. '
-            'When set, attempts to automaticaly determine engine-src-path if engine-src-path is '
-            'not set. Not normally required.');
+            'Set this if you are building Flutter locally and want to use the debug build products.\n'
+            'Defaults to true if --engine-src-path is specified and --release is not, otherwise false.');
     argParser.addFlag('release',
         negatable: false,
         help:
-            'Set this if you are building Flutter locally and want to use the release build products. '
-            'When set, attempts to automaticaly determine engine-src-path if engine-src-path is '
-            'not set. Note that release is not compatible with the listen command '
-            'on iOS devices and simulators. Not normally required.');
-    argParser.addFlag('local-build',
-        negatable: false,
-        hide: true,
-        help:
-            'Automatically detect your engine src directory from an overridden Flutter package. '
-            'Useful if you are building Flutter locally and are using a dependency_override for'
-            'the Flutter package that points to your engine src directory.');
+            'Set this if you are building Flutter locally and want to use the release build products.\n'
+            'The --release option is not compatible with the listen command on iOS devices and simulators.');
     argParser.addOption('engine-src-path',
         help:
-            'Path to your engine src directory, if you are building Flutter locally. '
-            'Ignored if neither debug nor release is set. Not normally required.');
+            'Path to your engine src directory, if you are building Flutter locally.\n'
+            'Defaults to \$$kFlutterEngineEnvironmentVariableName if set, otherwise defaults to the path given in your pubspec.yaml\n'
+            'dependency_overrides for $kFlutterEnginePackageName, if any, or, failing that, tries to guess at the location\n'
+            'based on the value of the --flutter-root option.');
+    argParser.addOption('host-debug-build-path', hide: true,
+        help:
+            'Path to your host Debug out directory (i.e. the one that runs on your workstation, not a device), if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
+        defaultsTo: 'out/Debug/');
+    argParser.addOption('host-release-build-path', hide: true,
+        help:
+            'Path to your host Release out directory (i.e. the one that runs on your workstation, not a device), if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
+        defaultsTo: 'out/Release/');
     argParser.addOption('android-debug-build-path', hide: true,
         help:
-            'Path to your Android Debug out directory, if you are building Flutter locally. '
-            'This path is relative to engine-src-path. Not normally required.',
+            'Path to your Android Debug out directory, if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
         defaultsTo: 'out/android_Debug/');
     argParser.addOption('android-release-build-path', hide: true,
         help:
-            'Path to your Android Release out directory, if you are building Flutter locally. '
-            'This path is relative to engine-src-path. Not normally required.',
+            'Path to your Android Release out directory, if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
         defaultsTo: 'out/android_Release/');
     argParser.addOption('ios-debug-build-path', hide: true,
         help:
-            'Path to your iOS Debug out directory, if you are building Flutter locally. '
-            'This path is relative to engine-src-path. Not normally required.',
+            'Path to your iOS Debug out directory, if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
         defaultsTo: 'out/ios_Debug/');
     argParser.addOption('ios-release-build-path', hide: true,
         help:
-            'Path to your iOS Release out directory, if you are building Flutter locally. '
-            'This path is relative to engine-src-path. Not normally required.',
+            'Path to your iOS Release out directory, if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
         defaultsTo: 'out/ios_Release/');
     argParser.addOption('ios-sim-debug-build-path', hide: true,
         help:
-            'Path to your iOS Simulator Debug out directory, if you are building Sky locally. '
-            'This path is relative to engine-src-path. Not normally required.',
+            'Path to your iOS Simulator Debug out directory, if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
         defaultsTo: 'out/ios_sim_Debug/');
     argParser.addOption('ios-sim-release-build-path', hide: true,
         help:
-            'Path to your iOS Simulator Release out directory, if you are building Sky locally. '
-            'This path is relative to engine-src-path. Not normally required.',
+            'Path to your iOS Simulator Release out directory, if you are building Flutter locally.\n'
+            'This path is relative to --engine-src-path. Not normally required.',
         defaultsTo: 'out/ios_sim_Release/');
   }
 
@@ -100,6 +115,17 @@
 
   ArgResults _globalResults;
 
+  String get defaultFlutterRoot {
+    String script = Platform.script.toFilePath();
+    if (Platform.environment.containsKey(kFlutterRootEnvironmentVariableName))
+      return Platform.environment[kFlutterRootEnvironmentVariableName];
+    if (path.basename(script) == kSnapshotFileName)
+      return path.dirname(path.dirname(path.dirname(script)));
+    if (path.basename(script) == kFlutterToolsScriptFileName)
+      return path.dirname(path.dirname(path.dirname(path.dirname(script))));
+    return '.';
+  }
+
   Future<int> runCommand(ArgResults globalResults) {
     if (globalResults['verbose'])
       Logger.root.level = Level.INFO;
@@ -108,39 +134,44 @@
       Logger.root.level = Level.FINE;
 
     _globalResults = globalResults;
-    ArtifactStore.flutterRoot = globalResults['flutter-root'] ?? Platform.environment['FLUTTER_ROOT'];
-    ArtifactStore.packageRoot = globalResults['package-root'];
+    ArtifactStore.flutterRoot = globalResults['flutter-root'];
+    if (globalResults.wasParsed('package-root'))
+      ArtifactStore.packageRoot = globalResults['package-root'];
 
     return super.runCommand(globalResults);
   }
 
   List<BuildConfiguration> _createBuildConfigurations(ArgResults globalResults) {
-    if (!FileSystemEntity.isDirectorySync(ArtifactStore.packageRoot)) {
-      String message = '${ArtifactStore.packageRoot} is not a valid directory.';
-      if (ArtifactStore.packageRoot == 'packages') {
-        if (FileSystemEntity.isFileSync('pubspec.yaml'))
-          message += '\nDid you run `pub get` in this directory?';
-        else
-          message += '\nDid you run this command from the same directory as your pubspec.yaml file?';
-      }
-      stderr.writeln(message);
-      throw new ProcessExit(2);
-    }
-
-    String enginePath = globalResults['engine-src-path'];
+    String enginePath = globalResults['engine-src-path'] ?? Platform.environment[kFlutterEngineEnvironmentVariableName];
     bool isDebug = globalResults['debug'];
     bool isRelease = globalResults['release'];
     HostPlatform hostPlatform = getCurrentHostPlatform();
+    TargetPlatform hostPlatformAsTarget = getCurrentHostPlatformAsTarget();
 
-    if (enginePath == null && globalResults['local-build']) {
-      Directory flutterDir = new Directory(path.join(globalResults['package-root'], 'flutter'));
-      String realFlutterPath = flutterDir.resolveSymbolicLinksSync();
-
-      enginePath = path.dirname(path.dirname(path.dirname(path.dirname(realFlutterPath))));
-      bool dirExists = FileSystemEntity.isDirectorySync(path.join(enginePath, 'out'));
-      if (enginePath == '/' || enginePath.isEmpty || !dirExists) {
-        stderr.writeln('Unable to detect local build in $enginePath.\n'
-            'Do you have a dependency override for the flutter package?');
+    if (enginePath == null && (isDebug || isRelease)) {
+      if (ArtifactStore.isPackageRootValid) {
+        Directory engineDir = new Directory(path.join(ArtifactStore.packageRoot, kFlutterEnginePackageName));
+        try {
+          String realEnginePath = engineDir.resolveSymbolicLinksSync();
+          enginePath = path.dirname(path.dirname(path.dirname(path.dirname(realEnginePath))));
+          bool dirExists = FileSystemEntity.isDirectorySync(path.join(enginePath, 'out'));
+          if (enginePath == '/' || enginePath.isEmpty || !dirExists)
+            enginePath = null;
+        } on FileSystemException { }
+      }
+      if (enginePath == null) {
+        String tryEnginePath(String enginePath) {
+          if (FileSystemEntity.isDirectorySync(path.join(enginePath, 'out')))
+            return enginePath;
+          return null;
+        }
+        enginePath = tryEnginePath(path.join(ArtifactStore.flutterRoot, '../engine/src'));
+      }
+      if (enginePath == null) {
+        stderr.writeln('Unable to detect local Flutter engine build directory.\n'
+            'Either specify a dependency_override for the $kFlutterEnginePackageName package in your pubspec.yaml and\n'
+            'ensure --package-root is set if necessary, or set the \$$kFlutterEngineEnvironmentVariableName environment variable, or\n'
+            'use --engine-src-path to specify the path to the root of your flutter/engine repository.');
         throw new ProcessExit(2);
       }
     }
@@ -164,6 +195,15 @@
         configs.add(new BuildConfiguration.local(
           type: BuildType.debug,
           hostPlatform: hostPlatform,
+          targetPlatform: hostPlatformAsTarget,
+          enginePath: enginePath,
+          buildPath: globalResults['host-debug-build-path'],
+          testable: true
+        ));
+
+        configs.add(new BuildConfiguration.local(
+          type: BuildType.debug,
+          hostPlatform: hostPlatform,
           targetPlatform: TargetPlatform.android,
           enginePath: enginePath,
           buildPath: globalResults['android-debug-build-path'],
@@ -193,6 +233,15 @@
         configs.add(new BuildConfiguration.local(
           type: BuildType.release,
           hostPlatform: hostPlatform,
+          targetPlatform: hostPlatformAsTarget,
+          enginePath: enginePath,
+          buildPath: globalResults['host-release-build-path'],
+          testable: true
+        ));
+
+        configs.add(new BuildConfiguration.local(
+          type: BuildType.release,
+          hostPlatform: hostPlatform,
           targetPlatform: TargetPlatform.android,
           enginePath: enginePath,
           buildPath: globalResults['android-release-build-path'],
diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart
index ac10a12..6cbc9a9 100644
--- a/packages/flutter_tools/lib/src/commands/test.dart
+++ b/packages/flutter_tools/lib/src/commands/test.dart
@@ -9,36 +9,66 @@
 import 'package:path/path.dart' as path;
 import 'package:test/src/executable.dart' as executable;
 
-import 'flutter_command.dart';
+import '../artifacts.dart';
+import '../build_configuration.dart';
 import '../test/loader.dart' as loader;
+import 'flutter_command.dart';
 
 final Logger _logging = new Logger('sky_tools.test');
 
 class TestCommand extends FlutterCommand {
-  final String name = 'test';
-  final String description = 'Runs Flutter unit tests for the current project (requires a local build of the engine).';
+  String get name => 'test';
+  String get description => 'Runs Flutter unit tests for the current project. At least one of --debug and --release must be set.';
 
-  TestCommand() {
-    argParser.addOption('build-dir', help: 'The directory in which to find a prebuilt engine');
-  }
+  bool get requiresProjectRoot => false;
 
-  String get _shellPath {
-    if (Platform.isLinux)
-      return path.join(argResults['build-dir'], 'sky_shell');
-    if (Platform.isMacOS)
-      return path.join(argResults['build-dir'], 'SkyShell.app', 'Contents', 'MacOS', 'SkyShell');
-    throw new Exception('Unsupported platform.');
+  String getShellPath(TargetPlatform platform, String buildPath) {
+    switch (platform) {
+      case TargetPlatform.linux:
+        return path.join(buildPath, 'sky_shell');
+      case TargetPlatform.mac:
+        return path.join(buildPath, 'SkyShell.app', 'Contents', 'MacOS', 'SkyShell');
+      default:
+        throw new Exception('Unsupported platform.');
+    }
   }
 
   @override
   Future<int> runInProject() async {
-    loader.shellPath = _shellPath;
-    if (!FileSystemEntity.isFileSync(loader.shellPath)) {
-      _logging.severe('Cannot find Flutter Shell at ${loader.shellPath}');
+    List<String> testArgs = argResults.rest.toList();
+    Directory testDir = new Directory(path.join(ArtifactStore.flutterRoot, 'packages/unit/test'));
+    if (testArgs.isEmpty) {
+      testArgs.addAll(testDir.listSync(recursive: true, followLinks: false)
+                             .where((FileSystemEntity entity) => entity.path.endsWith('_test.dart') && FileSystemEntity.isFileSync(entity.path))
+                             .map((FileSystemEntity entity) => path.absolute(entity.path)));
+    }
+    testArgs.insert(0, '--');
+    if (Platform.environment['TERM'] == 'dumb')
+      testArgs.insert(0, '--no-color');
+    List<BuildConfiguration> configs = buildConfigurations;
+    bool foundOne = false;
+    String currentDirectory = Directory.current.path;
+    Directory.current = testDir.path;
+    // TODO(ianh): Verify that this directory has had 'pub get' run in it at least once.
+    loader.installHook();
+    for (BuildConfiguration config in configs) {
+      if (!config.testable)
+        continue;
+      foundOne = true;
+      loader.shellPath = path.join(currentDirectory, getShellPath(config.targetPlatform, config.buildDir));
+      if (!FileSystemEntity.isFileSync(loader.shellPath)) {
+          _logging.severe('Cannot find Flutter shell at ${loader.shellPath}');
+        return 1;
+      }
+      await executable.main(testArgs);
+      if (exitCode != 0)
+        return exitCode;
+    }
+    if (!foundOne) {
+      stderr.writeln('At least one of --debug or --release must be set, to specify the local build products to test.');
       return 1;
     }
-    loader.installHook();
-    await executable.main(argResults.rest);
-    return exitCode;
+
+    return 0;
   }
-}
+}
\ No newline at end of file
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index d92cf96..2f01eea 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -920,6 +920,7 @@
           assert(iOSSimulator == null);
           iOSSimulator = new IOSSimulator();
           break;
+        case TargetPlatform.mac:
         case TargetPlatform.linux:
           break;
       }
diff --git a/travis/test.sh b/travis/test.sh
index d2fd711..45d8db6 100755
--- a/travis/test.sh
+++ b/travis/test.sh
@@ -6,4 +6,4 @@
 (cd packages/flx; pub global run tuneup check; pub run test -j1)
 (cd packages/newton; pub global run tuneup check; pub run test -j1)
 
-./dev/run_tests --engine-src-path bin/cache/travis
+./bin/flutter test --engine-src-path bin/cache/travis