summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tests/Android.mk10
-rw-r--r--tests/common/test_constants.h12
-rw-r--r--tests/component/applypatch_test.cpp10
-rw-r--r--tests/component/imgdiff_test.cpp364
-rw-r--r--tests/component/install_test.cpp97
-rw-r--r--tests/component/updater_test.cpp242
-rw-r--r--tests/testdata/deflate_src.zipbin0 -> 164491 bytes
-rw-r--r--tests/testdata/deflate_tgt.zipbin0 -> 160385 bytes
-rw-r--r--tests/unit/dirutil_test.cpp64
-rw-r--r--tests/unit/rangeset_test.cpp49
10 files changed, 646 insertions, 202 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index f2497b8b3..b0f71a832 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -18,7 +18,7 @@ LOCAL_PATH := $(call my-dir)
# Unit tests
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE := recovery_unit_test
LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_STATIC_LIBRARIES := \
@@ -46,7 +46,7 @@ include $(BUILD_NATIVE_TEST)
# Manual tests
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE := recovery_manual_test
LOCAL_STATIC_LIBRARIES := \
libminui \
@@ -81,6 +81,7 @@ include $(BUILD_NATIVE_TEST)
# Component tests
include $(CLEAR_VARS)
LOCAL_CFLAGS := \
+ -Wall \
-Werror \
-D_FILE_OFFSET_BITS=64
@@ -142,7 +143,6 @@ LOCAL_STATIC_LIBRARIES := \
libdivsufsort \
libdivsufsort64 \
libfs_mgr \
- liblog \
libvintf_recovery \
libvintf \
libtinyxml2 \
@@ -153,6 +153,7 @@ LOCAL_STATIC_LIBRARIES := \
libcrypto \
libbz \
libziparchive \
+ liblog \
libutils \
libz \
libbase \
@@ -191,7 +192,7 @@ include $(BUILD_NATIVE_TEST)
# Host tests
include $(CLEAR_VARS)
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Wall -Werror
LOCAL_MODULE := recovery_host_test
LOCAL_MODULE_HOST_OS := linux
LOCAL_C_INCLUDES := bootable/recovery
@@ -200,6 +201,7 @@ LOCAL_SRC_FILES := \
LOCAL_STATIC_LIBRARIES := \
libimgdiff \
libimgpatch \
+ libotautil \
libbsdiff \
libbspatch \
libziparchive \
diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h
index f6b6922a4..514818e0a 100644
--- a/tests/common/test_constants.h
+++ b/tests/common/test_constants.h
@@ -19,6 +19,8 @@
#include <stdlib.h>
+#include <string>
+
// Zip entries in ziptest_valid.zip.
static const std::string kATxtContents("abcdefghabcdefgh\n");
static const std::string kBTxtContents("abcdefgh\n");
@@ -30,10 +32,14 @@ static const std::string kATxtSha1Sum("32c96a03dc8cd20097940f351bca6261ee5a1643"
// echo -n -e "abcdefgh\n" | sha1sum
static const std::string kBTxtSha1Sum("e414af7161c9554089f4106d6f1797ef14a73666");
-static const char* data_root = getenv("ANDROID_DATA");
-
static std::string from_testdata_base(const std::string& fname) {
- return std::string(data_root) + "/nativetest/recovery/testdata/" + fname;
+#ifdef __ANDROID__
+ static std::string data_root = getenv("ANDROID_DATA");
+#else
+ static std::string data_root = std::string(getenv("ANDROID_PRODUCT_OUT")) + "/data";
+#endif
+
+ return data_root + "/nativetest/recovery/testdata/" + fname;
}
#endif // _OTA_TEST_CONSTANTS_H
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
index 016fed9b1..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);
@@ -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() {
diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp
index bf25aebb0..6de804e06 100644
--- a/tests/component/imgdiff_test.cpp
+++ b/tests/component/imgdiff_test.cpp
@@ -16,17 +16,24 @@
#include <stdio.h>
+#include <algorithm>
#include <string>
+#include <tuple>
#include <vector>
#include <android-base/file.h>
#include <android-base/memory.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/test_utils.h>
#include <applypatch/imgdiff.h>
+#include <applypatch/imgdiff_image.h>
#include <applypatch/imgpatch.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_writer.h>
+#include "common/test_constants.h"
+
using android::base::get_unaligned;
// Sanity check for the given imgdiff patch header.
@@ -75,15 +82,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<const unsigned char*>(src.data()), src.size(),
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
- [&patched](const unsigned char* data, size_t len) {
- patched.append(reinterpret_cast<const char*>(data), len);
+ [&](const unsigned char* data, size_t len) {
+ patched->append(reinterpret_cast<const char*>(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);
}
@@ -138,7 +150,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");
@@ -148,7 +160,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");
@@ -187,7 +199,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");
@@ -197,7 +209,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");
@@ -236,7 +248,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");
@@ -246,7 +258,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");
@@ -623,3 +635,333 @@ TEST(ImgpatchTest, image_mode_patch_corruption) {
reinterpret_cast<const unsigned char*>(patch.data()), patch.size(),
[](const unsigned char* /*data*/, size_t len) { return len; }));
}
+
+static void construct_store_entry(const std::vector<std::tuple<std::string, size_t, char>>& 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<std::tuple<std::string, size_t, size_t>>& 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<ImageChunk> ConstructImageChunks(
+ const std::vector<uint8_t>& content, const std::vector<std::tuple<std::string, size_t>>& info) {
+ std::vector<ImageChunk> 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<uint8_t> 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<ImageChunk> 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<uint8_t>(content.begin(), content.begin() + 82520));
+
+ tgt_image.DumpChunks();
+
+ ZipModeImage src_image(true, 4096 * 10);
+ std::vector<ImageChunk> 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<uint8_t>(content.begin(), content.begin() + 137880));
+
+ std::vector<ZipModeImage> split_tgt_images;
+ std::vector<ZipModeImage> split_src_images;
+ std::vector<SortedRangeSet> 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<size_t>(4), split_tgt_images.size());
+
+ ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[0].NumOfChunks());
+ ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[0][0].DataLengthForPatch());
+ ASSERT_EQ("4,0,3,15,20", split_src_ranges[0].ToString());
+
+ ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[1].NumOfChunks());
+ ASSERT_EQ(static_cast<size_t>(12288), split_tgt_images[1][0].DataLengthForPatch());
+ ASSERT_EQ("2,3,13", split_src_ranges[1].ToString());
+
+ ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[2].NumOfChunks());
+ ASSERT_EQ(static_cast<size_t>(40960), split_tgt_images[2][0].DataLengthForPatch());
+ ASSERT_EQ("2,20,30", split_src_ranges[2].ToString());
+
+ ASSERT_EQ(static_cast<size_t>(1), split_tgt_images[3].NumOfChunks());
+ ASSERT_EQ(static_cast<size_t>(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.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' } },
+ &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({ { "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;
+ 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<const char*> 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 4 pieces of patch. (Roughly 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) {
+ // 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" 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_path));
+ ASSERT_TRUE(tgt_image.Initialize(tgt_path));
+ ASSERT_TRUE(ZipModeImage::CheckAndProcessChunks(&tgt_image, &src_image));
+
+ src_image.DumpChunks();
+ tgt_image.DumpChunks();
+
+ std::vector<ZipModeImage> split_tgt_images;
+ std::vector<ZipModeImage> split_src_images;
+ std::vector<SortedRangeSet> split_src_ranges;
+ ZipModeImage::SplitZipModeImageWithLimit(tgt_image, src_image, &split_tgt_images,
+ &split_src_images, &split_src_ranges);
+
+ // 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<size_t>(5), split_tgt_images.size());
+
+ ASSERT_EQ(static_cast<size_t>(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<size_t>(1), split_src_images[1].NumOfChunks());
+ ASSERT_EQ("c", split_src_images[1][0].GetEntryName());
+
+ ASSERT_EQ(static_cast<size_t>(0), split_src_images[2].NumOfChunks());
+ ASSERT_EQ(static_cast<size_t>(0), split_src_images[3].NumOfChunks());
+ ASSERT_EQ(static_cast<size_t>(0), split_src_images[4].NumOfChunks());
+
+ // 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, 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<std::string> info_list =
+ android::base::Split(android::base::Trim(split_info_string), "\n");
+
+ ASSERT_EQ(static_cast<size_t>(7), info_list.size());
+ ASSERT_EQ("2", android::base::Trim(info_list[0]));
+ ASSERT_EQ("5", android::base::Trim(info_list[1]));
+
+ std::string tgt;
+ ASSERT_TRUE(android::base::ReadFileToString(tgt_path, &tgt));
+ ASSERT_EQ(static_cast<size_t>(160385), tgt.size());
+ std::vector<std::string> 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;
+ std::string path = android::base::StringPrintf("%s/patch-%zu", debug_dir.path, i);
+ ASSERT_EQ(0, stat(path.c_str(), &st));
+ ASSERT_EQ(std::to_string(st.st_size) + " " + tgt_file_ranges[i],
+ android::base::Trim(info_list[i + 2]));
+ }
+
+ 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.release(), "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.release(), "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;
+ 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<const char*> args = {
+ "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()));
+
+ 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.release(), "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.release(), "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;
+ 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<const char*> args = {
+ "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()));
+
+ std::string tgt;
+ ASSERT_TRUE(android::base::ReadFileToString(tgt_file.path, &tgt));
+
+ // 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 968196fc0..d19d788e4 100644
--- a/tests/component/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
#include <string>
#include <vector>
@@ -36,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));
@@ -53,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());
@@ -69,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");
@@ -86,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()));
@@ -103,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());
@@ -119,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";
@@ -129,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;
@@ -164,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(),
@@ -174,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;
@@ -198,10 +199,10 @@ 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");
+ 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());
@@ -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<std::string>{
- "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp,
- },
- "\n");
+
+ std::vector<std::string> 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,17 +237,28 @@ TEST(InstallTest, update_binary_command_smoke) {
std::string package = "/path/to/update.zip";
std::string binary_path = "/sbin/update_engine_sideload";
std::vector<std::string> 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");
+ 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));
@@ -289,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));
@@ -320,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));
@@ -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
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
index 6c341c111..d9d01d427 100644
--- a/tests/component/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -23,6 +23,7 @@
#include <algorithm>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
#include <android-base/file.h>
@@ -32,16 +33,16 @@
#include <android-base/test_utils.h>
#include <bootloader_message/bootloader_message.h>
#include <brotli/encode.h>
-#include <bsdiff.h>
+#include <bsdiff/bsdiff.h>
#include <gtest/gtest.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_writer.h>
#include "common/test_constants.h"
#include "edify/expr.h"
-#include "error_code.h"
#include "otautil/SysUtil.h"
-#include "print_sha1.h"
+#include "otautil/error_code.h"
+#include "otautil/print_sha1.h"
#include "updater/blockimg.h"
#include "updater/install.h"
#include "updater/updater.h"
@@ -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<std::string, std::string>& 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<const uint8_t*>(content.c_str()), content.size(), digest);
@@ -383,7 +401,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 +410,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 <frac>").
ASSERT_EQ(2U, android::base::Split(cmd, " ").size());
+ ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
}
TEST_F(UpdaterTest, show_progress) {
@@ -407,7 +426,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,32 +435,22 @@ TEST_F(UpdaterTest, show_progress) {
ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd);
// recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
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");
- 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<const uint8_t*>(src_content.data()),
src_content.size(), reinterpret_cast<const uint8_t*>(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<std::string> transfer_list = {
@@ -454,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<std::string> fail_transfer_list = {
- "4",
- "2",
- "0",
- "2",
- "stash " + tgt_hash + " 2,0,2",
- "free " + tgt_hash,
- "fail",
+ std::unordered_map<std::string, std::string> 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));
@@ -485,11 +483,11 @@ 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;
- // 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) +
@@ -500,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<std::string> 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<std::string, std::string> 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<std::string> transfer_list = {
+ "4", "1", "0", "0", "new 2,0,1",
+ };
+
+ // Write 4096 + 100 bytes of new data.
+ std::unordered_map<std::string, std::string> 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.fd, "wb");
- ZipWriter zip_writer(zip_file_ptr);
+ BuildUpdatePackage(entries, zip_file.release());
- // 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());
+ 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;
+ 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<std::string> transfer_list = {
"4",
"1",
@@ -545,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<std::string, std::string> 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));
@@ -561,7 +618,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,18 +636,12 @@ 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");
- 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;
@@ -598,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<uint8_t> 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<const uint8_t*>(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<const uint8_t*>(brotli_new_data.data()), &encoded_size,
+ reinterpret_cast<uint8_t*>(const_cast<char*>(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.
@@ -623,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<std::string, std::string> 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));
@@ -639,7 +689,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 +703,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);
}
diff --git a/tests/testdata/deflate_src.zip b/tests/testdata/deflate_src.zip
new file mode 100644
index 000000000..bdb2b3216
--- /dev/null
+++ b/tests/testdata/deflate_src.zip
Binary files differ
diff --git a/tests/testdata/deflate_tgt.zip b/tests/testdata/deflate_tgt.zip
new file mode 100644
index 000000000..2a21760ec
--- /dev/null
+++ b/tests/testdata/deflate_tgt.zip
Binary files differ
diff --git a/tests/unit/dirutil_test.cpp b/tests/unit/dirutil_test.cpp
index 5e2ae4fb5..7f85d13ea 100644
--- a/tests/unit/dirutil_test.cpp
+++ b/tests/unit/dirutil_test.cpp
@@ -26,23 +26,23 @@
TEST(DirUtilTest, create_invalid) {
// Requesting to create an empty dir is invalid.
- ASSERT_EQ(-1, dirCreateHierarchy("", 0755, nullptr, false, nullptr));
+ ASSERT_EQ(-1, mkdir_recursively("", 0755, false, nullptr));
ASSERT_EQ(ENOENT, errno);
// Requesting to strip the name with no slash present.
- ASSERT_EQ(-1, dirCreateHierarchy("abc", 0755, nullptr, true, nullptr));
+ ASSERT_EQ(-1, mkdir_recursively("abc", 0755, true, nullptr));
ASSERT_EQ(ENOENT, errno);
// Creating a dir that already exists.
TemporaryDir td;
- ASSERT_EQ(0, dirCreateHierarchy(td.path, 0755, nullptr, false, nullptr));
+ ASSERT_EQ(0, mkdir_recursively(td.path, 0755, false, nullptr));
// "///" is a valid dir.
- ASSERT_EQ(0, dirCreateHierarchy("///", 0755, nullptr, false, nullptr));
+ ASSERT_EQ(0, mkdir_recursively("///", 0755, false, nullptr));
// Request to create a dir, but a file with the same name already exists.
TemporaryFile tf;
- ASSERT_EQ(-1, dirCreateHierarchy(tf.path, 0755, nullptr, false, nullptr));
+ ASSERT_EQ(-1, mkdir_recursively(tf.path, 0755, false, nullptr));
ASSERT_EQ(ENOTDIR, errno);
}
@@ -51,7 +51,7 @@ TEST(DirUtilTest, create_smoke) {
std::string prefix(td.path);
std::string path = prefix + "/a/b";
constexpr mode_t mode = 0755;
- ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, nullptr, false, nullptr));
+ ASSERT_EQ(0, mkdir_recursively(path, mode, false, nullptr));
// Verify.
struct stat sb;
@@ -69,7 +69,7 @@ TEST(DirUtilTest, create_strip_filename) {
TemporaryDir td;
std::string prefix(td.path);
std::string path = prefix + "/a/b";
- ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), 0755, nullptr, true, nullptr));
+ ASSERT_EQ(0, mkdir_recursively(path, 0755, true, nullptr));
// Verify that "../a" exists but not "../a/b".
struct stat sb;
@@ -83,31 +83,21 @@ TEST(DirUtilTest, create_strip_filename) {
ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
}
-TEST(DirUtilTest, create_mode_and_timestamp) {
+TEST(DirUtilTest, create_mode) {
TemporaryDir td;
std::string prefix(td.path);
std::string path = prefix + "/a/b";
- // Set the timestamp to 8/1/2008.
- constexpr struct utimbuf timestamp = { 1217592000, 1217592000 };
constexpr mode_t mode = 0751;
- ASSERT_EQ(0, dirCreateHierarchy(path.c_str(), mode, &timestamp, false, nullptr));
+ ASSERT_EQ(0, mkdir_recursively(path, mode, false, nullptr));
- // Verify the mode and timestamp for "../a/b".
+ // Verify the mode for "../a/b".
struct stat sb;
ASSERT_EQ(0, stat(path.c_str(), &sb)) << strerror(errno);
ASSERT_TRUE(S_ISDIR(sb.st_mode));
constexpr mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
ASSERT_EQ(mode, sb.st_mode & mask);
- timespec time;
- time.tv_sec = 1217592000;
- time.tv_nsec = 0;
-
- ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_atime));
- ASSERT_EQ(time.tv_sec, static_cast<long>(sb.st_mtime));
-
- // Verify the mode for "../a". Note that the timestamp for intermediate directories (e.g. "../a")
- // may not be 'timestamp' according to the current implementation.
+ // Verify the mode for "../a".
ASSERT_EQ(0, stat((prefix + "/a").c_str(), &sb)) << strerror(errno);
ASSERT_TRUE(S_ISDIR(sb.st_mode));
ASSERT_EQ(mode, sb.st_mode & mask);
@@ -116,35 +106,3 @@ TEST(DirUtilTest, create_mode_and_timestamp) {
ASSERT_EQ(0, rmdir((prefix + "/a/b").c_str()));
ASSERT_EQ(0, rmdir((prefix + "/a").c_str()));
}
-
-TEST(DirUtilTest, unlink_invalid) {
- // File doesn't exist.
- ASSERT_EQ(-1, dirUnlinkHierarchy("doesntexist"));
-
- // Nonexistent directory.
- TemporaryDir td;
- std::string path(td.path);
- ASSERT_EQ(-1, dirUnlinkHierarchy((path + "/a").c_str()));
- ASSERT_EQ(ENOENT, errno);
-}
-
-TEST(DirUtilTest, unlink_smoke) {
- // Unlink a file.
- TemporaryFile tf;
- ASSERT_EQ(0, dirUnlinkHierarchy(tf.path));
- ASSERT_EQ(-1, access(tf.path, F_OK));
-
- TemporaryDir td;
- std::string path(td.path);
- constexpr mode_t mode = 0700;
- ASSERT_EQ(0, mkdir((path + "/a").c_str(), mode));
- ASSERT_EQ(0, mkdir((path + "/a/b").c_str(), mode));
- ASSERT_EQ(0, mkdir((path + "/a/b/c").c_str(), mode));
- ASSERT_EQ(0, mkdir((path + "/a/d").c_str(), mode));
-
- // Remove "../a" recursively.
- ASSERT_EQ(0, dirUnlinkHierarchy((path + "/a").c_str()));
-
- // Verify it's gone.
- ASSERT_EQ(-1, access((path + "/a").c_str(), F_OK));
-}
diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp
index 3c6d77ef5..b3ed99215 100644
--- a/tests/unit/rangeset_test.cpp
+++ b/tests/unit/rangeset_test.cpp
@@ -21,7 +21,7 @@
#include <gtest/gtest.h>
-#include "updater/rangeset.h"
+#include "otautil/rangeset.h"
TEST(RangeSetTest, Parse_smoke) {
RangeSet rs = RangeSet::Parse("2,1,10");
@@ -110,3 +110,50 @@ TEST(RangeSetTest, iterators) {
}
ASSERT_EQ((std::vector<Range>{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges);
}
+
+TEST(RangeSetTest, tostring) {
+ ASSERT_EQ("2,1,6", RangeSet::Parse("2,1,6").ToString());
+ ASSERT_EQ("4,1,5,8,10", RangeSet::Parse("4,1,5,8,10").ToString());
+ ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString());
+}
+
+TEST(SortedRangeSetTest, insertion) {
+ SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } });
+ rs.Insert({ 1, 2 });
+ ASSERT_EQ(SortedRangeSet({ { 1, 3 }, { 4, 6 }, { 8, 14 } }), rs);
+ ASSERT_EQ(static_cast<size_t>(10), rs.blocks());
+ rs.Insert({ 3, 5 });
+ ASSERT_EQ(SortedRangeSet({ { 1, 6 }, { 8, 14 } }), rs);
+ ASSERT_EQ(static_cast<size_t>(11), rs.blocks());
+
+ SortedRangeSet r1({ { 20, 22 }, { 15, 18 } });
+ rs.Insert(r1);
+ ASSERT_EQ(SortedRangeSet({ { 1, 6 }, { 8, 14 }, { 15, 18 }, { 20, 22 } }), rs);
+ ASSERT_EQ(static_cast<size_t>(16), rs.blocks());
+
+ SortedRangeSet r2({ { 2, 7 }, { 15, 21 }, { 20, 25 } });
+ rs.Insert(r2);
+ ASSERT_EQ(SortedRangeSet({ { 1, 7 }, { 8, 14 }, { 15, 25 } }), rs);
+ ASSERT_EQ(static_cast<size_t>(22), rs.blocks());
+}
+
+TEST(SortedRangeSetTest, file_range) {
+ SortedRangeSet rs;
+ rs.Insert(4096, 4096);
+ ASSERT_EQ(SortedRangeSet({ { 1, 2 } }), rs);
+ // insert block 2-9
+ rs.Insert(4096 * 3 - 1, 4096 * 7);
+ ASSERT_EQ(SortedRangeSet({ { 1, 10 } }), rs);
+ // insert block 15-19
+ rs.Insert(4096 * 15 + 1, 4096 * 4);
+ ASSERT_EQ(SortedRangeSet({ { 1, 10 }, { 15, 20 } }), rs);
+
+ // rs overlaps block 2-2
+ ASSERT_TRUE(rs.Overlaps(4096 * 2 - 1, 10));
+ ASSERT_FALSE(rs.Overlaps(4096 * 10, 4096 * 5));
+
+ ASSERT_EQ(static_cast<size_t>(10), rs.GetOffsetInRangeSet(4106));
+ ASSERT_EQ(static_cast<size_t>(40970), rs.GetOffsetInRangeSet(4096 * 16 + 10));
+ // block#10 not in range.
+ ASSERT_EXIT(rs.GetOffsetInRangeSet(40970), ::testing::KilledBySignal(SIGABRT), "");
+}