ART: Add dex2oat swap-usage parameters

Make the dex2oat swap usage decision dependent on parameters that
can be changed on the command line. Both the dex file size and dex
file count can be modified through the following to new parameters:

  --swap-dex-size-threshold
  --swap-dex-count-threshold

The defaults remain the same (20MB and 2).

Factor out some dex2oat setup code from oat_file_assistant_test
and use it in a new dex2oat test.

Bug: 29557002
Change-Id: I8863224f0304c98afe251572ffda8c5b3ead735b
Test: m test-art-host-gtest-dex2oat_test
Test: m test-art-host-gtest-oat_file_assistant_test
Test: m test-art-host
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c09116f..424aa7a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -65,15 +65,18 @@
 	$(call dexpreopt-remove-classes.dex,$@)
 
 # Dex file dependencies for each gtest.
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+
 ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MultiDex MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
+ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
-ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
+ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
 ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
 ART_GTEST_oat_test_DEX_DEPS := Main
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -89,14 +92,25 @@
 ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
 ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
 
-ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
+ART_GTEST_dex2oat_environment_tests_HOST_DEPS := \
   $(HOST_CORE_IMAGE_default_no-pic_64) \
-  $(HOST_CORE_IMAGE_default_no-pic_32) \
-  $(HOST_OUT_EXECUTABLES)/patchoatd
-ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
+  $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_dex2oat_environment_tests_TARGET_DEPS := \
   $(TARGET_CORE_IMAGE_default_no-pic_64) \
-  $(TARGET_CORE_IMAGE_default_no-pic_32) \
-  $(TARGET_OUT_EXECUTABLES)/patchoatd
+  $(TARGET_CORE_IMAGE_default_no-pic_32)
+
+ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
+   $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+   $(HOST_OUT_EXECUTABLES)/patchoatd
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
+   $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+   $(TARGET_OUT_EXECUTABLES)/patchoatd
+
+
+ART_GTEST_dex2oat_test_HOST_DEPS := \
+  $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_dex2oat_test_TARGET_DEPS := \
+  $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
 
 # TODO: document why this is needed.
 ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
@@ -157,6 +171,7 @@
   cmdline/cmdline_parser_test.cc \
   dexdump/dexdump_test.cc \
   dexlist/dexlist_test.cc \
+  dex2oat/dex2oat_test.cc \
   imgdiag/imgdiag_test.cc \
   oatdump/oatdump_test.cc \
   profman/profile_assistant_test.cc \
@@ -808,11 +823,15 @@
 ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
 ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
 ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
+ART_GTEST_dex2oat_test_DEX_DEPS :=
+ART_GTEST_dex2oat_test_HOST_DEPS :=
+ART_GTEST_dex2oat_test_TARGET_DEPS :=
 ART_GTEST_object_test_DEX_DEPS :=
 ART_GTEST_proxy_test_DEX_DEPS :=
 ART_GTEST_reflection_test_DEX_DEPS :=
 ART_GTEST_stub_test_DEX_DEPS :=
 ART_GTEST_transaction_test_DEX_DEPS :=
+ART_GTEST_dex2oat_environment_tests_DEX_DEPS :=
 ART_VALGRIND_DEPENDENCIES :=
 ART_VALGRIND_TARGET_DEPENDENCIES :=
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 24a4d58..437aba7 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -81,6 +81,9 @@
 
 namespace art {
 
+static constexpr size_t kDefaultMinDexFilesForSwap = 2;
+static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
+
 static int original_argc;
 static char** original_argv;
 
@@ -351,6 +354,16 @@
   UsageError("  --swap-fd=<file-descriptor>:  specifies a file to use for swap (by descriptor).");
   UsageError("      Example: --swap-fd=10");
   UsageError("");
+  UsageError("  --swap-dex-size-threshold=<size>:  specifies the minimum total dex file size in");
+  UsageError("      bytes to allow the use of swap.");
+  UsageError("      Example: --swap-dex-size-threshold=1000000");
+  UsageError("      Default: %zu", kDefaultMinDexFileCumulativeSizeForSwap);
+  UsageError("");
+  UsageError("  --swap-dex-count-threshold=<count>:  specifies the minimum number of dex files to");
+  UsageError("      allow the use of swap.");
+  UsageError("      Example: --swap-dex-count-threshold=10");
+  UsageError("      Default: %zu", kDefaultMinDexFilesForSwap);
+  UsageError("");
   UsageError("  --app-image-fd=<file-descriptor>: specify output file descriptor for app image.");
   UsageError("      Example: --app-image-fd=10");
   UsageError("");
@@ -473,25 +486,6 @@
   pthread_t pthread_;
 };
 
-static constexpr size_t kMinDexFilesForSwap = 2;
-static constexpr size_t kMinDexFileCumulativeSizeForSwap = 20 * MB;
-
-static bool UseSwap(bool is_image, std::vector<const DexFile*>& dex_files) {
-  if (is_image) {
-    // Don't use swap, we know generation should succeed, and we don't want to slow it down.
-    return false;
-  }
-  if (dex_files.size() < kMinDexFilesForSwap) {
-    // If there are less dex files than the threshold, assume it's gonna be fine.
-    return false;
-  }
-  size_t dex_files_size = 0;
-  for (const auto* dex_file : dex_files) {
-    dex_files_size += dex_file->GetHeader().file_size_;
-  }
-  return dex_files_size >= kMinDexFileCumulativeSizeForSwap;
-}
-
 class Dex2Oat FINAL {
  public:
   explicit Dex2Oat(TimingLogger* timings) :
@@ -1132,6 +1126,16 @@
         swap_file_name_ = option.substr(strlen("--swap-file=")).data();
       } else if (option.starts_with("--swap-fd=")) {
         ParseUintOption(option, "--swap-fd", &swap_fd_, Usage);
+      } else if (option.starts_with("--swap-dex-size-threshold=")) {
+        ParseUintOption(option,
+                        "--swap-dex-size-threshold",
+                        &min_dex_file_cumulative_size_for_swap_,
+                        Usage);
+      } else if (option.starts_with("--swap-dex-count-threshold=")) {
+        ParseUintOption(option,
+                        "--swap-dex-count-threshold",
+                        &min_dex_files_for_swap_,
+                        Usage);
       } else if (option.starts_with("--app-image-file=")) {
         app_image_file_name_ = option.substr(strlen("--app-image-file=")).data();
       } else if (option.starts_with("--app-image-fd=")) {
@@ -1842,10 +1846,6 @@
     }
   }
 
-  CompilerOptions* GetCompilerOptions() const {
-    return compiler_options_.get();
-  }
-
   bool IsImage() const {
     return IsAppImage() || IsBootImage();
   }
@@ -1897,6 +1897,22 @@
   }
 
  private:
+  bool UseSwap(bool is_image, const std::vector<const DexFile*>& dex_files) {
+    if (is_image) {
+      // Don't use swap, we know generation should succeed, and we don't want to slow it down.
+      return false;
+    }
+    if (dex_files.size() < min_dex_files_for_swap_) {
+      // If there are less dex files than the threshold, assume it's gonna be fine.
+      return false;
+    }
+    size_t dex_files_size = 0;
+    for (const auto* dex_file : dex_files) {
+      dex_files_size += dex_file->GetHeader().file_size_;
+    }
+    return dex_files_size >= min_dex_file_cumulative_size_for_swap_;
+  }
+
   template <typename T>
   static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
     std::vector<T*> result;
@@ -2486,6 +2502,8 @@
   bool dump_slow_timing_;
   std::string swap_file_name_;
   int swap_fd_;
+  size_t min_dex_files_for_swap_ = kDefaultMinDexFilesForSwap;
+  size_t min_dex_file_cumulative_size_for_swap_ = kDefaultMinDexFileCumulativeSizeForSwap;
   std::string app_image_file_name_;
   int app_image_fd_;
   std::string profile_file_;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
new file mode 100644
index 0000000..de3aed9
--- /dev/null
+++ b/dex2oat/dex2oat_test.cc
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "common_runtime_test.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stringprintf.h"
+#include "dex2oat_environment_test.h"
+#include "utils.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+namespace art {
+
+class Dex2oatTest : public Dex2oatEnvironmentTest {
+ public:
+  virtual void TearDown() OVERRIDE {
+    Dex2oatEnvironmentTest::TearDown();
+
+    output_ = "";
+    error_msg_ = "";
+    success_ = false;
+  }
+
+ protected:
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           CompilerFilter::Filter filter,
+                           const std::vector<std::string>& extra_args = {},
+                           bool expect_success = true) {
+    std::vector<std::string> args;
+    args.push_back("--dex-file=" + dex_location);
+    args.push_back("--oat-file=" + odex_location);
+    args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+    args.push_back("--runtime-arg");
+    args.push_back("-Xnorelocate");
+
+    args.insert(args.end(), extra_args.begin(), extra_args.end());
+
+    std::string error_msg;
+    bool success = Dex2Oat(args, &error_msg);
+
+    if (expect_success) {
+      ASSERT_TRUE(success) << error_msg;
+
+      // Verify the odex file was generated as expected.
+      std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                       odex_location.c_str(),
+                                                       nullptr,
+                                                       nullptr,
+                                                       false,
+                                                       /*low_4gb*/false,
+                                                       dex_location.c_str(),
+                                                       &error_msg));
+      ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+
+      CheckFilter(filter, odex_file->GetCompilerFilter());
+    } else {
+      ASSERT_FALSE(success) << output_;
+
+      error_msg_ = error_msg;
+
+      // Verify there's no loadable odex file.
+      std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+                                                       odex_location.c_str(),
+                                                       nullptr,
+                                                       nullptr,
+                                                       false,
+                                                       /*low_4gb*/false,
+                                                       dex_location.c_str(),
+                                                       &error_msg));
+      ASSERT_TRUE(odex_file.get() == nullptr);
+    }
+  }
+
+  // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
+  // in subclasses when equality is not expected.
+  virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
+    EXPECT_EQ(expected, actual);
+  }
+
+  bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
+    Runtime* runtime = Runtime::Current();
+
+    const std::vector<gc::space::ImageSpace*>& image_spaces =
+        runtime->GetHeap()->GetBootImageSpaces();
+    if (image_spaces.empty()) {
+      *error_msg = "No image location found for Dex2Oat.";
+      return false;
+    }
+    std::string image_location = image_spaces[0]->GetImageLocation();
+
+    std::vector<std::string> argv;
+    argv.push_back(runtime->GetCompilerExecutable());
+    argv.push_back("--runtime-arg");
+    argv.push_back("-classpath");
+    argv.push_back("--runtime-arg");
+    std::string class_path = runtime->GetClassPathString();
+    if (class_path == "") {
+      class_path = OatFile::kSpecialSharedLibrary;
+    }
+    argv.push_back(class_path);
+    if (runtime->IsDebuggable()) {
+      argv.push_back("--debuggable");
+    }
+    runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+    if (!runtime->IsVerificationEnabled()) {
+      argv.push_back("--compiler-filter=verify-none");
+    }
+
+    if (runtime->MustRelocateIfPossible()) {
+      argv.push_back("--runtime-arg");
+      argv.push_back("-Xrelocate");
+    } else {
+      argv.push_back("--runtime-arg");
+      argv.push_back("-Xnorelocate");
+    }
+
+    if (!kIsTargetBuild) {
+      argv.push_back("--host");
+    }
+
+    argv.push_back("--boot-image=" + image_location);
+
+    std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+    argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+    argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end());
+
+    // We must set --android-root.
+    const char* android_root = getenv("ANDROID_ROOT");
+    CHECK(android_root != nullptr);
+    argv.push_back("--android-root=" + std::string(android_root));
+
+    std::string command_line(Join(argv, ' '));
+
+    // We need to fix up the '&' being used for "do not check classpath."
+    size_t ampersand = command_line.find(" &");
+    CHECK_NE(ampersand, std::string::npos);
+    command_line = command_line.replace(ampersand, 2, " \\&");
+
+    command_line += " 2>&1";
+
+    // We need dex2oat to actually log things.
+    setenv("ANDROID_LOG_TAGS", "*:d", 1);
+
+    FILE* pipe = popen(command_line.c_str(), "r");
+
+    setenv("ANDROID_LOG_TAGS", "*:e", 1);
+
+    if (pipe == nullptr) {
+      success_ = false;
+    } else {
+      char buffer[128];
+
+      while (fgets(buffer, 128, pipe) != nullptr) {
+        output_ += buffer;
+      }
+
+      int result = pclose(pipe);
+      success_ = result == 0;
+    }
+    return success_;
+  }
+
+  std::string output_ = "";
+  std::string error_msg_ = "";
+  bool success_ = false;
+};
+
+class Dex2oatSwapTest : public Dex2oatTest {
+ protected:
+  void RunTest(bool use_fd, bool expect_use, const std::vector<std::string>& extra_args = {}) {
+    std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
+    std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
+
+    Copy(GetDexSrc1(), dex_location);
+
+    std::vector<std::string> copy(extra_args);
+
+    std::unique_ptr<ScratchFile> sf;
+    if (use_fd) {
+      sf.reset(new ScratchFile());
+      copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
+    } else {
+      std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
+      copy.push_back("--swap-file=" + swap_location);
+    }
+    GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy);
+
+    CheckValidity();
+    ASSERT_TRUE(success_);
+    CheckResult(expect_use);
+  }
+
+  void CheckResult(bool expect_use) {
+    if (kIsTargetBuild) {
+      CheckTargetResult(expect_use);
+    } else {
+      CheckHostResult(expect_use);
+    }
+  }
+
+  void CheckTargetResult(bool expect_use ATTRIBUTE_UNUSED) {
+    // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
+    //       something for variants with file descriptor where we can control the lifetime of
+    //       the swap file and thus take a look at it.
+  }
+
+  void CheckHostResult(bool expect_use) {
+    if (!kIsTargetBuild) {
+      if (expect_use) {
+        EXPECT_NE(output_.find("Large app, accepted running with swap."), std::string::npos)
+            << output_;
+      } else {
+        EXPECT_EQ(output_.find("Large app, accepted running with swap."), std::string::npos)
+            << output_;
+      }
+    }
+  }
+
+  // Check whether the dex2oat run was really successful.
+  void CheckValidity() {
+    if (kIsTargetBuild) {
+      CheckTargetValidity();
+    } else {
+      CheckHostValidity();
+    }
+  }
+
+  void CheckTargetValidity() {
+    // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
+    //       something for variants with file descriptor where we can control the lifetime of
+    //       the swap file and thus take a look at it.
+  }
+
+  // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
+  void CheckHostValidity() {
+    EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
+  }
+};
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapDefaultSingleSmall) {
+  RunTest(false /* use_fd */, false /* expect_use */);
+  RunTest(true /* use_fd */, false /* expect_use */);
+}
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapSingle) {
+  RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
+  RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
+}
+
+TEST_F(Dex2oatSwapTest, DoNotUseSwapSmall) {
+  RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
+  RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
+}
+
+TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) {
+  RunTest(false /* use_fd */,
+          true /* expect_use */,
+          { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
+  RunTest(true /* use_fd */,
+          true /* expect_use */,
+          { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
+}
+
+}  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 3509d9a..741b682 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -463,7 +463,7 @@
 #define ART_TARGET_NATIVETEST_DIR_STRING ""
 #endif
 
-std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) {
+std::string CommonRuntimeTestImpl::GetTestDexFileName(const char* name) const {
   CHECK(name != nullptr);
   std::string filename;
   if (IsHost()) {
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 0ce40e8..b68eb19 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -111,7 +111,7 @@
 
   std::string GetTestAndroidRoot();
 
-  std::string GetTestDexFileName(const char* name);
+  std::string GetTestDexFileName(const char* name) const;
 
   std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
       SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
new file mode 100644
index 0000000..743fbb9
--- /dev/null
+++ b/runtime/dex2oat_environment_test.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
+#define ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "oat_file_assistant.h"
+#include "os.h"
+#include "runtime.h"
+#include "utils.h"
+
+namespace art {
+
+// Test class that provides some helpers to set a test up for compilation using dex2oat.
+class Dex2oatEnvironmentTest : public CommonRuntimeTest {
+ public:
+  virtual void SetUp() OVERRIDE {
+    CommonRuntimeTest::SetUp();
+
+    // Create a scratch directory to work from.
+    scratch_dir_ = android_data_ + "/Dex2oatEnvironmentTest";
+    ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
+
+    // Create a subdirectory in scratch for odex files.
+    odex_oat_dir_ = scratch_dir_ + "/oat";
+    ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
+
+    odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
+    ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
+
+    // Verify the environment is as we expect
+    uint32_t checksum;
+    std::string error_msg;
+    ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
+      << "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
+    ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
+      << "Expected dex file to be at: " << GetDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
+      << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
+    ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+      << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
+      << "Expected dex file to be at: " << GetDexSrc2();
+
+    // GetMultiDexSrc2 should have the same primary dex checksum as
+    // GetMultiDexSrc1, but a different secondary dex checksum.
+    static constexpr bool kVerifyChecksum = true;
+    std::vector<std::unique_ptr<const DexFile>> multi1;
+    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
+          GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg;
+    ASSERT_GT(multi1.size(), 1u);
+
+    std::vector<std::unique_ptr<const DexFile>> multi2;
+    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
+          GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg;
+    ASSERT_GT(multi2.size(), 1u);
+
+    ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
+    ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+  }
+
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
+    // options->push_back(std::make_pair("-verbose:oat", nullptr));
+
+    // Set up the image location.
+    options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
+          nullptr));
+    // Make sure compilercallbacks are not set so that relocation will be
+    // enabled.
+    callbacks_.reset();
+  }
+
+  virtual void TearDown() OVERRIDE {
+    ClearDirectory(odex_dir_.c_str());
+    ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
+
+    ClearDirectory(odex_oat_dir_.c_str());
+    ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
+
+    ClearDirectory(scratch_dir_.c_str());
+    ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
+
+    CommonRuntimeTest::TearDown();
+  }
+
+  static void Copy(const std::string& src, const std::string& dst) {
+    std::ifstream  src_stream(src, std::ios::binary);
+    std::ofstream  dst_stream(dst, std::ios::binary);
+
+    dst_stream << src_stream.rdbuf();
+  }
+
+  // Returns the directory where the pre-compiled core.art can be found.
+  // TODO: We should factor out this into common tests somewhere rather than
+  // re-hardcoding it here (This was copied originally from the elf writer
+  // test).
+  std::string GetImageDirectory() const {
+    if (IsHost()) {
+      const char* host_dir = getenv("ANDROID_HOST_OUT");
+      CHECK(host_dir != nullptr);
+      return std::string(host_dir) + "/framework";
+    } else {
+      return std::string("/data/art-test");
+    }
+  }
+
+  std::string GetImageLocation() const {
+    return GetImageDirectory() + "/core.art";
+  }
+
+  std::string GetSystemImageFile() const {
+    return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
+      + "/core.art";
+  }
+
+  bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
+    std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
+    return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
+  }
+
+  std::string GetDexSrc1() const {
+    return GetTestDexFileName("Main");
+  }
+
+  // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
+  // file stripped.
+  std::string GetStrippedDexSrc1() const {
+    return GetTestDexFileName("MainStripped");
+  }
+
+  std::string GetMultiDexSrc1() const {
+    return GetTestDexFileName("MultiDex");
+  }
+
+  // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
+  // with the contents of the secondary dex file changed.
+  std::string GetMultiDexSrc2() const {
+    return GetTestDexFileName("MultiDexModifiedSecondary");
+  }
+
+  std::string GetDexSrc2() const {
+    return GetTestDexFileName("Nested");
+  }
+
+  // Scratch directory, for dex and odex files (oat files will go in the
+  // dalvik cache).
+  const std::string& GetScratchDir() const {
+    return scratch_dir_;
+  }
+
+  // Odex directory is the subdirectory in the scratch directory where odex
+  // files should be located.
+  const std::string& GetOdexDir() const {
+    return odex_dir_;
+  }
+
+ private:
+  std::string scratch_dir_;
+  std::string odex_oat_dir_;
+  std::string odex_dir_;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index e3cc77f..a1d3ed9 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include "oat_file_assistant.h"
-
 #include <algorithm>
 #include <fstream>
 #include <string>
@@ -29,8 +27,10 @@
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "compiler_callbacks.h"
+#include "dex2oat_environment_test.h"
 #include "gc/space/image_space.h"
 #include "mem_map.h"
+#include "oat_file_assistant.h"
 #include "oat_file_manager.h"
 #include "os.h"
 #include "scoped_thread_state_change.h"
@@ -39,52 +39,11 @@
 
 namespace art {
 
-class OatFileAssistantTest : public CommonRuntimeTest {
+class OatFileAssistantTest : public Dex2oatEnvironmentTest {
  public:
-  virtual void SetUp() {
+  virtual void SetUp() OVERRIDE {
     ReserveImageSpace();
-    CommonRuntimeTest::SetUp();
-
-    // Create a scratch directory to work from.
-    scratch_dir_ = android_data_ + "/OatFileAssistantTest";
-    ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
-
-    // Create a subdirectory in scratch for odex files.
-    odex_oat_dir_ = scratch_dir_ + "/oat";
-    ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700));
-
-    odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA));
-    ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700));
-
-    // Verify the environment is as we expect
-    uint32_t checksum;
-    std::string error_msg;
-    ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str()))
-      << "Expected pre-compiled boot image to be at: " << GetSystemImageFile();
-    ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
-      << "Expected dex file to be at: " << GetDexSrc1();
-    ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
-      << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
-    ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
-      << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
-    ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
-      << "Expected dex file to be at: " << GetDexSrc2();
-
-    // GetMultiDexSrc2 should have the same primary dex checksum as
-    // GetMultiDexSrc1, but a different secondary dex checksum.
-    static constexpr bool kVerifyChecksum = true;
-    std::vector<std::unique_ptr<const DexFile>> multi1;
-    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc1().c_str(),
-          GetMultiDexSrc1().c_str(), kVerifyChecksum, &error_msg, &multi1)) << error_msg;
-    ASSERT_GT(multi1.size(), 1u);
-
-    std::vector<std::unique_ptr<const DexFile>> multi2;
-    ASSERT_TRUE(DexFile::Open(GetMultiDexSrc2().c_str(),
-          GetMultiDexSrc2().c_str(), kVerifyChecksum, &error_msg, &multi2)) << error_msg;
-    ASSERT_GT(multi2.size(), 1u);
-
-    ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum());
-    ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum());
+    Dex2oatEnvironmentTest::SetUp();
   }
 
   // Pre-Relocate the image to a known non-zero offset so we don't have to
@@ -108,17 +67,6 @@
     return Exec(argv, error_msg);
   }
 
-  virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
-    // options->push_back(std::make_pair("-verbose:oat", nullptr));
-
-    // Set up the image location.
-    options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
-          nullptr));
-    // Make sure compilercallbacks are not set so that relocation will be
-    // enabled.
-    callbacks_.reset();
-  }
-
   virtual void PreRuntimeCreate() {
     std::string error_msg;
     ASSERT_TRUE(PreRelocateImage(&error_msg)) << error_msg;
@@ -126,94 +74,10 @@
     UnreserveImageSpace();
   }
 
-  virtual void PostRuntimeCreate() {
+  virtual void PostRuntimeCreate() OVERRIDE {
     ReserveImageSpace();
   }
 
-  virtual void TearDown() {
-    ClearDirectory(odex_dir_.c_str());
-    ASSERT_EQ(0, rmdir(odex_dir_.c_str()));
-
-    ClearDirectory(odex_oat_dir_.c_str());
-    ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str()));
-
-    ClearDirectory(scratch_dir_.c_str());
-    ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
-
-    CommonRuntimeTest::TearDown();
-  }
-
-  void Copy(std::string src, std::string dst) {
-    std::ifstream  src_stream(src, std::ios::binary);
-    std::ofstream  dst_stream(dst, std::ios::binary);
-
-    dst_stream << src_stream.rdbuf();
-  }
-
-  // Returns the directory where the pre-compiled core.art can be found.
-  // TODO: We should factor out this into common tests somewhere rather than
-  // re-hardcoding it here (This was copied originally from the elf writer
-  // test).
-  std::string GetImageDirectory() {
-    if (IsHost()) {
-      const char* host_dir = getenv("ANDROID_HOST_OUT");
-      CHECK(host_dir != nullptr);
-      return std::string(host_dir) + "/framework";
-    } else {
-      return std::string("/data/art-test");
-    }
-  }
-
-  std::string GetImageLocation() {
-    return GetImageDirectory() + "/core.art";
-  }
-
-  std::string GetSystemImageFile() {
-    return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
-      + "/core.art";
-  }
-
-  bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) {
-    std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
-    return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
-  }
-
-  std::string GetDexSrc1() {
-    return GetTestDexFileName("Main");
-  }
-
-  // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
-  // file stripped.
-  std::string GetStrippedDexSrc1() {
-    return GetTestDexFileName("MainStripped");
-  }
-
-  std::string GetMultiDexSrc1() {
-    return GetTestDexFileName("MultiDex");
-  }
-
-  // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but
-  // with the contents of the secondary dex file changed.
-  std::string GetMultiDexSrc2() {
-    return GetTestDexFileName("MultiDexModifiedSecondary");
-  }
-
-  std::string GetDexSrc2() {
-    return GetTestDexFileName("Nested");
-  }
-
-  // Scratch directory, for dex and odex files (oat files will go in the
-  // dalvik cache).
-  std::string GetScratchDir() {
-    return scratch_dir_;
-  }
-
-  // Odex directory is the subdirectory in the scratch directory where odex
-  // files should be located.
-  std::string GetOdexDir() {
-    return odex_dir_;
-  }
-
   // Generate a non-PIC odex file for the purposes of test.
   // The generated odex file will be un-relocated.
   void GenerateOdexForTest(const std::string& dex_location,
@@ -334,9 +198,6 @@
     image_reservation_.clear();
   }
 
-  std::string scratch_dir_;
-  std::string odex_oat_dir_;
-  std::string odex_dir_;
   std::vector<std::unique_ptr<MemMap>> image_reservation_;
 };