Make image test multi image

Required for testing multi image layout in future CLs.

Bug: 28640955

Test: clean-oat-host, test-art-host, device booting

(cherry picked from commit 25adcfb7dc81131add3a0a681ae18bced6f7a0e0)

Change-Id: I14809f56e711b4a87e01056c327eddbbd087f4ee
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 7234952..8a48604 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -76,9 +76,10 @@
   file_.reset(new File(fd, GetFilename(), true));
 }
 
-ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix) {
-  filename_ = other.GetFilename();
-  filename_ += suffix;
+ScratchFile::ScratchFile(const ScratchFile& other, const char* suffix)
+    : ScratchFile(other.GetFilename() + suffix) {}
+
+ScratchFile::ScratchFile(const std::string& filename) : filename_(filename) {
   int fd = open(filename_.c_str(), O_RDWR | O_CREAT, 0666);
   CHECK_NE(-1, fd);
   file_.reset(new File(fd, GetFilename(), true));
@@ -90,6 +91,18 @@
   file_.reset(file);
 }
 
+ScratchFile::ScratchFile(ScratchFile&& other) {
+  *this = std::move(other);
+}
+
+ScratchFile& ScratchFile::operator=(ScratchFile&& other) {
+  if (GetFile() != other.GetFile()) {
+    std::swap(filename_, other.filename_);
+    std::swap(file_, other.file_);
+  }
+  return *this;
+}
+
 ScratchFile::~ScratchFile() {
   Unlink();
 }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index b2090b7..2376e6a 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -40,8 +40,14 @@
  public:
   ScratchFile();
 
+  explicit ScratchFile(const std::string& filename);
+
   ScratchFile(const ScratchFile& other, const char* suffix);
 
+  explicit ScratchFile(ScratchFile&& other);
+
+  ScratchFile& operator=(ScratchFile&& other);
+
   explicit ScratchFile(File* file);
 
   ~ScratchFile();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index cb5226b..11d4af8 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -327,9 +327,9 @@
             continue;
           }
 
-          space::ImageSpace::CreateMultiImageLocations(image_file_name,
-                                                       boot_classpath,
-                                                       &image_file_names);
+          space::ImageSpace::ExtractMultiImageLocations(image_file_name,
+                                                        boot_classpath,
+                                                        &image_file_names);
         }
       } else {
         LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. "
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e41c532..d17ef81 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1619,9 +1619,54 @@
       << ",name=\"" << GetName() << "\"]";
 }
 
-void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_name,
-                                           const std::string& boot_classpath,
-                                           std::vector<std::string>* image_file_names) {
+std::string ImageSpace::GetMultiImageBootClassPath(
+    const std::vector<const char*>& dex_locations,
+    const std::vector<const char*>& oat_filenames,
+    const std::vector<const char*>& image_filenames) {
+  DCHECK_GT(oat_filenames.size(), 1u);
+  // If the image filename was adapted (e.g., for our tests), we need to change this here,
+  // too, but need to strip all path components (they will be re-established when loading).
+  std::ostringstream bootcp_oss;
+  bool first_bootcp = true;
+  for (size_t i = 0; i < dex_locations.size(); ++i) {
+    if (!first_bootcp) {
+      bootcp_oss << ":";
+    }
+
+    std::string dex_loc = dex_locations[i];
+    std::string image_filename = image_filenames[i];
+
+    // Use the dex_loc path, but the image_filename name (without path elements).
+    size_t dex_last_slash = dex_loc.rfind('/');
+
+    // npos is max(size_t). That makes this a bit ugly.
+    size_t image_last_slash = image_filename.rfind('/');
+    size_t image_last_at = image_filename.rfind('@');
+    size_t image_last_sep = (image_last_slash == std::string::npos)
+                                ? image_last_at
+                                : (image_last_at == std::string::npos)
+                                      ? std::string::npos
+                                      : std::max(image_last_slash, image_last_at);
+    // Note: whenever image_last_sep == npos, +1 overflow means using the full string.
+
+    if (dex_last_slash == std::string::npos) {
+      dex_loc = image_filename.substr(image_last_sep + 1);
+    } else {
+      dex_loc = dex_loc.substr(0, dex_last_slash + 1) +
+          image_filename.substr(image_last_sep + 1);
+    }
+
+    // Image filenames already end with .art, no need to replace.
+
+    bootcp_oss << dex_loc;
+    first_bootcp = false;
+  }
+  return bootcp_oss.str();
+}
+
+void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name,
+                                            const std::string& boot_classpath,
+                                            std::vector<std::string>* image_file_names) {
   DCHECK(image_file_names != nullptr);
 
   std::vector<std::string> images;
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index c407259..0ba131b 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -125,10 +125,14 @@
 
   // Use the input image filename to adapt the names in the given boot classpath to establish
   // complete locations for secondary images.
-  static void CreateMultiImageLocations(const std::string& input_image_file_name,
+  static void ExtractMultiImageLocations(const std::string& input_image_file_name,
                                         const std::string& boot_classpath,
                                         std::vector<std::string>* image_filenames);
 
+  static std::string GetMultiImageBootClassPath(const std::vector<const char*>& dex_locations,
+                                                const std::vector<const char*>& oat_filenames,
+                                                const std::vector<const char*>& image_filenames);
+
   // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
   uint8_t* GetImageEnd() const {
     return Begin() + GetImageHeader().GetImageSize();
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 15e3b1c..2be3b52 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -856,9 +856,9 @@
       const OatHeader& boot_oat_header = oat_file->GetOatHeader();
       const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
       if (boot_cp != nullptr) {
-        gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0],
-                                                         boot_cp,
-                                                         &image_locations);
+        gc::space::ImageSpace::ExtractMultiImageLocations(image_locations[0],
+                                                          boot_cp,
+                                                          &image_locations);
       }
     }