ART: Don't prepend '/' if classpath_dir is empty

When no classpath_dir is given, do not prepend '/' to a relative
dex location.

Add tests.

Bug: 65318303
Test: m test-art-host-gtest-class_loader_context_test
Test: m test-art-host
Change-Id: Ib374815ce3fa9d67694f3a23037cd3b8eea35173
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e97c6a0..07afedf 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -206,7 +206,7 @@
     for (const std::string& cp_elem : info.classpath) {
       // If path is relative, append it to the provided base directory.
       std::string raw_location = cp_elem;
-      if (raw_location[0] != '/') {
+      if (raw_location[0] != '/' && !classpath_dir.empty()) {
         raw_location = classpath_dir + '/' + raw_location;
       }
 
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 458f9f3..930160f 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -14,9 +14,12 @@
  * limitations under the License.
  */
 
+#include "class_loader_context.h"
+
 #include <gtest/gtest.h>
 #include <stdlib.h>
 
+#include "android-base/strings.h"
 
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
@@ -72,10 +75,21 @@
         context, index, ClassLoaderContext::kDelegateLastClassLoader, test_name);
   }
 
+  enum class LocationCheck {
+    kEquals,
+    kEndsWith
+  };
+  enum class BaseLocationCheck {
+    kEquals,
+    kEndsWith
+  };
+
   void VerifyOpenDexFiles(
       ClassLoaderContext* context,
       size_t index,
-      std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files) {
+      std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files,
+      LocationCheck mode = LocationCheck::kEquals,
+      BaseLocationCheck base_mode = BaseLocationCheck::kEquals) {
     ASSERT_TRUE(context != nullptr);
     ASSERT_TRUE(context->dex_files_open_attempted_);
     ASSERT_TRUE(context->dex_files_open_result_);
@@ -98,7 +112,16 @@
         expected_location.assign(expected_real_location.get());
         expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
 
-        ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
+        switch (mode) {
+          case LocationCheck::kEquals:
+            ASSERT_EQ(expected_dex_file->GetLocation(), opened_dex_file->GetLocation());
+            break;
+          case LocationCheck::kEndsWith:
+            ASSERT_TRUE(android::base::EndsWith(expected_dex_file->GetLocation(),
+                                                opened_dex_file->GetLocation().c_str()))
+                << opened_dex_file->GetLocation() << " vs " << expected_dex_file->GetLocation();
+            break;
+        }
         ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
 
         std::string class_path_location = info.classpath[k];
@@ -106,7 +129,17 @@
             realpath(class_path_location.c_str(), nullptr));
         ASSERT_TRUE(class_path_location_real != nullptr);
         class_path_location.assign(class_path_location_real.get());
-        ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation());
+        switch (base_mode) {
+          case BaseLocationCheck::kEquals:
+            ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation());
+            break;
+
+          case BaseLocationCheck::kEndsWith:
+            ASSERT_TRUE(android::base::EndsWith(opened_dex_file->GetBaseLocation(),
+                                                class_path_location.c_str()))
+                << info.classpath[k] << " vs " << opened_dex_file->GetBaseLocation();
+            break;
+        }
       }
     }
   }
@@ -304,6 +337,77 @@
   VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
 }
 
+static std::string CreateRelativeString(const std::string& in, const char* cwd) {
+  if (!android::base::StartsWith(in, cwd)) {
+    LOG(FATAL) << in << " " << cwd;
+  }
+  return in.substr(strlen(cwd) + 1);
+}
+
+TEST_F(ClassLoaderContextTest, OpenValidDexFilesRelative) {
+  char cwd_buf[4096];
+  if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) {
+    PLOG(FATAL) << "Could not get working directory";
+  }
+  std::string multidex_name = CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf);
+  std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex");
+  std::string myclass_dex_name = CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf);
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+  std::string dex_name = CreateRelativeString(GetTestDexFileName("Main"), cwd_buf);
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
+
+
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create(
+          "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
+          "DLC[" + dex_name + "]");
+
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
+
+  VerifyContextSize(context.get(), 2);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
+  all_dex_files0.push_back(&multidex_files);
+  all_dex_files0.push_back(&myclass_dex_files);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1;
+  all_dex_files1.push_back(&dex_files);
+
+  VerifyOpenDexFiles(context.get(), 0, all_dex_files0, LocationCheck::kEndsWith);
+  VerifyOpenDexFiles(context.get(), 1, all_dex_files1, LocationCheck::kEndsWith);
+}
+
+TEST_F(ClassLoaderContextTest, OpenValidDexFilesClasspathDir) {
+  char cwd_buf[4096];
+  if (getcwd(cwd_buf, arraysize(cwd_buf)) == nullptr) {
+    PLOG(FATAL) << "Could not get working directory";
+  }
+  std::string multidex_name = CreateRelativeString(GetTestDexFileName("MultiDex"), cwd_buf);
+  std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex");
+  std::string myclass_dex_name = CreateRelativeString(GetTestDexFileName("MyClass"), cwd_buf);
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+  std::string dex_name = CreateRelativeString(GetTestDexFileName("Main"), cwd_buf);
+  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
+
+
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create(
+          "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
+          "DLC[" + dex_name + "]");
+
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, cwd_buf));
+
+  VerifyContextSize(context.get(), 2);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
+  all_dex_files0.push_back(&multidex_files);
+  all_dex_files0.push_back(&myclass_dex_files);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1;
+  all_dex_files1.push_back(&dex_files);
+
+  VerifyOpenDexFiles(
+      context.get(), 0, all_dex_files0, LocationCheck::kEquals, BaseLocationCheck::kEndsWith);
+  VerifyOpenDexFiles(
+      context.get(), 1, all_dex_files1, LocationCheck::kEquals, BaseLocationCheck::kEndsWith);
+}
+
 TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
   std::string dex_name = GetTestDexFileName("Main");
   std::unique_ptr<ClassLoaderContext> context =