diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Android.mk | 48 | ||||
-rw-r--r-- | tests/common/test_constants.h | 15 | ||||
-rw-r--r-- | tests/component/applypatch_test.cpp | 595 | ||||
-rw-r--r-- | tests/component/updater_test.cpp | 134 | ||||
-rw-r--r-- | tests/component/verifier_test.cpp | 11 | ||||
-rw-r--r-- | tests/manual/recovery_test.cpp | 87 | ||||
-rw-r--r-- | tests/testdata/bonus.file | bin | 0 -> 557334 bytes | |||
-rw-r--r-- | tests/testdata/boot.img | bin | 0 -> 783655 bytes | |||
-rw-r--r-- | tests/testdata/recovery-from-boot-with-bonus.p | bin | 0 -> 381615 bytes | |||
-rw-r--r-- | tests/testdata/recovery-from-boot.p | bin | 0 -> 5404 bytes | |||
-rw-r--r-- | tests/testdata/recovery.img | bin | 0 -> 529707 bytes | |||
-rw-r--r-- | tests/unit/recovery_test.cpp | 91 | ||||
-rw-r--r-- | tests/unit/sysutil_test.cpp | 140 | ||||
-rw-r--r-- | tests/unit/zip_test.cpp | 109 |
14 files changed, 787 insertions, 443 deletions
diff --git a/tests/Android.mk b/tests/Android.mk index 3d05386b0..e87a22964 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -18,7 +18,6 @@ LOCAL_PATH := $(call my-dir) # Unit tests include $(CLEAR_VARS) -LOCAL_CLANG := true LOCAL_CFLAGS := -Werror LOCAL_MODULE := recovery_unit_test LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk @@ -32,18 +31,31 @@ LOCAL_STATIC_LIBRARIES := \ libselinux \ libbase -LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp -LOCAL_SRC_FILES += unit/recovery_test.cpp -LOCAL_SRC_FILES += unit/locale_test.cpp -LOCAL_SRC_FILES += unit/zip_test.cpp +LOCAL_SRC_FILES := \ + unit/asn1_decoder_test.cpp \ + unit/locale_test.cpp \ + unit/sysutil_test.cpp \ + unit/zip_test.cpp + LOCAL_C_INCLUDES := bootable/recovery LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_NATIVE_TEST) -# Component tests +# Manual tests include $(CLEAR_VARS) LOCAL_CLANG := true -LOCAL_CFLAGS += -Wno-unused-parameter -Werror +LOCAL_CFLAGS := -Werror +LOCAL_MODULE := recovery_manual_test +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk +LOCAL_STATIC_LIBRARIES := libbase + +LOCAL_SRC_FILES := manual/recovery_test.cpp +LOCAL_SHARED_LIBRARIES := liblog +include $(BUILD_NATIVE_TEST) + +# Component tests +include $(CLEAR_VARS) +LOCAL_CFLAGS := -Werror LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := recovery_component_test LOCAL_C_INCLUDES := bootable/recovery @@ -63,6 +75,7 @@ tune2fs_static_libraries := \ libext2fs LOCAL_STATIC_LIBRARIES := \ + libapplypatch_modes \ libapplypatch \ libedify \ libotafault \ @@ -86,7 +99,8 @@ LOCAL_STATIC_LIBRARIES := \ testdata_files := $(call find-subdir-files, testdata/*) -testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery +# The testdata files that will go to $OUT/data/nativetest/recovery. +testdata_out_path := $(TARGET_OUT_DATA)/nativetest/recovery GEN := $(addprefix $(testdata_out_path)/, $(testdata_files)) $(GEN): PRIVATE_PATH := $(LOCAL_PATH) $(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ @@ -94,14 +108,16 @@ $(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/% $(transform-generated-source) LOCAL_GENERATED_SOURCES += $(GEN) -ifdef TARGET_2ND_ARCH -testdata_out_path_2nd_arch := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/recovery -GEN_2ND_ARCH := $(addprefix $(testdata_out_path_2nd_arch)/, $(testdata_files)) -$(GEN_2ND_ARCH): PRIVATE_PATH := $(LOCAL_PATH) -$(GEN_2ND_ARCH): PRIVATE_CUSTOM_TOOL = cp $< $@ -$(GEN_2ND_ARCH): $(testdata_out_path_2nd_arch)/% : $(LOCAL_PATH)/% +# A copy of the testdata to be packed into continuous_native_tests.zip. +testdata_continuous_zip_prefix := \ + $(call intermediates-dir-for,PACKAGING,recovery_component_test)/DATA +testdata_continuous_zip_path := $(testdata_continuous_zip_prefix)/nativetest/recovery +GEN := $(addprefix $(testdata_continuous_zip_path)/, $(testdata_files)) +$(GEN): PRIVATE_PATH := $(LOCAL_PATH) +$(GEN): PRIVATE_CUSTOM_TOOL = cp $< $@ +$(GEN): $(testdata_continuous_zip_path)/% : $(LOCAL_PATH)/% $(transform-generated-source) -LOCAL_GENERATED_SOURCES += $(GEN_2ND_ARCH) -endif # TARGET_2ND_ARCH +LOCAL_GENERATED_SOURCES += $(GEN) +LOCAL_PICKUP_FILES := $(testdata_continuous_zip_prefix) include $(BUILD_NATIVE_TEST) diff --git a/tests/common/test_constants.h b/tests/common/test_constants.h index 3490f6805..97e74a3c2 100644 --- a/tests/common/test_constants.h +++ b/tests/common/test_constants.h @@ -13,13 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef _OTA_TEST_CONSTANTS_H #define _OTA_TEST_CONSTANTS_H -#if defined(__LP64__) -#define NATIVE_TEST_PATH "/nativetest64" -#else -#define NATIVE_TEST_PATH "/nativetest" -#endif +#include <stdlib.h> + +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; +} -#endif +#endif // _OTA_TEST_CONSTANTS_H diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 908a9f5f5..1a0b19113 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -23,167 +23,158 @@ #include <sys/types.h> #include <time.h> +#include <memory> #include <string> +#include <vector> #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/test_utils.h> +#include <openssl/sha.h> #include "applypatch/applypatch.h" +#include "applypatch/applypatch_modes.h" #include "common/test_constants.h" -#include "openssl/sha.h" #include "print_sha1.h" -static const std::string DATA_PATH = getenv("ANDROID_DATA"); -static const std::string TESTDATA_PATH = "/recovery/testdata"; -static const std::string WORK_FS = "/data"; +static void sha1sum(const std::string& fname, std::string* sha1, size_t* fsize = nullptr) { + ASSERT_NE(nullptr, sha1); -static std::string sha1sum(const std::string& fname) { - uint8_t digest[SHA_DIGEST_LENGTH]; - std::string data; - android::base::ReadFileToString(fname, &data); + std::string data; + ASSERT_TRUE(android::base::ReadFileToString(fname, &data)); - SHA1((const uint8_t*)data.c_str(), data.size(), digest); - return print_sha1(digest); -} + if (fsize != nullptr) { + *fsize = data.size(); + } -static void mangle_file(const std::string& fname) { - FILE* fh = fopen(&fname[0], "w"); - int r; - for (int i=0; i < 1024; i++) { - r = rand(); - fwrite(&r, sizeof(short), 1, fh); - } - fclose(fh); + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest); + *sha1 = print_sha1(digest); } -static bool file_cmp(std::string& f1, std::string& f2) { - std::string c1; - std::string c2; - android::base::ReadFileToString(f1, &c1); - android::base::ReadFileToString(f2, &c2); - return c1 == c2; +static void mangle_file(const std::string& fname) { + std::string content; + content.reserve(1024); + for (size_t i = 0; i < 1024; i++) { + content[i] = rand() % 256; + } + ASSERT_TRUE(android::base::WriteStringToFile(content, fname)); } -static std::string from_testdata_base(const std::string& fname) { - return android::base::StringPrintf("%s%s%s/%s", - &DATA_PATH[0], - &NATIVE_TEST_PATH[0], - &TESTDATA_PATH[0], - &fname[0]); +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() { - // 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 - android::base::WriteStringToFile("hello", rand_file); - - // set up SHA constants - old_sha1 = sha1sum(old_file); - new_sha1 = sha1sum(new_file); - srand(time(NULL)); - bad_sha1_a = android::base::StringPrintf("%040x", rand()); - bad_sha1_b = android::base::StringPrintf("%040x", rand()); - - struct stat st; - stat(&new_file[0], &st); - new_size = st.st_size; - } - - 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; - - static std::string old_sha1; - static std::string new_sha1; - static std::string bad_sha1_a; - static std::string bad_sha1_b; - - static size_t new_size; + public: + static void SetUpTestCase() { + // 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)); + + // set up SHA constants + sha1sum(old_file, &old_sha1); + sha1sum(new_file, &new_sha1); + srand(time(nullptr)); + bad_sha1_a = android::base::StringPrintf("%040x", rand()); + bad_sha1_b = android::base::StringPrintf("%040x", rand()); + + struct stat st; + stat(&new_file[0], &st); + new_size = st.st_size; + } + + 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; + + static std::string old_sha1; + static std::string new_sha1; + static std::string bad_sha1_a; + static std::string bad_sha1_b; + + static size_t new_size; }; std::string ApplyPatchTest::old_file; std::string ApplyPatchTest::new_file; -static void cp(std::string src, std::string tgt) { - std::string cmd = android::base::StringPrintf("cp %s %s", - &src[0], - &tgt[0]); - system(&cmd[0]); +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); + cp(ApplyPatchTest::old_file, ApplyPatchTest::cache_file); } static void restore_old() { - cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); + cp(ApplyPatchTest::cache_file, ApplyPatchTest::old_file); } class ApplyPatchCacheTest : public ApplyPatchTest { - public: - virtual void SetUp() { - backup_old(); - } - - virtual void TearDown() { - restore_old(); - } + public: + virtual void SetUp() { + backup_old(); + } + + virtual void TearDown() { + restore_old(); + } }; class ApplyPatchFullTest : public ApplyPatchCacheTest { - public: - static void SetUpTestCase() { - ApplyPatchTest::SetUpTestCase(); - unsigned long free_kb = FreeSpaceForFile(&WORK_FS[0]); - ASSERT_GE(free_kb * 1024, new_size * 3 / 2); - output_f = new TemporaryFile(); - output_loc = std::string(output_f->path); - - struct FileContents fc; - - ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc)); - Value* patch1 = new Value(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())); - patches.push_back(patch1); - - ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); - Value* patch2 = new Value(VAL_BLOB, std::string(fc.data.begin(), fc.data.end())); - patches.push_back(patch2); - } - static void TearDownTestCase() { - delete output_f; - for (auto it = patches.begin(); it != patches.end(); ++it) { - delete *it; - } - patches.clear(); - } - - static std::vector<Value*> patches; - static TemporaryFile* output_f; - static std::string output_loc; + public: + static void SetUpTestCase() { + ApplyPatchTest::SetUpTestCase(); + + output_f = new TemporaryFile(); + output_loc = std::string(output_f->path); + + struct FileContents fc; + + ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc)); + patches.push_back( + std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()))); + + ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); + patches.push_back( + std::make_unique<Value>(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()))); + } + + static void TearDownTestCase() { + delete output_f; + patches.clear(); + } + + static std::vector<std::unique_ptr<Value>> patches; + static TemporaryFile* output_f; + static std::string output_loc; }; class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest { - public: - virtual void SetUp() { - ApplyPatchCacheTest::SetUp(); - cp(cache_file, "/cache/reallysaved.file"); - } - - virtual void TearDown() { - cp("/cache/reallysaved.file", cache_file); - ApplyPatchCacheTest::TearDown(); - } + public: + virtual void SetUp() { + ApplyPatchCacheTest::SetUp(); + cp(cache_file, "/cache/reallysaved.file"); + } + + virtual void TearDown() { + cp("/cache/reallysaved.file", cache_file); + ApplyPatchCacheTest::TearDown(); + } }; std::string ApplyPatchTest::rand_file; @@ -196,188 +187,268 @@ std::string ApplyPatchTest::bad_sha1_b; size_t ApplyPatchTest::new_size; -std::vector<Value*> ApplyPatchFullTest::patches; +std::vector<std::unique_ptr<Value>> ApplyPatchFullTest::patches; TemporaryFile* ApplyPatchFullTest::output_f; std::string ApplyPatchFullTest::output_loc; TEST_F(ApplyPatchTest, CheckModeSkip) { - std::vector<std::string> sha1s; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchTest, CheckModeSingle) { - std::vector<std::string> sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchTest, CheckModeMultiple) { - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1, - bad_sha1_b - }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchTest, CheckModeFailure) { - std::vector<std::string> sha1s = { - bad_sha1_a, - bad_sha1_b - }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); + std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedSingle) { - mangle_file(old_file); - std::vector<std::string> sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + mangle_file(old_file); + std::vector<std::string> sha1s = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedMultiple) { - mangle_file(old_file); - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1, - bad_sha1_b - }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + mangle_file(old_file); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1, bad_sha1_b }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheCorruptedFailure) { - mangle_file(old_file); - std::vector<std::string> sha1s = { - bad_sha1_a, - bad_sha1_b - }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); + mangle_file(old_file); + std::vector<std::string> 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<std::string> sha1s = { old_sha1 }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + unlink(&old_file[0]); + std::vector<std::string> sha1s = { old_sha1 }; + ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchCacheTest, CheckCacheMissingMultiple) { - unlink(&old_file[0]); - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1, - bad_sha1_b - }; - ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); + unlink(&old_file[0]); + std::vector<std::string> 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<std::string> sha1s = { - bad_sha1_a, - bad_sha1_b - }; - ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); + unlink(&old_file[0]); + std::vector<std::string> sha1s = { bad_sha1_a, bad_sha1_b }; + ASSERT_NE(0, applypatch_check(&old_file[0], sha1s)); } TEST_F(ApplyPatchFullTest, ApplyInPlace) { - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - "-", - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(old_file, new_file)); - // reapply, applypatch is idempotent so it should succeed - ap_result = applypatch(&old_file[0], - "-", - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(old_file, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + ASSERT_EQ(0, applypatch(&old_file[0], "-", &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(old_file, new_file)); + + // reapply, applypatch is idempotent so it should succeed + ASSERT_EQ(0, applypatch(&old_file[0], "-", &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(old_file, new_file)); } TEST_F(ApplyPatchFullTest, ApplyInNewLocation) { - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); - ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + // Apply bsdiff patch to new location. + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); + + // Reapply to the same location. + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); } TEST_F(ApplyPatchFullTest, ApplyCorruptedInNewLocation) { - mangle_file(old_file); - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); - ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_EQ(0, ap_result); - ASSERT_TRUE(file_cmp(output_loc, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + // Apply bsdiff patch to new location with corrupted source. + mangle_file(old_file); + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); + + // Reapply bsdiff patch to new location with corrupted source. + ASSERT_EQ( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_TRUE(file_cmp(output_loc, new_file)); } TEST_F(ApplyPatchDoubleCacheTest, ApplyDoubleCorruptedInNewLocation) { - mangle_file(old_file); - mangle_file(cache_file); - - std::vector<std::string> sha1s = { - bad_sha1_a, - old_sha1 - }; - int ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_NE(0, ap_result); - ASSERT_FALSE(file_cmp(output_loc, new_file)); - ap_result = applypatch(&old_file[0], - &output_loc[0], - &new_sha1[0], - new_size, - sha1s, - patches.data(), - nullptr); - ASSERT_NE(0, ap_result); - ASSERT_FALSE(file_cmp(output_loc, new_file)); + std::vector<std::string> sha1s = { bad_sha1_a, old_sha1 }; + + // Apply bsdiff patch to new location with corrupted source and copy (no new file). + // Expected to fail. + mangle_file(old_file); + mangle_file(cache_file); + ASSERT_NE( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_FALSE(file_cmp(output_loc, new_file)); + + // Expected to fail again on retry. + ASSERT_NE( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_FALSE(file_cmp(output_loc, new_file)); + + // Expected to fail with incorrect new file. + mangle_file(output_loc); + ASSERT_NE( + 0, applypatch(&old_file[0], &output_loc[0], &new_sha1[0], new_size, sha1s, patches, nullptr)); + ASSERT_FALSE(file_cmp(output_loc, new_file)); +} + +TEST(ApplyPatchModes, InvalidArgs) { + // At least two args (including the filename). + ASSERT_EQ(2, applypatch_modes(1, (const char* []){ "applypatch" })); + + // Unrecognized args. + ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-x" })); +} + +TEST(ApplyPatchModes, PatchMode) { + std::string boot_img = from_testdata_base("boot.img"); + size_t boot_img_size; + std::string boot_img_sha1; + sha1sum(boot_img, &boot_img_sha1, &boot_img_size); + + std::string recovery_img = from_testdata_base("recovery.img"); + size_t recovery_img_size; + std::string recovery_img_sha1; + sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size); + + std::string bonus_file = from_testdata_base("bonus.file"); + + // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch> + TemporaryFile tmp1; + std::vector<const char*> args = { + "applypatch", + "-b", + bonus_file.c_str(), + boot_img.c_str(), + tmp1.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p")).c_str() + }; + ASSERT_EQ(0, applypatch_modes(args.size(), args.data())); + + // applypatch <src-file> <tgt-file> <tgt-sha1> <tgt-size> <src-sha1>:<patch> + TemporaryFile tmp2; + std::vector<const char*> args2 = { + "applypatch", + boot_img.c_str(), + tmp2.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str() + }; + ASSERT_EQ(0, applypatch_modes(args2.size(), args2.data())); + + // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> \ + // <src-sha1-fake>:<patch1> <src-sha1>:<patch2> + TemporaryFile tmp3; + std::string bad_sha1_a = android::base::StringPrintf("%040x", rand()); + std::string bad_sha1_b = android::base::StringPrintf("%040x", rand()); + std::vector<const char*> args3 = { + "applypatch", + "-b", + bonus_file.c_str(), + boot_img.c_str(), + tmp3.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + (bad_sha1_a + ":" + from_testdata_base("recovery-from-boot.p")).c_str(), + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot.p")).c_str(), + (bad_sha1_b + ":" + from_testdata_base("recovery-from-boot.p")).c_str(), + }; + ASSERT_EQ(0, applypatch_modes(args3.size(), args3.data())); +} + +TEST(ApplyPatchModes, PatchModeInvalidArgs) { + // Invalid bonus file. + ASSERT_NE(0, applypatch_modes(3, (const char* []){ "applypatch", "-b", "/doesntexist" })); + + std::string bonus_file = from_testdata_base("bonus.file"); + // With bonus file, but missing args. + ASSERT_EQ(2, applypatch_modes(3, (const char* []){ "applypatch", "-b", bonus_file.c_str() })); + + std::string boot_img = from_testdata_base("boot.img"); + size_t boot_img_size; + std::string boot_img_sha1; + sha1sum(boot_img, &boot_img_sha1, &boot_img_size); + + std::string recovery_img = from_testdata_base("recovery.img"); + size_t recovery_img_size; + std::string recovery_img_sha1; + sha1sum(recovery_img, &recovery_img_sha1, &recovery_img_size); + + // Bonus file is not supported in flash mode. + // applypatch -b <bonus-file> <src-file> <tgt-file> <tgt-sha1> <tgt-size> + TemporaryFile tmp4; + std::vector<const char*> args4 = { + "applypatch", + "-b", + bonus_file.c_str(), + boot_img.c_str(), + tmp4.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str() }; + ASSERT_NE(0, applypatch_modes(args4.size(), args4.data())); + + // Failed to parse patch args. + TemporaryFile tmp5; + std::vector<const char*> args5 = { + "applypatch", + boot_img.c_str(), + tmp5.path, + recovery_img_sha1.c_str(), + std::to_string(recovery_img_size).c_str(), + ("invalid-sha1:filename" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str(), + }; + ASSERT_NE(0, applypatch_modes(args5.size(), args5.data())); + + // Target size cannot be zero. + TemporaryFile tmp6; + std::vector<const char*> args6 = { + "applypatch", + boot_img.c_str(), + tmp6.path, + recovery_img_sha1.c_str(), + "0", // target size + (boot_img_sha1 + ":" + from_testdata_base("recovery-from-boot-with-bonus.p")).c_str() + }; + ASSERT_NE(0, applypatch_modes(args6.size(), args6.data())); +} + +TEST(ApplyPatchModes, CheckModeInvalidArgs) { + // Insufficient args. + ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" })); +} + +TEST(ApplyPatchModes, SpaceModeInvalidArgs) { + // Insufficient args. + ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" })); + + // Invalid bytes arg. + ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" })); + + // 0 is invalid. + ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" })); + + // 0x10 is fine. + ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" })); +} + +TEST(ApplyPatchModes, ShowLicenses) { + ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" })); } diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index a859f11c1..acc2b4040 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -16,7 +16,9 @@ #include <string> +#include <android-base/file.h> #include <android-base/properties.h> +#include <android-base/test_utils.h> #include <gtest/gtest.h> #include "edify/expr.h" @@ -97,3 +99,135 @@ TEST_F(UpdaterTest, sha1_check) { // sha1_check() expects at least one argument. expect(nullptr, "sha1_check()", kArgsParsingFailure); } + +TEST_F(UpdaterTest, file_getprop) { + // file_getprop() expects two arguments. + expect(nullptr, "file_getprop()", kArgsParsingFailure); + expect(nullptr, "file_getprop(\"arg1\")", kArgsParsingFailure); + expect(nullptr, "file_getprop(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); + + // File doesn't exist. + expect(nullptr, "file_getprop(\"/doesntexist\", \"key1\")", kFileGetPropFailure); + + // Reject too large files (current limit = 65536). + TemporaryFile temp_file1; + std::string buffer(65540, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(buffer, temp_file1.path)); + + // Read some keys. + TemporaryFile temp_file2; + std::string content("ro.product.name=tardis\n" + "# comment\n\n\n" + "ro.product.model\n" + "ro.product.board = magic \n"); + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file2.path)); + + std::string script1("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.name\")"); + expect("tardis", script1.c_str(), kNoCause); + + std::string script2("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.board\")"); + expect("magic", script2.c_str(), kNoCause); + + // No match. + std::string script3("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.wrong\")"); + expect("", script3.c_str(), kNoCause); + + std::string script4("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.name=\")"); + expect("", script4.c_str(), kNoCause); + + std::string script5("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.nam\")"); + expect("", script5.c_str(), kNoCause); + + std::string script6("file_getprop(\"" + std::string(temp_file2.path) + + "\", \"ro.product.model\")"); + expect("", script6.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, delete) { + // Delete none. + expect("0", "delete()", kNoCause); + expect("0", "delete(\"/doesntexist\")", kNoCause); + expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\")", kNoCause); + expect("0", "delete(\"/doesntexist1\", \"/doesntexist2\", \"/doesntexist3\")", kNoCause); + + // Delete one file. + TemporaryFile temp_file1; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path)); + std::string script1("delete(\"" + std::string(temp_file1.path) + "\")"); + expect("1", script1.c_str(), kNoCause); + + // Delete two files. + TemporaryFile temp_file2; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file2.path)); + TemporaryFile temp_file3; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file3.path)); + std::string script2("delete(\"" + std::string(temp_file2.path) + "\", \"" + + std::string(temp_file3.path) + "\")"); + expect("2", script2.c_str(), kNoCause); + + // Delete already deleted files. + expect("0", script2.c_str(), kNoCause); + + // Delete one out of three. + TemporaryFile temp_file4; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file4.path)); + std::string script3("delete(\"/doesntexist1\", \"" + std::string(temp_file4.path) + + "\", \"/doesntexist2\")"); + expect("1", script3.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, rename) { + // rename() expects two arguments. + expect(nullptr, "rename()", kArgsParsingFailure); + expect(nullptr, "rename(\"arg1\")", kArgsParsingFailure); + expect(nullptr, "rename(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); + + // src_name or dst_name cannot be empty. + expect(nullptr, "rename(\"\", \"arg2\")", kArgsParsingFailure); + expect(nullptr, "rename(\"arg1\", \"\")", kArgsParsingFailure); + + // File doesn't exist (both of src and dst). + expect(nullptr, "rename(\"/doesntexist\", \"/doesntexisteither\")" , kFileRenameFailure); + + // Can't create parent directory. + TemporaryFile temp_file1; + ASSERT_TRUE(android::base::WriteStringToFile("abc", temp_file1.path)); + std::string script1("rename(\"" + std::string(temp_file1.path) + "\", \"/proc/0/file1\")"); + expect(nullptr, script1.c_str(), kFileRenameFailure); + + // Rename. + TemporaryFile temp_file2; + std::string script2("rename(\"" + std::string(temp_file1.path) + "\", \"" + + std::string(temp_file2.path) + "\")"); + expect(temp_file2.path, script2.c_str(), kNoCause); + + // Already renamed. + expect(temp_file2.path, script2.c_str(), kNoCause); + + // Parents create successfully. + TemporaryFile temp_file3; + TemporaryDir td; + std::string temp_dir = std::string(td.path) + "/aaa/bbb/a.txt"; + std::string script3("rename(\"" + std::string(temp_file3.path) + "\", \"" + + temp_dir + "\")"); + expect(temp_dir.c_str(), script3.c_str(), kNoCause); +} + +TEST_F(UpdaterTest, symlink) { + // symlink expects 1+ argument. + expect(nullptr, "symlink()", kArgsParsingFailure); + + // symlink should fail if src is an empty string. + TemporaryFile temp_file1; + std::string script1("symlink(\"" + std::string(temp_file1.path) + "\", \"\")"); + expect(nullptr, script1.c_str(), kSymlinkFailure); + + // symlink failed to remove old src. + std::string script2("symlink(\"" + std::string(temp_file1.path) + "\", \"/proc\")"); + expect(nullptr, script2.c_str(), kSymlinkFailure); +} diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 7f9a71408..60a78f5c3 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -37,9 +37,6 @@ #include "ui.h" #include "verifier.h" -static const char* DATA_PATH = getenv("ANDROID_DATA"); -static const char* TESTDATA_PATH = "/recovery/testdata/"; - RecoveryUI* ui = NULL; class MockUI : public RecoveryUI { @@ -92,17 +89,13 @@ class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { virtual void SetUp() { std::vector<std::string> args = GetParam(); - std::string package = - android::base::StringPrintf("%s%s%s%s", DATA_PATH, NATIVE_TEST_PATH, - TESTDATA_PATH, args[0].c_str()); + std::string package = from_testdata_base(args[0]); if (sysMapFile(package.c_str(), &memmap) != 0) { FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } for (auto it = ++(args.cbegin()); it != args.cend(); ++it) { - std::string public_key_file = android::base::StringPrintf( - "%s%s%stestkey_%s.txt", DATA_PATH, NATIVE_TEST_PATH, - TESTDATA_PATH, it->c_str()); + std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt"); ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); } } diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp new file mode 100644 index 000000000..e83849546 --- /dev/null +++ b/tests/manual/recovery_test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <string> + +#include <android-base/file.h> +#include <android/log.h> +#include <gtest/gtest.h> +#include <private/android_logger.h> + +static const std::string myFilename = "/data/misc/recovery/inject.txt"; +static const std::string myContent = "Hello World\nWelcome to my recovery\n"; + +// Failure is expected on systems that do not deliver either the +// recovery-persist or recovery-refresh executables. Tests also require +// a reboot sequence of test to truly verify. + +static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename, + const char *buf, size_t len, void *arg) { + EXPECT_EQ(LOG_ID_SYSTEM, logId); + EXPECT_EQ(ANDROID_LOG_INFO, prio); + EXPECT_NE(std::string::npos, myFilename.find(filename)); + EXPECT_EQ(myContent, buf); + EXPECT_EQ(myContent.size(), len); + EXPECT_EQ(nullptr, arg); + return len; +} + +// recovery.refresh - May fail. Requires recovery.inject, two reboots, +// then expect success after second reboot. +TEST(recovery, refresh) { + EXPECT_EQ(0, access("/system/bin/recovery-refresh", F_OK)); + + ssize_t ret = __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, nullptr); + if (ret == -ENOENT) { + EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + myFilename.c_str(), myContent.c_str(), myContent.size())); + + fprintf(stderr, "injected test data, requires two intervening reboots " + "to check for replication\n"); + } + EXPECT_EQ(static_cast<ssize_t>(myContent.size()), ret); +} + +// recovery.persist - Requires recovery.inject, then a reboot, then +// expect success after for this test on that boot. +TEST(recovery, persist) { + EXPECT_EQ(0, access("/system/bin/recovery-persist", F_OK)); + + ssize_t ret = __android_log_pmsg_file_read( + LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, nullptr); + if (ret == -ENOENT) { + EXPECT_LT(0, __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, + myFilename.c_str(), myContent.c_str(), myContent.size())); + + fprintf(stderr, "injected test data, requires intervening reboot " + "to check for storage\n"); + } + + std::string buf; + EXPECT_TRUE(android::base::ReadFileToString(myFilename, &buf)); + EXPECT_EQ(myContent, buf); + if (access(myFilename.c_str(), O_RDONLY) == 0) { + fprintf(stderr, "Removing persistent test data, " + "check if reconstructed on reboot\n"); + } + EXPECT_EQ(0, unlink(myFilename.c_str())); +} diff --git a/tests/testdata/bonus.file b/tests/testdata/bonus.file Binary files differnew file mode 100644 index 000000000..918ef8ac5 --- /dev/null +++ b/tests/testdata/bonus.file diff --git a/tests/testdata/boot.img b/tests/testdata/boot.img Binary files differnew file mode 100644 index 000000000..dd4897510 --- /dev/null +++ b/tests/testdata/boot.img diff --git a/tests/testdata/recovery-from-boot-with-bonus.p b/tests/testdata/recovery-from-boot-with-bonus.p Binary files differnew file mode 100644 index 000000000..08b6f55e4 --- /dev/null +++ b/tests/testdata/recovery-from-boot-with-bonus.p diff --git a/tests/testdata/recovery-from-boot.p b/tests/testdata/recovery-from-boot.p Binary files differnew file mode 100644 index 000000000..06f6c299f --- /dev/null +++ b/tests/testdata/recovery-from-boot.p diff --git a/tests/testdata/recovery.img b/tests/testdata/recovery.img Binary files differnew file mode 100644 index 000000000..b862e6f0c --- /dev/null +++ b/tests/testdata/recovery.img diff --git a/tests/unit/recovery_test.cpp b/tests/unit/recovery_test.cpp deleted file mode 100644 index 28b845fb9..000000000 --- a/tests/unit/recovery_test.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <fcntl.h> -#include <string.h> -#include <sys/types.h> -#include <unistd.h> - -#include <android/log.h> -#include <gtest/gtest.h> -#include <private/android_logger.h> - -static const char myFilename[] = "/data/misc/recovery/inject.txt"; -static const char myContent[] = "Hello World\nWelcome to my recovery\n"; - -// Failure is expected on systems that do not deliver either the -// recovery-persist or recovery-refresh executables. Tests also require -// a reboot sequence of test to truly verify. - -static ssize_t __pmsg_fn(log_id_t logId, char prio, const char *filename, - const char *buf, size_t len, void *arg) { - EXPECT_EQ(LOG_ID_SYSTEM, logId); - EXPECT_EQ(ANDROID_LOG_INFO, prio); - EXPECT_EQ(0, NULL == strstr(myFilename,filename)); - EXPECT_EQ(0, strcmp(myContent, buf)); - EXPECT_EQ(sizeof(myContent), len); - EXPECT_EQ(0, NULL != arg); - return len; -} - -// recovery.refresh - May fail. Requires recovery.inject, two reboots, -// then expect success after second reboot. -TEST(recovery, refresh) { - EXPECT_EQ(0, access("/system/bin/recovery-refresh", F_OK)); - - ssize_t ret = __android_log_pmsg_file_read( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, NULL); - if (ret == -ENOENT) { - EXPECT_LT(0, __android_log_pmsg_file_write( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, - myFilename, myContent, sizeof(myContent))); - fprintf(stderr, "injected test data, " - "requires two intervening reboots " - "to check for replication\n"); - } - EXPECT_EQ((ssize_t)sizeof(myContent), ret); -} - -// recovery.persist - Requires recovery.inject, then a reboot, then -// expect success after for this test on that boot. -TEST(recovery, persist) { - EXPECT_EQ(0, access("/system/bin/recovery-persist", F_OK)); - - ssize_t ret = __android_log_pmsg_file_read( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, "recovery/", __pmsg_fn, NULL); - if (ret == -ENOENT) { - EXPECT_LT(0, __android_log_pmsg_file_write( - LOG_ID_SYSTEM, ANDROID_LOG_INFO, - myFilename, myContent, sizeof(myContent))); - fprintf(stderr, "injected test data, " - "requires intervening reboot " - "to check for storage\n"); - } - - int fd = open(myFilename, O_RDONLY); - EXPECT_LE(0, fd); - - char buf[sizeof(myContent) + 32]; - ret = read(fd, buf, sizeof(buf)); - close(fd); - EXPECT_EQ(ret, (ssize_t)sizeof(myContent)); - EXPECT_EQ(0, strcmp(myContent, buf)); - if (fd >= 0) { - fprintf(stderr, "Removing persistent test data, " - "check if reconstructed on reboot\n"); - } - EXPECT_EQ(0, unlink(myFilename)); -} diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp new file mode 100644 index 000000000..f4699664b --- /dev/null +++ b/tests/unit/sysutil_test.cpp @@ -0,0 +1,140 @@ +/* + * Copyright 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <string> + +#include <android-base/file.h> +#include <android-base/test_utils.h> + +#include "otautil/SysUtil.h" + +TEST(SysUtilTest, InvalidArgs) { + MemMapping mapping; + + // Invalid argument. + ASSERT_EQ(-1, sysMapFile(nullptr, &mapping)); + ASSERT_EQ(-1, sysMapFile("/somefile", nullptr)); +} + +TEST(SysUtilTest, sysMapFileRegularFile) { + TemporaryFile temp_file1; + std::string content = "abc"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file1.path)); + + // sysMapFile() should map the file to one range. + MemMapping mapping; + ASSERT_EQ(0, sysMapFile(temp_file1.path, &mapping)); + ASSERT_NE(nullptr, mapping.addr); + ASSERT_EQ(content.size(), mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + sysReleaseMap(&mapping); + ASSERT_EQ(0U, mapping.ranges.size()); +} + +TEST(SysUtilTest, sysMapFileBlockMap) { + // Create a file that has 10 blocks. + TemporaryFile package; + std::string content; + constexpr size_t file_size = 4096 * 10; + content.reserve(file_size); + ASSERT_TRUE(android::base::WriteStringToFile(content, package.path)); + + TemporaryFile block_map_file; + std::string filename = std::string("@") + block_map_file.path; + MemMapping mapping; + + // One range. + std::string block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + // It's okay to not have the trailing '\n'. + block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + // Or having multiple trailing '\n's. + block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n\n\n"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(1U, mapping.ranges.size()); + + // Multiple ranges. + block_map_content = std::string(package.path) + "\n40960 4096\n3\n0 3\n3 5\n5 10\n"; + ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); + + ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_EQ(file_size, mapping.length); + ASSERT_EQ(3U, mapping.ranges.size()); + + sysReleaseMap(&mapping); + ASSERT_EQ(0U, mapping.ranges.size()); +} + +TEST(SysUtilTest, sysMapFileBlockMapInvalidBlockMap) { + MemMapping mapping; + TemporaryFile temp_file; + std::string filename = std::string("@") + temp_file.path; + + // Block map file is too short. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // Block map file has unexpected number of lines. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n2\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // Invalid size/blksize/range_count. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\nabc 4096\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // size/blksize/range_count don't match. + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n0 4096\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 0\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + // Invalid block dev path. + ASSERT_TRUE(android::base::WriteStringToFile("/doesntexist\n4096 4096\n1\n0 1\n", temp_file.path)); + ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + + sysReleaseMap(&mapping); + ASSERT_EQ(0U, mapping.ranges.size()); +} diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index b617446b8..49729467d 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -16,78 +16,69 @@ #include <errno.h> #include <fcntl.h> -#include <pthread.h> #include <unistd.h> #include <memory> #include <vector> #include <android-base/file.h> -#include <android-base/stringprintf.h> -#include <android-base/unique_fd.h> #include <android-base/test_utils.h> #include <gtest/gtest.h> #include <otautil/SysUtil.h> #include <otautil/ZipUtil.h> #include <ziparchive/zip_archive.h> -static const std::string DATA_PATH(getenv("ANDROID_DATA")); -static const std::string TESTDATA_PATH("/recovery/testdata/"); - -static const std::vector<uint8_t> kATxtContents { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - '\n' -}; - -static const std::vector<uint8_t> kBTxtContents { - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', - '\n' -}; - -TEST(otazip, ExtractPackageRecursive) { - TemporaryDir td; - ASSERT_NE(td.path, nullptr); - ZipArchiveHandle handle; - std::string zip_path = DATA_PATH + TESTDATA_PATH + "/ziptest_valid.zip"; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - // Extract the whole package into a temp directory. - ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr); - // Make sure all the files are extracted correctly. - std::string path(td.path); - android::base::unique_fd fd(open((path + "/a.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - std::vector<uint8_t> read_data; - read_data.resize(kATxtContents.size()); - // The content of the file is the same as expected. - ASSERT_TRUE(android::base::ReadFully(fd.get(), read_data.data(), read_data.size())); - ASSERT_EQ(0, memcmp(read_data.data(), kATxtContents.data(), kATxtContents.size())); - - fd.reset(open((path + "/b.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - fd.reset(open((path + "/b/c.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - fd.reset(open((path + "/b/d.txt").c_str(), O_RDONLY)); - ASSERT_NE(fd, -1); - read_data.resize(kBTxtContents.size()); - ASSERT_TRUE(android::base::ReadFully(fd.get(), read_data.data(), read_data.size())); - ASSERT_EQ(0, memcmp(read_data.data(), kBTxtContents.data(), kBTxtContents.size())); +#include "common/test_constants.h" + +static const std::string kATxtContents("abcdefghabcdefgh\n"); +static const std::string kBTxtContents("abcdefgh\n"); + +TEST(ZipTest, ExtractPackageRecursive) { + std::string zip_path = from_testdata_base("ziptest_valid.zip"); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); + + // Extract the whole package into a temp directory. + TemporaryDir td; + ASSERT_NE(nullptr, td.path); + ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr); + + // Make sure all the files are extracted correctly. + std::string path(td.path); + ASSERT_EQ(0, access((path + "/a.txt").c_str(), O_RDONLY)); + ASSERT_EQ(0, access((path + "/b.txt").c_str(), O_RDONLY)); + ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), O_RDONLY)); + ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), O_RDONLY)); + + // The content of the file is the same as expected. + std::string content1; + ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1)); + ASSERT_EQ(kATxtContents, content1); + + std::string content2; + ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2)); + ASSERT_EQ(kBTxtContents, content2); } -TEST(otazip, OpenFromMemory) { - MemMapping map; - std::string zip_path = DATA_PATH + TESTDATA_PATH + "/ziptest_dummy-update.zip"; - ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map)); - // Map an update package into memory and open the archive from there. - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle)); - static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary"; - ZipString binary_path(BINARY_PATH); - ZipEntry binary_entry; - // Make sure the package opens correctly and its entry can be read. - ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry)); - TemporaryFile tmp_binary; - ASSERT_NE(-1, tmp_binary.fd); - ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd)); +TEST(ZipTest, OpenFromMemory) { + MemMapping map; + std::string zip_path = from_testdata_base("ziptest_dummy-update.zip"); + ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map)); + + // Map an update package into memory and open the archive from there. + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle)); + + static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary"; + ZipString binary_path(BINARY_PATH); + ZipEntry binary_entry; + // Make sure the package opens correctly and its entry can be read. + ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry)); + + TemporaryFile tmp_binary; + ASSERT_NE(-1, tmp_binary.fd); + ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd)); + + sysReleaseMap(&map); } |