/* * Copyright (C) 2018 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_LIBARTBASE_BASE_COMMON_ART_TEST_H_ #define ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_ #include <sys/types.h> #include <sys/wait.h> #include <functional> #include <string> #include <vector> #include "android-base/logging.h" #include "android-base/scopeguard.h" #include "base/file_utils.h" #include "base/globals.h" #include "base/memory_tool.h" #include "base/mutex.h" #include "base/os.h" #include "base/unix_file/fd_file.h" #include "dex/art_dex_file_loader.h" #include "dex/compact_dex_file.h" #include "dex/compact_dex_level.h" #include "gtest/gtest.h" namespace art { using LogSeverity = android::base::LogSeverity; using ScopedLogSeverity = android::base::ScopedLogSeverity; class DexFile; class ScratchDir { public: explicit ScratchDir(bool keep_files = false); ~ScratchDir(); const std::string& GetPath() const { return path_; } private: std::string path_; bool keep_files_; // Useful for debugging. DISALLOW_COPY_AND_ASSIGN(ScratchDir); }; class ScratchFile { public: ScratchFile(); explicit ScratchFile(const std::string& filename); ScratchFile(const ScratchFile& other, const char* suffix); ScratchFile(ScratchFile&& other) noexcept; ScratchFile& operator=(ScratchFile&& other) noexcept; explicit ScratchFile(File* file); ~ScratchFile(); const std::string& GetFilename() const { return filename_; } File* GetFile() const { return file_.get(); } int GetFd() const; void Close(); void Unlink(); private: std::string filename_; std::unique_ptr<File> file_; }; // Helper class that removes an environment variable whilst in scope. class ScopedUnsetEnvironmentVariable { public: explicit ScopedUnsetEnvironmentVariable(const char* variable) : variable_{variable}, old_value_{GetOldValue(variable)} { unsetenv(variable); } ~ScopedUnsetEnvironmentVariable() { if (old_value_.has_value()) { static constexpr int kReplace = 1; // tidy-issue: replace argument has libc dependent name. setenv(variable_, old_value_.value().c_str(), kReplace); } else { unsetenv(variable_); } } private: static std::optional<std::string> GetOldValue(const char* variable) { const char* value = getenv(variable); return value != nullptr ? std::optional<std::string>{value} : std::nullopt; } const char* variable_; std::optional<std::string> old_value_; DISALLOW_COPY_AND_ASSIGN(ScopedUnsetEnvironmentVariable); }; // Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise. android::base::ScopeGuard<std::function<void()>> ScopedUnroot(); // Temporarily drops all permissions on a file/directory. android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path); class CommonArtTestImpl { public: CommonArtTestImpl() = default; virtual ~CommonArtTestImpl() = default; // Set up ANDROID_BUILD_TOP, ANDROID_HOST_OUT, ANDROID_ROOT, ANDROID_I18N_ROOT, // ANDROID_ART_ROOT, and ANDROID_TZDATA_ROOT environment variables using sensible defaults // if not already set. static void SetUpAndroidRootEnvVars(); // Set up the ANDROID_DATA environment variable, creating the directory if required. // Note: setting up ANDROID_DATA may create a temporary directory. If this is used in a // non-derived class, be sure to also call the corresponding tear-down below. static void SetUpAndroidDataDir(std::string& android_data); static void TearDownAndroidDataDir(const std::string& android_data, bool fail_on_error); static void ClearDirectory(const char* dirpath, bool recursive = true); // Get the names of the libcore modules. virtual std::vector<std::string> GetLibCoreModuleNames() const; // Gets the paths of the libcore dex files for given modules. std::vector<std::string> GetLibCoreDexFileNames(const std::vector<std::string>& modules) const; // Gets the paths of the libcore dex files. std::vector<std::string> GetLibCoreDexFileNames() const; // Gets the on-host or on-device locations of the libcore dex files for given modules. std::vector<std::string> GetLibCoreDexLocations(const std::vector<std::string>& modules) const; // Gets the on-host or on-device locations of the libcore dex files. std::vector<std::string> GetLibCoreDexLocations() const; static std::string GetClassPathOption(const char* option, const std::vector<std::string>& class_path); // Retuerns the filename for a test dex (i.e. XandY or ManyMethods). std::string GetTestDexFileName(const char* name) const; template <typename Mutator> bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) { std::vector<std::unique_ptr<const DexFile>> dex_files; std::string error_msg; ArtDexFileLoader dex_file_loader(input_jar); CHECK(dex_file_loader.Open(/*verify*/ true, /*verify_checksum*/ true, &error_msg, &dex_files)) << error_msg; EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported"; const std::unique_ptr<const DexFile>& dex = dex_files[0]; CHECK(dex->EnableWrite()) << "Failed to enable write"; DexFile* dex_file = const_cast<DexFile*>(dex.get()); size_t original_size = dex_file->Size(); mutator(dex_file); // NB: mutation might have changed the DEX size in the header. std::vector<uint8_t> copy(dex_file->Begin(), dex_file->Begin() + original_size); copy.resize(dex_file->Size()); // Shrink/expand to new size. uint32_t checksum = DexFile::CalculateChecksum(copy.data(), copy.size()); CHECK_GE(copy.size(), sizeof(DexFile::Header)); reinterpret_cast<DexFile::Header*>(copy.data())->checksum_ = checksum; if (!output_dex->WriteFully(copy.data(), copy.size())) { return false; } if (output_dex->Flush() != 0) { PLOG(FATAL) << "Could not flush the output file."; } return true; } struct ForkAndExecResult { enum Stage { kLink, kFork, kWaitpid, kFinished, }; Stage stage; int status_code; bool StandardSuccess() { return stage == kFinished && WIFEXITED(status_code) && WEXITSTATUS(status_code) == 0; } }; using OutputHandlerFn = std::function<void(char*, size_t)>; using PostForkFn = std::function<bool()>; static ForkAndExecResult ForkAndExec(const std::vector<std::string>& argv, const PostForkFn& post_fork, const OutputHandlerFn& handler); static ForkAndExecResult ForkAndExec(const std::vector<std::string>& argv, const PostForkFn& post_fork, std::string* output); // Helper - find prebuilt tool (e.g. objdump). static std::string GetAndroidTool(const char* name, InstructionSet isa = InstructionSet::kX86_64); protected: static bool IsHost() { return !kIsTargetBuild; } // Returns ${ANDROID_BUILD_TOP}. Ensure it has tailing /. static std::string GetAndroidBuildTop(); // Returns ${ANDROID_HOST_OUT}. static std::string GetAndroidHostOut(); // Returns the path where boot classpath and boot image files are installed // for host tests (by the art_common mk module, typically built through "m // art-host-tests"). Different in CI where they are unpacked from the // art-host-tests.zip file. static std::string GetHostBootClasspathInstallRoot(); // File location to boot.art, e.g. /apex/com.android.art/javalib/boot.art static std::string GetCoreArtLocation(); // File location to boot.oat, e.g. /apex/com.android.art/javalib/boot.oat static std::string GetCoreOatLocation(); std::unique_ptr<const DexFile> LoadExpectSingleDexFile(const char* location); // Open a file (allows reading of framework jars). std::vector<std::unique_ptr<const DexFile>> OpenDexFiles(const char* filename); // Open a single dex file (aborts if there are more than one). std::unique_ptr<const DexFile> OpenDexFile(const char* filename); // Open a test file (art-gtest-*.jar). std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name); std::unique_ptr<const DexFile> OpenTestDexFile(const char* name); std::string android_data_; std::string android_system_ext_; std::string dalvik_cache_; virtual void SetUp(); virtual void TearDown(); // Creates the class path string for the given dex files (the list of dex file locations // separated by ':'). std::string CreateClassPath(const std::vector<std::unique_ptr<const DexFile>>& dex_files); // Same as CreateClassPath but add the dex file checksum after each location. The separator // is '*'. std::string CreateClassPathWithChecksums( const std::vector<std::unique_ptr<const DexFile>>& dex_files); static std::string GetImageDirectory(); static std::string GetCoreFileLocation(const char* suffix); std::vector<std::unique_ptr<const DexFile>> loaded_dex_files_; }; template <typename TestType> class CommonArtTestBase : public TestType, public CommonArtTestImpl { public: CommonArtTestBase() {} virtual ~CommonArtTestBase() {} protected: void SetUp() override { CommonArtTestImpl::SetUp(); } void TearDown() override { CommonArtTestImpl::TearDown(); } }; using CommonArtTest = CommonArtTestBase<testing::Test>; template <typename Param> using CommonArtTestWithParam = CommonArtTestBase<testing::TestWithParam<Param>>; // Returns a list of PIDs of the processes whose process name (the first commandline argument) fully // matches the given name. std::vector<pid_t> GetPidByName(const std::string& process_name); #define TEST_DISABLED_FOR_TARGET() \ if (kIsTargetBuild) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR TARGET"; \ } #define TEST_DISABLED_FOR_HOST() \ if (!kIsTargetBuild) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR HOST"; \ } #define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \ if (!kHostStaticBuildEnabled) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS"; \ } #define TEST_DISABLED_FOR_DEBUG_BUILD() \ if (kIsDebugBuild) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR DEBUG BUILD"; \ } #define TEST_DISABLED_FOR_MEMORY_TOOL() \ if (kRunningOnMemoryTool) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR MEMORY TOOL"; \ } #define TEST_DISABLED_FOR_HEAP_POISONING() \ if (kPoisonHeapReferences) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR HEAP POISONING"; \ } } // namespace art #define TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING() \ if (kRunningOnMemoryTool && kPoisonHeapReferences) { \ GTEST_SKIP() << "WARNING: TEST DISABLED FOR MEMORY TOOL WITH HEAP POISONING"; \ } #endif // ART_LIBARTBASE_BASE_COMMON_ART_TEST_H_