From a88cc5440ed79a0927f57bbd03377ff3ce04a760 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 25 Oct 2017 13:16:54 -0700 Subject: Switch to bionic gtest in bootable/recovery We encountered segfaults in Imgdiff host tests due to the failure to reset states of getopt. The problem can be solved by switching to use bionic's gtest where a new process is forked for each test. Also modify the recovery_component_test to make sure it runs in parallel. Changes include: 1. Merge the writes to misc partition into one single test. 2. Change the hard coded location "/cache/saved.file" into a configurable variable. Bug: 67849209 Test: recovery tests pass Change-Id: I165d313f32b83393fb7922c5078636ac40b50bc2 --- applypatch/applypatch.cpp | 18 +-- applypatch/freecache.cpp | 7 +- applypatch/include/applypatch/applypatch.h | 11 +- bootloader_message/bootloader_message.cpp | 33 ++--- .../bootloader_message/bootloader_message.h | 5 + minadbd/Android.mk | 9 +- tests/Android.mk | 10 +- tests/component/applypatch_test.cpp | 131 +++++++------------ tests/component/bootloader_message_test.cpp | 140 ++++----------------- tests/component/uncrypt_test.cpp | 99 +++++++++------ 10 files changed, 188 insertions(+), 275 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index c8b75df79..2153b5f19 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -42,6 +42,8 @@ #include "otafault/ota_io.h" #include "otautil/print_sha1.h" +std::string cache_temp_source = "/cache/saved.file"; + static int LoadPartitionContents(const std::string& filename, FileContents* file); static size_t FileSink(const unsigned char* data, size_t len, int fd); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, @@ -411,12 +413,10 @@ int applypatch_check(const char* filename, const std::vector& patch (!patch_sha1_str.empty() && FindMatchingPatch(file.sha1, patch_sha1_str) < 0)) { printf("file \"%s\" doesn't have any of expected sha1 sums; checking cache\n", filename); - // If the source file is missing or corrupted, it might be because - // we were killed in the middle of patching it. A copy of it - // should have been made in CACHE_TEMP_SOURCE. If that file - // exists and matches the sha1 we're looking for, the check still - // passes. - if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) { + // If the source file is missing or corrupted, it might be because we were killed in the middle + // of patching it. A copy of it should have been made in cache_temp_source. If that file + // exists and matches the sha1 we're looking for, the check still passes. + if (LoadFileContents(cache_temp_source.c_str(), &file) != 0) { printf("failed to load cache file\n"); return 1; } @@ -539,7 +539,7 @@ int applypatch(const char* source_filename, const char* target_filename, printf("source file is bad; trying copy\n"); FileContents copy_file; - if (LoadFileContents(CACHE_TEMP_SOURCE, ©_file) < 0) { + if (LoadFileContents(cache_temp_source.c_str(), ©_file) < 0) { printf("failed to read copy file\n"); return 1; } @@ -634,7 +634,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr printf("not enough free space on /cache\n"); return 1; } - if (SaveFileContents(CACHE_TEMP_SOURCE, &source_file) < 0) { + if (SaveFileContents(cache_temp_source.c_str(), &source_file) < 0) { printf("failed to back up source file\n"); return 1; } @@ -680,7 +680,7 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr } // Delete the backup copy of the source. - unlink(CACHE_TEMP_SOURCE); + unlink(cache_temp_source.c_str()); // Success! return 0; diff --git a/applypatch/freecache.cpp b/applypatch/freecache.cpp index 331cae265..0a40baa97 100644 --- a/applypatch/freecache.cpp +++ b/applypatch/freecache.cpp @@ -90,10 +90,9 @@ static std::set FindExpendableFiles() { while ((de = readdir(d.get())) != 0) { std::string path = std::string(dirs[i]) + "/" + de->d_name; - // We can't delete CACHE_TEMP_SOURCE; if it's there we might have - // restarted during installation and could be depending on it to - // be there. - if (path == CACHE_TEMP_SOURCE) { + // We can't delete cache_temp_source; if it's there we might have restarted during + // installation and could be depending on it to be there. + if (path == cache_temp_source) { continue; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 2a3b3ef39..bcb8a4126 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -36,12 +36,11 @@ struct FileContents { struct stat st; }; -// When there isn't enough room on the target filesystem to hold the -// patched version of the file, we copy the original here and delete -// it to free up space. If the expected source file doesn't exist, or -// is corrupted, we look to see if this file contains the bits we want -// and use it as the source instead. -#define CACHE_TEMP_SOURCE "/cache/saved.file" +// When there isn't enough room on the target filesystem to hold the patched version of the file, +// we copy the original here and delete it to free up space. If the expected source file doesn't +// exist, or is corrupted, we look to see if the cached file contains the bits we want and use it as +// the source instead. The default location for the cached source is "/cache/saved.file". +extern std::string cache_temp_source; using SinkFn = std::function; diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index f91446b43..aaeffdc5c 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -159,14 +159,8 @@ bool clear_bootloader_message(std::string* err) { bool write_bootloader_message(const std::vector& options, std::string* err) { bootloader_message boot = {}; - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); - for (const auto& s : options) { - strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); - if (s.back() != '\n') { - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); - } - } + update_bootloader_message_in_struct(&boot, options); + return write_bootloader_message(boot, err); } @@ -175,20 +169,27 @@ bool update_bootloader_message(const std::vector& options, std::str if (!read_bootloader_message(&boot, err)) { return false; } + update_bootloader_message_in_struct(&boot, options); - // Zero out the entire fields. - memset(boot.command, 0, sizeof(boot.command)); - memset(boot.recovery, 0, sizeof(boot.recovery)); + return write_bootloader_message(boot, err); +} + +bool update_bootloader_message_in_struct(bootloader_message* boot, + const std::vector& options) { + if (!boot) return false; + // Replace the command & recovery fields. + memset(boot->command, 0, sizeof(boot->command)); + memset(boot->recovery, 0, sizeof(boot->recovery)); - strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); - strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); + strlcpy(boot->command, "boot-recovery", sizeof(boot->command)); + strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery)); for (const auto& s : options) { - strlcat(boot.recovery, s.c_str(), sizeof(boot.recovery)); + strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery)); if (s.back() != '\n') { - strlcat(boot.recovery, "\n", sizeof(boot.recovery)); + strlcat(boot->recovery, "\n", sizeof(boot->recovery)); } } - return write_bootloader_message(boot, err); + return true; } bool write_reboot_bootloader(std::string* err) { diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 2ffbfc9e3..798f3bb8c 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -207,6 +207,11 @@ bool write_bootloader_message(const std::vector& options, std::stri // only update the command and recovery fields. bool update_bootloader_message(const std::vector& options, std::string* err); +// Update bootloader message (boots into recovery with the |options|) in |boot|. Will only update +// the command and recovery fields. +bool update_bootloader_message_in_struct(bootloader_message* boot, + const std::vector& options); + // Clear BCB. bool clear_bootloader_message(std::string* err); diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 803171d99..3c9ab3a7f 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -46,7 +46,12 @@ LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_SRC_FILES := fuse_adb_provider_test.cpp LOCAL_CFLAGS := $(minadbd_cflags) LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb -LOCAL_STATIC_LIBRARIES := libminadbd -LOCAL_SHARED_LIBRARIES := liblog libbase libcutils +LOCAL_STATIC_LIBRARIES := \ + libBionicGtestMain \ + libminadbd +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libbase \ + libcutils include $(BUILD_NATIVE_TEST) diff --git a/tests/Android.mk b/tests/Android.mk index 8ebb60308..d911c25e4 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -30,7 +30,8 @@ LOCAL_STATIC_LIBRARIES := \ libutils \ libz \ libselinux \ - libbase + libbase \ + libBionicGtestMain LOCAL_SRC_FILES := \ unit/asn1_decoder_test.cpp \ @@ -50,7 +51,8 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_MODULE := recovery_manual_test LOCAL_STATIC_LIBRARIES := \ libminui \ - libbase + libbase \ + libBionicGtestMain LOCAL_SRC_FILES := manual/recovery_test.cpp LOCAL_SHARED_LIBRARIES := \ @@ -163,6 +165,7 @@ LOCAL_STATIC_LIBRARIES := \ libsquashfs_utils \ libcutils \ libbrotli \ + libBionicGtestMain \ $(tune2fs_static_libraries) testdata_files := $(call find-subdir-files, testdata/*) @@ -212,7 +215,8 @@ LOCAL_STATIC_LIBRARIES := \ libbz \ libdivsufsort64 \ libdivsufsort \ - libz + libz \ + libBionicGtestMain LOCAL_SHARED_LIBRARIES := \ liblog include $(BUILD_HOST_NATIVE_TEST) diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 15ec08fe7..21c9a52dc 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -53,8 +53,7 @@ static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = } static void mangle_file(const std::string& fname) { - std::string content; - content.reserve(1024); + std::string content(1024, '\0'); for (size_t i = 0; i < 1024; i++) { content[i] = rand() % 256; } @@ -63,16 +62,11 @@ static void mangle_file(const std::string& fname) { class ApplyPatchTest : public ::testing::Test { public: - static void SetUpTestCase() { + virtual void SetUp() override { // set up files old_file = from_testdata_base("old.file"); new_file = from_testdata_base("new.file"); - patch_file = from_testdata_base("patch.bsdiff"); - rand_file = "/cache/applypatch_test_rand.file"; - cache_file = "/cache/saved.file"; - - // write stuff to rand_file - ASSERT_TRUE(android::base::WriteStringToFile("hello", rand_file)); + nonexistent_file = from_testdata_base("nonexistent.file"); // set up SHA constants sha1sum(old_file, &old_sha1, &old_size); @@ -82,56 +76,35 @@ class ApplyPatchTest : public ::testing::Test { bad_sha1_b = android::base::StringPrintf("%040x", rand()); } - static std::string old_file; - static std::string new_file; - static std::string rand_file; - static std::string cache_file; - static std::string patch_file; + std::string old_file; + std::string new_file; + std::string nonexistent_file; - static std::string old_sha1; - static std::string new_sha1; - static std::string bad_sha1_a; - static std::string bad_sha1_b; + std::string old_sha1; + std::string new_sha1; + std::string bad_sha1_a; + std::string bad_sha1_b; - static size_t old_size; - static size_t new_size; + size_t old_size; + size_t new_size; }; -static void cp(const std::string& src, const std::string& tgt) { - std::string cmd = "cp " + src + " " + tgt; - system(cmd.c_str()); -} - -static void backup_old() { - cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); -} - -static void restore_old() { - cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); -} - class ApplyPatchCacheTest : public ApplyPatchTest { - public: - virtual void SetUp() { - backup_old(); + protected: + void SetUp() override { + ApplyPatchTest::SetUp(); + cache_temp_source = old_file; } +}; - virtual void TearDown() { - restore_old(); +class ApplyPatchModesTest : public ::testing::Test { + protected: + void SetUp() override { + cache_temp_source = cache_source.path; } -}; -std::string ApplyPatchTest::old_file; -std::string ApplyPatchTest::new_file; -std::string ApplyPatchTest::rand_file; -std::string ApplyPatchTest::patch_file; -std::string ApplyPatchTest::cache_file; -std::string ApplyPatchTest::old_sha1; -std::string ApplyPatchTest::new_sha1; -std::string ApplyPatchTest::bad_sha1_a; -std::string ApplyPatchTest::bad_sha1_b; -size_t ApplyPatchTest::old_size; -size_t ApplyPatchTest::new_size; + TemporaryFile cache_source; +}; TEST_F(ApplyPatchTest, CheckModeSkip) { std::vector sha1s; @@ -189,43 +162,31 @@ TEST_F(ApplyPatchTest, CheckModeEmmcTarget) { ASSERT_EQ(0, applypatch_check(src_file.c_str(), sha1s)); } -TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) { - mangle_file(old_file); - std::vector sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceSingle) { + TemporaryFile temp_file; + mangle_file(temp_file.path); + std::vector sha1s_single = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_single)); + ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_single)); } -TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) { - mangle_file(old_file); - std::vector sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceMultiple) { + TemporaryFile temp_file; + mangle_file(temp_file.path); + std::vector sha1s_multiple = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(temp_file.path, sha1s_multiple)); + ASSERT_EQ(0, applypatch_check(nonexistent_file.c_str(), sha1s_multiple)); } -TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) { - mangle_file(old_file); - std::vector sha1s = { bad_sha1_a, bad_sha1_b }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); -} - -TEST_F(ApplyPatchCacheTest, CheckCacheMissingSingle) { - unlink(&old_file[0]); - std::vector sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); -} - -TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) { - unlink(&old_file[0]); - std::vector sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); -} - -TEST_F(ApplyPatchCacheTest, CheckCacheMissingFailure) { - unlink(&old_file[0]); - std::vector sha1s = { bad_sha1_a, bad_sha1_b }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); +TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSourceFailure) { + TemporaryFile temp_file; + mangle_file(temp_file.path); + std::vector sha1s_failure = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(temp_file.path, sha1s_failure)); + ASSERT_NE(0, applypatch_check(nonexistent_file.c_str(), sha1s_failure)); } -TEST(ApplyPatchModesTest, InvalidArgs) { +TEST_F(ApplyPatchModesTest, InvalidArgs) { // At least two args (including the filename). ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" })); @@ -233,7 +194,7 @@ TEST(ApplyPatchModesTest, InvalidArgs) { ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" })); } -TEST(ApplyPatchModesTest, PatchModeEmmcTarget) { +TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) { std::string boot_img = from_testdata_base("boot.img"); size_t boot_img_size; std::string boot_img_sha1; @@ -303,7 +264,7 @@ TEST(ApplyPatchModesTest, PatchModeEmmcTarget) { ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data())); } -TEST(ApplyPatchModesTest, PatchModeInvalidArgs) { +TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) { // Invalid bonus file. ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" })); @@ -364,11 +325,11 @@ TEST(ApplyPatchModesTest, PatchModeInvalidArgs) { ASSERT_NE(0, applypatch_modes(args6.size(), args6.data())); } -TEST(ApplyPatchModesTest, CheckModeInvalidArgs) { +TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) { // Insufficient args. ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" })); } -TEST(ApplyPatchModesTest, ShowLicenses) { +TEST_F(ApplyPatchModesTest, ShowLicenses) { ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" })); } diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp index b38bc7134..6cc59a495 100644 --- a/tests/component/bootloader_message_test.cpp +++ b/tests/component/bootloader_message_test.cpp @@ -18,53 +18,12 @@ #include #include +#include #include #include -class BootloaderMessageTest : public ::testing::Test { - protected: - BootloaderMessageTest() : has_misc(true) {} - - virtual void SetUp() override { - std::string err; - has_misc = !get_bootloader_message_blk_device(&err).empty(); - } - - virtual void TearDown() override { - // Clear the BCB. - if (has_misc) { - std::string err; - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - } - } - - bool has_misc; -}; - -TEST_F(BootloaderMessageTest, clear_bootloader_message) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - // Clear the BCB. - std::string err; - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; - - // Verify the content. - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; - - // All the bytes should be cleared. - ASSERT_EQ(std::string(sizeof(boot), '\0'), - std::string(reinterpret_cast(&boot), sizeof(boot))); -} - -TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } +TEST(BootloaderMessageTest, read_and_write_bootloader_message) { + TemporaryFile temp_misc; // Write the BCB. bootloader_message boot = {}; @@ -73,90 +32,71 @@ TEST_F(BootloaderMessageTest, read_and_write_bootloader_message) { strlcpy(boot.status, "status1", sizeof(boot.status)); std::string err; - ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; + ASSERT_TRUE(write_bootloader_message_to(boot, temp_misc.path, &err)) + << "Failed to write BCB: " << err; // Read and verify. bootloader_message boot_verify; - ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err; + ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_misc.path, &err)) + << "Failed to read BCB: " << err; ASSERT_EQ(std::string(reinterpret_cast(&boot), sizeof(boot)), std::string(reinterpret_cast(&boot_verify), sizeof(boot_verify))); } -TEST_F(BootloaderMessageTest, write_bootloader_message_options) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - +TEST(BootloaderMessageTest, update_bootloader_message_in_struct) { // Write the options to BCB. std::vector options = { "option1", "option2" }; - std::string err; - ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err; - // Inject some bytes into boot, which should be overwritten while reading. - bootloader_message boot; + bootloader_message boot = {}; + // Inject some bytes into boot. strlcpy(boot.recovery, "random message", sizeof(boot.recovery)); + strlcpy(boot.status, "status bytes", sizeof(boot.status)); + strlcpy(boot.stage, "stage bytes", sizeof(boot.stage)); strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved)); - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); // Verify that command and recovery fields should be set. ASSERT_EQ("boot-recovery", std::string(boot.command)); std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; ASSERT_EQ(expected, std::string(boot.recovery)); - // The rest should be cleared. - ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); - ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); - ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), - std::string(boot.reserved, sizeof(boot.reserved))); + // The rest should be intact. + ASSERT_EQ("status bytes", std::string(boot.status)); + ASSERT_EQ("stage bytes", std::string(boot.stage)); + ASSERT_EQ("reserved bytes", std::string(boot.reserved)); } -TEST_F(BootloaderMessageTest, write_bootloader_message_options_empty) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - +TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_empty) { // Write empty vector. std::vector options; - std::string err; - ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err; // Read and verify. - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + bootloader_message boot = {}; + ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); // command and recovery fields should be set. ASSERT_EQ("boot-recovery", std::string(boot.command)); ASSERT_EQ("recovery\n", std::string(boot.recovery)); - // The rest should be cleared. + // The rest should be empty. ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), std::string(boot.reserved, sizeof(boot.reserved))); } -TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - +TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { // Write super long message. std::vector options; for (int i = 0; i < 100; i++) { options.push_back("option: " + std::to_string(i)); } - std::string err; - ASSERT_TRUE(write_bootloader_message(options, &err)) << "Failed to write BCB: " << err; - // Read and verify. - bootloader_message boot; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + bootloader_message boot = {}; + ASSERT_TRUE(update_bootloader_message_in_struct(&boot, options)); // Make sure it's long enough. std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; @@ -167,40 +107,10 @@ TEST_F(BootloaderMessageTest, write_bootloader_message_options_long) { ASSERT_EQ(expected.substr(0, sizeof(boot.recovery) - 1), std::string(boot.recovery)); ASSERT_EQ('\0', boot.recovery[sizeof(boot.recovery) - 1]); - // The rest should be cleared. + // The rest should be empty. ASSERT_EQ(std::string(sizeof(boot.status), '\0'), std::string(boot.status, sizeof(boot.status))); ASSERT_EQ(std::string(sizeof(boot.stage), '\0'), std::string(boot.stage, sizeof(boot.stage))); ASSERT_EQ(std::string(sizeof(boot.reserved), '\0'), std::string(boot.reserved, sizeof(boot.reserved))); } -TEST_F(BootloaderMessageTest, update_bootloader_message) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - // Inject some bytes into boot, which should be not overwritten later. - bootloader_message boot; - strlcpy(boot.recovery, "random message", sizeof(boot.recovery)); - strlcpy(boot.reserved, "reserved bytes", sizeof(boot.reserved)); - std::string err; - ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; - - // Update the BCB message. - std::vector options = { "option1", "option2" }; - ASSERT_TRUE(update_bootloader_message(options, &err)) << "Failed to update BCB: " << err; - - bootloader_message boot_verify; - ASSERT_TRUE(read_bootloader_message(&boot_verify, &err)) << "Failed to read BCB: " << err; - - // Verify that command and recovery fields should be set. - ASSERT_EQ("boot-recovery", std::string(boot_verify.command)); - std::string expected = "recovery\n" + android::base::Join(options, "\n") + "\n"; - ASSERT_EQ(expected, std::string(boot_verify.recovery)); - - // The rest should be intact. - ASSERT_EQ(std::string(boot.status), std::string(boot_verify.status)); - ASSERT_EQ(std::string(boot.stage), std::string(boot_verify.stage)); - ASSERT_EQ(std::string(boot.reserved), std::string(boot_verify.reserved)); -} diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 3925236a5..55baca2e3 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -38,43 +39,49 @@ static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb"; static const std::string INIT_SVC_UNCRYPT = "init.svc.uncrypt"; static constexpr int SOCKET_CONNECTION_MAX_RETRY = 30; +static void StopService() { + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt")); + + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, ""); + std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, ""); + std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, ""); + GTEST_LOG_(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb + << "] uncrypt: [" << uncrypt << "]"; + if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") { + success = true; + break; + } + sleep(1); + } + + ASSERT_TRUE(success) << "uncrypt service is not available."; +} + class UncryptTest : public ::testing::Test { protected: UncryptTest() : has_misc(true) {} - virtual void SetUp() override { - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "uncrypt")); - - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - std::string setup_bcb = android::base::GetProperty(INIT_SVC_SETUP_BCB, ""); - std::string clear_bcb = android::base::GetProperty(INIT_SVC_CLEAR_BCB, ""); - std::string uncrypt = android::base::GetProperty(INIT_SVC_UNCRYPT, ""); - LOG(INFO) << "setup-bcb: [" << setup_bcb << "] clear-bcb: [" << clear_bcb << "] uncrypt: [" - << uncrypt << "]"; - if (setup_bcb != "running" && clear_bcb != "running" && uncrypt != "running") { - success = true; - break; - } - sleep(1); - } - - ASSERT_TRUE(success) << "uncrypt service is not available."; - + void SetUp() override { std::string err; has_misc = !get_bootloader_message_blk_device(&err).empty(); } - void SetupOrClearBcb(bool isSetup, const std::string& message, - const std::string& message_in_bcb) const { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; + void TearDown() override { + // Clear the BCB. + if (has_misc) { + std::string err; + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; } + } - // Trigger the setup-bcb service. + void SetupOrClearBcb(bool isSetup, const std::string& message, + const std::string& message_in_bcb) const { + // Restart the setup-bcb service. + StopService(); ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb")); // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). @@ -144,27 +151,49 @@ class UncryptTest : public ::testing::Test { } } + void VerifyBootloaderMessage(const std::string& expected) { + std::string err; + bootloader_message boot; + ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + + // Check that we have all the expected bytes. + ASSERT_EQ(expected, std::string(reinterpret_cast(&boot), sizeof(boot))); + } + bool has_misc; }; TEST_F(UncryptTest, setup_bcb) { + if (!has_misc) { + GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; + return; + } + + std::string random_data; + random_data.reserve(sizeof(bootloader_message)); + generate_n(back_inserter(random_data), sizeof(bootloader_message), []() { return rand() % 128; }); + + bootloader_message boot; + memcpy(&boot, random_data.c_str(), random_data.size()); + + std::string err; + ASSERT_TRUE(write_bootloader_message(boot, &err)) << "Failed to write BCB: " << err; + VerifyBootloaderMessage(random_data); + + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; + VerifyBootloaderMessage(std::string(sizeof(bootloader_message), '\0')); + std::string message = "--update_message=abc value"; std::string message_in_bcb = "recovery\n--update_message=abc value\n"; SetupOrClearBcb(true, message, message_in_bcb); -} -TEST_F(UncryptTest, clear_bcb) { SetupOrClearBcb(false, "", ""); -} -TEST_F(UncryptTest, setup_bcb_wipe_ab) { TemporaryFile wipe_package; ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path)); // It's expected to store a wipe package in /misc, with the package size passed to recovery. - std::string message = - "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; - std::string message_in_bcb = - "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; + message = "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; + message_in_bcb = "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; SetupOrClearBcb(true, message, message_in_bcb); } -- cgit v1.2.3