use the new cache code to locate more artifacts (#3361)

* use the new cache code to locate more artifacts

* add todo

* review comments
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index 022cbab..07a9318 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -47,7 +47,7 @@
   }
 
   /// Creates a new AndroidApk based on the information in the Android manifest.
-  factory AndroidApk.fromBuildConfiguration(BuildConfiguration config) {
+  factory AndroidApk.fromCurrentDirectory() {
     String manifestPath = path.join('android', 'AndroidManifest.xml');
     if (!FileSystemEntity.isFileSync(manifestPath))
       return null;
@@ -82,8 +82,8 @@
     String iosProjectBundleId
   }) : super(localPath: iosProjectDir, id: iosProjectBundleId);
 
-  factory IOSApp.fromBuildConfiguration(BuildConfiguration config) {
-    if (getCurrentHostPlatform() != HostPlatform.mac)
+  factory IOSApp.fromCurrentDirectory() {
+    if (getCurrentHostPlatform() != HostPlatform.darwin_x64)
       return null;
 
     String plistPath = path.join('ios', 'Info.plist');
@@ -99,9 +99,22 @@
   String get displayName => id;
 }
 
+ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform) {
+  switch (platform) {
+    case TargetPlatform.android_arm:
+    case TargetPlatform.android_x64:
+      return new AndroidApk.fromCurrentDirectory();
+    case TargetPlatform.ios:
+      return new IOSApp.fromCurrentDirectory();
+    case TargetPlatform.darwin_x64:
+    case TargetPlatform.linux_x64:
+      return null;
+  }
+}
+
 class ApplicationPackageStore {
-  final AndroidApk android;
-  final IOSApp iOS;
+  AndroidApk android;
+  IOSApp iOS;
 
   ApplicationPackageStore({ this.android, this.iOS });
 
@@ -109,36 +122,14 @@
     switch (platform) {
       case TargetPlatform.android_arm:
       case TargetPlatform.android_x64:
+        android ??= new AndroidApk.fromCurrentDirectory();
         return android;
       case TargetPlatform.ios:
+        iOS ??= new IOSApp.fromCurrentDirectory();
         return iOS;
       case TargetPlatform.darwin_x64:
       case TargetPlatform.linux_x64:
         return null;
     }
   }
-
-  static ApplicationPackageStore forConfigs(List<BuildConfiguration> configs) {
-    AndroidApk android;
-    IOSApp iOS;
-
-    for (BuildConfiguration config in configs) {
-      switch (config.targetPlatform) {
-        case TargetPlatform.android_arm:
-        case TargetPlatform.android_x64:
-          android ??= new AndroidApk.fromBuildConfiguration(config);
-          break;
-
-        case TargetPlatform.ios:
-          iOS ??= new IOSApp.fromBuildConfiguration(config);
-          break;
-
-        case TargetPlatform.darwin_x64:
-        case TargetPlatform.linux_x64:
-          break;
-      }
-    }
-
-    return new ApplicationPackageStore(android: android, iOS: iOS);
-  }
 }
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 80d0c44..7d7819f 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -9,30 +9,6 @@
 import 'build_configuration.dart';
 import 'globals.dart';
 
-String _getNameForHostPlatform(HostPlatform platform) {
-  switch (platform) {
-    case HostPlatform.linux:
-      return 'linux-x64';
-    case HostPlatform.mac:
-      return 'darwin-x64';
-  }
-}
-
-String getNameForTargetPlatform(TargetPlatform platform) {
-  switch (platform) {
-    case TargetPlatform.android_arm:
-      return 'android-arm';
-    case TargetPlatform.android_x64:
-      return 'android-x64';
-    case TargetPlatform.ios:
-      return 'ios';
-    case TargetPlatform.darwin_x64:
-      return 'darwin-x64';
-    case TargetPlatform.linux_x64:
-      return 'linux-x64';
-  }
-}
-
 enum ArtifactType {
   snapshot,
   shell,
@@ -41,7 +17,6 @@
   androidIcuData,
   androidKeystore,
   androidLibSkyShell,
-  iosXcodeProject,
 }
 
 class Artifact {
@@ -63,7 +38,7 @@
     if (targetPlatform != null)
       return getNameForTargetPlatform(targetPlatform);
     if (hostPlatform != null)
-      return _getNameForHostPlatform(hostPlatform);
+      return getNameForHostPlatform(hostPlatform);
     assert(false);
     return null;
   }
@@ -84,13 +59,13 @@
       name: 'Sky Snapshot',
       fileName: 'sky_snapshot',
       type: ArtifactType.snapshot,
-      hostPlatform: HostPlatform.linux
+      hostPlatform: HostPlatform.linux_x64
     ),
     const Artifact._(
       name: 'Sky Snapshot',
       fileName: 'sky_snapshot',
       type: ArtifactType.snapshot,
-      hostPlatform: HostPlatform.mac
+      hostPlatform: HostPlatform.darwin_x64
     ),
 
     // mojo
@@ -158,14 +133,6 @@
       type: ArtifactType.androidLibSkyShell,
       targetPlatform: TargetPlatform.android_x64
     ),
-
-    // iOS
-    const Artifact._(
-      name: 'iOS Runner (Xcode Project)',
-      fileName: 'FlutterXcode.zip',
-      type: ArtifactType.iosXcodeProject,
-      targetPlatform: TargetPlatform.ios
-    ),
   ];
 
   static Artifact getArtifact({
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart
index f88a834..8b1ba05 100644
--- a/packages/flutter_tools/lib/src/base/utils.dart
+++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -34,6 +34,13 @@
 /// Return the plural of the given word (`cat(s)`).
 String pluralize(String word, int count) => count == 1 ? word : word + 's';
 
+/// Return the name of an enum item.
+String getEnumName(dynamic enumItem) {
+  String name = '$enumItem';
+  int index = name.indexOf('.');
+  return index == -1 ? name : name.substring(index + 1);
+}
+
 File getUniqueFile(Directory dir, String baseName, String ext) {
   int i = 1;
 
diff --git a/packages/flutter_tools/lib/src/build_configuration.dart b/packages/flutter_tools/lib/src/build_configuration.dart
index 38bb16408..8e573d3 100644
--- a/packages/flutter_tools/lib/src/build_configuration.dart
+++ b/packages/flutter_tools/lib/src/build_configuration.dart
@@ -6,6 +6,7 @@
 
 import 'package:path/path.dart' as path;
 
+import 'base/utils.dart';
 import 'globals.dart';
 
 enum BuildType {
@@ -22,15 +23,15 @@
   deploy
 }
 
-String getVariantName(BuildVariant variant) {
-  String name = '$variant';
-  int index = name.indexOf('.');
-  return index == -1 ? name : name.substring(index + 1);
-}
+String getVariantName(BuildVariant variant) => getEnumName(variant);
 
 enum HostPlatform {
-  mac,
-  linux,
+  darwin_x64,
+  linux_x64,
+}
+
+String getNameForHostPlatform(HostPlatform platform) {
+  return getEnumName(platform).replaceAll('_', '-');
 }
 
 enum TargetPlatform {
@@ -41,13 +42,19 @@
   linux_x64
 }
 
+String getNameForTargetPlatform(TargetPlatform platform) {
+  return getEnumName(platform).replaceAll('_', '-');
+}
+
 HostPlatform getCurrentHostPlatform() {
   if (Platform.isMacOS)
-    return HostPlatform.mac;
+    return HostPlatform.darwin_x64;
   if (Platform.isLinux)
-    return HostPlatform.linux;
+    return HostPlatform.linux_x64;
+
   printError('Unsupported host platform, defaulting to Linux');
-  return HostPlatform.linux;
+
+  return HostPlatform.linux_x64;
 }
 
 TargetPlatform getCurrentHostPlatformAsTarget() {
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index db5d951..7d0b190 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -15,16 +15,28 @@
 
 /// A warpper around the `bin/cache/` directory.
 class Cache {
+  /// [rootOverride] is configurable for testing.
+  Cache({ Directory rootOverride }) {
+    this._rootOverride = rootOverride;
+  }
+
+  Directory _rootOverride;
+
   static Cache get instance => context[Cache] ?? (context[Cache] = new Cache());
 
   /// Return the top-level directory in the cache; this is `bin/cache`.
-  Directory getRoot() => new Directory(path.join(ArtifactStore.flutterRoot, 'bin', 'cache'));
+  Directory getRoot() {
+    if (_rootOverride != null)
+      return new Directory(path.join(_rootOverride.path, 'bin', 'cache'));
+    else
+      return new Directory(path.join(ArtifactStore.flutterRoot, 'bin', 'cache'));
+  }
 
   /// Return a directory in the cache dir. For `pkg`, this will return `bin/cache/pkg`.
   Directory getCacheDir(String name) {
     Directory dir = new Directory(path.join(getRoot().path, name));
     if (!dir.existsSync())
-      dir.createSync();
+      dir.createSync(recursive: true);
     return dir;
   }
 
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart
index 0d46b4c..7062d58 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -25,7 +25,7 @@
 
   @override
   Future<int> runInProject() async {
-    if (getCurrentHostPlatform() != HostPlatform.mac) {
+    if (getCurrentHostPlatform() != HostPlatform.darwin_x64) {
       printError('Building for iOS is only supported on the Mac.');
       return 1;
     }
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index 3946d05..11f6ae4 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -8,9 +8,9 @@
 
 import '../android/android_device.dart';
 import '../application_package.dart';
-import '../artifacts.dart';
 import '../base/context.dart';
 import '../base/logger.dart';
+import '../build_configuration.dart';
 import '../device.dart';
 import '../globals.dart';
 import '../ios/devices.dart';
@@ -264,7 +264,6 @@
     try {
       int result = await startApp(
         device,
-        command.applicationPackages,
         command.toolchain,
         command.buildConfigurations,
         stop: true,
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart
index 10d4588..98ba72e 100644
--- a/packages/flutter_tools/lib/src/commands/drive.dart
+++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -304,8 +304,7 @@
 
 Future<int> stopApp(DriveCommand command) async {
   printTrace('Stopping application.');
-  ApplicationPackage package = command.applicationPackages
-      .getPackageForPlatform(command.device.platform);
+  ApplicationPackage package = command.applicationPackages.getPackageForPlatform(command.device.platform);
   bool stopped = await command.device.stopApp(package);
   return stopped ? 0 : 1;
 }
diff --git a/packages/flutter_tools/lib/src/commands/listen.dart b/packages/flutter_tools/lib/src/commands/listen.dart
index 68fdfa9..f50eb4d 100644
--- a/packages/flutter_tools/lib/src/commands/listen.dart
+++ b/packages/flutter_tools/lib/src/commands/listen.dart
@@ -58,7 +58,6 @@
 
       result = await startApp(
         deviceForCommand,
-        applicationPackages,
         toolchain,
         buildConfigurations,
         target: target,
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 7299fe5..f30cf4e 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -96,7 +96,6 @@
 
     int result = await startApp(
       deviceForCommand,
-      applicationPackages,
       toolchain,
       buildConfigurations,
       target: target,
@@ -128,7 +127,6 @@
 
 Future<int> startApp(
   Device device,
-  ApplicationPackageStore applicationPackages,
   Toolchain toolchain,
   List<BuildConfiguration> configs, {
   String target,
@@ -151,7 +149,7 @@
     return 1;
   }
 
-  ApplicationPackage package = applicationPackages.getPackageForPlatform(device.platform);
+  ApplicationPackage package = getApplicationPackageForPlatform(device.platform);
 
   if (package == null) {
     String message = 'No application found for ${device.platform}.';
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 4496be1..7a5d3f5 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -8,7 +8,6 @@
 
 import 'android/android_device.dart';
 import 'application_package.dart';
-import 'artifacts.dart';
 import 'base/common.dart';
 import 'base/utils.dart';
 import 'build_configuration.dart';
diff --git a/packages/flutter_tools/lib/src/globals.dart b/packages/flutter_tools/lib/src/globals.dart
index 877c276..dfe72ed 100644
--- a/packages/flutter_tools/lib/src/globals.dart
+++ b/packages/flutter_tools/lib/src/globals.dart
@@ -8,12 +8,14 @@
 import 'cache.dart';
 import 'device.dart';
 import 'doctor.dart';
+import 'toolchain.dart';
 
 DeviceManager get deviceManager => context[DeviceManager];
 Logger get logger => context[Logger];
 AndroidSdk get androidSdk => context[AndroidSdk];
 Doctor get doctor => context[Doctor];
 Cache get cache => Cache.instance;
+ToolConfiguration get tools => ToolConfiguration.instance;
 
 /// Display an error level message to the user. Commands should use this if they
 /// fail in some way.
diff --git a/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart b/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart
index b3940ee..46dc356 100644
--- a/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart
+++ b/packages/flutter_tools/lib/src/ios/setup_xcodeproj.dart
@@ -99,13 +99,9 @@
   String iosFilesPath = path.join(flutterProjectPath, 'ios');
   String xcodeprojPath = path.join(iosFilesPath, '.generated');
 
-  Artifact xcodeProject = ArtifactStore.getArtifact(
-    type: ArtifactType.iosXcodeProject,
-    targetPlatform: TargetPlatform.ios
-  );
-
-  String xcodeProjectPath = ArtifactStore.getPath(xcodeProject);
-  List<int> archiveBytes = new File(xcodeProjectPath).readAsBytesSync();
+  Directory toolDir = tools.getEngineArtifactsDirectory(TargetPlatform.ios, BuildVariant.develop);
+  File archiveFile = new File(path.join(toolDir.path, 'FlutterXcode.zip'));
+  List<int> archiveBytes = archiveFile.readAsBytesSync();
 
   if (archiveBytes.isEmpty) {
     printError('Error: No archive bytes received.');
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index a2b3f48..6c64be0 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -65,7 +65,7 @@
   }
 
   void _setupApplicationPackages() {
-    applicationPackages ??= ApplicationPackageStore.forConfigs(buildConfigurations);
+    applicationPackages ??= new ApplicationPackageStore();
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
index af9412f..ee8aa59 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart
@@ -17,6 +17,7 @@
 import '../build_configuration.dart';
 import '../globals.dart';
 import '../package_map.dart';
+import '../toolchain.dart';
 import 'version.dart';
 
 const String kFlutterRootEnvironmentVariableName = 'FLUTTER_ROOT'; // should point to //flutter/ (root of flutter/flutter repo)
@@ -79,6 +80,7 @@
             '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: !verboseHelp,
         help:
@@ -91,6 +93,7 @@
             '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: !verboseHelp,
         help:
@@ -209,6 +212,16 @@
     // See if the user specified a specific device.
     deviceManager.specifiedDeviceId = globalResults['device-id'];
 
+    // Set up the tooling configuration.
+    if (enginePath != null) {
+      ToolConfiguration.instance.engineSrcPath = enginePath;
+
+      if (globalResults.wasParsed('release'))
+        ToolConfiguration.instance.release = globalResults['release'];
+      if (globalResults.wasParsed('debug'))
+        ToolConfiguration.instance.release = !globalResults['debug'];
+    }
+
     // The Android SDK could already have been set by tests.
     if (!context.isSet(AndroidSdk)) {
       if (enginePath != null) {
@@ -287,17 +300,17 @@
         targetPlatform: TargetPlatform.android_x64
       ));
 
-      if (hostPlatform == HostPlatform.linux) {
+      if (hostPlatform == HostPlatform.linux_x64) {
         configs.add(new BuildConfiguration.prebuilt(
-          hostPlatform: HostPlatform.linux,
+          hostPlatform: HostPlatform.linux_x64,
           targetPlatform: TargetPlatform.linux_x64,
           testable: true
         ));
       }
 
-      if (hostPlatform == HostPlatform.mac) {
+      if (hostPlatform == HostPlatform.darwin_x64) {
         configs.add(new BuildConfiguration.prebuilt(
-          hostPlatform: HostPlatform.mac,
+          hostPlatform: HostPlatform.darwin_x64,
           targetPlatform: TargetPlatform.ios
         ));
       }
diff --git a/packages/flutter_tools/lib/src/toolchain.dart b/packages/flutter_tools/lib/src/toolchain.dart
index db8679b..39db5cb 100644
--- a/packages/flutter_tools/lib/src/toolchain.dart
+++ b/packages/flutter_tools/lib/src/toolchain.dart
@@ -8,8 +8,11 @@
 import 'package:path/path.dart' as path;
 
 import 'artifacts.dart';
+import 'base/context.dart';
 import 'base/process.dart';
 import 'build_configuration.dart';
+import 'cache.dart';
+import 'globals.dart';
 import 'package_map.dart';
 
 class SnapshotCompiler {
@@ -37,6 +40,8 @@
   }
 }
 
+// TODO(devoncarew): This should instead take a host platform and target platform.
+
 String _getCompilerPath(BuildConfiguration config) {
   if (config.type != BuildType.prebuilt) {
     String compilerPath = path.join(config.buildDir, 'clang_x64', 'sky_snapshot');
@@ -66,3 +71,99 @@
     return null;
   }
 }
+
+/// A ToolConfiguration can return the tools directory for the current host platform
+/// and the engine artifact directory for a given target platform. It is configurable
+/// via command-line arguments in order to support local engine builds.
+class ToolConfiguration {
+  /// [overrideCache] is configurable for testing.
+  ToolConfiguration({ Cache overrideCache }) {
+    _cache = overrideCache ?? cache;
+  }
+
+  Cache _cache;
+
+  static ToolConfiguration get instance {
+    if (context[ToolConfiguration] == null)
+      context[ToolConfiguration] = new ToolConfiguration();
+    return context[ToolConfiguration];
+  }
+
+  /// Override using the artifacts from the cache directory (--engine-src-path).
+  String engineSrcPath;
+
+  /// The engine mode to use (only relevent when [engineSrcPath] is set).
+  bool release;
+
+  /// Used to override the directory calculated from engineSrcPath (--engine-out-dir).
+  String engineOutDir;
+
+  String get _modeStr => release ? 'Release' : 'Debug';
+
+  /// The directory that contains development tools for the given platform. This
+  /// includes things like `sky_shell` and `sky_snapshot`.
+  ///
+  /// If [platform] is not specified it defaults to [getCurrentHostPlatform].
+  Directory getToolsDirectory({ HostPlatform platform }) {
+    Directory dir = _getToolsDirectory(platform: platform);
+    if (dir != null)
+      printTrace('Using engine tools dir: ${dir.path}');
+    return dir;
+  }
+
+  Directory _getToolsDirectory({ HostPlatform platform }) {
+    platform ??= getCurrentHostPlatform();
+
+    if (engineSrcPath != null) {
+      return new Directory(path.join(engineSrcPath, 'out/$_modeStr'));
+    } else {
+      Directory engineDir = _cache.getArtifactDirectory('engine');
+      return new Directory(path.join(engineDir.path, getNameForHostPlatform(platform)));
+    }
+  }
+
+  /// Return the directory that contains engine artifacts for the given targets.
+  /// This directory might contain artifacts like `libsky_shell.so`.
+  Directory getEngineArtifactsDirectory(TargetPlatform platform, BuildVariant variant) {
+    Directory dir = _getEngineArtifactsDirectory(platform, variant);
+    if (dir != null)
+      printTrace('Using engine artifacts dir: ${dir.path}');
+    return dir;
+  }
+
+  Directory _getEngineArtifactsDirectory(TargetPlatform platform, BuildVariant variant) {
+    if (engineOutDir != null) {
+      return new Directory(engineOutDir);
+    } else if (engineSrcPath != null) {
+      String type;
+
+      switch (platform) {
+        case TargetPlatform.android_arm:
+          type = 'android';
+          break;
+        case TargetPlatform.android_x64:
+          type = 'android_sim';
+          break;
+
+        // TODO(devoncarew): We will need an ios vs ios_x86 target (for ios vs. ios_sim).
+        case TargetPlatform.ios:
+          type = 'ios';
+          break;
+
+        // These targets don't have engine artifacts.
+        case TargetPlatform.darwin_x64:
+        case TargetPlatform.linux_x64:
+          return null;
+      }
+
+      // Return something like 'out/android_Release'.
+      return new Directory(path.join(engineSrcPath, 'out/${type}_$_modeStr'));
+    } else {
+      // TODO(devoncarew): We'll want to suffix the directory name with the variant.
+
+      String dirName = getNameForTargetPlatform(platform);
+      Directory engineDir = _cache.getArtifactDirectory('engine');
+      return new Directory(path.join(engineDir.path, dirName));
+    }
+  }
+}
diff --git a/packages/flutter_tools/test/all.dart b/packages/flutter_tools/test/all.dart
index 067e4ab..76cea4f 100644
--- a/packages/flutter_tools/test/all.dart
+++ b/packages/flutter_tools/test/all.dart
@@ -25,6 +25,7 @@
 import 'run_test.dart' as run_test;
 import 'service_protocol_test.dart' as service_protocol_test;
 import 'stop_test.dart' as stop_test;
+import 'toolchain_test.dart' as toolchain_test;
 import 'trace_test.dart' as trace_test;
 import 'upgrade_test.dart' as upgrade_test;
 
@@ -47,6 +48,7 @@
   run_test.main();
   service_protocol_test.main();
   stop_test.main();
+  toolchain_test.main();
   trace_test.main();
   upgrade_test.main();
 }
diff --git a/packages/flutter_tools/test/toolchain_test.dart b/packages/flutter_tools/test/toolchain_test.dart
new file mode 100644
index 0000000..7b83f63
--- /dev/null
+++ b/packages/flutter_tools/test/toolchain_test.dart
@@ -0,0 +1,66 @@
+// 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';
+
+import 'package:flutter_tools/src/build_configuration.dart';
+import 'package:flutter_tools/src/cache.dart';
+import 'package:flutter_tools/src/toolchain.dart';
+import 'package:test/test.dart';
+
+import 'src/context.dart';
+
+void main() {
+  group('ToolConfiguration', () {
+    Directory tempDir;
+
+    setUp(() {
+      tempDir = Directory.systemTemp.createTempSync('flutter_temp');
+    });
+
+    tearDown(() {
+      tempDir.deleteSync(recursive: true);
+    });
+
+    testUsingContext('using cache', () {
+      ToolConfiguration toolConfig = new ToolConfiguration(
+        overrideCache: new Cache(rootOverride: tempDir)
+      );
+
+      expect(
+        toolConfig.getToolsDirectory(platform: HostPlatform.linux_x64).path,
+        endsWith('cache/artifacts/engine/linux-x64')
+      );
+      expect(
+        toolConfig.getEngineArtifactsDirectory(TargetPlatform.android_arm, BuildVariant.develop).path,
+        endsWith('cache/artifacts/engine/android-arm')
+      );
+      expect(
+        toolConfig.getEngineArtifactsDirectory(TargetPlatform.android_arm, BuildVariant.deploy).path,
+        endsWith('cache/artifacts/engine/android-arm')
+      );
+    });
+
+    testUsingContext('using enginePath', () {
+      ToolConfiguration toolConfig = new ToolConfiguration();
+      toolConfig.engineSrcPath = 'engine';
+      toolConfig.release = true;
+
+      expect(
+        toolConfig.getToolsDirectory(platform: HostPlatform.linux_x64).path,
+        'engine/out/Release'
+      );
+      expect(
+        toolConfig.getEngineArtifactsDirectory(TargetPlatform.android_arm, BuildVariant.develop).path,
+        'engine/out/android_Release'
+      );
+
+      toolConfig.release = false;
+      expect(
+        toolConfig.getToolsDirectory(platform: HostPlatform.linux_x64).path,
+        'engine/out/Debug'
+      );
+    });
+  });
+}