class OatFileAssistantBaseTest : public DexoptTest {};
class OatFileAssistantTest : public OatFileAssistantBaseTest, public ::testing::WithParamInterface<bool> { public: void SetUp() override {
DexoptTest::SetUp();
with_runtime_ = GetParam();
ofa_context_ = CreateOatFileAssistantContext();
}
// Verifies all variants of `GetOptimizationStatus`. // // `expected_filter` can be either a value of `CompilerFilter::Filter` or a string. // If `check_context` is true, only verifies the variants that checks class loader context. template <typename T> void VerifyOptimizationStatus(const std::string& file,
ClassLoaderContext* context, const T& expected_filter, const std::string& expected_reason, const std::string& expected_odex_status,
OatFileAssistant::Location expected_location, bool expected_is_backed_by_vdex_only, bool check_context = false) {
std::string expected_filter_name; if constexpr (std::is_same_v<T, CompilerFilter::Filter>) {
expected_filter_name = CompilerFilter::NameOfFilter(expected_filter);
} else {
expected_filter_name = expected_filter;
}
// Verify the static method (called from PM for dumpsys). // This variant does not check class loader context. if (!check_context) {
std::string compilation_filter;
std::string compilation_reason;
// Verifies the current version of `GetDexOptNeeded` (called from artd). void VerifyGetDexOptNeeded(OatFileAssistant* assistant,
CompilerFilter::Filter compiler_filter, const OatFileAssistant::DexoptTrigger& dexopt_trigger, bool expected_dexopt_needed, bool expected_is_vdex_usable,
OatFileAssistant::Location expected_location) {
OatFileAssistant::DexOptStatus status;
EXPECT_EQ(assistant->GetDexOptNeeded(compiler_filter, dexopt_trigger, &status),
expected_dexopt_needed);
EXPECT_EQ(status.IsVdexUsable(), expected_is_vdex_usable);
EXPECT_EQ(status.GetLocation(), expected_location);
}
// Verifies all versions of `GetDexOptNeeded` with the default dexopt trigger. void VerifyGetDexOptNeededDefault(OatFileAssistant* assistant,
CompilerFilter::Filter compiler_filter, bool expected_dexopt_needed, bool expected_is_vdex_usable,
OatFileAssistant::Location expected_location, int expected_legacy_result) { // Verify the current version (called from artd).
VerifyGetDexOptNeeded(assistant,
compiler_filter,
default_trigger_,
expected_dexopt_needed,
expected_is_vdex_usable,
expected_location);
// Verify the legacy version (called from PM).
EXPECT_EQ(
assistant->GetDexOptNeeded(compiler_filter, /*profile_changed=*/false, /*downgrade=*/false),
expected_legacy_result);
}
// Temporarily disables the pointer to the current runtime if `with_runtime_` is false. // Essentially simulates an environment where there is no active runtime.
android::base::ScopeGuard<std::function<void()>> ScopedMaybeWithoutRuntime() { if (!with_runtime_) {
Runtime::TestOnlySetCurrent(nullptr);
} return android::base::make_scope_guard(
[this]() { Runtime::TestOnlySetCurrent(runtime_.get()); });
}
// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative // encoded dex locations. // Expect: The oat file status is kNoDexOptNeeded.
TEST_P(OatFileAssistantTest, RelativeEncodedDexLocation) {
std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
// Create the dex file
Copy(GetMultiDexSrc1(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Verify we can load both dex files.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
// Case: We have a DEX file, but no OAT file for it. // Expect: The status is kDex2OatNeeded.
TEST_P(OatFileAssistantTest, DexNoOat) {
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have no DEX file and no OAT file. // Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash.
TEST_P(OatFileAssistantTest, NoDexNoOat) {
std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
EXPECT_EQ(nullptr, oat_file.get());
// Case: We have a DEX file and an ODEX file, but no OAT file. // Expect: The status is kNoDexOptNeeded.
TEST_P(OatFileAssistantTest, OdexUpToDate) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Force the use of oat location by making the dex parent not writable.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
// Case: We have an ODEX file compiled against partial boot image. // Expect: The status is kNoDexOptNeeded.
TEST_P(OatFileAssistantTest, OdexUpToDatePartialBootImage) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
// Insert an extra dex file to the boot class path.
std::string error_msg;
ASSERT_TRUE(InsertNewBootClasspathEntry(GetMultiDexSrc1(), &error_msg)) << error_msg;
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Force the use of oat location by making the dex parent not writable.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
// Case: We have a DEX file and a PIC ODEX file, but no OAT file. We load the dex // file via a symlink. // Expect: The status is kNoDexOptNeeded.
TEST_P(OatFileAssistantTest, OdexUpToDateSymLink) {
std::string scratch_dir = GetScratchDir();
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
// Now replace the dex location with a symlink.
std::string link = scratch_dir + "/link";
ASSERT_EQ(0, symlink(scratch_dir.c_str(), link.c_str()));
dex_location = link + "/OdexUpToDate.jar";
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and up-to-date OAT file for it. // Expect: The status is kNoDexOptNeeded.
TEST_P(OatFileAssistantTest, OatUpToDate) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: Passing invalid vdex fd and valid odex and zip fds. // Expect: The status should be kDex2OatFromScratch.
TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
// Case: Passing invalid vdex and odex fd with valid zip fd. // Expect: The status is kDex2oatFromScratch.
TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
// Case: We have a DEX file and up-to-date VDEX file for it, but no ODEX file, and the DEX file is // compressed.
TEST_P(OatFileAssistantTest, VdexUpToDateNoOdex) {
std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar";
std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat";
Copy(GetDexSrc1(), dex_location);
// Generating and deleting the oat file should have the side effect of // creating an up-to-date vdex file.
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
ASSERT_EQ(0, unlink(odex_location.c_str()));
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and speed-profile OAT file for it. // Expect: The status is kNoDexOptNeeded if the profile hasn't changed, but // kDex2Oat if the profile has changed.
TEST_P(OatFileAssistantTest, ProfileOatUpToDate) {
std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// We should not recompile even if `profile_changed` is true because the compiler filter should // not be downgraded.
VerifyGetDexOptNeeded(&oat_file_assistant,
CompilerFilter::kVerify,
profile_changed_trigger, /*expected_dexopt_needed=*/false, /*expected_is_vdex_usable=*/true, /*expected_location=*/OatFileAssistant::kLocationOat);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify, /*profile_changed=*/true));
// Case: We have a MultiDEX file and up-to-date OAT file for it. // Expect: The status is kNoDexOptNeeded and we load all dex files.
TEST_P(OatFileAssistantTest, MultiDexOatUpToDate) {
std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Verify we can load both dex files.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr); if (with_runtime_) {
EXPECT_TRUE(oat_file->IsExecutable());
}
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
}
// Case: We have a MultiDEX file where the non-main multdex entry is out of date. // Expect: The status is kDex2OatNeeded.
TEST_P(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar";
// Compile code for GetMultiDexSrc1.
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
// Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum // is out of date.
Copy(GetMultiDexSrc2(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and an OAT file out of date with respect to the // dex checksum.
TEST_P(OatFileAssistantTest, OatDexOutOfDate) {
std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
// We create a dex, generate an oat for it, then overwrite the dex with a // different dex to make the oat out of date.
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
Copy(GetDexSrc2(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and an (ODEX) VDEX file out of date with respect // to the dex checksum, but no ODEX file.
TEST_P(OatFileAssistantTest, VdexDexOutOfDate) {
std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat";
// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry // is out of date and there is no corresponding ODEX file.
TEST_P(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex";
// Case: We have a DEX file and an OAT file out of date with respect to the // boot image.
TEST_P(OatFileAssistantTest, OatImageOutOfDate) {
std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
// Case: We have a DEX file and an ODEX file, but no OAT file.
TEST_P(OatFileAssistantTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Verify the status.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
// We should still be able to get the non-executable odex file to run from.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
}
// Case: We have a resource-only DEX file, no ODEX file and no // OAT file. Expect: The status is kNoDexOptNeeded.
TEST_P(OatFileAssistantTest, ResourceOnlyDex) {
std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
Copy(GetResourceOnlySrc1(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Verify the status.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
// Case: We have a DEX file, an ODEX file and an OAT file. // Expect: It shouldn't crash. We should load the oat file executable.
TEST_P(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
// Create the dex, the odex and the oat files.
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Verify things don't go bad.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
// Case: We have a DEX file and up-to-date OAT file for it. // Expect: We should load an executable dex file.
TEST_P(OatFileAssistantTest, LoadOatUpToDate) {
std::string dex_location = GetScratchDir() + "/LoadOatUpToDate.jar";
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Load the oat using an oat file assistant.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
// Case: We have a DEX file and up-to-date quicken OAT file for it. // Expect: We should still load the oat file as executable.
TEST_P(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
std::string dex_location = GetScratchDir() + "/LoadExecInterpretOnlyOatUpToDate.jar";
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Load the oat using an oat file assistant.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
// Case: We have a DEX file and up-to-date OAT file for it. // Expect: Loading non-executable should load the oat non-executable.
TEST_P(OatFileAssistantTest, LoadNoExecOatUpToDate) {
std::string dex_location = GetScratchDir() + "/LoadNoExecOatUpToDate.jar";
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Load the oat using an oat file assistant.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
// Turn an absolute path into a path relative to the current working // directory. static std::string MakePathRelative(const std::string& target) { char buf[MAXPATHLEN];
std::string cwd = getcwd(buf, MAXPATHLEN);
// Split the target and cwd paths into components.
std::vector<std::string> target_path;
std::vector<std::string> cwd_path;
Split(target, '/', &target_path);
Split(cwd, '/', &cwd_path);
// Reverse the path components, so we can use pop_back().
std::reverse(target_path.begin(), target_path.end());
std::reverse(cwd_path.begin(), cwd_path.end());
// Drop the common prefix of the paths. Because we reversed the path // components, this becomes the common suffix of target_path and cwd_path. while (!target_path.empty() && !cwd_path.empty() && target_path.back() == cwd_path.back()) {
target_path.pop_back();
cwd_path.pop_back();
}
// For each element of the remaining cwd_path, add '..' to the beginning // of the target path. Because we reversed the path components, we add to // the end of target_path. for (unsignedint i = 0; i < cwd_path.size(); i++) {
target_path.push_back("..");
}
// Reverse again to get the right path order, and join to get the result.
std::reverse(target_path.begin(), target_path.end()); return android::base::Join(target_path, '/');
}
// Case: Non-absolute path to Dex location. // Expect: Not sure, but it shouldn't crash.
TEST_P(OatFileAssistantTest, NonAbsoluteDexLocation) {
std::string abs_dex_location = GetScratchDir() + "/NonAbsoluteDexLocation.jar";
Copy(GetDexSrc1(), abs_dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Ensure both unset and differing runtime SDK versions yield out-of-date dexopt status. for (uint32_t runtime_sdk_version : {static_cast<uint32_t>(SdkVersion::kUnset), 77u}) {
SCOPED_TRACE("Runtime SDK version: " + std::to_string(runtime_sdk_version)); // Override the runtime SDK version and recreate the OFA context to reflect this.
SetRuntimeSdkVersion(runtime_sdk_version);
ofa_context_ = CreateOatFileAssistantContext();
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str()); // When the runtime SDK_INT differs from the compiled SDK_INT, reject the ODEX file. // Note that the VDEX remains usable.
EXPECT_EQ(OatFileAssistant::kOatAssumedValuesOutOfDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
VerifyGetDexOptNeededDefault(&oat_file_assistant,
CompilerFilter::kSpeed, /*expected_dexopt_needed=*/true, /*expected_is_vdex_usable=*/true, /*expected_location=*/OatFileAssistant::kLocationOdex, /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
}
}
// A task to generate a dex location. Used by the RaceToGenerate test. class RaceGenerateTask : public Task { public:
RaceGenerateTask(OatFileAssistantBaseTest& test, const std::string& dex_location, const std::string& oat_location,
Mutex* lock)
: test_(test),
dex_location_(dex_location),
oat_location_(oat_location),
lock_(lock),
loaded_oat_file_(nullptr) {}
void Run([[maybe_unused]] Thread* self) override { // Load the dex files, and save a pointer to the loaded oat file, so that // we can verify only one oat file was loaded for the dex location.
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs; const OatFile* oat_file = nullptr;
{
MutexLock mu(Thread::Current(), *lock_); // Create the oat file.
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location_);
args.push_back("--oat-file=" + oat_location_);
std::string error_msg;
ASSERT_TRUE(test_.Dex2Oat(args, &error_msg)) << error_msg;
}
// Test the case where dex2oat invocations race with multiple processes trying to // load the oat file.
TEST_F(OatFileAssistantBaseTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
// Start the runtime to initialize the system's class loader.
Thread::Current()->TransitionFromSuspendedToRunnable();
runtime_->Start();
// We use the lib core dex file, because it's large, and hopefully should // take a while to generate.
Copy(GetLibCoreDexFileNames()[0], dex_location);
// Verify that tasks which got an oat file got a unique one.
std::set<const OatFile*> oat_files; for (auto& task : tasks) { const OatFile* oat_file = task->GetLoadedOatFile(); if (oat_file != nullptr) {
EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end());
oat_files.insert(oat_file);
}
}
}
// Case: We have a DEX file and an ODEX file, and no OAT file, // Expect: We should load the odex file executable.
TEST_P(OatFileAssistantTest, LoadDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Load the oat using an executable oat file assistant.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
// Case: We have a MultiDEX file and an ODEX file, and no OAT file. // Expect: We should load the odex file executable.
TEST_P(OatFileAssistantTest, LoadMultiDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex";
// Create the dex and odex files
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Load the oat using an executable oat file assistant.
OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), /*context=*/nullptr, /*load_executable=*/true);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
OatFileAssistant oat_file_assistant =
CreateOatFileAssistant(dex_location.c_str(), updated_context.get()); // Now check that DexOptNeeded does not advise compilation if we only verify the file.
VerifyGetDexOptNeededDefault(&oat_file_assistant,
CompilerFilter::kVerify, /*expected_dexopt_needed=*/false, /*expected_is_vdex_usable=*/true, /*expected_location=*/OatFileAssistant::kLocationOdex, /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
}
}
// Case: We have a DEX file and speed-profile ODEX file for it. The caller's intention is to // downgrade the compiler filter. // Expect: Dexopt should be performed only if the target compiler filter is worse than the current // one.
TEST_P(OatFileAssistantTest, Downgrade) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file but we don't have an ODEX file for it. The caller's intention is to // downgrade the compiler filter. // Expect: Dexopt should never be performed regardless of the target compiler filter.
TEST_P(OatFileAssistantTest, DowngradeNoOdex) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
Copy(GetDexSrc1(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and speed-profile ODEX file for it. The legacy version is called with // both `profile_changed` and `downgrade` being true. This won't happen in the real case. Just to be // complete. // Expect: The behavior should be as `profile_changed` is false and `downgrade` is true.
TEST_P(OatFileAssistantTest, ProfileChangedDowngrade) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and speed-profile ODEX file for it. The caller's intention is to force // the compilation. // Expect: Dexopt should be performed regardless of the target compiler filter. The VDEX file is // usable. // // The legacy version does not support this case. Historically, Package Manager does not take the // result from OatFileAssistant for forced compilation. It uses an arbitrary non-zero value instead. // Therefore, we don't test the legacy version here.
TEST_P(OatFileAssistantTest, Force) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file but we don't have an ODEX file for it. The caller's intention is to // force the compilation. // Expect: Dexopt should be performed regardless of the target compiler filter. No VDEX file is // usable. // // The legacy version does not support this case. Historically, Package Manager does not take the // result from OatFileAssistant for forced compilation. It uses an arbitrary non-zero value instead. // Therefore, we don't test the legacy version here.
TEST_P(OatFileAssistantTest, ForceNoOdex) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
Copy(GetDexSrc1(), dex_location);
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and a DM file for it, and the DEX file is uncompressed. // Expect: Dexopt should be performed if the compiler filter is better than "verify". The location // should be kLocationDm. // // The legacy version should return kDex2OatFromScratch if the target compiler filter is better than // "verify".
TEST_P(OatFileAssistantTest, DmUpToDateDexUncompressed) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Generate temporary ODEX and VDEX files in order to create the DM file from.
GenerateOdexForTest(
dex_location, odex_location, CompilerFilter::kVerify, "install", {"--copy-dex-files=false"});
CreateDexMetadata(vdex_location, dm_location);
// Cleanup the temporary files.
ASSERT_EQ(0, unlink(odex_location.c_str()));
ASSERT_EQ(0, unlink(vdex_location.c_str()));
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have a DEX file and a DM file for it, and the DEX file is compressed. // Expect: Dexopt should be performed regardless of the compiler filter. The location // should be kLocationDm.
TEST_P(OatFileAssistantTest, DmUpToDateDexCompressed) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
Copy(GetMultiDexSrc1(), dex_location);
// Generate temporary ODEX and VDEX files in order to create the DM file from.
GenerateOdexForTest(
dex_location, odex_location, CompilerFilter::kVerify, "install", {"--copy-dex-files=false"});
CreateDexMetadata(vdex_location, dm_location);
// Cleanup the temporary files.
ASSERT_EQ(0, unlink(odex_location.c_str()));
ASSERT_EQ(0, unlink(vdex_location.c_str()));
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have an ODEX file, but the DEX file is gone. // Expect: No dexopt is needed, as there's nothing we can do.
TEST_P(OatFileAssistantTest, OdexNoDex) {
std::string dex_location = GetScratchDir() + "/OdexNoDex.jar";
std::string odex_location = GetOdexDir() + "/OdexNoDex.oat";
// Case: We have a VDEX file, but the DEX file is gone. // Expect: No dexopt is needed, as there's nothing we can do.
TEST_P(OatFileAssistantTest, VdexNoDex) {
std::string dex_location = GetScratchDir() + "/VdexNoDex.jar";
std::string odex_location = GetOdexDir() + "/VdexNoDex.oat";
// Case: We have a VDEX file, generated without a boot image, and we now have a boot image. // Expect: Dexopt only if the target compiler filter >= "speed-profile".
TEST_P(OatFileAssistantTest, ShouldRecompileForImageFromVdex) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
Copy(GetMultiDexSrc1(), dex_location);
// Compile without a boot image.
GenerateOdexForTest(dex_location,
odex_location,
CompilerFilter::kVerify, "install",
{"--boot-image=/nonx/boot.art"});
// Delete the odex file and only keep the vdex.
ASSERT_EQ(0, unlink(odex_location.c_str()));
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have an ODEX file, generated without a boot image (filter: "verify"), and we now have a // boot image. // Expect: Dexopt only if the target compiler filter >= "speed-profile".
TEST_P(OatFileAssistantTest, ShouldRecompileForImageFromVerify) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
Copy(GetMultiDexSrc1(), dex_location);
// Compile without a boot image.
GenerateOdexForTest(dex_location,
odex_location,
CompilerFilter::kVerify, "install",
{"--boot-image=/nonx/boot.art"});
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have an ODEX file, generated without a boot image (filter: "speed-profile"), and we now // have a boot image. // Expect: Dexopt only if the target compiler filter >= "speed-profile".
TEST_P(OatFileAssistantTest, ShouldRecompileForImageFromSpeedProfile) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
Copy(GetMultiDexSrc1(), dex_location);
// Compile without a boot image.
GenerateOdexForTest(dex_location,
odex_location,
CompilerFilter::kSpeedProfile, "install",
{"--boot-image=/nonx/boot.art"});
auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDC file is in odex // location. // Expect: The best artifact location should be kLocationSdmOdex. Dexopt should be performed only if // the compiler filter is better than "speed-profile". // // The legacy version should return kDex2OatFromScratch if the target compiler filter is better than // "verify".
TEST_P(OatFileAssistantTest, SdmUpToDate) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDC file is in oat // location. // Expect: The best artifact location should be kLocationSdmOat.
TEST_P(OatFileAssistantTest, SdmUpToDateSdcInOatLocation) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, and the SDM file contains no // ART file. // Expect: The best artifact location should be kLocationSdmOdex.
TEST_P(OatFileAssistantTest, SdmUpToDateNoArt) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Case: We have SDM, DM, and SDC files for an uncompressed DEX file. Meanwhile, we have an ODEX // file that is also up to date. // Expect: The ODEX file is preferred over the SDM file. The best artifact location should be // kLocationOdex.
TEST_P(OatFileAssistantTest, SdmAndOdexUpToDate) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Case: We have SDM, DM, and SDC files for an uncompressed DEX file. Meanwhile, we have a VDEX // file that is also up to date. // Expect: The SDM file is preferred over the VDEX file. The best artifact location should be // kLocationSdmOdex.
TEST_P(OatFileAssistantTest, SdmAndVdexUpToDate) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Case: We have SDM, DM, and SDC files for a compressed DEX file. // Expect: The SDM file is still picked. Dexopt should be performed if the compiler filter is // "speed-profile" or above.
TEST_P(OatFileAssistantTest, SdmUpToDateCompressedDex) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
Copy(GetMultiDexSrc1(), dex_location);
// Case: We have SDM, DM, and SDC files for an uncompressed DEX file, but the SDC file contains the // wrong APEX versions. // Expect: The SDM file is rejected, while the DM file is still picked. Dexopt should be performed // if the compiler filter is better than "verify".
TEST_P(OatFileAssistantTest, SdmApexVersionMismatch) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string sdm_location =
GetScratchDir() + ART_FORMAT("/TestDex.{}.sdm", GetInstructionSetString(kRuntimeISA));
std::string dm_location = GetScratchDir() + "/TestDex.dm";
std::string sdc_location = GetOdexDir() + "/TestDex.sdc";
Copy(GetMultiDexUncompressedAlignedSrc1(), dex_location);
// Test that, no matter the dex file is backed by an oat file or not, the location fields in // DexFile, OatDexFile, and DexCache are the same as the actual dex location.
TEST_F(OatFileAssistantBaseTest, GetDexLocation) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string dex_location_multidex = dex_location + "!classes2.dex";
std::string oat_location = GetOdexDir() + "/TestDex.odex";
std::string art_location = GetOdexDir() + "/TestDex.art";
// Start the runtime to initialize the system's class loader.
Thread::Current()->TransitionFromSuspendedToRunnable();
runtime_->Start();
// Test that a dex file on the platform location gets the right hiddenapi domain, // regardless of whether it has a backing oat file.
TEST_F(OatFileAssistantBaseTest, SystemFrameworkDir) { // Host tests rely on ANDROID_ROOT, but it's not supported by // OatFileManager::OpenDexFilesFromOat.
TEST_DISABLED_FOR_HOST();
// Verify that the created instance is usable.
VerifyOptimizationStatusWithInstance(oat_file_assistant.get(), "speed", "install", "up-to-date",
OatFileAssistant::kLocationOdex, /*is_backed_by_vdex_only=*/false);
}
// Verify that the created instance is usable.
VerifyOptimizationStatusWithInstance(oat_file_assistant.get(), "speed", "install", "up-to-date",
OatFileAssistant::kLocationOdex, /*is_backed_by_vdex_only=*/false);
}
// TODO: More Tests: // * Test class linker falls back to unquickened dex for DexNoOat // * Test class linker falls back to unquickened dex for MultiDexNoOat // * Test using secondary isa // * Test for status of oat while oat is being generated (how?) // * Test case where 32 and 64 bit boot class paths differ, // and we ask IsInBootClassPath for a class in exactly one of the 32 or // 64 bit boot class paths. // * Test unexpected scenarios (?): // - Dex is stripped, don't have odex. // - Oat file corrupted after status check, before reload unexecutable // because it's unrelocated and no dex2oat
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.88Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.