From 6ed175d5412deeaec9691f85757e45452407b8e3 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 18 Jul 2017 11:29:40 -0700 Subject: Fix a case when brotli writer fails to write last few blocks of data receive_new_data may exit too early if the zip processor has sent all the raw data. As a result, the last few 'new' commands will fail even though the brotli decoder has more output in its buffer. Restruct the code so that 'NewThreadInfo' owns the decoder state solely; and receive_brotli_new_data is responsible for the decompression. Also reduce the test data size to 100 blocks to avoid the test timeout. Bug: 63802629 Test: recovery_component_test. on bullhead, apply full updates with and w/o brotli compressed entries, apply an incremental update. Change-Id: I9442f2536b74e48dbf7eeb062a8539c82c6dab47 --- tests/component/updater_test.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 357a39ef7..0a9328849 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -592,10 +592,10 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0)); auto generator = []() { return rand() % 128; }; - // Generate 2048 blocks of random data. + // Generate 100 blocks of random data. std::string brotli_new_data; - brotli_new_data.reserve(4096 * 2048); - generate_n(back_inserter(brotli_new_data), 4096 * 2048, generator); + brotli_new_data.reserve(4096 * 100); + generate_n(back_inserter(brotli_new_data), 4096 * 100, generator); size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); std::vector encoded_data(encoded_size); @@ -609,8 +609,19 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); ASSERT_EQ(0, zip_writer.FinishEntry()); + // Write a few small chunks of new data, then a large chunk, and finally a few small chunks. + // This helps us to catch potential short writes. std::vector transfer_list = { - "4", "2048", "0", "0", "new 4,0,512,512,1024", "new 2,1024,2048", + "4", + "100", + "0", + "0", + "new 2,0,1", + "new 2,1,2", + "new 4,2,50,50,97", + "new 2,97,98", + "new 2,98,99", + "new 2,99,100", }; ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); std::string commands = android::base::Join(transfer_list, '\n'); -- cgit v1.2.3 From c319613e06f996aed32da3a1c3e93bf9b04ffa95 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 24 Jul 2017 09:22:39 -0700 Subject: tests: Add a test to cover legacy care_map.txt handling. This is to cover the code added by commit 5a1dee01df3af346729b5791606b72d59b8e9815, where an O update_verifier should not reject N care_map.txt. Bug: 63544345 Test: recovery_component_test passes on marlin. Change-Id: Ia944e16cba3cc635098b3ffd92842d725b570fec --- tests/component/update_verifier_test.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'tests/component') diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index 5fc7ef63f..b04e1185e 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -81,3 +81,16 @@ TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) { ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); ASSERT_FALSE(verify_image(temp_file.path)); } + +TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) { + // This test relies on dm-verity support. + if (!verity_supported) { + GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; + return; + } + + TemporaryFile temp_file; + std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); + ASSERT_TRUE(verify_image(temp_file.path)); +} -- cgit v1.2.3 From 69b9649e9bfafc2ddb5aa4dd609895b1fc097364 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 17 Aug 2017 16:42:57 -0700 Subject: Allow comparison against multi serial nums for A/B package The metadata file now can have multiple serial numbers in the format: serialno=serialno1|serialno2|serialno3 ... Verifier will pass the check if the device serial number matches any of these numbers. Bug: 64802465 Test: Create a metadata file with 1000 numbers and sideload in sailfish. The checker detects both match and mismatch cases. Change-Id: I3f12b75e15f4179df260778e37f4563d65db0fa8 --- tests/component/install_test.cpp | 71 ++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 13 deletions(-) (limited to 'tests/component') diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 968196fc0..7bb496066 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -198,8 +199,8 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml CloseArchive(zip); } -TEST(InstallTest, update_binary_command_smoke) { #ifdef AB_OTA_UPDATER +static void VerifyAbUpdateBinaryCommand(const std::string& serialno, bool success = true) { TemporaryFile temp_file; FILE* zip_file = fdopen(temp_file.fd, "w"); ZipWriter writer(zip_file); @@ -215,11 +216,13 @@ TEST(InstallTest, update_binary_command_smoke) { ASSERT_NE("", device); std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); ASSERT_NE("", timestamp); - std::string metadata = android::base::Join( - std::vector{ - "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, - }, - "\n"); + + std::vector meta{ "ota-type=AB", "pre-device=" + device, + "post-timestamp=" + timestamp }; + if (!serialno.empty()) { + meta.push_back("serialno=" + serialno); + } + std::string metadata = android::base::Join(meta, "\n"); ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); ASSERT_EQ(0, writer.FinishEntry()); ASSERT_EQ(0, writer.Finish()); @@ -234,14 +237,25 @@ TEST(InstallTest, update_binary_command_smoke) { std::string package = "/path/to/update.zip"; std::string binary_path = "/sbin/update_engine_sideload"; std::vector cmd; - ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); - ASSERT_EQ(5U, cmd.size()); - ASSERT_EQ(binary_path, cmd[0]); - ASSERT_EQ("--payload=file://" + package, cmd[1]); - ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); - ASSERT_EQ("--headers=" + properties, cmd[3]); - ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); + if (success) { + ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + ASSERT_EQ(5U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("--payload=file://" + package, cmd[1]); + ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); + ASSERT_EQ("--headers=" + properties, cmd[3]); + ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); + } else { + ASSERT_EQ(INSTALL_ERROR, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + } CloseArchive(zip); +} +#endif // AB_OTA_UPDATER + +TEST(InstallTest, update_binary_command_smoke) { +#ifdef AB_OTA_UPDATER + // Empty serialno will pass the verification. + VerifyAbUpdateBinaryCommand({}); #else TemporaryFile temp_file; FILE* zip_file = fdopen(temp_file.fd, "w"); @@ -340,3 +354,34 @@ TEST(InstallTest, update_binary_command_invalid) { CloseArchive(zip); #endif // AB_OTA_UPDATER } + +#ifdef AB_OTA_UPDATER +TEST(InstallTest, update_binary_command_multiple_serialno) { + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + + // Single matching serialno will pass the verification. + VerifyAbUpdateBinaryCommand(serialno); + + static constexpr char alphabet[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + auto generator = []() { return alphabet[rand() % (sizeof(alphabet) - 1)]; }; + + // Generate 900 random serial numbers. + std::string random_serial; + for (size_t i = 0; i < 900; i++) { + generate_n(back_inserter(random_serial), serialno.size(), generator); + random_serial.append("|"); + } + // Random serialnos should fail the verification. + VerifyAbUpdateBinaryCommand(random_serial, false); + + std::string long_serial = random_serial + serialno + "|"; + for (size_t i = 0; i < 99; i++) { + generate_n(back_inserter(long_serial), serialno.size(), generator); + long_serial.append("|"); + } + // String with the matching serialno should pass the verification. + VerifyAbUpdateBinaryCommand(long_serial); +} +#endif // AB_OTA_UPDATER -- cgit v1.2.3 From c89d1e7e2a6a8a07bb336db35db88f5452d8a51d Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 28 Aug 2017 14:15:07 -0700 Subject: Turn on -Wall for recovery modules Turn on -Wall for all modules. Also remove the obsolete file_cmp() in apply_patch test and now() in wear_ui. The only exception is lib_edify due to the unused functions in the intermediate cpp files generated from the lex files. It will be handled in a seperate CL. Bug: 64939312 Test: mma, unit tests pass Change-Id: Ic53f76b60b6401ab20db3d98130d674c08e3702f --- tests/component/applypatch_test.cpp | 8 -------- 1 file changed, 8 deletions(-) (limited to 'tests/component') diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 016fed9b1..42542898b 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -61,14 +61,6 @@ static void mangle_file(const std::string& fname) { ASSERT_TRUE(android::base::WriteStringToFile(content, fname)); } -static bool file_cmp(const std::string& f1, const std::string& f2) { - std::string c1; - android::base::ReadFileToString(f1, &c1); - std::string c2; - android::base::ReadFileToString(f2, &c2); - return c1 == c2; -} - class ApplyPatchTest : public ::testing::Test { public: static void SetUpTestCase() { -- cgit v1.2.3 From 2903cddb58f6ee99116e0751a2305f75f9a86461 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 18 Aug 2017 18:15:47 -0700 Subject: Improve imgdiff for large zip files Due to the cache size limit for OTA generation, we used to split large zip files linearly into pieces and do bsdiff on them. As a result, i) we lose the advantage of imgdiff; ii) if there's an accidental order change of some huge files inside the zip, we'll create an insanely large patch. This patch splits the src&tgt more smartly based on the zip entry_name. If the entry_name is empty or no matching source is found for a target chunk, we'll skip adding its source and later do a bsdiff against the whole split source image (this rarely happens in our use cases except for the metadata inside a ziparchive). After the split, the target pieces are continuous and block aligned, while the sources pieces are mutually exclusive. (Some of the source blocks may not be used if there's no matching entry_name in the target.) Then we will generate patches accordingly between each split image pairs. Afterwards, if we apply imgpatch to each pair of split source/target images and add up the patched result, we can get back the original target image. For example: Input: [src_image, tgt_image] Split: [src-0,tgt-0; src-1,tgt-1, src-2,tgt-2] Diff: [ patch-0; patch-1; patch-2] Patch: [(src-0,patch-0)=tgt-0; (src-1,patch-1)=tgt-1; (src-2,patch-2)=tgt-2;] Append: [tgt-0 + tgt-1 + tgt-2 = tgt_image] Peformance: For the small package in b/34220646, we decrease the patch size of chrome.apk dramatically from 30M to 400K due to the order change of two big .so files. On two versions of angler, I also observe decent patch size decrease. For chrome.apk, we reduced the size from 5.9M to 3.2M; and for vevlet.apk from 8.0M to 6.5M. Bug: 34220646 Test: recovery component test && apply imgdiff & imgpatch on two chrome.apk Change-Id: I145d802984fa805efbbac9d01a2e64d82ef9728b --- tests/component/imgdiff_test.cpp | 339 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 334 insertions(+), 5 deletions(-) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index bf25aebb0..3163a57cf 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -16,13 +16,17 @@ #include +#include #include +#include #include #include #include +#include #include #include +#include #include #include #include @@ -75,15 +79,20 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si if (num_deflate != nullptr) *num_deflate = deflate; } -static void verify_patched_image(const std::string& src, const std::string& patch, - const std::string& tgt) { - std::string patched; +static void GenerateTarget(const std::string& src, const std::string& patch, std::string* patched) { + patched->clear(); ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), reinterpret_cast(patch.data()), patch.size(), - [&patched](const unsigned char* data, size_t len) { - patched.append(reinterpret_cast(data), len); + [&](const unsigned char* data, size_t len) { + patched->append(reinterpret_cast(data), len); return len; })); +} + +static void verify_patched_image(const std::string& src, const std::string& patch, + const std::string& tgt) { + std::string patched; + GenerateTarget(src, patch, &patched); ASSERT_EQ(tgt, patched); } @@ -623,3 +632,323 @@ TEST(ImgpatchTest, image_mode_patch_corruption) { reinterpret_cast(patch.data()), patch.size(), [](const unsigned char* /*data*/, size_t len) { return len; })); } + +static void construct_store_entry(const std::vector>& info, + ZipWriter* writer) { + for (auto& t : info) { + // Create t(1) blocks of t(2), and write the data to t(0) + ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), 0)); + const std::string content(std::get<1>(t) * 4096, std::get<2>(t)); + ASSERT_EQ(0, writer->WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer->FinishEntry()); + } +} + +static void construct_deflate_entry(const std::vector>& info, + ZipWriter* writer, const std::string& data) { + for (auto& t : info) { + // t(0): entry_name; t(1): block offset; t(2) length in blocks. + ASSERT_EQ(0, writer->StartEntry(std::get<0>(t).c_str(), ZipWriter::kCompress)); + ASSERT_EQ(0, writer->WriteBytes(data.data() + std::get<1>(t) * 4096, std::get<2>(t) * 4096)); + ASSERT_EQ(0, writer->FinishEntry()); + } +} + +// Look for the generated source and patch pieces in the debug_dir and generate the target on +// each pair. Concatenate the split target and match against the orignal one. +static void GenerateAndCheckSplitTarget(const std::string& debug_dir, size_t count, + const std::string& tgt) { + std::string patched; + for (size_t i = 0; i < count; i++) { + std::string split_src_path = android::base::StringPrintf("%s/src-%zu", debug_dir.c_str(), i); + std::string split_patch_path = android::base::StringPrintf("%s/patch-%zu", debug_dir.c_str(), i); + + std::string split_src; + std::string split_patch; + ASSERT_TRUE(android::base::ReadFileToString(split_src_path, &split_src)); + ASSERT_TRUE(android::base::ReadFileToString(split_patch_path, &split_patch)); + + std::string split_tgt; + GenerateTarget(split_src, split_patch, &split_tgt); + patched += split_tgt; + } + + // Verify we can get back the original target image. + ASSERT_EQ(tgt, patched); +} + +std::vector ConstructImageChunks( + const std::vector& content, const std::vector>& info) { + std::vector chunks; + size_t start = 0; + for (const auto& t : info) { + size_t length = std::get<1>(t); + chunks.emplace_back(CHUNK_NORMAL, start, &content, length, std::get<0>(t)); + start += length; + } + + return chunks; +} + +TEST(ImgdiffTest, zip_mode_split_image_smoke) { + std::vector content; + content.reserve(4096 * 50); + uint8_t n = 0; + generate_n(back_inserter(content), 4096 * 50, [&n]() { return n++ / 4096; }); + + ZipModeImage tgt_image(false, 4096 * 10); + std::vector tgt_chunks = ConstructImageChunks(content, { { "a", 100 }, + { "b", 4096 * 2 }, + { "c", 4096 * 3 }, + { "d", 300 }, + { "e-0", 4096 * 10 }, + { "e-1", 4096 * 5 }, + { "CD", 200 } }); + tgt_image.Initialize(std::move(tgt_chunks), + std::vector(content.begin(), content.begin() + 82520)); + + tgt_image.DumpChunks(); + + ZipModeImage src_image(true, 4096 * 10); + std::vector src_chunks = ConstructImageChunks(content, { { "b", 4096 * 3 }, + { "c-0", 4096 * 10 }, + { "c-1", 4096 * 2 }, + { "a", 4096 * 5 }, + { "e-0", 4096 * 10 }, + { "e-1", 10000 }, + { "CD", 5000 } }); + src_image.Initialize(std::move(src_chunks), + std::vector(content.begin(), content.begin() + 137880)); + + std::vector split_tgt_images; + std::vector split_src_images; + std::vector split_src_ranges; + + ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, + &split_src_images, &split_src_ranges); + + // src_piece 1: a 5 blocks, b 3 blocks + // src_piece 2: c-0 10 blocks + // src_piece 3: d 0 block, e-0 10 blocks + // src_piece 4: e-1 2 blocks; CD 2 blocks + ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); + ASSERT_EQ(static_cast(4), split_tgt_images.size()); + + ASSERT_EQ(static_cast(1), split_tgt_images[0].NumOfChunks()); + ASSERT_EQ(static_cast(12288), split_tgt_images[0][0].DataLengthForPatch()); + ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString()); + + ASSERT_EQ(static_cast(1), split_tgt_images[1].NumOfChunks()); + ASSERT_EQ(static_cast(12288), split_tgt_images[1][0].DataLengthForPatch()); + ASSERT_EQ("2,3,13", split_src_ranges[1].ToString()); + + ASSERT_EQ(static_cast(1), split_tgt_images[2].NumOfChunks()); + ASSERT_EQ(static_cast(40960), split_tgt_images[2][0].DataLengthForPatch()); + ASSERT_EQ("2,20,30", split_src_ranges[2].ToString()); + + ASSERT_EQ(static_cast(1), split_tgt_images[3].NumOfChunks()); + ASSERT_EQ(static_cast(16984), split_tgt_images[3][0].DataLengthForPatch()); + ASSERT_EQ("2,30,34", split_src_ranges[3].ToString()); +} + +TEST(ImgdiffTest, zip_mode_store_large_apk) { + // Construct src and tgt zip files with limit = 10 blocks. + // src tgt + // 12 blocks 'd' 3 blocks 'a' + // 8 blocks 'c' 3 blocks 'b' + // 3 blocks 'b' 8 blocks 'c' (exceeds limit) + // 3 blocks 'a' 12 blocks 'd' (exceeds limit) + // 3 blocks 'e' + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + construct_store_entry( + { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } }, + &tgt_writer); + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } }, + &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryDir debug_dir; + std::vector args = { + "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( + "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 4 pieces of patch.(Rougly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') + GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt); +} + +TEST(ImgdiffTest, zip_mode_deflate_large_apk) { + // Generate 50 blocks of random data. + std::string random_data; + random_data.reserve(4096 * 50); + generate_n(back_inserter(random_data), 4096 * 50, []() { return rand() % 256; }); + + // Construct src and tgt zip files with limit = 10 blocks. + // src tgt + // 22 blocks, "d" 4 blocks, "a" + // 5 blocks, "b" 4 blocks, "b" + // 3 blocks, "a" 7 blocks, "c" (exceeds limit) + // 8 blocks, "c" 20 blocks, "d" (exceeds limit) + // 1 block, "f" 2 blocks, "e" + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_deflate_entry( + { { "a", 0, 4 }, { "b", 5, 4 }, { "c", 12, 8 }, { "d", 21, 20 }, { "e", 45, 2 }, + { "f", 48, 1 } }, &tgt_writer, random_data); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + + construct_deflate_entry( + { { "d", 21, 22 }, { "b", 5, 5 }, { "a", 0, 3 }, { "g", 9, 1 }, { "c", 11, 8 }, + { "f", 45, 1 } }, &src_writer, random_data); + + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + ZipModeImage src_image(true, 10 * 4096); + ZipModeImage tgt_image(false, 10 * 4096); + ASSERT_TRUE(src_image.Initialize(src_file.path)); + ASSERT_TRUE(tgt_image.Initialize(tgt_file.path)); + ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)); + + src_image.DumpChunks(); + tgt_image.DumpChunks(); + + std::vector split_tgt_images; + std::vector split_src_images; + std::vector split_src_ranges; + ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, + &split_src_images, &split_src_ranges); + + // src_piece 1: a 3 blocks, b 5 blocks + // src_piece 2: c 8 blocks + // src_piece 3: d-0 10 block + // src_piece 4: d-1 10 blocks + // src_piece 5: e 1 block, CD + ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); + ASSERT_EQ(static_cast(5), split_tgt_images.size()); + + ASSERT_EQ(static_cast(2), split_src_images[0].NumOfChunks()); + ASSERT_EQ("a", split_src_images[0][0].GetEntryName()); + ASSERT_EQ("b", split_src_images[0][1].GetEntryName()); + + ASSERT_EQ(static_cast(1), split_src_images[1].NumOfChunks()); + ASSERT_EQ("c", split_src_images[1][0].GetEntryName()); + + ASSERT_EQ(static_cast(0), split_src_images[2].NumOfChunks()); + ASSERT_EQ(static_cast(0), split_src_images[3].NumOfChunks()); + ASSERT_EQ(static_cast(0), split_src_images[4].NumOfChunks()); + + // Compute patch. + TemporaryFile patch_file; + TemporaryDir debug_dir; + ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, + patch_file.path, debug_dir.path)); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] + GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); +} + +TEST(ImgdiffTest, zip_mode_no_match_source) { + // Generate 20 blocks of random data. + std::string random_data; + random_data.reserve(4096 * 20); + generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); + + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer, + random_data); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + // We don't have a matching source entry. + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry({ { "d", 1, 'd' } }, &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryDir debug_dir; + std::vector args = { + "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( + "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 1 pieces of patch due to no matching source entry. + GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); +} + +TEST(ImgdiffTest, zip_mode_large_enough_limit) { + // Generate 20 blocks of random data. + std::string random_data; + random_data.reserve(4096 * 20); + generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); + + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + // Construct 10 blocks of source. + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + ZipWriter src_writer(src_file_ptr); + construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch with a limit of 20 blocks. + TemporaryFile patch_file; + TemporaryDir debug_dir; + std::vector args = { + "imgdiff", "-z", "--block-limit=20", android::base::StringPrintf( + "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect 1 pieces of patch since limit is larger than the zip file size. + GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); +} -- cgit v1.2.3 From 79327ac21dcc58830845c99470873c5967504cbd Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 8 Sep 2017 17:09:10 -0700 Subject: Close cmd_pipe properly after updater test finishes Otherwise the test may fail after a large number of iterations due to file open failure. Bug: 65430057 Test: run recovery_component_test on sailfish for 2000 iterations. Change-Id: I0d456284d6064467038911d63eade95740cbec2c --- tests/component/updater_test.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 6c341c111..2a0575a31 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -383,7 +383,7 @@ TEST_F(UpdaterTest, set_progress) { TemporaryFile tf; UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.fd, "w"); + updater_info.cmd_pipe = fdopen(tf.release(), "w"); expect(".52", "set_progress(\".52\")", kNoCause, &updater_info); fflush(updater_info.cmd_pipe); @@ -392,6 +392,7 @@ TEST_F(UpdaterTest, set_progress) { ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd); // recovery-updater protocol expects 2 tokens ("set_progress "). ASSERT_EQ(2U, android::base::Split(cmd, " ").size()); + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } TEST_F(UpdaterTest, show_progress) { @@ -407,7 +408,7 @@ TEST_F(UpdaterTest, show_progress) { TemporaryFile tf; UpdaterInfo updater_info; - updater_info.cmd_pipe = fdopen(tf.fd, "w"); + updater_info.cmd_pipe = fdopen(tf.release(), "w"); expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info); fflush(updater_info.cmd_pipe); @@ -416,12 +417,13 @@ TEST_F(UpdaterTest, show_progress) { ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd); // recovery-updater protocol expects 3 tokens ("progress "). ASSERT_EQ(3U, android::base::Split(cmd, " ").size()); + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } TEST_F(UpdaterTest, block_image_update) { // Create a zip file with new_data and patch_data. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); ZipWriter zip_writer(zip_file_ptr); // Add a dummy new data. @@ -485,7 +487,7 @@ TEST_F(UpdaterTest, block_image_update) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe"); + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -518,7 +520,7 @@ TEST_F(UpdaterTest, block_image_update) { TEST_F(UpdaterTest, new_data_short_write) { // Create a zip file with new_data. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); ZipWriter zip_writer(zip_file_ptr); // Add the empty new data. @@ -561,7 +563,7 @@ TEST_F(UpdaterTest, new_data_short_write) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe"); + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -579,13 +581,15 @@ TEST_F(UpdaterTest, new_data_short_write) { std::string script_exact_data = "block_image_update(\"" + std::string(update_file.path) + R"(", package_extract_file("transfer_list"), "exact_new_data", "patch_data"))"; expect("t", script_exact_data.c_str(), kNoCause, &updater_info); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } TEST_F(UpdaterTest, brotli_new_data) { // Create a zip file with new_data. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); ZipWriter zip_writer(zip_file_ptr); // Add a brotli compressed new data entry. @@ -639,7 +643,7 @@ TEST_F(UpdaterTest, brotli_new_data) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wb"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -653,5 +657,7 @@ TEST_F(UpdaterTest, brotli_new_data) { std::string updated_content; ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); ASSERT_EQ(brotli_new_data, updated_content); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } -- cgit v1.2.3 From 8ba7c45e0b6165fd0b3df72abf94bea459fcef85 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 12 Sep 2017 11:13:02 -0700 Subject: Fix the dangling pointer when setting up arguments of imgdiff Test: unit tests pass Change-Id: If884e805ccd4df73671ab3436eb90860786ff6c9 --- tests/component/imgdiff_test.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 3163a57cf..73516050b 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -779,9 +779,10 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { // Compute patch. TemporaryFile patch_file; TemporaryDir debug_dir; + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( - "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, + patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -901,9 +902,10 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { // Compute patch. TemporaryFile patch_file; TemporaryDir debug_dir; + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", android::base::StringPrintf( - "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, + patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -940,9 +942,10 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Compute patch with a limit of 20 blocks. TemporaryFile patch_file; TemporaryDir debug_dir; + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=20", android::base::StringPrintf( - "--debug-dir=%s", debug_dir.path).c_str(), src_file.path, tgt_file.path, patch_file.path, + "imgdiff", "-z", "--block-limit=20", debug_dir_arg.c_str(), src_file.path, tgt_file.path, + patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); -- cgit v1.2.3 From 82582b4562bd2ffa9ebe9d25ecdc6222b053d6ef Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 31 Aug 2017 18:05:19 -0700 Subject: Output split information for imgdiff when handling large apks Add a mandatory option in imgdiff to write the split info (i.e. patch_size, tgt_size, src_ranges) to file when handling large apks. Therefore, the caller of imgdiff can create split transfers based on the info. Bug: 63542719 Test: unit tests pass Change-Id: I853d55d1f999fd576474faa81077f7307f4d856d --- tests/component/imgdiff_test.cpp | 48 +++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 73516050b..161d58d45 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -778,11 +778,13 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { // Compute patch. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, - patch_file.path, + "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -864,14 +866,40 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { // Compute patch. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; ASSERT_TRUE(ZipModeImage::GeneratePatches(split_tgt_images, split_src_images, split_src_ranges, - patch_file.path, debug_dir.path)); + patch_file.path, split_info_file.path, debug_dir.path)); + + // Verify the content of split info. + // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] + std::string split_info_string; + android::base::ReadFileToString(split_info_file.path, &split_info_string); + std::vector info_list = + android::base::Split(android::base::Trim(split_info_string), "\n"); + + ASSERT_EQ(static_cast(7), info_list.size()); + ASSERT_EQ("2", android::base::Trim(info_list[0])); + ASSERT_EQ("5", android::base::Trim(info_list[1])); + + std::vector patch_size; + for (size_t i = 0; i < 5; i++) { + struct stat st = {}; + std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i); + ASSERT_EQ(0, stat(path.c_str(), &st)); + patch_size.push_back(st.st_size); + } + + ASSERT_EQ(std::to_string(patch_size[0]) + " 36864 2,22,31", android::base::Trim(info_list[2])); + ASSERT_EQ(std::to_string(patch_size[1]) + " 32768 2,31,40", android::base::Trim(info_list[3])); + ASSERT_EQ(std::to_string(patch_size[2]) + " 40960 2,0,11", android::base::Trim(info_list[4])); + ASSERT_EQ(std::to_string(patch_size[3]) + " 40960 2,11,21", android::base::Trim(info_list[5])); + ASSERT_EQ(std::to_string(patch_size[4]) + " 8833 4,21,22,40,41", + android::base::Trim(info_list[6])); std::string tgt; ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - // Expect 5 pieces of patch. ["a","b"; "c"; "d-0"; "d-1"; "e"] GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); } @@ -901,11 +929,13 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { // Compute patch. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), src_file.path, tgt_file.path, - patch_file.path, + "imgdiff", "-z", "--block-limit=10", debug_dir_arg.c_str(), split_info_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); @@ -941,11 +971,13 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Compute patch with a limit of 20 blocks. TemporaryFile patch_file; + TemporaryFile split_info_file; TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); std::vector args = { - "imgdiff", "-z", "--block-limit=20", debug_dir_arg.c_str(), src_file.path, tgt_file.path, - patch_file.path, + "imgdiff", "-z", "--block-limit=20", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, }; ASSERT_EQ(0, imgdiff(args.size(), args.data())); -- cgit v1.2.3 From 623fe7e701d5d0fb17082d1ced14498af1b44e5b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 2 Oct 2017 16:28:06 -0700 Subject: Move error_code.h into otautil. This way it stops requiring relative path ".." in LOCAL_C_INCLUDES (uncrypt and edify). Soong doesn't accept non-local ".." in "local_include_dirs". Test: mmma bootable/recovery Change-Id: Ia4649789cef2aaeb2785483660e9ea5a8b389c62 --- tests/component/updater_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 2a0575a31..e35870dc7 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -39,8 +39,8 @@ #include "common/test_constants.h" #include "edify/expr.h" -#include "error_code.h" #include "otautil/SysUtil.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" -- cgit v1.2.3 From 26436d6d6010d5323349af7e119ff8f34f85c40c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 5 Oct 2017 17:16:31 +0000 Subject: Revert "Move error_code.h into otautil." This reverts commit 623fe7e701d5d0fb17082d1ced14498af1b44e5b. Reason for revert: Need to address device-specific modules. Change-Id: Ib7a4191e7f193dfff49b02d3de76dda856800251 --- tests/component/updater_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index e35870dc7..2a0575a31 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -39,8 +39,8 @@ #include "common/test_constants.h" #include "edify/expr.h" +#include "error_code.h" #include "otautil/SysUtil.h" -#include "otautil/error_code.h" #include "print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" -- cgit v1.2.3 From 1fc5bf353a8719d16fd9ba29a661d211bad4038f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 6 Oct 2017 07:43:41 -0700 Subject: Revert "Revert "Move error_code.h into otautil."" This reverts commit 26436d6d6010d5323349af7e119ff8f34f85c40c to re-land "Move error_code.h into otautil.". This way it stops requiring relative path ".." in LOCAL_C_INCLUDES (uncrypt and edify). Soong doesn't accept non-local ".." in "local_include_dirs". This CL needs to land with device-specific module changes (e.g. adding the dependency on libotautil). Test: lunch aosp_{angler,bullhead,dragon,fugu,sailfish}-userdebug; mmma bootable/recovery Change-Id: If193241801af2dae73eccd31ce57cd2b81c9fd96 --- tests/component/updater_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 2a0575a31..e35870dc7 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -39,8 +39,8 @@ #include "common/test_constants.h" #include "edify/expr.h" -#include "error_code.h" #include "otautil/SysUtil.h" +#include "otautil/error_code.h" #include "print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" -- cgit v1.2.3 From 09e468f84cc245fba61d69165b4af8f1ec4cdfd5 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 29 Sep 2017 14:39:33 -0700 Subject: Move rangeset.h and print_sha1.h into otautil. Also drop the "bootable/recovery" path in LOCAL_C_INCLUDES from applypatch modules. Test: lunch aosp_{angler,bullhead,fugu,dragon,sailfish}-userdebug; mmma bootable/recovery Change-Id: Idd602a796894f971ee4f8fa3eafe36c42d9de986 --- tests/component/applypatch_test.cpp | 2 +- tests/component/updater_test.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests/component') diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 42542898b..15ec08fe7 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -35,7 +35,7 @@ #include "applypatch/applypatch.h" #include "applypatch/applypatch_modes.h" #include "common/test_constants.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { ASSERT_NE(nullptr, sha1); diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index e35870dc7..e6aec4ad6 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -41,7 +41,7 @@ #include "edify/expr.h" #include "otautil/SysUtil.h" #include "otautil/error_code.h" -#include "print_sha1.h" +#include "otautil/print_sha1.h" #include "updater/blockimg.h" #include "updater/install.h" #include "updater/updater.h" -- cgit v1.2.3 From 45685820029fb191fe8509418df91a049227ea3a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 13 Oct 2017 14:54:12 -0700 Subject: otautil: Move RangeSet implementation into rangeset.cpp. Since it has grown much larger, users of the header shouldn't compile and carry their full copies. Also add missing header includes in imgdiff.cpp and imgdiff_test.cpp. Test: mmma bootable/recovery Test: recovery_unit_test; recovery_component_test; recovery_host_test Change-Id: I88ca54171765e5606ab0d61580fbc1ada578fd7d --- tests/component/imgdiff_test.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 161d58d45..bf591dae4 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 5450c84ba4cc62b887e69ec11a922194b36756a9 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 18 Oct 2017 13:15:21 -0700 Subject: Finish the new data receiver when update fails The thread to receive new data may still be alive after we exit PerformBlockImageUpdate() upon failures. This caused memory corruption when we run the unittest repeatedly. Set the receiver_available flag to false and make sure the receiver exits when the update fails. Bug: 65430057 Test: unittests passed with tsan Change-Id: Icb232d13fb96c78262249ffbd29cdbe5b77f1fce --- tests/component/updater_test.cpp | 220 +++++++++++++++++++++++---------------- 1 file changed, 133 insertions(+), 87 deletions(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index e6aec4ad6..266657d31 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,23 @@ static void expect(const char* expected, const char* expr_str, CauseCode cause_c ASSERT_EQ(cause_code, state.cause_code); } +static void BuildUpdatePackage(const std::unordered_map& entries, + int fd) { + FILE* zip_file_ptr = fdopen(fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + for (const auto& entry : entries) { + ASSERT_EQ(0, zip_writer.StartEntry(entry.first.c_str(), 0)); + if (!entry.second.empty()) { + ASSERT_EQ(0, zip_writer.WriteBytes(entry.second.data(), entry.second.size())); + } + ASSERT_EQ(0, zip_writer.FinishEntry()); + } + + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); +} + static std::string get_sha1(const std::string& content) { uint8_t digest[SHA_DIGEST_LENGTH]; SHA1(reinterpret_cast(content.c_str()), content.size(), digest); @@ -420,30 +438,19 @@ TEST_F(UpdaterTest, show_progress) { ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); } -TEST_F(UpdaterTest, block_image_update) { - // Create a zip file with new_data and patch_data. - TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); - ZipWriter zip_writer(zip_file_ptr); - - // Add a dummy new data. - ASSERT_EQ(0, zip_writer.StartEntry("new_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); - - // Generate and add the patch data. +TEST_F(UpdaterTest, block_image_update_patch_data) { std::string src_content = std::string(4096, 'a') + std::string(4096, 'c'); std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd'); + + // Generate the patch data. TemporaryFile patch_file; ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast(src_content.data()), src_content.size(), reinterpret_cast(tgt_content.data()), tgt_content.size(), patch_file.path, nullptr)); std::string patch_content; ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); - ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); - ASSERT_EQ(0, zip_writer.WriteBytes(patch_content.data(), patch_content.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add two transfer lists. The first one contains a bsdiff; and we expect the update to succeed. + // Create the transfer list that contains a bsdiff. std::string src_hash = get_sha1(src_content); std::string tgt_hash = get_sha1(tgt_content); std::vector transfer_list = { @@ -456,27 +463,16 @@ TEST_F(UpdaterTest, block_image_update) { src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()), "free " + src_hash, }; - ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); - std::string commands = android::base::Join(transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Stash and free some blocks, then fail the 2nd update intentionally. - std::vector fail_transfer_list = { - "4", - "2", - "0", - "2", - "stash " + tgt_hash + " 2,0,2", - "free " + tgt_hash, - "fail", + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", patch_content }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, }; - ASSERT_EQ(0, zip_writer.StartEntry("fail_transfer_list", 0)); - std::string fail_commands = android::base::Join(fail_transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(fail_commands.data(), fail_commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); MemMapping map; ASSERT_TRUE(map.MapFile(zip_file.path)); @@ -491,7 +487,7 @@ TEST_F(UpdaterTest, block_image_update) { updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; - // Execute the commands in the 1st transfer list. + // Execute the commands in the transfer list. TemporaryFile update_file; ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); std::string script = "block_image_update(\"" + std::string(update_file.path) + @@ -502,44 +498,98 @@ TEST_F(UpdaterTest, block_image_update) { ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); ASSERT_EQ(tgt_hash, get_sha1(updated_content)); - // Expect the 2nd update to fail, but expect the stashed blocks to be freed. - script = "block_image_update(\"" + std::string(update_file.path) + - R"(", package_extract_file("fail_transfer_list"), "new_data", "patch_data"))"; + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, block_image_update_fail) { + std::string src_content(4096 * 2, 'e'); + std::string src_hash = get_sha1(src_content); + // Stash and free some blocks, then fail the update intentionally. + std::vector transfer_list = { + "4", "2", "0", "2", "stash " + src_hash + " 2,0,2", "free " + src_hash, "fail", + }; + + // Add a new data of 10 bytes to test the deadlock. + std::unordered_map entries = { + { "new_data", std::string(10, 0) }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + // Expect the stashed blocks to be freed. + std::string script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))"; expect("", script.c_str(), kNoCause, &updater_info); // Updater generates the stash name based on the input file name. std::string name_digest = get_sha1(update_file.path); std::string stash_base = "/cache/recovery/" + name_digest; ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); - ASSERT_EQ(-1, access((stash_base + tgt_hash).c_str(), F_OK)); + ASSERT_EQ(-1, access((stash_base + src_hash).c_str(), F_OK)); ASSERT_EQ(0, rmdir(stash_base.c_str())); ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } -TEST_F(UpdaterTest, new_data_short_write) { - // Create a zip file with new_data. +TEST_F(UpdaterTest, new_data_over_write) { + std::vector transfer_list = { + "4", "1", "0", "0", "new 2,0,1", + }; + + // Write 4096 + 100 bytes of new data. + std::unordered_map entries = { + { "new_data", std::string(4196, 0) }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + // Build the update package. TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); - ZipWriter zip_writer(zip_file_ptr); + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); - // Add the empty new data. - ASSERT_EQ(0, zip_writer.StartEntry("empty_new_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add the short written new data. - ASSERT_EQ(0, zip_writer.StartEntry("short_new_data", 0)); - std::string new_data_short = std::string(10, 'a'); - ASSERT_EQ(0, zip_writer.WriteBytes(new_data_short.data(), new_data_short.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add the data of exactly one block. - ASSERT_EQ(0, zip_writer.StartEntry("exact_new_data", 0)); - std::string new_data_exact = std::string(4096, 'a'); - ASSERT_EQ(0, zip_writer.WriteBytes(new_data_exact.data(), new_data_exact.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add a dummy patch data. - ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + TemporaryFile update_file; + std::string script = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new_data", "patch_data"))"; + expect("t", script.c_str(), kNoCause, &updater_info); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, new_data_short_write) { std::vector transfer_list = { "4", "1", @@ -547,12 +597,17 @@ TEST_F(UpdaterTest, new_data_short_write) { "0", "new 2,0,1", }; - ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); - std::string commands = android::base::Join(transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); + + std::unordered_map entries = { + { "empty_new_data", "" }, + { "short_new_data", std::string(10, 'a') }, + { "exact_new_data", std::string(4096, 'a') }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); MemMapping map; ASSERT_TRUE(map.MapFile(zip_file.path)); @@ -587,14 +642,6 @@ TEST_F(UpdaterTest, new_data_short_write) { } TEST_F(UpdaterTest, brotli_new_data) { - // Create a zip file with new_data. - TemporaryFile zip_file; - FILE* zip_file_ptr = fdopen(zip_file.release(), "wb"); - ZipWriter zip_writer(zip_file_ptr); - - // Add a brotli compressed new data entry. - ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0)); - auto generator = []() { return rand() % 128; }; // Generate 100 blocks of random data. std::string brotli_new_data; @@ -602,16 +649,12 @@ TEST_F(UpdaterTest, brotli_new_data) { generate_n(back_inserter(brotli_new_data), 4096 * 100, generator); size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); - std::vector encoded_data(encoded_size); + std::string encoded_data(encoded_size, 0); ASSERT_TRUE(BrotliEncoderCompress( BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(), - reinterpret_cast(brotli_new_data.data()), &encoded_size, encoded_data.data())); - - ASSERT_EQ(0, zip_writer.WriteBytes(encoded_data.data(), encoded_size)); - ASSERT_EQ(0, zip_writer.FinishEntry()); - // Add a dummy patch data. - ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); - ASSERT_EQ(0, zip_writer.FinishEntry()); + reinterpret_cast(brotli_new_data.data()), &encoded_size, + reinterpret_cast(const_cast(encoded_data.data())))); + encoded_data.resize(encoded_size); // Write a few small chunks of new data, then a large chunk, and finally a few small chunks. // This helps us to catch potential short writes. @@ -627,12 +670,15 @@ TEST_F(UpdaterTest, brotli_new_data) { "new 2,98,99", "new 2,99,100", }; - ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); - std::string commands = android::base::Join(transfer_list, '\n'); - ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); - ASSERT_EQ(0, zip_writer.FinishEntry()); - ASSERT_EQ(0, zip_writer.Finish()); - ASSERT_EQ(0, fclose(zip_file_ptr)); + + std::unordered_map entries = { + { "new.dat.br", std::move(encoded_data) }, + { "patch_data", "" }, + { "transfer_list", android::base::Join(transfer_list, '\n') }, + }; + + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); MemMapping map; ASSERT_TRUE(map.MapFile(zip_file.path)); -- cgit v1.2.3 From fa188268e43ab75732a480d6b2ec748d9d0dbfae Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Tue, 10 Oct 2017 17:56:17 +0200 Subject: Use SuffixArrayIndexInterface opaque type instead of the underlying data pointer. bsdiff interface is changing such that it hides the suffix array pointer from the public interface. This allows to use a different suffix array data size depending on the input size, running much faster in the normal case. Bug: 34220646 Test: `make checkbuild`; Ran an incremental update generation on a non-A/B device. Change-Id: I78e766da56cf28bc7774b8c8e58527bc11d919fb --- tests/component/updater_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 2a0575a31..19653ec50 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From f29ed3e843409e77db2423e13ac4041cf1445229 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 24 Oct 2017 16:48:32 -0700 Subject: tests: Take the ownership of the FD when calling fdopen. To avoid closing the same FD twice. Test: recovery_component_test && recovery_host_test Change-Id: I95195be8109101081410e9224efda535b2560e72 --- tests/component/imgdiff_test.cpp | 32 ++++++++++++++++---------------- tests/component/install_test.cpp | 26 +++++++++++++------------- 2 files changed, 29 insertions(+), 29 deletions(-) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index bf591dae4..f82a9db57 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -148,7 +148,7 @@ TEST(ImgdiffTest, image_mode_smoke) { TEST(ImgdiffTest, zip_mode_smoke_store) { // Construct src and tgt zip files. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); ASSERT_EQ(0, src_writer.StartEntry("file1.txt", 0)); // Store mode. const std::string src_content("abcdefg"); @@ -158,7 +158,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) { ASSERT_EQ(0, fclose(src_file_ptr)); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", 0)); // Store mode. const std::string tgt_content("abcdefgxyz"); @@ -197,7 +197,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) { TEST(ImgdiffTest, zip_mode_smoke_compressed) { // Construct src and tgt zip files. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string src_content("abcdefg"); @@ -207,7 +207,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) { ASSERT_EQ(0, fclose(src_file_ptr)); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string tgt_content("abcdefgxyz"); @@ -246,7 +246,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) { TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { // Construct src and tgt zip files. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); ASSERT_EQ(0, src_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string src_content("abcdefg"); @@ -256,7 +256,7 @@ TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { ASSERT_EQ(0, fclose(src_file_ptr)); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); ASSERT_EQ(0, tgt_writer.StartEntry("file1.txt", ZipWriter::kCompress)); const std::string tgt_content("abcdefgxyz"); @@ -761,7 +761,7 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { // 3 blocks 'a' 12 blocks 'd' (exceeds limit) // 3 blocks 'e' TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_store_entry( { { "a", 3, 'a' }, { "b", 3, 'b' }, { "c", 8, 'c' }, { "d", 12, 'd' }, { "e", 3, 'e' } }, @@ -770,7 +770,7 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { ASSERT_EQ(0, fclose(tgt_file_ptr)); TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_store_entry({ { "d", 12, 'd' }, { "c", 8, 'c' }, { "b", 3, 'b' }, { "a", 3, 'a' } }, &src_writer); @@ -792,7 +792,7 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { std::string tgt; ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - // Expect 4 pieces of patch.(Rougly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') + // Expect 4 pieces of patch. (Roughly 3'a',3'b'; 8'c'; 10'd'; 2'd'3'e') GenerateAndCheckSplitTarget(debug_dir.path, 4, tgt); } @@ -810,7 +810,7 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { // 8 blocks, "c" 20 blocks, "d" (exceeds limit) // 1 block, "f" 2 blocks, "e" TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_deflate_entry( @@ -821,7 +821,7 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { ASSERT_EQ(0, fclose(tgt_file_ptr)); TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_deflate_entry( @@ -911,7 +911,7 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_deflate_entry({ { "a", 0, 4 }, { "b", 5, 5 }, { "c", 11, 5 } }, &tgt_writer, @@ -922,7 +922,7 @@ TEST(ImgdiffTest, zip_mode_no_match_source) { // We don't have a matching source entry. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_store_entry({ { "d", 1, 'd' } }, &src_writer); ASSERT_EQ(0, src_writer.Finish()); @@ -954,7 +954,7 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { generate_n(back_inserter(random_data), 4096 * 20, []() { return rand() % 256; }); TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.fd, "wb"); + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); ZipWriter tgt_writer(tgt_file_ptr); construct_deflate_entry({ { "a", 0, 10 }, { "b", 10, 5 } }, &tgt_writer, random_data); @@ -964,7 +964,7 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Construct 10 blocks of source. TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.fd, "wb"); + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); ZipWriter src_writer(src_file_ptr); construct_deflate_entry({ { "a", 1, 10 } }, &src_writer, random_data); ASSERT_EQ(0, src_writer.Finish()); @@ -985,6 +985,6 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { std::string tgt; ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - // Expect 1 pieces of patch since limit is larger than the zip file size. + // Expect 1 piece of patch since limit is larger than the zip file size. GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); } diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 7bb496066..d19d788e4 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -37,7 +37,7 @@ TEST(InstallTest, verify_package_compatibility_no_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); // The archive must have something to be opened correctly. ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); @@ -54,7 +54,7 @@ TEST(InstallTest, verify_package_compatibility_no_entry) { TEST(InstallTest, verify_package_compatibility_invalid_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("compatibility.zip", 0)); ASSERT_EQ(0, writer.FinishEntry()); @@ -70,7 +70,7 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) { TEST(InstallTest, read_metadata_from_package_smoke) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); const std::string content("abcdefg"); @@ -87,7 +87,7 @@ TEST(InstallTest, read_metadata_from_package_smoke) { CloseArchive(zip); TemporaryFile temp_file2; - FILE* zip_file2 = fdopen(temp_file2.fd, "w"); + FILE* zip_file2 = fdopen(temp_file2.release(), "w"); ZipWriter writer2(zip_file2); ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated)); ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size())); @@ -104,7 +104,7 @@ TEST(InstallTest, read_metadata_from_package_smoke) { TEST(InstallTest, read_metadata_from_package_no_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored)); ASSERT_EQ(0, writer.FinishEntry()); @@ -120,7 +120,7 @@ TEST(InstallTest, read_metadata_from_package_no_entry) { TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w"); + FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); ZipWriter compatibility_zip_writer(compatibility_zip); ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); std::string malformed_xml = "malformed"; @@ -130,7 +130,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { ASSERT_EQ(0, fclose(compatibility_zip)); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; @@ -165,7 +165,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml ASSERT_TRUE( android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w"); + FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); ZipWriter compatibility_zip_writer(compatibility_zip); ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(system_manifest_xml_content.data(), @@ -175,7 +175,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml ASSERT_EQ(0, fclose(compatibility_zip)); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; @@ -202,7 +202,7 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml #ifdef AB_OTA_UPDATER static void VerifyAbUpdateBinaryCommand(const std::string& serialno, bool success = true) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); ASSERT_EQ(0, writer.FinishEntry()); @@ -258,7 +258,7 @@ TEST(InstallTest, update_binary_command_smoke) { VerifyAbUpdateBinaryCommand({}); #else TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; ASSERT_EQ(0, writer.StartEntry(UPDATE_BINARY_NAME, kCompressStored)); @@ -303,7 +303,7 @@ TEST(InstallTest, update_binary_command_smoke) { TEST(InstallTest, update_binary_command_invalid) { #ifdef AB_OTA_UPDATER TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); // Missing payload_properties.txt. ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); @@ -334,7 +334,7 @@ TEST(InstallTest, update_binary_command_invalid) { CloseArchive(zip); #else TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.fd, "w"); + FILE* zip_file = fdopen(temp_file.release(), "w"); ZipWriter writer(zip_file); // The archive must have something to be opened correctly. ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); -- cgit v1.2.3 From 113fe05ee0d33dbafd0923be97d448feb27d8f4b Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 24 Oct 2017 16:12:35 -0700 Subject: Fix the size mismatch in imgdiff As we construct the deflate entries of the target zip file with random data, the total size of the zip file may vary from case to case. This leads to occasional failures in the split test for deflate large apk files. This CL fixes the issue by adding two static zip files in the testdata instead of generating them dynamically. Bug: 67849209 Test: run the deflate_large_test repeatedly Change-Id: Iaeffad9205adefa10c9f62f9f088c33c4360a650 --- tests/component/imgdiff_test.cpp | 77 ++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 50 deletions(-) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index f82a9db57..6de804e06 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -32,6 +32,8 @@ #include #include +#include "common/test_constants.h" + using android::base::get_unaligned; // Sanity check for the given imgdiff patch header. @@ -797,44 +799,21 @@ TEST(ImgdiffTest, zip_mode_store_large_apk) { } TEST(ImgdiffTest, zip_mode_deflate_large_apk) { - // Generate 50 blocks of random data. - std::string random_data; - random_data.reserve(4096 * 50); - generate_n(back_inserter(random_data), 4096 * 50, []() { return rand() % 256; }); - - // Construct src and tgt zip files with limit = 10 blocks. + // Src and tgt zip files are constructed as follows. // src tgt // 22 blocks, "d" 4 blocks, "a" // 5 blocks, "b" 4 blocks, "b" - // 3 blocks, "a" 7 blocks, "c" (exceeds limit) - // 8 blocks, "c" 20 blocks, "d" (exceeds limit) - // 1 block, "f" 2 blocks, "e" - TemporaryFile tgt_file; - FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); - ZipWriter tgt_writer(tgt_file_ptr); - - construct_deflate_entry( - { { "a", 0, 4 }, { "b", 5, 4 }, { "c", 12, 8 }, { "d", 21, 20 }, { "e", 45, 2 }, - { "f", 48, 1 } }, &tgt_writer, random_data); - - ASSERT_EQ(0, tgt_writer.Finish()); - ASSERT_EQ(0, fclose(tgt_file_ptr)); - - TemporaryFile src_file; - FILE* src_file_ptr = fdopen(src_file.release(), "wb"); - ZipWriter src_writer(src_file_ptr); - - construct_deflate_entry( - { { "d", 21, 22 }, { "b", 5, 5 }, { "a", 0, 3 }, { "g", 9, 1 }, { "c", 11, 8 }, - { "f", 45, 1 } }, &src_writer, random_data); - - ASSERT_EQ(0, src_writer.Finish()); - ASSERT_EQ(0, fclose(src_file_ptr)); + // 3 blocks, "a" 8 blocks, "c" (exceeds limit) + // 1 block, "g" 20 blocks, "d" (exceeds limit) + // 8 blocks, "c" 2 blocks, "e" + // 1 block, "f" 1 block , "f" + std::string tgt_path = from_testdata_base("deflate_tgt.zip"); + std::string src_path = from_testdata_base("deflate_src.zip"); ZipModeImage src_image(true, 10 * 4096); ZipModeImage tgt_image(false, 10 * 4096); - ASSERT_TRUE(src_image.Initialize(src_file.path)); - ASSERT_TRUE(tgt_image.Initialize(tgt_file.path)); + ASSERT_TRUE(src_image.Initialize(src_path)); + ASSERT_TRUE(tgt_image.Initialize(tgt_path)); ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image)); src_image.DumpChunks(); @@ -846,11 +825,12 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images, &split_src_images, &split_src_ranges); - // src_piece 1: a 3 blocks, b 5 blocks - // src_piece 2: c 8 blocks - // src_piece 3: d-0 10 block - // src_piece 4: d-1 10 blocks - // src_piece 5: e 1 block, CD + // Expected split images with limit = 10 blocks. + // src_piece 0: a 3 blocks, b 5 blocks + // src_piece 1: c 8 blocks + // src_piece 2: d-0 10 block + // src_piece 3: d-1 10 blocks + // src_piece 4: e 1 block, CD ASSERT_EQ(split_tgt_images.size(), split_src_images.size()); ASSERT_EQ(static_cast(5), split_tgt_images.size()); @@ -883,24 +863,21 @@ TEST(ImgdiffTest, zip_mode_deflate_large_apk) { ASSERT_EQ("2", android::base::Trim(info_list[0])); ASSERT_EQ("5", android::base::Trim(info_list[1])); - std::vector patch_size; + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt)); + ASSERT_EQ(static_cast(160385), tgt.size()); + std::vector tgt_file_ranges = { + "36864 2,22,31", "32768 2,31,40", "40960 2,0,11", "40960 2,11,21", "8833 4,21,22,40,41", + }; + for (size_t i = 0; i < 5; i++) { - struct stat st = {}; + struct stat st; std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i); ASSERT_EQ(0, stat(path.c_str(), &st)); - patch_size.push_back(st.st_size); + ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i], + android::base::Trim(info_list[i + 2])); } - ASSERT_EQ(std::to_string(patch_size[0]) + " 36864 2,22,31", android::base::Trim(info_list[2])); - ASSERT_EQ(std::to_string(patch_size[1]) + " 32768 2,31,40", android::base::Trim(info_list[3])); - ASSERT_EQ(std::to_string(patch_size[2]) + " 40960 2,0,11", android::base::Trim(info_list[4])); - ASSERT_EQ(std::to_string(patch_size[3]) + " 40960 2,11,21", android::base::Trim(info_list[5])); - ASSERT_EQ(std::to_string(patch_size[4]) + " 8833 4,21,22,40,41", - android::base::Trim(info_list[6])); - - std::string tgt; - ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); - GenerateAndCheckSplitTarget(debug_dir.path, 5, tgt); } -- cgit v1.2.3 From 54c1db4b3e6becf85f2b685f16425244b24138a6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 1 Nov 2017 22:21:55 -0700 Subject: tests: Clean up the files in TemporaryDir. ~TemporaryDir() calls rmdir(2) directly, which works with empty directories only. Test: Run recovery_host_test; No leftover on host. Test; Run recovery_component_test on marlin; No leftover on device. Change-Id: Ib510efb16eeda61b34161e2b386499e6cb79a4ca --- tests/component/imgdiff_test.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 6de804e06..728b6cc76 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -657,19 +657,23 @@ static void construct_deflate_entry(const std::vector 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 --- tests/component/applypatch_test.cpp | 131 +++++++++----------------- tests/component/bootloader_message_test.cpp | 140 +++++----------------------- tests/component/uncrypt_test.cpp | 99 +++++++++++++------- 3 files changed, 135 insertions(+), 235 deletions(-) (limited to 'tests/component') 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 From 91a7aa4577feb5f80038a30d15765e8b79845cf1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 1 May 2017 15:57:38 -0700 Subject: Clean up fuse_sideload and add a testcase. This CL mainly changes: a) moving the interface in struct provider_vtab to std::function; b) code cleanup, such as moving the declaration closer to the uses, using explicit type conversion. Test: recovery_component_test Test: minadbd_test Test: Sideload a package on marlin. Change-Id: Id0e3c70f1ada54a4cd985b54c84438c23ed4687e --- tests/component/sideload_test.cpp | 70 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) (limited to 'tests/component') diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index 40cfc6975..b7109fcc2 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -16,6 +16,12 @@ #include +#include +#include + +#include +#include +#include #include #include "fuse_sideload.h" @@ -26,11 +32,67 @@ TEST(SideloadTest, fuse_device) { TEST(SideloadTest, run_fuse_sideload_wrong_parameters) { provider_vtab vtab; - vtab.close = [](void*) {}; + vtab.close = [](void) {}; - ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, 4095)); - ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, (1 << 22) + 1)); + ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, 4095)); + ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, (1 << 22) + 1)); // Too many blocks. - ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, ((1 << 18) + 1) * 4096, 4096)); + ASSERT_EQ(-1, run_fuse_sideload(vtab, ((1 << 18) + 1) * 4096, 4096)); +} + +TEST(SideloadTest, run_fuse_sideload) { + const std::vector blocks = { + std::string(2048, 'a') + std::string(2048, 'b'), + std::string(2048, 'c') + std::string(2048, 'd'), + std::string(2048, 'e') + std::string(2048, 'f'), + std::string(2048, 'g') + std::string(2048, 'h'), + }; + const std::string content = android::base::Join(blocks, ""); + ASSERT_EQ(16384U, content.size()); + + provider_vtab vtab; + vtab.close = [](void) {}; + vtab.read_block = [&blocks](uint32_t block, uint8_t* buffer, uint32_t fetch_size) { + if (block >= 4) return -1; + blocks[block].copy(reinterpret_cast(buffer), fetch_size); + return 0; + }; + + TemporaryDir mount_point; + pid_t pid = fork(); + if (pid == 0) { + ASSERT_EQ(0, run_fuse_sideload(vtab, 16384, 4096, mount_point.path)); + _exit(EXIT_SUCCESS); + } + + std::string package = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME; + int status; + static constexpr int kSideloadInstallTimeout = 10; + for (int i = 0; i < kSideloadInstallTimeout; ++i) { + ASSERT_NE(-1, waitpid(pid, &status, WNOHANG)); + + struct stat sb; + if (stat(package.c_str(), &sb) == 0) { + break; + } + + if (errno == ENOENT && i < kSideloadInstallTimeout - 1) { + sleep(1); + continue; + } + FAIL() << "Timed out waiting for the fuse-provided package."; + } + + std::string content_via_fuse; + ASSERT_TRUE(android::base::ReadFileToString(package, &content_via_fuse)); + ASSERT_EQ(content, content_via_fuse); + + std::string exit_flag = std::string(mount_point.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG; + struct stat sb; + ASSERT_EQ(0, stat(exit_flag.c_str(), &sb)); + + waitpid(pid, &status, 0); + ASSERT_EQ(0, WEXITSTATUS(status)); + ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); } -- cgit v1.2.3 From 284752e2bc2daf93778d317c47bc202c668e3d89 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 5 Dec 2017 11:04:17 -0800 Subject: Log the last command to cache When performing an update, save the index and cmdline of the current command into the last command file if this command writes to the stash either explicitly of implicitly. This mitigates the overhead to update the last command file for every command. I ran a simple test on angler and the time to update 1000 times is ~2.3 seconds. Upon resuming an update, read the saved index first; then 1. In verification mode, check if all commands before the saved index have already produced the expected target blocks. If not, delete the last command file so that we will later resume the update from the start of the transfer list. 2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting stashes with duplicate id unintentionally; and also speed up the update. If an update succeeds or is unresumable, delete the last command file. Bug: 69858743 Test: Unittest passed, apply a failed update with invalid cmd on angler and check the last_command content, apply a failed update with invalid source hash and last_command is deleted. Change-Id: Ib60ba1e3c6d111d9f33097759b17dbcef97a37bf --- tests/component/updater_test.cpp | 217 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) (limited to 'tests/component') diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index d9d01d427..448fe4935 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -707,3 +707,220 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } + +TEST_F(UpdaterTest, last_command_update) { + TemporaryFile temp_file; + last_command_file = temp_file.path; + + std::string block1 = std::string(4096, '1'); + std::string block2 = std::string(4096, '2'); + std::string block3 = std::string(4096, '3'); + std::string block1_hash = get_sha1(block1); + std::string block2_hash = get_sha1(block2); + std::string block3_hash = get_sha1(block3); + + // Compose the transfer list to fail the first update. + std::vector transfer_list_fail = { + "4", + "2", + "0", + "2", + "stash " + block1_hash + " 2,0,1", + "move " + block1_hash + " 2,1,2 1 2,0,1", + "stash " + block3_hash + " 2,2,3", + "fail", + }; + + // Mimic a resumed update with the same transfer commands. + std::vector transfer_list_continue = { + "4", + "2", + "0", + "2", + "stash " + block1_hash + " 2,0,1", + "move " + block1_hash + " 2,1,2 1 2,0,1", + "stash " + block3_hash + " 2,2,3", + "move " + block1_hash + " 2,2,3 1 2,0,1", + }; + + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", "" }, + { "transfer_list_fail", android::base::Join(transfer_list_fail, '\n') }, + { "transfer_list_continue", android::base::Join(transfer_list_continue, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + std::string src_content = block1 + block2 + block3; + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_fail"), "new_data", "patch_data"))"; + expect("", script.c_str(), kNoCause, &updater_info); + + // Expect last_command to contain the last stash command. + std::string last_command_content; + ASSERT_TRUE(android::base::ReadFileToString(last_command_file.c_str(), &last_command_content)); + EXPECT_EQ("2\nstash " + block3_hash + " 2,2,3", last_command_content); + std::string updated_contents; + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_contents)); + ASSERT_EQ(block1 + block1 + block3, updated_contents); + + // Resume the update, expect the first 'move' to be skipped but the second 'move' to be executed. + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script_second_update = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_continue"), "new_data", "patch_data"))"; + expect("t", script_second_update.c_str(), kNoCause, &updater_info); + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_contents)); + ASSERT_EQ(block1 + block2 + block1, updated_contents); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, last_command_update_unresumable) { + TemporaryFile temp_file; + last_command_file = temp_file.path; + + std::string block1 = std::string(4096, '1'); + std::string block2 = std::string(4096, '2'); + std::string block1_hash = get_sha1(block1); + std::string block2_hash = get_sha1(block2); + + // Construct an unresumable update with source blocks mismatch. + std::vector transfer_list_unresumable = { + "4", "2", "0", "2", "stash " + block1_hash + " 2,0,1", "move " + block2_hash + " 2,1,2 1 2,0,1", + }; + + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", "" }, + { "transfer_list_unresumable", android::base::Join(transfer_list_unresumable, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + // Set up the last_command_file + ASSERT_TRUE( + android::base::WriteStringToFile("0\nstash " + block1_hash + " 2,0,1", last_command_file)); + + // The last_command_file will be deleted if the update encounters an unresumable failure + // later. + std::string src_content = block1 + block1; + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + std::string script = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_unresumable"), "new_data", "patch_data"))"; + expect("", script.c_str(), kNoCause, &updater_info); + ASSERT_EQ(-1, access(last_command_file.c_str(), R_OK)); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, last_command_verify) { + TemporaryFile temp_file; + last_command_file = temp_file.path; + + std::string block1 = std::string(4096, '1'); + std::string block2 = std::string(4096, '2'); + std::string block3 = std::string(4096, '3'); + std::string block1_hash = get_sha1(block1); + std::string block2_hash = get_sha1(block2); + std::string block3_hash = get_sha1(block3); + + std::vector transfer_list_verify = { + "4", + "2", + "0", + "2", + "stash " + block1_hash + " 2,0,1", + "move " + block1_hash + " 2,0,1 1 2,0,1", + "move " + block1_hash + " 2,1,2 1 2,0,1", + "stash " + block3_hash + " 2,2,3", + }; + + std::unordered_map entries = { + { "new_data", "" }, + { "patch_data", "" }, + { "transfer_list_verify", android::base::Join(transfer_list_verify, '\n') }, + }; + + // Build the update package. + TemporaryFile zip_file; + BuildUpdatePackage(entries, zip_file.release()); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + std::string src_content = block1 + block1 + block3; + TemporaryFile update_file; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + + ASSERT_TRUE( + android::base::WriteStringToFile("2\nstash " + block3_hash + " 2,2,3", last_command_file)); + + // Expect the verification to succeed and the last_command_file is intact. + std::string script_verify = + "block_image_verify(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list_verify"), "new_data","patch_data"))"; + expect("t", script_verify.c_str(), kNoCause, &updater_info); + + std::string last_command_content; + ASSERT_TRUE(android::base::ReadFileToString(last_command_file.c_str(), &last_command_content)); + EXPECT_EQ("2\nstash " + block3_hash + " 2,2,3", last_command_content); + + // Expect the verification to succeed but last_command_file to be deleted; because the target + // blocks don't have the expected contents for the second move command. + src_content = block1 + block2 + block3; + ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); + expect("t", script_verify.c_str(), kNoCause, &updater_info); + ASSERT_EQ(-1, access(last_command_file.c_str(), R_OK)); + + ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); + CloseArchive(handle); +} -- cgit v1.2.3 From 572abbb81cfa12cddf742fa35cd8a4b9eebdc7d1 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 22 Feb 2018 15:40:39 -0800 Subject: Remove the assumption of target chunk size in imgdiff In the split mode of imgdiff, we used to assume that the size of a split target chunk is always greater than the blocksize i.e. 4096. This may lead to the following assertion failure: I0221 04:57:33.451323 818464 common.py:205 imgdiff F 02-21 04:57:33 821203 821203 imgdiff.cpp:999] Check failed: tgt_size >= BLOCK_SIZE (tgt_size=476, BLOCK_SIZE=4096) This CL removes the assumption and handles the edge cases. Test: generate and verify the incremental update for TFs in the bug; unit test passes Bug: 73757557 Bug: 73711365 Change-Id: Iadbb4ee658995f5856cd488f3793980881a59620 --- tests/component/imgdiff_test.cpp | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) (limited to 'tests/component') diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 728b6cc76..6c23def01 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -969,3 +969,104 @@ TEST(ImgdiffTest, zip_mode_large_enough_limit) { // Expect 1 piece of patch since limit is larger than the zip file size. GenerateAndCheckSplitTarget(debug_dir.path, 1, tgt); } + +TEST(ImgdiffTest, zip_mode_large_apk_small_target_chunk) { + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + // The first entry is less than 4096 bytes, followed immediately by an entry that has a very + // large counterpart in the source file. Therefore the first entry will be patched separately. + std::string small_chunk("a", 2000); + ASSERT_EQ(0, tgt_writer.StartEntry("a", 0)); + ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size())); + ASSERT_EQ(0, tgt_writer.FinishEntry()); + construct_store_entry( + { + { "b", 12, 'b' }, { "c", 3, 'c' }, + }, + &tgt_writer); + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry({ { "a", 1, 'a' }, { "b", 13, 'b' }, { "c", 1, 'c' } }, &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryFile split_info_file; + TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); + std::vector args = { + "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect three split src images: + // src_piece 0: a 1 blocks + // src_piece 1: b-0 10 blocks + // src_piece 2: b-1 3 blocks, c 1 blocks, CD + GenerateAndCheckSplitTarget(debug_dir.path, 3, tgt); +} + +TEST(ImgdiffTest, zip_mode_large_apk_skipped_small_target_chunk) { + TemporaryFile tgt_file; + FILE* tgt_file_ptr = fdopen(tgt_file.release(), "wb"); + ZipWriter tgt_writer(tgt_file_ptr); + + construct_store_entry( + { + { "a", 11, 'a' }, + }, + &tgt_writer); + + // Construct a tiny target entry of 1 byte, which will be skipped due to the tail alignment of + // the previous entry. + std::string small_chunk("b", 1); + ASSERT_EQ(0, tgt_writer.StartEntry("b", 0)); + ASSERT_EQ(0, tgt_writer.WriteBytes(small_chunk.data(), small_chunk.size())); + ASSERT_EQ(0, tgt_writer.FinishEntry()); + + ASSERT_EQ(0, tgt_writer.Finish()); + ASSERT_EQ(0, fclose(tgt_file_ptr)); + + TemporaryFile src_file; + FILE* src_file_ptr = fdopen(src_file.release(), "wb"); + ZipWriter src_writer(src_file_ptr); + construct_store_entry( + { + { "a", 11, 'a' }, { "b", 11, 'b' }, + }, + &src_writer); + ASSERT_EQ(0, src_writer.Finish()); + ASSERT_EQ(0, fclose(src_file_ptr)); + + // Compute patch. + TemporaryFile patch_file; + TemporaryFile split_info_file; + TemporaryDir debug_dir; + std::string split_info_arg = android::base::StringPrintf("--split-info=%s", split_info_file.path); + std::string debug_dir_arg = android::base::StringPrintf("--debug-dir=%s", debug_dir.path); + std::vector args = { + "imgdiff", "-z", "--block-limit=10", split_info_arg.c_str(), debug_dir_arg.c_str(), + src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + std::string tgt; + ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt)); + + // Expect two split src images: + // src_piece 0: a-0 10 blocks + // src_piece 1: a-0 1 block, CD + GenerateAndCheckSplitTarget(debug_dir.path, 2, tgt); +} -- cgit v1.2.3 From 3bbb20f557790c015e44098098375eb6cc376a42 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 27 Feb 2018 15:56:11 -0800 Subject: Add a singleton CacheLocation to replace the hard coded locations This class allows us to set the following locations dynamically: cache_temp_source, last_command_file, stash_directory_base. In the updater's main function, we reset the values of these variables to their default locations in /cache; while we can set them to temp files in unit tests or host simulation. Test: unit tests pass Change-Id: I528652650caa41373617ab055d41b1f1a4ec0f87 --- tests/component/applypatch_test.cpp | 5 +++-- tests/component/updater_test.cpp | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'tests/component') diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 21c9a52dc..b6d092557 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -35,6 +35,7 @@ #include "applypatch/applypatch.h" #include "applypatch/applypatch_modes.h" #include "common/test_constants.h" +#include "otautil/cache_location.h" #include "otautil/print_sha1.h" static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { @@ -93,14 +94,14 @@ class ApplyPatchCacheTest : public ApplyPatchTest { protected: void SetUp() override { ApplyPatchTest::SetUp(); - cache_temp_source = old_file; + CacheLocation::location().set_cache_temp_source(old_file); } }; class ApplyPatchModesTest : public ::testing::Test { protected: void SetUp() override { - cache_temp_source = cache_source.path; + CacheLocation::location().set_cache_temp_source(cache_source.path); } TemporaryFile cache_source; diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 448fe4935..5bfd7cb40 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -41,6 +41,7 @@ #include "common/test_constants.h" #include "edify/expr.h" #include "otautil/SysUtil.h" +#include "otautil/cache_location.h" #include "otautil/error_code.h" #include "otautil/print_sha1.h" #include "updater/blockimg.h" @@ -104,7 +105,16 @@ class UpdaterTest : public ::testing::Test { RegisterBuiltins(); RegisterInstallFunctions(); RegisterBlockImageFunctions(); + + // Mock the location of last_command_file. + CacheLocation::location().set_cache_temp_source(temp_saved_source_.path); + CacheLocation::location().set_last_command_file(temp_last_command_.path); + CacheLocation::location().set_stash_directory_base(temp_stash_base_.path); } + + TemporaryFile temp_saved_source_; + TemporaryFile temp_last_command_; + TemporaryDir temp_stash_base_; }; TEST_F(UpdaterTest, getprop) { @@ -542,7 +552,7 @@ TEST_F(UpdaterTest, block_image_update_fail) { expect("", script.c_str(), kNoCause, &updater_info); // Updater generates the stash name based on the input file name. std::string name_digest = get_sha1(update_file.path); - std::string stash_base = "/cache/recovery/" + name_digest; + std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest; ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); ASSERT_EQ(-1, access((stash_base + src_hash).c_str(), F_OK)); ASSERT_EQ(0, rmdir(stash_base.c_str())); @@ -709,8 +719,7 @@ TEST_F(UpdaterTest, brotli_new_data) { } TEST_F(UpdaterTest, last_command_update) { - TemporaryFile temp_file; - last_command_file = temp_file.path; + std::string last_command_file = CacheLocation::location().last_command_file(); std::string block1 = std::string(4096, '1'); std::string block2 = std::string(4096, '2'); @@ -797,8 +806,7 @@ TEST_F(UpdaterTest, last_command_update) { } TEST_F(UpdaterTest, last_command_update_unresumable) { - TemporaryFile temp_file; - last_command_file = temp_file.path; + std::string last_command_file = CacheLocation::location().last_command_file(); std::string block1 = std::string(4096, '1'); std::string block2 = std::string(4096, '2'); @@ -853,8 +861,7 @@ TEST_F(UpdaterTest, last_command_update_unresumable) { } TEST_F(UpdaterTest, last_command_verify) { - TemporaryFile temp_file; - last_command_file = temp_file.path; + std::string last_command_file = CacheLocation::location().last_command_file(); std::string block1 = std::string(4096, '1'); std::string block2 = std::string(4096, '2'); -- cgit v1.2.3 From d26f95287a0efc6a3160624fa4e577891a2c9794 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 12 Mar 2018 21:18:52 -0700 Subject: tests: Add ApplyPatchModesTest.PatchModeEmmcTargetWithBsdiffPatch test. /system/bin/applypatch on device is expected to work with bsdiff based recovery-from-boot patch automatically. Adding a test to ensure that's always the case. Bug: 72731506 Test: Run recovery_component_test on marlin. Change-Id: I56283cd3ce7cf0215cc3bb3619b206fa01d552c4 Merged-In: I56283cd3ce7cf0215cc3bb3619b206fa01d552c4 (cherry picked from commit d612b23dfd58dbe5059ba53d8fd13cbb343b177c) --- tests/component/applypatch_test.cpp | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'tests/component') diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index b6d092557..61e06adb6 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "applypatch/applypatch.h" @@ -38,6 +39,8 @@ #include "otautil/cache_location.h" #include "otautil/print_sha1.h" +using namespace std::string_literals; + static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { ASSERT_NE(nullptr, sha1); @@ -265,6 +268,54 @@ TEST_F(ApplyPatchModesTest, PatchModeEmmcTarget) { ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data())); } +// Ensures that applypatch works with a bsdiff based recovery-from-boot.p. +TEST_F(ApplyPatchModesTest, PatchModeEmmcTargetWithBsdiffPatch) { + std::string boot_img_file = from_testdata_base("boot.img"); + std::string boot_img_sha1; + size_t boot_img_size; + sha1sum(boot_img_file, &boot_img_sha1, &boot_img_size); + + std::string recovery_img_file = from_testdata_base("recovery.img"); + std::string recovery_img_sha1; + size_t recovery_img_size; + sha1sum(recovery_img_file, &recovery_img_sha1, &recovery_img_size); + + // Generate the bsdiff patch of recovery-from-boot.p. + std::string src_content; + ASSERT_TRUE(android::base::ReadFileToString(boot_img_file, &src_content)); + + std::string tgt_content; + ASSERT_TRUE(android::base::ReadFileToString(recovery_img_file, &tgt_content)); + + TemporaryFile patch_file; + ASSERT_EQ(0, + bsdiff::bsdiff(reinterpret_cast(src_content.data()), src_content.size(), + reinterpret_cast(tgt_content.data()), tgt_content.size(), + patch_file.path, nullptr)); + + // applypatch : + std::string src_file_arg = + "EMMC:" + boot_img_file + ":" + std::to_string(boot_img_size) + ":" + boot_img_sha1; + TemporaryFile tgt_file; + std::string tgt_file_arg = "EMMC:"s + tgt_file.path; + std::string recovery_img_size_arg = std::to_string(recovery_img_size); + std::string patch_arg = boot_img_sha1 + ":" + patch_file.path; + std::vector args = { "applypatch", + src_file_arg.c_str(), + tgt_file_arg.c_str(), + recovery_img_sha1.c_str(), + recovery_img_size_arg.c_str(), + patch_arg.c_str() }; + ASSERT_EQ(0, applypatch_modes(args.size(), args.data())); + + // Double check the patched recovery image. + std::string tgt_file_sha1; + size_t tgt_file_size; + sha1sum(tgt_file.path, &tgt_file_sha1, &tgt_file_size); + ASSERT_EQ(recovery_img_size, tgt_file_size); + ASSERT_EQ(recovery_img_sha1, tgt_file_sha1); +} + TEST_F(ApplyPatchModesTest, PatchModeInvalidArgs) { // Invalid bonus file. ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" })); -- cgit v1.2.3 From 454d8cb805e633aaa1c6456a57ec9c2dc6c6e163 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 22 Mar 2018 16:07:00 -0700 Subject: update_verifier: Support verifying product partition. We have added the support for building /product partition in build system (the CL in [1]), where /product is an optional partition that contains system files. This CL adds the matching support if /product needs to be verified during A/B OTA (i.e. listed in care_map file). [1]: commit b7735d81054002961b681f4bdf296d4de2701135, https://android-review.googlesource.com/c/platform/build/+/598454 Bug: 63974895 Test: Run update_verifier test on walleye. Change-Id: Ia1c35e9583b8e66c98a4495b1f81a5ea7e65036f (cherry picked from commit ec2e8c6c1ef3cbafa129ade95abca3203e062b5f) --- tests/component/update_verifier_test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tests/component') diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index b04e1185e..1544bb2a4 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -46,7 +46,6 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { return; } - // The care map file can have only two or four lines. TemporaryFile temp_file; std::string content = "system\n2,0,1"; ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); @@ -58,7 +57,7 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { } TEST_F(UpdateVerifierTest, verify_image_wrong_lines) { - // The care map file can have only two or four lines. + // The care map file can have only 2 / 4 / 6 lines. TemporaryFile temp_file; ASSERT_FALSE(verify_image(temp_file.path)); -- cgit v1.2.3