diff options
author | 2024-08-23 23:59:56 +0100 | |
---|---|---|
committer | 2024-08-29 19:54:54 +0000 | |
commit | 4809d2fceecbfeb16324ee382587934bb5cc8d69 (patch) | |
tree | b59bb076a85dd47dcbc7a4f777187cc4ade10105 | |
parent | e40d236781908dc7b63368f57f0adca2d475ede0 (diff) |
Fix error propagation from dex2oat command execution in tests.
There's no magic to report assertion errors just by creating
::testing::AssertionFailure objects - they have to be returned and
checked. Fix that by using Result wrappers.
Add the libgmock dependency to art_dex2oat_tests_defaults, because it's
necessary for the macros in android-base/result-gmock.h but libbase
doesn't pull it in.
#codehealth
Test: atest art_standalone_dex2oat_cts_tests \
art_standalone_dex2oat_tests
Bug: 360737799
Change-Id: I3ca73c5ee625ff09329bb36272d3a654c719768f
-rw-r--r-- | dex2oat/Android.bp | 1 | ||||
-rw-r--r-- | dex2oat/dex2oat_cts_test.cc | 30 | ||||
-rw-r--r-- | dex2oat/dex2oat_test.cc | 236 | ||||
-rw-r--r-- | dex2oat/dex2oat_vdex_test.cc | 83 | ||||
-rw-r--r-- | runtime/dex2oat_environment_test.h | 29 |
5 files changed, 198 insertions, 181 deletions
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 25125fee38..84eb7a529b 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -519,6 +519,7 @@ art_cc_defaults { ], static_libs: [ "libcrypto_for_art", + "libgmock", "liblz4", // libart(d)-dex2oat dependency; must be repeated here since it's a static lib. ], } diff --git a/dex2oat/dex2oat_cts_test.cc b/dex2oat/dex2oat_cts_test.cc index 0143fa6d72..63a7fea59e 100644 --- a/dex2oat/dex2oat_cts_test.cc +++ b/dex2oat/dex2oat_cts_test.cc @@ -14,11 +14,19 @@ * limitations under the License. */ +#include <sys/wait.h> + +#include "android-base/result-gmock.h" +#include "android-base/result.h" +#include "android-base/strings.h" #include "base/file_utils.h" #include "dex2oat_environment_test.h" namespace art { +using ::android::base::Result; +using ::android::base::testing::HasValue; + // Test the binary with the same bitness as the test. This is also done to avoid // the symlink /apex/com.android.art/bin/dex2oat, which we don't have selinux // permission to read on S. @@ -43,9 +51,7 @@ class Dex2oatCtsTest : public CommonArtTest, public Dex2oatScratchDirs { protected: // Stripped down counterpart to Dex2oatEnvironmentTest::Dex2Oat that only adds // enough arguments for our purposes. - int Dex2Oat(const std::vector<std::string>& dex2oat_args, - std::string* output, - std::string* error_msg) { + Result<int> Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* output) { std::vector<std::string> argv = {std::string(kAndroidArtApexDefaultPath) + "/bin/" + kDex2oatBinary}; argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end()); @@ -57,18 +63,20 @@ class Dex2oatCtsTest : public CommonArtTest, public Dex2oatScratchDirs { // We need dex2oat to actually log things. auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; }; + ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, output); if (res.stage != ForkAndExecResult::kFinished) { - *error_msg = strerror(errno); - ::testing::AssertionFailure() << "Failed to finish dex2oat invocation: " << *error_msg; + return ErrnoErrorf("Failed to finish dex2oat invocation '{}'", + android::base::Join(argv, ' ')); } - if (!res.StandardSuccess()) { - // We cannot use ASSERT_TRUE since the method returns an int and not void. - ::testing::AssertionFailure() << "dex2oat fork/exec failed: " << *error_msg; + if (!WIFEXITED(res.status_code)) { + return Errorf("dex2oat didn't terminate normally (status_code={:#x}): {}", + res.status_code, + android::base::Join(argv, ' ')); } - return res.status_code; + return WEXITSTATUS(res.status_code); } }; @@ -94,9 +102,7 @@ TEST_F(Dex2oatCtsTest, CompilationHooks) { args.emplace_back("--force-palette-compilation-hooks"); std::string output = ""; - std::string error_msg; - int res = Dex2Oat(args, &output, &error_msg); - EXPECT_EQ(res, 0) << error_msg; + EXPECT_THAT(Dex2Oat(args, &output), HasValue(0)); EXPECT_EQ(oat_file->FlushCloseOrErase(), 0); EXPECT_EQ(vdex_file->FlushCloseOrErase(), 0); } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 3956608086..05af1d1b7e 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -27,6 +27,8 @@ #include "android-base/logging.h" #include "android-base/macros.h" +#include "android-base/result-gmock.h" +#include "android-base/result.h" #include "android-base/stringprintf.h" #include "arch/instruction_set_features.h" #include "base/macros.h" @@ -54,7 +56,15 @@ namespace art { -using android::base::StringPrintf; +using ::android::base::Result; +using ::android::base::StringPrintf; +using ::android::base::testing::HasValue; +using ::android::base::testing::Ok; +using ::testing::AssertionFailure; +using ::testing::AssertionResult; +using ::testing::AssertionSuccess; +using ::testing::Ne; +using ::testing::Not; class Dex2oatTest : public Dex2oatEnvironmentTest { public: @@ -62,16 +72,14 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { Dex2oatEnvironmentTest::TearDown(); output_ = ""; - error_msg_ = ""; } protected: - int GenerateOdexForTestWithStatus(const std::vector<std::string>& dex_locations, - const std::string& odex_location, - CompilerFilter::Filter filter, - std::string* error_msg, - const std::vector<std::string>& extra_args = {}, - bool use_fd = false) { + Result<int> GenerateOdexForTestWithStatus(const std::vector<std::string>& dex_locations, + const std::string& odex_location, + CompilerFilter::Filter filter, + const std::vector<std::string>& extra_args = {}, + bool use_fd = false) { std::unique_ptr<File> oat_file; std::vector<std::string> args; args.reserve(dex_locations.size() + extra_args.size() + 6); @@ -81,7 +89,9 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { } if (use_fd) { oat_file.reset(OS::CreateEmptyFile(odex_location.c_str())); - CHECK(oat_file != nullptr) << odex_location; + if (oat_file == nullptr) { + return ErrnoErrorf("CreateEmptyFile failed on {}", odex_location); + } args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); args.push_back("--oat-location=" + odex_location); } else { @@ -97,20 +107,24 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { args.insert(args.end(), extra_args.begin(), extra_args.end()); - int status = Dex2Oat(args, &output_, error_msg); + int status = OR_RETURN(Dex2Oat(args, &output_)); if (oat_file != nullptr) { - CHECK_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file"; + int fc_errno = oat_file->FlushClose(); + if (fc_errno != 0) { + return Errorf( + "Could not flush and close oat file {}: {}", odex_location, strerror(-fc_errno)); + } } return status; } - ::testing::AssertionResult 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, - bool use_fd = false, - bool use_zip_fd = false) WARN_UNUSED { + AssertionResult 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, + bool use_fd = false, + bool use_zip_fd = false) WARN_UNUSED { return GenerateOdexForTest(dex_location, odex_location, filter, @@ -124,14 +138,14 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { bool test_accepts_odex_file_on_failure = false; template <typename T> - ::testing::AssertionResult GenerateOdexForTest(const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter, - const std::vector<std::string>& extra_args, - bool expect_success, - bool use_fd, - bool use_zip_fd, - T check_oat) WARN_UNUSED { + AssertionResult GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter, + const std::vector<std::string>& extra_args, + bool expect_success, + bool use_fd, + bool use_zip_fd, + T check_oat) WARN_UNUSED { std::vector<std::string> dex_locations; if (use_zip_fd) { std::string loc_arg = "--zip-location=" + dex_location; @@ -144,17 +158,21 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { } else { dex_locations.push_back(dex_location); } - std::string error_msg; - int status = GenerateOdexForTestWithStatus( - dex_locations, odex_location, filter, &error_msg, extra_args, use_fd); - bool success = (WIFEXITED(status) && WEXITSTATUS(status) == 0); + + int status = UNWRAP_OR_DO( + res, + GenerateOdexForTestWithStatus(dex_locations, odex_location, filter, extra_args, use_fd), + { return AssertionFailure() << res.error(); }); + + bool success = status == 0; if (expect_success) { if (!success) { - return ::testing::AssertionFailure() << "Failed to compile odex: " << error_msg << std::endl - << output_; + return AssertionFailure() << "Failed to compile odex (status=" << status + << "): " << output_; } // Verify the odex file was generated as expected. + std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, odex_location, odex_location, @@ -163,20 +181,19 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { dex_location, &error_msg)); if (odex_file == nullptr) { - return ::testing::AssertionFailure() << "Could not open odex file: " << error_msg; + return AssertionFailure() << "Could not open odex file: " << error_msg; } CheckFilter(filter, odex_file->GetCompilerFilter()); check_oat(*(odex_file.get())); } else { if (success) { - return ::testing::AssertionFailure() << "Succeeded to compile odex: " << output_; + return AssertionFailure() << "Succeeded to compile odex: " << output_; } - error_msg_ = error_msg; - if (!test_accepts_odex_file_on_failure) { // Verify there's no loadable odex file. + std::string error_msg; std::unique_ptr<OatFile> odex_file(OatFile::Open(/*zip_fd=*/-1, odex_location, odex_location, @@ -185,11 +202,11 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { dex_location, &error_msg)); if (odex_file != nullptr) { - return ::testing::AssertionFailure() << "Could open odex file: " << error_msg; + return AssertionFailure() << "Could open odex file: " << error_msg; } } } - return ::testing::AssertionSuccess(); + return AssertionSuccess(); } // Check the input compiler filter against the generated oat file's filter. May be overridden @@ -199,7 +216,6 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { } std::string output_ = ""; - std::string error_msg_ = ""; }; // This test class provides an easy way to validate an expected filter which is different @@ -1023,7 +1039,6 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest { Copy(use_second_source ? GetDexSrc2() : GetDexSrc1(), dex_location); - std::string error_msg; std::vector<std::string> extra_args; if (class_loader_context != nullptr) { extra_args.push_back(std::string("--class-loader-context=") + class_loader_context); @@ -1179,18 +1194,15 @@ TEST_F(Dex2oatDeterminism, UnloadCompile) { const std::string unload_vdex_name = out_dir + "/unload.vdex"; const std::string no_unload_oat_name = out_dir + "/nounload.oat"; const std::string no_unload_vdex_name = out_dir + "/nounload.vdex"; - std::string error_msg; const std::vector<gc::space::ImageSpace*>& spaces = runtime->GetHeap()->GetBootImageSpaces(); ASSERT_GT(spaces.size(), 0u); const std::string image_location = spaces[0]->GetImageLocation(); // Without passing in an app image, it will unload in between compilations. - const int res = - GenerateOdexForTestWithStatus(GetLibCoreDexFileNames(), - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--force-determinism", "--avoid-storing-invocation"}); - ASSERT_EQ(res, 0); + ASSERT_THAT(GenerateOdexForTestWithStatus(GetLibCoreDexFileNames(), + base_oat_name, + CompilerFilter::Filter::kVerify, + {"--force-determinism", "--avoid-storing-invocation"}), + HasValue(0)); Copy(base_oat_name, unload_oat_name); Copy(base_vdex_name, unload_vdex_name); std::unique_ptr<File> unload_oat(OS::OpenFileForReading(unload_oat_name.c_str())); @@ -1201,13 +1213,12 @@ TEST_F(Dex2oatDeterminism, UnloadCompile) { EXPECT_GT(unload_vdex->GetLength(), 0u); // Regenerate with an app image to disable the dex2oat unloading and verify that the output is // the same. - const int res2 = GenerateOdexForTestWithStatus( - GetLibCoreDexFileNames(), - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--force-determinism", "--avoid-storing-invocation", "--compile-individually"}); - ASSERT_EQ(res2, 0); + ASSERT_THAT(GenerateOdexForTestWithStatus( + GetLibCoreDexFileNames(), + base_oat_name, + CompilerFilter::Filter::kVerify, + {"--force-determinism", "--avoid-storing-invocation", "--compile-individually"}), + HasValue(0)); Copy(base_oat_name, no_unload_oat_name); Copy(base_vdex_name, no_unload_vdex_name); std::unique_ptr<File> no_unload_oat(OS::OpenFileForReading(no_unload_oat_name.c_str())); @@ -1232,20 +1243,18 @@ TEST_F(Dex2oatVerifierAbort, HardFail) { std::unique_ptr<const DexFile> dex(OpenTestDexFile("VerifierDeps")); std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; - std::string error_msg; - const int res_fail = GenerateOdexForTestWithStatus({dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--abort-on-hard-verifier-error"}); - EXPECT_NE(0, res_fail); - - const int res_no_fail = GenerateOdexForTestWithStatus({dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {"--no-abort-on-hard-verifier-error"}); - EXPECT_EQ(0, res_no_fail); + + EXPECT_THAT(GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kVerify, + {"--abort-on-hard-verifier-error"}), + HasValue(Ne(0))); + + EXPECT_THAT(GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kVerify, + {"--no-abort-on-hard-verifier-error"}), + HasValue(0)); } class Dex2oatDedupeCode : public Dex2oatTest {}; @@ -1308,31 +1317,25 @@ TEST_F(Dex2oatTest, MissingBootImageTest) { TEST_F(Dex2oatTest, EmptyUncompressedDexTest) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; - std::string error_msg; - int status = GenerateOdexForTestWithStatus({GetTestDexFileName("MainEmptyUncompressed")}, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {}, - /*use_fd*/ false); // Expect to fail with code 1 and not SIGSEGV or SIGABRT. - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), 1) << error_msg; + EXPECT_THAT(GenerateOdexForTestWithStatus({GetTestDexFileName("MainEmptyUncompressed")}, + base_oat_name, + CompilerFilter::Filter::kVerify, + /*extra_args*/ {}, + /*use_fd*/ false), + HasValue(1)); } TEST_F(Dex2oatTest, EmptyUncompressedAlignedDexTest) { std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; - std::string error_msg; - int status = GenerateOdexForTestWithStatus({GetTestDexFileName("MainEmptyUncompressedAligned")}, - base_oat_name, - CompilerFilter::Filter::kVerify, - &error_msg, - {}, - /*use_fd*/ false); // Expect to fail with code 1 and not SIGSEGV or SIGABRT. - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), 1) << error_msg; + EXPECT_THAT(GenerateOdexForTestWithStatus({GetTestDexFileName("MainEmptyUncompressedAligned")}, + base_oat_name, + CompilerFilter::Filter::kVerify, + /*extra_args*/ {}, + /*use_fd*/ false), + HasValue(1)); } TEST_F(Dex2oatTest, StderrLoggerOutput) { @@ -1510,10 +1513,10 @@ TEST_F(Dex2oatTest, CompactDexInvalidSource) { } const std::string& dex_location = invalid_dex.GetFilename(); const std::string odex_location = GetOdexDir() + "/output.odex"; - std::string error_msg; - int status = GenerateOdexForTestWithStatus( - {dex_location}, odex_location, CompilerFilter::kVerify, &error_msg, {}); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; + EXPECT_THAT(GenerateOdexForTestWithStatus( + {dex_location}, odex_location, CompilerFilter::kVerify, /*extra_args*/ {}), + HasValue(Ne(0))) + << " " << output_; } // Retain the header magic for the now removed compact dex files. @@ -1557,22 +1560,20 @@ TEST_F(Dex2oatTest, CompactDexInZip) { ASSERT_GE(invalid_dex.GetFile()->WriteFully(&header, sizeof(header)), 0); ASSERT_EQ(invalid_dex.GetFile()->Flush(), 0); } - std::string error_msg; - int status = 0u; - - status = GenerateOdexForTestWithStatus({invalid_dex_zip.GetFilename()}, - GetOdexDir() + "/output_apk.odex", - CompilerFilter::kVerify, - &error_msg, - {}); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; - - status = GenerateOdexForTestWithStatus({invalid_dex.GetFilename()}, - GetOdexDir() + "/output.odex", - CompilerFilter::kVerify, - &error_msg, - {}); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) != 0) << status << " " << output_; + + EXPECT_THAT(GenerateOdexForTestWithStatus({invalid_dex_zip.GetFilename()}, + GetOdexDir() + "/output_apk.odex", + CompilerFilter::kVerify, + /*extra_args*/ {}), + HasValue(Ne(0))) + << " " << output_; + + EXPECT_THAT(GenerateOdexForTestWithStatus({invalid_dex.GetFilename()}, + GetOdexDir() + "/output.odex", + CompilerFilter::kVerify, + /*extra_args*/ {}), + HasValue(Ne(0))) + << " " << output_; } TEST_F(Dex2oatWithExpectedFilterTest, AppImageNoProfile) { @@ -1962,21 +1963,18 @@ TEST_F(LinkageTest, LinkageEnabled) { std::unique_ptr<const DexFile> dex(OpenTestDexFile("LinkageTest")); std::string out_dir = GetScratchDir(); const std::string base_oat_name = out_dir + "/base.oat"; - std::string error_msg; - const int res_fail = + EXPECT_THAT( GenerateOdexForTestWithStatus({dex->GetLocation()}, base_oat_name, CompilerFilter::Filter::kSpeed, - &error_msg, - {"--check-linkage-conditions", "--crash-on-linkage-violation"}); - EXPECT_NE(0, res_fail); - - const int res_no_fail = GenerateOdexForTestWithStatus({dex->GetLocation()}, - base_oat_name, - CompilerFilter::Filter::kSpeed, - &error_msg, - {"--check-linkage-conditions"}); - EXPECT_EQ(0, res_no_fail); + {"--check-linkage-conditions", "--crash-on-linkage-violation"}), + Not(Ok())); + + EXPECT_THAT(GenerateOdexForTestWithStatus({dex->GetLocation()}, + base_oat_name, + CompilerFilter::Filter::kSpeed, + {"--check-linkage-conditions"}), + HasValue(0)); } // Regression test for bug 179221298. diff --git a/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc index 27fcc180a5..afea20cadf 100644 --- a/dex2oat/dex2oat_vdex_test.cc +++ b/dex2oat/dex2oat_vdex_test.cc @@ -17,6 +17,8 @@ #include <string> #include <vector> +#include "android-base/result-gmock.h" +#include "android-base/result.h" #include "common_runtime_test.h" #include "dex2oat_environment_test.h" #include "vdex_file.h" @@ -24,6 +26,8 @@ namespace art { +using ::android::base::Result; +using ::android::base::testing::HasValue; using verifier::VerifierDeps; class Dex2oatVdexTest : public Dex2oatEnvironmentTest { @@ -32,16 +36,15 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { Dex2oatEnvironmentTest::TearDown(); output_ = ""; - error_msg_ = ""; opened_vdex_files_.clear(); } protected: - bool RunDex2oat(const std::string& dex_location, - const std::string& odex_location, - const std::string* public_sdk, - bool copy_dex_files = false, - const std::vector<std::string>& extra_args = {}) { + Result<bool> RunDex2oat(const std::string& dex_location, + const std::string& odex_location, + const std::string* public_sdk, + bool copy_dex_files = false, + const std::vector<std::string>& extra_args = {}) { std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); args.push_back("--oat-file=" + odex_location); @@ -62,25 +65,27 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { args.insert(args.end(), extra_args.begin(), extra_args.end()); - return Dex2Oat(args, &output_, &error_msg_) == 0; + int status = OR_RETURN(Dex2Oat(args, &output_)); + return status == 0; } - std::unique_ptr<VerifierDeps> GetVerifierDeps(const std::string& vdex_location, - const DexFile* dex_file) { + Result<std::unique_ptr<VerifierDeps>> GetVerifierDeps(const std::string& vdex_location, + const DexFile* dex_file) { // Verify the vdex file content: only the classes using public APIs should be verified. + std::string error_msg; std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location, /*writable=*/false, /*low_4gb=*/false, - &error_msg_)); + &error_msg)); // Check the vdex doesn't have dex. if (vdex->HasDexSection()) { - ::testing::AssertionFailure() << "The vdex should not contain dex code"; + return Errorf("The vdex {} should not contain dex code", vdex_location); } // Verify the deps. VdexFile::VdexFileHeader vdex_header = vdex->GetVdexFileHeader(); if (!vdex_header.IsValid()) { - ::testing::AssertionFailure() << "Invalid vdex header"; + return Errorf("Invalid vdex header in {}", vdex_location); } std::vector<const DexFile*> dex_files; @@ -88,7 +93,7 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { std::unique_ptr<VerifierDeps> deps(new VerifierDeps(dex_files, /*output_only=*/false)); if (!deps->ParseStoredData(dex_files, vdex->GetVerifierDepsData())) { - ::testing::AssertionFailure() << error_msg_; + return Errorf("{}", error_msg); } opened_vdex_files_.push_back(std::move(vdex)); @@ -131,7 +136,6 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { } std::string output_; - std::string error_msg_; std::vector<std::unique_ptr<VdexFile>> opened_vdex_files_; }; @@ -139,17 +143,17 @@ class Dex2oatVdexTest : public Dex2oatEnvironmentTest { // - create a vdex file contraints by a predefined list of public API (passed as separate dex) // - compile with the above vdex file as input to validate the compilation flow TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubs) { - std::string error_msg; - // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath. std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); // Dex2oatVdexPublicSdkDex serves as the public API-stubs, restricting what can be verified. const std::string api_dex_location = GetTestDexFileName("Dex2oatVdexPublicSdkDex"); // Compile the subject app using the predefined API-stubs - ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), &api_dex_location)); + ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), &api_dex_location), + HasValue(true)); - std::unique_ptr<VerifierDeps> deps = GetVerifierDeps(GetVdex(dex_file), dex_file.get()); + std::unique_ptr<VerifierDeps> deps = + OR_ASSERT_FAIL(GetVerifierDeps(GetVdex(dex_file), dex_file.get())); // Verify public API usage. The classes should be verified. ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicCtor;", *dex_file)); @@ -175,9 +179,11 @@ TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubs) { std::vector<std::string> extra_args; extra_args.push_back("--dm-file=" + dm_file); output_ = ""; - ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), nullptr, false, extra_args)); + ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), nullptr, false, extra_args), + HasValue(true)); - std::unique_ptr<VerifierDeps> deps2 = GetVerifierDeps(GetVdex(dex_file), dex_file.get()); + std::unique_ptr<VerifierDeps> deps2 = + OR_ASSERT_FAIL(GetVerifierDeps(GetVdex(dex_file), dex_file.get())); ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicCtor;", *dex_file)); ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicMethod;", *dex_file)); @@ -194,16 +200,15 @@ TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubs) { // Check that if the input dm does contain dex files then the compilation fails TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) { - std::string error_msg; - // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath. std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); // Compile the subject app using the predefined API-stubs - ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), /*public_sdk=*/nullptr, - /*copy_dex_files=*/true)); + /*copy_dex_files=*/true), + HasValue(true)); // Create the .dm file with the output. std::string dm_file = GetScratchDir() + "/base.dm"; @@ -213,17 +218,16 @@ TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) { // Recompile again with the .dm file which contains a vdex with code. // The compilation will pass, but dex2oat will not use the vdex file. - ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file, "v2"), /*public_sdk=*/nullptr, /*copy_dex_files=*/true, - extra_args)); + extra_args), + HasValue(true)); } // Check that corrupt vdex files from .dm archives are ignored. TEST_F(Dex2oatVdexTest, VerifyCorruptVdexFile) { - std::string error_msg; - // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath. std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); @@ -236,25 +240,25 @@ TEST_F(Dex2oatVdexTest, VerifyCorruptVdexFile) { extra_args.push_back("--dm-file=" + dm_file); // Compile the dex file. Despite having a corrupt input .vdex, we should not crash. - ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), /*public_sdk=*/nullptr, /*copy_dex_files=*/true, - extra_args)) + extra_args), + HasValue(true)) << output_; } // Check that if the input dm a vdex with mismatching checksums the compilation fails TEST_F(Dex2oatVdexTest, VerifyInputDmWithMismatchedChecksums) { - std::string error_msg; - // Generate a vdex file for Dex2oatVdexTestDex. std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex")); - ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), + ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), /*public_sdk=*/nullptr, - /*copy_dex_files=*/false)); + /*copy_dex_files=*/false), + HasValue(true)); // Create the .dm file with the output. std::string dm_file = GetScratchDir() + "/base.dm"; @@ -265,11 +269,12 @@ TEST_F(Dex2oatVdexTest, VerifyInputDmWithMismatchedChecksums) { // Try to compile Main using an input dm which contains the vdex for // Dex2oatVdexTestDex. It should fail. std::unique_ptr<const DexFile> dex_file2(OpenTestDexFile("Main")); - ASSERT_FALSE(RunDex2oat(dex_file2->GetLocation(), - GetOdex(dex_file2, "v2"), - /*public_sdk=*/nullptr, - /*copy_dex_files=*/false, - extra_args)) + ASSERT_THAT(RunDex2oat(dex_file2->GetLocation(), + GetOdex(dex_file2, "v2"), + /*public_sdk=*/nullptr, + /*copy_dex_files=*/false, + extra_args), + HasValue(false)) << output_; } diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 08b36bbb37..7e8378aa67 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -17,11 +17,15 @@ #ifndef ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_ #define ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_ +#include <sys/wait.h> + #include <fstream> #include <optional> #include <string> #include <vector> +#include "android-base/result.h" +#include "android-base/strings.h" #include "base/file_utils.h" #include "base/macros.h" #include "base/os.h" @@ -41,6 +45,8 @@ namespace art HIDDEN { +using ::android::base::Result; + static constexpr bool kDebugArgs = false; class Dex2oatScratchDirs { @@ -193,12 +199,11 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe return GetTestDexFileName("Nested"); } - int Dex2Oat(const std::vector<std::string>& dex2oat_args, - std::string* output, - std::string* error_msg) { + Result<int> Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* output) { std::vector<std::string> argv; - if (!CommonRuntimeTest::StartDex2OatCommandLine(&argv, error_msg)) { - ::testing::AssertionFailure() << "Could not start dex2oat cmd line " << *error_msg; + std::string error_msg; + if (!CommonRuntimeTest::StartDex2OatCommandLine(&argv, &error_msg)) { + return Errorf("Could not start dex2oat cmd line: {}", error_msg); } Runtime* runtime = Runtime::Current(); @@ -235,18 +240,20 @@ class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTe // We need dex2oat to actually log things. auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; }; + ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, output); if (res.stage != ForkAndExecResult::kFinished) { - *error_msg = strerror(errno); - ::testing::AssertionFailure() << "Failed to finish dex2oat invocation: " << *error_msg; + return ErrnoErrorf("Failed to finish dex2oat invocation '{}'", + android::base::Join(argv, ' ')); } - if (!res.StandardSuccess()) { - // We cannot use ASSERT_TRUE since the method returns an int and not void. - ::testing::AssertionFailure() << "dex2oat fork/exec failed: " << *error_msg; + if (!WIFEXITED(res.status_code)) { + return Errorf("dex2oat didn't terminate normally (status_code={:#x}): {}", + res.status_code, + android::base::Join(argv, ' ')); } - return res.status_code; + return WEXITSTATUS(res.status_code); } void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) { |