summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk88
-rw-r--r--tests/common/test_constants.h26
-rw-r--r--tests/component/applypatch_test.cpp610
-rw-r--r--tests/component/edify_test.cpp169
-rw-r--r--tests/component/updater_test.cpp512
-rw-r--r--tests/component/verifier_test.cpp96
-rw-r--r--tests/manual/recovery_test.cpp87
-rw-r--r--tests/testdata/bonus.filebin0 -> 557334 bytes
-rw-r--r--tests/testdata/boot.imgbin0 -> 783655 bytes
-rw-r--r--tests/testdata/otasigned_v1.zip (renamed from tests/testdata/otasigned.zip)bin4009 -> 4009 bytes
-rw-r--r--tests/testdata/otasigned_v2.zip (renamed from tests/testdata/otasigned_f4.zip)bin5195 -> 5195 bytes
-rw-r--r--tests/testdata/otasigned_v3.zip (renamed from tests/testdata/otasigned_sha256.zip)bin5326 -> 5326 bytes
-rw-r--r--tests/testdata/otasigned_v4.zip (renamed from tests/testdata/otasigned_f4_sha256.zip)bin5319 -> 5319 bytes
-rw-r--r--tests/testdata/otasigned_v5.zip (renamed from tests/testdata/otasigned_ecdsa_sha256.zip)bin3085 -> 3085 bytes
-rw-r--r--tests/testdata/recovery-from-boot-with-bonus.pbin0 -> 381615 bytes
-rw-r--r--tests/testdata/recovery-from-boot.pbin0 -> 5404 bytes
-rw-r--r--tests/testdata/recovery.imgbin0 -> 529707 bytes
-rw-r--r--tests/testdata/testkey_v1.pk8 (renamed from tests/testdata/testkey.pk8)bin1217 -> 1217 bytes
-rw-r--r--tests/testdata/testkey_v1.txt (renamed from tests/testdata/test_key_e3.txt)0
-rw-r--r--tests/testdata/testkey_v1.x509.pem (renamed from tests/testdata/testkey.x509.pem)0
-rw-r--r--tests/testdata/testkey_v2.pk8 (renamed from tests/testdata/test_f4.pk8)bin1217 -> 1217 bytes
-rw-r--r--tests/testdata/testkey_v2.txt (renamed from tests/testdata/test_key_f4.txt)0
-rw-r--r--tests/testdata/testkey_v2.x509.pem (renamed from tests/testdata/test_f4.x509.pem)0
l---------tests/testdata/testkey_v3.pk81
-rw-r--r--tests/testdata/testkey_v3.txt1
-rw-r--r--tests/testdata/testkey_v3.x509.pem (renamed from tests/testdata/testkey_sha256.x509.pem)0
l---------tests/testdata/testkey_v4.pk81
-rw-r--r--tests/testdata/testkey_v4.txt1
-rw-r--r--tests/testdata/testkey_v4.x509.pem (renamed from tests/testdata/test_f4_sha256.x509.pem)0
-rw-r--r--tests/testdata/testkey_v5.pk8 (renamed from tests/testdata/testkey_ecdsa.pk8)bin138 -> 138 bytes
-rw-r--r--tests/testdata/testkey_v5.txt (renamed from tests/testdata/test_key_ec.txt)0
-rw-r--r--tests/testdata/testkey_v5.x509.pem (renamed from tests/testdata/testkey_ecdsa.x509.pem)0
-rw-r--r--tests/testdata/ziptest_dummy-update.zipbin0 -> 1090065 bytes
-rw-r--r--tests/testdata/ziptest_valid.zipbin0 -> 758 bytes
-rw-r--r--tests/unit/recovery_test.cpp92
-rw-r--r--tests/unit/sysutil_test.cpp140
-rw-r--r--tests/unit/zip_test.cpp81
37 files changed, 1460 insertions, 445 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index a66991b21..5f6a7ce0c 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -18,52 +18,110 @@ 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
LOCAL_STATIC_LIBRARIES := \
libverifier \
- libminui
+ libminui \
+ libotautil \
+ libziparchive \
+ libutils \
+ libz \
+ libselinux \
+ libbase
+
+LOCAL_SRC_FILES := \
+ unit/asn1_decoder_test.cpp \
+ unit/locale_test.cpp \
+ unit/sysutil_test.cpp \
+ unit/zip_test.cpp
-LOCAL_SRC_FILES := unit/asn1_decoder_test.cpp
-LOCAL_SRC_FILES += unit/recovery_test.cpp
-LOCAL_SRC_FILES += unit/locale_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
+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
LOCAL_SRC_FILES := \
- component/verifier_test.cpp \
- component/applypatch_test.cpp
+ component/applypatch_test.cpp \
+ component/edify_test.cpp \
+ component/updater_test.cpp \
+ component/verifier_test.cpp
LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+tune2fs_static_libraries := \
+ libext2_com_err \
+ libext2_blkid \
+ libext2_quota \
+ libext2_uuid_static \
+ libext2_e2p \
+ libext2fs
+
LOCAL_STATIC_LIBRARIES := \
+ libapplypatch_modes \
libapplypatch \
+ libedify \
libotafault \
- libmtdutils \
- libbase \
+ libupdater \
+ libbootloader_message \
libverifier \
- libcrypto_static \
libminui \
- libminzip \
+ libotautil \
+ libmounts \
+ libfs_mgr \
+ liblog \
+ libselinux \
+ libext4_utils_static \
+ libsparse_static \
+ libcrypto_utils \
+ libcrypto \
libcutils \
libbz \
+ libziparchive \
+ libutils \
libz \
- libc
+ libbase \
+ libtune2fs \
+ $(tune2fs_static_libraries)
-testdata_out_path := $(TARGET_OUT_DATA_NATIVE_TESTS)/recovery
testdata_files := $(call find-subdir-files, testdata/*)
+# 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 $< $@
$(GEN): $(testdata_out_path)/% : $(LOCAL_PATH)/%
$(transform-generated-source)
LOCAL_GENERATED_SOURCES += $(GEN)
+
+# 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)
+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..f6b6922a4 100644
--- a/tests/common/test_constants.h
+++ b/tests/common/test_constants.h
@@ -13,13 +13,27 @@
* 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>
+
+// Zip entries in ziptest_valid.zip.
+static const std::string kATxtContents("abcdefghabcdefgh\n");
+static const std::string kBTxtContents("abcdefgh\n");
+static const std::string kCTxtContents("abcdefghabcdefgh\n");
+static const std::string kDTxtContents("abcdefgh\n");
+
+// echo -n -e "abcdefghabcdefgh\n" | sha1sum
+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;
+}
-#endif
+#endif // _OTA_TEST_CONSTANTS_H
diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp
index b44ddd17c..1a0b19113 100644
--- a/tests/component/applypatch_test.cpp
+++ b/tests/component/applypatch_test.cpp
@@ -23,176 +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();
- patch1->type = VAL_BLOB;
- patch1->size = fc.data.size();
- patch1->data = static_cast<char*>(malloc(fc.data.size()));
- memcpy(patch1->data, fc.data.data(), fc.data.size());
- patches.push_back(patch1);
-
- ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc));
- Value* patch2 = new Value();
- patch2->type = VAL_BLOB;
- patch2->size = fc.st.st_size;
- patch2->data = static_cast<char*>(malloc(fc.data.size()));
- memcpy(patch2->data, fc.data.data(), fc.data.size());
- patches.push_back(patch2);
- }
- static void TearDownTestCase() {
- delete output_f;
- for (auto it = patches.begin(); it != patches.end(); ++it) {
- free((*it)->data);
- 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;
@@ -205,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));
+}
+
TEST_F(ApplyPatchTest, CheckModeSingle) {
- char* s = &old_sha1[0];
- ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s));
+ std::vector<std::string> sha1s = { old_sha1 };
+ ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s));
}
TEST_F(ApplyPatchTest, CheckModeMultiple) {
- char* argv[3] = {
- &bad_sha1_a[0],
- &old_sha1[0],
- &bad_sha1_b[0]
- };
- ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv));
+ 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) {
- char* argv[2] = {
- &bad_sha1_a[0],
- &bad_sha1_b[0]
- };
- ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv));
+ 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);
- char* s = &old_sha1[0];
- ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s));
+ 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);
- char* argv[3] = {
- &bad_sha1_a[0],
- &old_sha1[0],
- &bad_sha1_b[0]
- };
- ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv));
+ 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);
- char* argv[2] = {
- &bad_sha1_a[0],
- &bad_sha1_b[0]
- };
- ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv));
+ 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]);
- char* s = &old_sha1[0];
- ASSERT_EQ(0, applypatch_check(&old_file[0], 1, &s));
+ 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]);
- char* argv[3] = {
- &bad_sha1_a[0],
- &old_sha1[0],
- &bad_sha1_b[0]
- };
- ASSERT_EQ(0, applypatch_check(&old_file[0], 3, argv));
+ 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]);
- char* argv[2] = {
- &bad_sha1_a[0],
- &bad_sha1_b[0]
- };
- ASSERT_NE(0, applypatch_check(&old_file[0], 2, argv));
+ 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<char*> sha1s;
- sha1s.push_back(&bad_sha1_a[0]);
- sha1s.push_back(&old_sha1[0]);
-
- int ap_result = applypatch(&old_file[0],
- "-",
- &new_sha1[0],
- new_size,
- 2,
- sha1s.data(),
- 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,
- 2,
- sha1s.data(),
- 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<char*> sha1s;
- sha1s.push_back(&bad_sha1_a[0]);
- sha1s.push_back(&old_sha1[0]);
- int ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- 2,
- sha1s.data(),
- 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,
- 2,
- sha1s.data(),
- 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<char*> sha1s;
- sha1s.push_back(&bad_sha1_a[0]);
- sha1s.push_back(&old_sha1[0]);
- int ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- 2,
- sha1s.data(),
- 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,
- 2,
- sha1s.data(),
- 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<char*> sha1s;
- sha1s.push_back(&bad_sha1_a[0]);
- sha1s.push_back(&old_sha1[0]);
- int ap_result = applypatch(&old_file[0],
- &output_loc[0],
- &new_sha1[0],
- new_size,
- 2,
- sha1s.data(),
- 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,
- 2,
- sha1s.data(),
- 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/edify_test.cpp b/tests/component/edify_test.cpp
new file mode 100644
index 000000000..287e40cc6
--- /dev/null
+++ b/tests/component/edify_test.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2009 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 <string>
+
+#include <gtest/gtest.h>
+
+#include "edify/expr.h"
+
+static void expect(const char* expr_str, const char* expected) {
+ Expr* e;
+ int error_count = 0;
+ EXPECT_EQ(0, parse_string(expr_str, &e, &error_count));
+ EXPECT_EQ(0, error_count);
+
+ State state(expr_str, nullptr);
+
+ std::string result;
+ bool status = Evaluate(&state, e, &result);
+
+ if (expected == nullptr) {
+ EXPECT_FALSE(status);
+ } else {
+ EXPECT_STREQ(expected, result.c_str());
+ }
+
+}
+
+class EdifyTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ RegisterBuiltins();
+ }
+};
+
+TEST_F(EdifyTest, parsing) {
+ expect("a", "a");
+ expect("\"a\"", "a");
+ expect("\"\\x61\"", "a");
+ expect("# this is a comment\n"
+ " a\n"
+ " \n",
+ "a");
+}
+
+TEST_F(EdifyTest, sequence) {
+ // sequence operator
+ expect("a; b; c", "c");
+}
+
+TEST_F(EdifyTest, concat) {
+ // string concat operator
+ expect("a + b", "ab");
+ expect("a + \n \"b\"", "ab");
+ expect("a + b +\nc\n", "abc");
+
+ // string concat function
+ expect("concat(a, b)", "ab");
+ expect("concat(a,\n \"b\")", "ab");
+ expect("concat(a + b,\nc,\"d\")", "abcd");
+ expect("\"concat\"(a + b,\nc,\"d\")", "abcd");
+}
+
+TEST_F(EdifyTest, logical) {
+ // logical and
+ expect("a && b", "b");
+ expect("a && \"\"", "");
+ expect("\"\" && b", "");
+ expect("\"\" && \"\"", "");
+ expect("\"\" && abort()", ""); // test short-circuiting
+ expect("t && abort()", nullptr);
+
+ // logical or
+ expect("a || b", "a");
+ expect("a || \"\"", "a");
+ expect("\"\" || b", "b");
+ expect("\"\" || \"\"", "");
+ expect("a || abort()", "a"); // test short-circuiting
+ expect("\"\" || abort()", NULL);
+
+ // logical not
+ expect("!a", "");
+ expect("! \"\"", "t");
+ expect("!!a", "t");
+}
+
+TEST_F(EdifyTest, precedence) {
+ // precedence
+ expect("\"\" == \"\" && b", "b");
+ expect("a + b == ab", "t");
+ expect("ab == a + b", "t");
+ expect("a + (b == ab)", "a");
+ expect("(ab == a) + b", "b");
+}
+
+TEST_F(EdifyTest, substring) {
+ // substring function
+ expect("is_substring(cad, abracadabra)", "t");
+ expect("is_substring(abrac, abracadabra)", "t");
+ expect("is_substring(dabra, abracadabra)", "t");
+ expect("is_substring(cad, abracxadabra)", "");
+ expect("is_substring(abrac, axbracadabra)", "");
+ expect("is_substring(dabra, abracadabrxa)", "");
+}
+
+TEST_F(EdifyTest, ifelse) {
+ // ifelse function
+ expect("ifelse(t, yes, no)", "yes");
+ expect("ifelse(!t, yes, no)", "no");
+ expect("ifelse(t, yes, abort())", "yes");
+ expect("ifelse(!t, abort(), no)", "no");
+}
+
+TEST_F(EdifyTest, if_statement) {
+ // if "statements"
+ expect("if t then yes else no endif", "yes");
+ expect("if \"\" then yes else no endif", "no");
+ expect("if \"\" then yes endif", "");
+ expect("if \"\"; t then yes endif", "yes");
+}
+
+TEST_F(EdifyTest, comparison) {
+ // numeric comparisons
+ expect("less_than_int(3, 14)", "t");
+ expect("less_than_int(14, 3)", "");
+ expect("less_than_int(x, 3)", "");
+ expect("less_than_int(3, x)", "");
+ expect("greater_than_int(3, 14)", "");
+ expect("greater_than_int(14, 3)", "t");
+ expect("greater_than_int(x, 3)", "");
+ expect("greater_than_int(3, x)", "");
+}
+
+TEST_F(EdifyTest, big_string) {
+ // big string
+ expect(std::string(8192, 's').c_str(), std::string(8192, 's').c_str());
+}
+
+TEST_F(EdifyTest, unknown_function) {
+ // unknown function
+ const char* script1 = "unknown_function()";
+ Expr* expr;
+ int error_count = 0;
+ EXPECT_EQ(1, parse_string(script1, &expr, &error_count));
+ EXPECT_EQ(1, error_count);
+
+ const char* script2 = "abc; unknown_function()";
+ error_count = 0;
+ EXPECT_EQ(1, parse_string(script2, &expr, &error_count));
+ EXPECT_EQ(1, error_count);
+
+ const char* script3 = "unknown_function1() || yes";
+ error_count = 0;
+ EXPECT_EQ(1, parse_string(script3, &expr, &error_count));
+ EXPECT_EQ(1, error_count);
+}
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp
new file mode 100644
index 000000000..f31f1f82a
--- /dev/null
+++ b/tests/component/updater_test.cpp
@@ -0,0 +1,512 @@
+/*
+ * 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 <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/test_utils.h>
+#include <bootloader_message/bootloader_message.h>
+#include <gtest/gtest.h>
+#include <ziparchive/zip_archive.h>
+
+#include "common/test_constants.h"
+#include "edify/expr.h"
+#include "error_code.h"
+#include "updater/install.h"
+#include "updater/updater.h"
+
+struct selabel_handle *sehandle = nullptr;
+
+static void expect(const char* expected, const char* expr_str, CauseCode cause_code,
+ UpdaterInfo* info = nullptr) {
+ Expr* e;
+ int error_count = 0;
+ ASSERT_EQ(0, parse_string(expr_str, &e, &error_count));
+ ASSERT_EQ(0, error_count);
+
+ State state(expr_str, info);
+
+ std::string result;
+ bool status = Evaluate(&state, e, &result);
+
+ if (expected == nullptr) {
+ ASSERT_FALSE(status);
+ } else {
+ ASSERT_TRUE(status);
+ ASSERT_STREQ(expected, result.c_str());
+ }
+
+ // Error code is set in updater/updater.cpp only, by parsing State.errmsg.
+ ASSERT_EQ(kNoError, state.error_code);
+
+ // Cause code should always be available.
+ ASSERT_EQ(cause_code, state.cause_code);
+}
+
+class UpdaterTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ RegisterBuiltins();
+ RegisterInstallFunctions();
+ }
+};
+
+TEST_F(UpdaterTest, getprop) {
+ expect(android::base::GetProperty("ro.product.device", "").c_str(),
+ "getprop(\"ro.product.device\")",
+ kNoCause);
+
+ expect(android::base::GetProperty("ro.build.fingerprint", "").c_str(),
+ "getprop(\"ro.build.fingerprint\")",
+ kNoCause);
+
+ // getprop() accepts only one parameter.
+ expect(nullptr, "getprop()", kArgsParsingFailure);
+ expect(nullptr, "getprop(\"arg1\", \"arg2\")", kArgsParsingFailure);
+}
+
+TEST_F(UpdaterTest, sha1_check) {
+ // sha1_check(data) returns the SHA-1 of the data.
+ expect("81fe8bfe87576c3ecb22426f8e57847382917acf", "sha1_check(\"abcd\")", kNoCause);
+ expect("da39a3ee5e6b4b0d3255bfef95601890afd80709", "sha1_check(\"\")", kNoCause);
+
+ // sha1_check(data, sha1_hex, [sha1_hex, ...]) returns the matched SHA-1.
+ expect("81fe8bfe87576c3ecb22426f8e57847382917acf",
+ "sha1_check(\"abcd\", \"81fe8bfe87576c3ecb22426f8e57847382917acf\")",
+ kNoCause);
+
+ expect("81fe8bfe87576c3ecb22426f8e57847382917acf",
+ "sha1_check(\"abcd\", \"wrong_sha1\", \"81fe8bfe87576c3ecb22426f8e57847382917acf\")",
+ kNoCause);
+
+ // Or "" if there's no match.
+ expect("",
+ "sha1_check(\"abcd\", \"wrong_sha1\")",
+ kNoCause);
+
+ expect("",
+ "sha1_check(\"abcd\", \"wrong_sha1\", \"wrong_sha2\")",
+ kNoCause);
+
+ // 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(td.path);
+ std::string dst_file = temp_dir + "/aaa/bbb/a.txt";
+ std::string script3("rename(\"" + std::string(temp_file3.path) + "\", \"" + dst_file + "\")");
+ expect(dst_file.c_str(), script3.c_str(), kNoCause);
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink(dst_file.c_str()));
+ ASSERT_EQ(0, rmdir((temp_dir + "/aaa/bbb").c_str()));
+ ASSERT_EQ(0, rmdir((temp_dir + "/aaa").c_str()));
+}
+
+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);
+
+ std::string script2("symlink(\"" + std::string(temp_file1.path) + "\", \"src1\", \"\")");
+ expect(nullptr, script2.c_str(), kSymlinkFailure);
+
+ // symlink failed to remove old src.
+ std::string script3("symlink(\"" + std::string(temp_file1.path) + "\", \"/proc\")");
+ expect(nullptr, script3.c_str(), kSymlinkFailure);
+
+ // symlink can create symlinks.
+ TemporaryFile temp_file;
+ std::string content = "magicvalue";
+ ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
+
+ TemporaryDir td;
+ std::string src1 = std::string(td.path) + "/symlink1";
+ std::string src2 = std::string(td.path) + "/symlink2";
+ std::string script4("symlink(\"" + std::string(temp_file.path) + "\", \"" +
+ src1 + "\", \"" + src2 + "\")");
+ expect("t", script4.c_str(), kNoCause);
+
+ // Verify the created symlinks.
+ struct stat sb;
+ ASSERT_TRUE(lstat(src1.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode));
+ ASSERT_TRUE(lstat(src2.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode));
+
+ // Clean up the leftovers.
+ ASSERT_EQ(0, unlink(src1.c_str()));
+ ASSERT_EQ(0, unlink(src2.c_str()));
+}
+
+TEST_F(UpdaterTest, package_extract_dir) {
+ // package_extract_dir expects 2 arguments.
+ expect(nullptr, "package_extract_dir()", kArgsParsingFailure);
+ expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Need to set up the ziphandle.
+ UpdaterInfo updater_info;
+ updater_info.package_zip = handle;
+
+ // Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", "<dir>").
+ TemporaryDir td;
+ std::string temp_dir(td.path);
+ std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")");
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ // Verify.
+ std::string data;
+ std::string file_c = temp_dir + "/c.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+
+ std::string file_d = temp_dir + "/d.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ // Modify the contents in order to retry. It's expected to be overwritten.
+ ASSERT_TRUE(android::base::WriteStringToFile("random", file_c));
+ ASSERT_TRUE(android::base::WriteStringToFile("random", file_d));
+
+ // Extract again and verify.
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+ ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ // Clean up the temp files under td.
+ ASSERT_EQ(0, unlink(file_c.c_str()));
+ ASSERT_EQ(0, unlink(file_d.c_str()));
+
+ // Extracting "b/" (with slash) should give the same result.
+ script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ ASSERT_TRUE(android::base::ReadFileToString(file_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+ ASSERT_TRUE(android::base::ReadFileToString(file_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ ASSERT_EQ(0, unlink(file_c.c_str()));
+ ASSERT_EQ(0, unlink(file_d.c_str()));
+
+ // Extracting "" is allowed. The entries will carry the path name.
+ script = "package_extract_dir(\"\", \"" + temp_dir + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ std::string file_a = temp_dir + "/a.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_a, &data));
+ ASSERT_EQ(kATxtContents, data);
+ std::string file_b = temp_dir + "/b.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_b, &data));
+ ASSERT_EQ(kBTxtContents, data);
+ std::string file_b_c = temp_dir + "/b/c.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data));
+ ASSERT_EQ(kCTxtContents, data);
+ std::string file_b_d = temp_dir + "/b/d.txt";
+ ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data));
+ ASSERT_EQ(kDTxtContents, data);
+
+ ASSERT_EQ(0, unlink(file_a.c_str()));
+ ASSERT_EQ(0, unlink(file_b.c_str()));
+ ASSERT_EQ(0, unlink(file_b_c.c_str()));
+ ASSERT_EQ(0, unlink(file_b_d.c_str()));
+ ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str()));
+
+ // Extracting non-existent entry should still give "t".
+ script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ // Only relative zip_path is allowed.
+ script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ // Only absolute dest_path is allowed.
+ script = "package_extract_dir(\"b\", \"path\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ CloseArchive(handle);
+}
+
+// TODO: Test extracting to block device.
+TEST_F(UpdaterTest, package_extract_file) {
+ // package_extract_file expects 1 or 2 arguments.
+ expect(nullptr, "package_extract_file()", kArgsParsingFailure);
+ expect(nullptr, "package_extract_file(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ std::string zip_path = from_testdata_base("ziptest_valid.zip");
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
+
+ // Need to set up the ziphandle.
+ UpdaterInfo updater_info;
+ updater_info.package_zip = handle;
+
+ // Two-argument version.
+ TemporaryFile temp_file1;
+ std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")");
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ // Verify the extracted entry.
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
+ ASSERT_EQ(kATxtContents, data);
+
+ // Now extract another entry to the same location, which should overwrite.
+ script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")";
+ expect("t", script.c_str(), kNoCause, &updater_info);
+
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
+ ASSERT_EQ(kBTxtContents, data);
+
+ // Missing zip entry. The two-argument version doesn't abort.
+ script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ // Extract to /dev/full should fail.
+ script = "package_extract_file(\"a.txt\", \"/dev/full\")";
+ expect("", script.c_str(), kNoCause, &updater_info);
+
+ // One-argument version.
+ script = "sha1_check(package_extract_file(\"a.txt\"))";
+ expect(kATxtSha1Sum.c_str(), script.c_str(), kNoCause, &updater_info);
+
+ script = "sha1_check(package_extract_file(\"b.txt\"))";
+ expect(kBTxtSha1Sum.c_str(), script.c_str(), kNoCause, &updater_info);
+
+ // Missing entry. The one-argument version aborts the evaluation.
+ script = "package_extract_file(\"doesntexist\")";
+ expect(nullptr, script.c_str(), kPackageExtractFileFailure, &updater_info);
+
+ CloseArchive(handle);
+}
+
+TEST_F(UpdaterTest, write_value) {
+ // write_value() expects two arguments.
+ expect(nullptr, "write_value()", kArgsParsingFailure);
+ expect(nullptr, "write_value(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "write_value(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // filename cannot be empty.
+ expect(nullptr, "write_value(\"value\", \"\")", kArgsParsingFailure);
+
+ // Write some value to file.
+ TemporaryFile temp_file;
+ std::string value = "magicvalue";
+ std::string script("write_value(\"" + value + "\", \"" + std::string(temp_file.path) + "\")");
+ expect("t", script.c_str(), kNoCause);
+
+ // Verify the content.
+ std::string content;
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
+ ASSERT_EQ(value, content);
+
+ // Allow writing empty string.
+ script = "write_value(\"\", \"" + std::string(temp_file.path) + "\")";
+ expect("t", script.c_str(), kNoCause);
+
+ // Verify the content.
+ ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &content));
+ ASSERT_EQ("", content);
+
+ // It should fail gracefully when write fails.
+ script = "write_value(\"value\", \"/proc/0/file1\")";
+ expect("", script.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, get_stage) {
+ // get_stage() expects one argument.
+ expect(nullptr, "get_stage()", kArgsParsingFailure);
+ expect(nullptr, "get_stage(\"arg1\", \"arg2\")", kArgsParsingFailure);
+ expect(nullptr, "get_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // Set up a local file as BCB.
+ TemporaryFile tf;
+ std::string temp_file(tf.path);
+ bootloader_message boot;
+ strlcpy(boot.stage, "2/3", sizeof(boot.stage));
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err));
+
+ // Can read the stage value.
+ std::string script("get_stage(\"" + temp_file + "\")");
+ expect("2/3", script.c_str(), kNoCause);
+
+ // Bad BCB path.
+ script = "get_stage(\"doesntexist\")";
+ expect("", script.c_str(), kNoCause);
+}
+
+TEST_F(UpdaterTest, set_stage) {
+ // set_stage() expects two arguments.
+ expect(nullptr, "set_stage()", kArgsParsingFailure);
+ expect(nullptr, "set_stage(\"arg1\")", kArgsParsingFailure);
+ expect(nullptr, "set_stage(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure);
+
+ // Set up a local file as BCB.
+ TemporaryFile tf;
+ std::string temp_file(tf.path);
+ bootloader_message boot;
+ strlcpy(boot.command, "command", sizeof(boot.command));
+ strlcpy(boot.stage, "2/3", sizeof(boot.stage));
+ std::string err;
+ ASSERT_TRUE(write_bootloader_message_to(boot, temp_file, &err));
+
+ // Write with set_stage().
+ std::string script("set_stage(\"" + temp_file + "\", \"1/3\")");
+ expect(tf.path, script.c_str(), kNoCause);
+
+ // Verify.
+ bootloader_message boot_verify;
+ ASSERT_TRUE(read_bootloader_message_from(&boot_verify, temp_file, &err));
+
+ // Stage should be updated, with command part untouched.
+ ASSERT_STREQ("1/3", boot_verify.stage);
+ ASSERT_STREQ(boot.command, boot_verify.command);
+
+ // Bad BCB path.
+ script = "set_stage(\"doesntexist\", \"1/3\")";
+ expect("", script.c_str(), kNoCause);
+
+ script = "set_stage(\"/dev/full\", \"1/3\")";
+ expect("", script.c_str(), kNoCause);
+}
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 780ff2816..60a78f5c3 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -29,30 +29,28 @@
#include <openssl/sha.h>
#include <android-base/stringprintf.h>
+#include <ziparchive/zip_archive.h>
#include "common.h"
#include "common/test_constants.h"
-#include "minzip/SysUtil.h"
+#include "otautil/SysUtil.h"
#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 {
void Init() { }
void SetStage(int, int) { }
void SetLocale(const char*) { }
- void SetBackground(Icon icon) { }
- void SetSystemUpdateText(bool security_update) { }
+ void SetBackground(Icon /*icon*/) { }
+ void SetSystemUpdateText(bool /*security_update*/) { }
- void SetProgressType(ProgressType determinate) { }
- void ShowProgress(float portion, float seconds) { }
- void SetProgress(float fraction) { }
+ void SetProgressType(ProgressType /*determinate*/) { }
+ void ShowProgress(float /*portion*/, float /*seconds*/) { }
+ void SetProgress(float /*fraction*/) { }
- void ShowText(bool visible) { }
+ void ShowText(bool /*visible*/) { }
bool IsTextVisible() { return false; }
bool WasTextEverVisible() { return false; }
void Print(const char* fmt, ...) {
@@ -69,9 +67,10 @@ class MockUI : public RecoveryUI {
}
void ShowFile(const char*) { }
- void StartMenu(const char* const * headers, const char* const * items,
- int initial_selection) { }
- int SelectMenu(int sel) { return 0; }
+ void StartMenu(const char* const* /*headers*/,
+ const char* const* /*items*/,
+ int /*initial_selection*/) { }
+ int SelectMenu(int /*sel*/) { return 0; }
void EndMenu() { }
};
@@ -90,34 +89,14 @@ 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";
+ FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
}
for (auto it = ++(args.cbegin()); it != args.cend(); ++it) {
- if (it->substr(it->length() - 3, it->length()) == "256") {
- if (certs.empty()) {
- FAIL() << "May only specify -sha256 after key type\n";
- }
- certs.back().hash_len = SHA256_DIGEST_LENGTH;
- } else {
- std::string public_key_file = android::base::StringPrintf(
- "%s%s%stest_key_%s.txt", DATA_PATH, NATIVE_TEST_PATH,
- TESTDATA_PATH, it->c_str());
- ASSERT_TRUE(load_keys(public_key_file.c_str(), certs));
- certs.back().hash_len = SHA_DIGEST_LENGTH;
- }
- }
- if (certs.empty()) {
- std::string public_key_file = android::base::StringPrintf(
- "%s%s%stest_key_e3.txt", DATA_PATH, NATIVE_TEST_PATH,
- TESTDATA_PATH);
+ std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt");
ASSERT_TRUE(load_keys(public_key_file.c_str(), certs));
- certs.back().hash_len = SHA_DIGEST_LENGTH;
}
}
@@ -142,37 +121,38 @@ TEST_P(VerifierFailureTest, VerifyFailure) {
INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest,
::testing::Values(
- std::vector<std::string>({"otasigned.zip", "e3"}),
- std::vector<std::string>({"otasigned_f4.zip", "f4"}),
- std::vector<std::string>({"otasigned_sha256.zip", "e3", "sha256"}),
- std::vector<std::string>({"otasigned_f4_sha256.zip", "f4", "sha256"}),
- std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "ec", "sha256"})));
+ std::vector<std::string>({"otasigned_v1.zip", "v1"}),
+ std::vector<std::string>({"otasigned_v2.zip", "v2"}),
+ std::vector<std::string>({"otasigned_v3.zip", "v3"}),
+ std::vector<std::string>({"otasigned_v4.zip", "v4"}),
+ std::vector<std::string>({"otasigned_v5.zip", "v5"})));
INSTANTIATE_TEST_CASE_P(MultiKeySuccess, VerifierSuccessTest,
::testing::Values(
- std::vector<std::string>({"otasigned.zip", "f4", "e3"}),
- std::vector<std::string>({"otasigned_f4.zip", "ec", "f4"}),
- std::vector<std::string>({"otasigned_sha256.zip", "ec", "e3", "e3", "sha256"}),
- std::vector<std::string>({"otasigned_f4_sha256.zip", "ec", "sha256", "e3", "f4", "sha256"}),
- std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "f4", "sha256", "e3", "ec", "sha256"})));
+ std::vector<std::string>({"otasigned_v1.zip", "v1", "v2"}),
+ std::vector<std::string>({"otasigned_v2.zip", "v5", "v2"}),
+ std::vector<std::string>({"otasigned_v3.zip", "v5", "v1", "v3"}),
+ std::vector<std::string>({"otasigned_v4.zip", "v5", "v1", "v4"}),
+ std::vector<std::string>({"otasigned_v5.zip", "v4", "v1", "v5"})));
INSTANTIATE_TEST_CASE_P(WrongKey, VerifierFailureTest,
::testing::Values(
- std::vector<std::string>({"otasigned.zip", "f4"}),
- std::vector<std::string>({"otasigned_f4.zip", "e3"}),
- std::vector<std::string>({"otasigned_ecdsa_sha256.zip", "e3", "sha256"})));
+ std::vector<std::string>({"otasigned_v1.zip", "v2"}),
+ std::vector<std::string>({"otasigned_v2.zip", "v1"}),
+ std::vector<std::string>({"otasigned_v3.zip", "v5"}),
+ std::vector<std::string>({"otasigned_v4.zip", "v5"}),
+ std::vector<std::string>({"otasigned_v5.zip", "v3"})));
INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest,
::testing::Values(
- std::vector<std::string>({"otasigned.zip", "e3", "sha256"}),
- std::vector<std::string>({"otasigned_f4.zip", "f4", "sha256"}),
- std::vector<std::string>({"otasigned_sha256.zip"}),
- std::vector<std::string>({"otasigned_f4_sha256.zip", "f4"}),
- std::vector<std::string>({"otasigned_ecdsa_sha256.zip"})));
+ std::vector<std::string>({"otasigned_v1.zip", "v3"}),
+ std::vector<std::string>({"otasigned_v2.zip", "v4"}),
+ std::vector<std::string>({"otasigned_v3.zip", "v1"}),
+ std::vector<std::string>({"otasigned_v4.zip", "v2"})));
INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest,
::testing::Values(
- std::vector<std::string>({"random.zip"}),
- std::vector<std::string>({"fake-eocd.zip"}),
- std::vector<std::string>({"alter-metadata.zip"}),
- std::vector<std::string>({"alter-footer.zip"})));
+ std::vector<std::string>({"random.zip", "v1"}),
+ std::vector<std::string>({"fake-eocd.zip", "v1"}),
+ std::vector<std::string>({"alter-metadata.zip", "v1"}),
+ std::vector<std::string>({"alter-footer.zip", "v1"})));
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
new file mode 100644
index 000000000..918ef8ac5
--- /dev/null
+++ b/tests/testdata/bonus.file
Binary files differ
diff --git a/tests/testdata/boot.img b/tests/testdata/boot.img
new file mode 100644
index 000000000..dd4897510
--- /dev/null
+++ b/tests/testdata/boot.img
Binary files differ
diff --git a/tests/testdata/otasigned.zip b/tests/testdata/otasigned_v1.zip
index a6bc53e41..a6bc53e41 100644
--- a/tests/testdata/otasigned.zip
+++ b/tests/testdata/otasigned_v1.zip
Binary files differ
diff --git a/tests/testdata/otasigned_f4.zip b/tests/testdata/otasigned_v2.zip
index dd1e4dd40..dd1e4dd40 100644
--- a/tests/testdata/otasigned_f4.zip
+++ b/tests/testdata/otasigned_v2.zip
Binary files differ
diff --git a/tests/testdata/otasigned_sha256.zip b/tests/testdata/otasigned_v3.zip
index 0ed4409b3..0ed4409b3 100644
--- a/tests/testdata/otasigned_sha256.zip
+++ b/tests/testdata/otasigned_v3.zip
Binary files differ
diff --git a/tests/testdata/otasigned_f4_sha256.zip b/tests/testdata/otasigned_v4.zip
index 3af408c40..3af408c40 100644
--- a/tests/testdata/otasigned_f4_sha256.zip
+++ b/tests/testdata/otasigned_v4.zip
Binary files differ
diff --git a/tests/testdata/otasigned_ecdsa_sha256.zip b/tests/testdata/otasigned_v5.zip
index 999fcdd0f..999fcdd0f 100644
--- a/tests/testdata/otasigned_ecdsa_sha256.zip
+++ b/tests/testdata/otasigned_v5.zip
Binary files differ
diff --git a/tests/testdata/recovery-from-boot-with-bonus.p b/tests/testdata/recovery-from-boot-with-bonus.p
new file mode 100644
index 000000000..08b6f55e4
--- /dev/null
+++ b/tests/testdata/recovery-from-boot-with-bonus.p
Binary files differ
diff --git a/tests/testdata/recovery-from-boot.p b/tests/testdata/recovery-from-boot.p
new file mode 100644
index 000000000..06f6c299f
--- /dev/null
+++ b/tests/testdata/recovery-from-boot.p
Binary files differ
diff --git a/tests/testdata/recovery.img b/tests/testdata/recovery.img
new file mode 100644
index 000000000..b862e6f0c
--- /dev/null
+++ b/tests/testdata/recovery.img
Binary files differ
diff --git a/tests/testdata/testkey.pk8 b/tests/testdata/testkey_v1.pk8
index 586c1bd5c..586c1bd5c 100644
--- a/tests/testdata/testkey.pk8
+++ b/tests/testdata/testkey_v1.pk8
Binary files differ
diff --git a/tests/testdata/test_key_e3.txt b/tests/testdata/testkey_v1.txt
index 53f5297bd..53f5297bd 100644
--- a/tests/testdata/test_key_e3.txt
+++ b/tests/testdata/testkey_v1.txt
diff --git a/tests/testdata/testkey.x509.pem b/tests/testdata/testkey_v1.x509.pem
index e242d83e2..e242d83e2 100644
--- a/tests/testdata/testkey.x509.pem
+++ b/tests/testdata/testkey_v1.x509.pem
diff --git a/tests/testdata/test_f4.pk8 b/tests/testdata/testkey_v2.pk8
index 3052613c5..3052613c5 100644
--- a/tests/testdata/test_f4.pk8
+++ b/tests/testdata/testkey_v2.pk8
Binary files differ
diff --git a/tests/testdata/test_key_f4.txt b/tests/testdata/testkey_v2.txt
index 54ddbbad1..54ddbbad1 100644
--- a/tests/testdata/test_key_f4.txt
+++ b/tests/testdata/testkey_v2.txt
diff --git a/tests/testdata/test_f4.x509.pem b/tests/testdata/testkey_v2.x509.pem
index 814abcf99..814abcf99 100644
--- a/tests/testdata/test_f4.x509.pem
+++ b/tests/testdata/testkey_v2.x509.pem
diff --git a/tests/testdata/testkey_v3.pk8 b/tests/testdata/testkey_v3.pk8
new file mode 120000
index 000000000..18ecf9815
--- /dev/null
+++ b/tests/testdata/testkey_v3.pk8
@@ -0,0 +1 @@
+testkey_v1.pk8 \ No newline at end of file
diff --git a/tests/testdata/testkey_v3.txt b/tests/testdata/testkey_v3.txt
new file mode 100644
index 000000000..3208571a5
--- /dev/null
+++ b/tests/testdata/testkey_v3.txt
@@ -0,0 +1 @@
+v3 {64,0xc926ad21,{1795090719,2141396315,950055447,2581568430,4268923165,1920809988,546586521,3498997798,1776797858,3740060814,1805317999,1429410244,129622599,1422441418,1783893377,1222374759,2563319927,323993566,28517732,609753416,1826472888,215237850,4261642700,4049082591,3228462402,774857746,154822455,2497198897,2758199418,3019015328,2794777644,87251430,2534927978,120774784,571297800,3695899472,2479925187,3811625450,3401832990,2394869647,3267246207,950095497,555058928,414729973,1136544882,3044590084,465547824,4058146728,2731796054,1689838846,3890756939,1048029507,895090649,247140249,178744550,3547885223,3165179243,109881576,3944604415,1044303212,3772373029,2985150306,3737520932,3599964420},{3437017481,3784475129,2800224972,3086222688,251333580,2131931323,512774938,325948880,2657486437,2102694287,3820568226,792812816,1026422502,2053275343,2800889200,3113586810,165549746,4273519969,4065247892,1902789247,772932719,3941848426,3652744109,216871947,3164400649,1942378755,3996765851,1055777370,964047799,629391717,2232744317,3910558992,191868569,2758883837,3682816752,2997714732,2702529250,3570700455,3776873832,3924067546,3555689545,2758825434,1323144535,61311905,1997411085,376844204,213777604,4077323584,9135381,1625809335,2804742137,2952293945,1117190829,4237312782,1825108855,3013147971,1111251351,2568837572,1684324211,2520978805,367251975,810756730,2353784344,1175080310}}
diff --git a/tests/testdata/testkey_sha256.x509.pem b/tests/testdata/testkey_v3.x509.pem
index 002ce8968..002ce8968 100644
--- a/tests/testdata/testkey_sha256.x509.pem
+++ b/tests/testdata/testkey_v3.x509.pem
diff --git a/tests/testdata/testkey_v4.pk8 b/tests/testdata/testkey_v4.pk8
new file mode 120000
index 000000000..683b9a3f1
--- /dev/null
+++ b/tests/testdata/testkey_v4.pk8
@@ -0,0 +1 @@
+testkey_v2.pk8 \ No newline at end of file
diff --git a/tests/testdata/testkey_v4.txt b/tests/testdata/testkey_v4.txt
new file mode 100644
index 000000000..532cbd51a
--- /dev/null
+++ b/tests/testdata/testkey_v4.txt
@@ -0,0 +1 @@
+v4 {64,0xc9bd1f21,{293133087,3210546773,865313125,250921607,3158780490,943703457,1242806226,2986289859,2942743769,2457906415,2719374299,1783459420,149579627,3081531591,3440738617,2788543742,2758457512,1146764939,3699497403,2446203424,1744968926,1159130537,2370028300,3978231572,3392699980,1487782451,1180150567,2841334302,3753960204,961373345,3333628321,748825784,2978557276,1566596926,1613056060,2600292737,1847226629,50398611,1890374404,2878700735,2286201787,1401186359,619285059,731930817,2340993166,1156490245,2992241729,151498140,318782170,3480838990,2100383433,4223552555,3628927011,4247846280,1759029513,4215632601,2719154626,3490334597,1751299340,3487864726,3668753795,4217506054,3748782284,3150295088},{1772626313,445326068,3477676155,1758201194,2986784722,491035581,3922936562,702212696,2979856666,3324974564,2488428922,3056318590,1626954946,664714029,398585816,3964097931,3356701905,2298377729,2040082097,3025491477,539143308,3348777868,2995302452,3602465520,212480763,2691021393,1307177300,704008044,2031136606,1054106474,3838318865,2441343869,1477566916,700949900,2534790355,3353533667,336163563,4106790558,2701448228,1571536379,1103842411,3623110423,1635278839,1577828979,910322800,715583630,138128831,1017877531,2289162787,447994798,1897243165,4121561445,4150719842,2131821093,2262395396,3305771534,980753571,3256525190,3128121808,1072869975,3507939515,4229109952,118381341,2209831334}}
diff --git a/tests/testdata/test_f4_sha256.x509.pem b/tests/testdata/testkey_v4.x509.pem
index 9d5376b45..9d5376b45 100644
--- a/tests/testdata/test_f4_sha256.x509.pem
+++ b/tests/testdata/testkey_v4.x509.pem
diff --git a/tests/testdata/testkey_ecdsa.pk8 b/tests/testdata/testkey_v5.pk8
index 9a521c8cf..9a521c8cf 100644
--- a/tests/testdata/testkey_ecdsa.pk8
+++ b/tests/testdata/testkey_v5.pk8
Binary files differ
diff --git a/tests/testdata/test_key_ec.txt b/tests/testdata/testkey_v5.txt
index 72b4395d9..72b4395d9 100644
--- a/tests/testdata/test_key_ec.txt
+++ b/tests/testdata/testkey_v5.txt
diff --git a/tests/testdata/testkey_ecdsa.x509.pem b/tests/testdata/testkey_v5.x509.pem
index b12283645..b12283645 100644
--- a/tests/testdata/testkey_ecdsa.x509.pem
+++ b/tests/testdata/testkey_v5.x509.pem
diff --git a/tests/testdata/ziptest_dummy-update.zip b/tests/testdata/ziptest_dummy-update.zip
new file mode 100644
index 000000000..6976bf155
--- /dev/null
+++ b/tests/testdata/ziptest_dummy-update.zip
Binary files differ
diff --git a/tests/testdata/ziptest_valid.zip b/tests/testdata/ziptest_valid.zip
new file mode 100644
index 000000000..9e7cb7800
--- /dev/null
+++ b/tests/testdata/ziptest_valid.zip
Binary files differ
diff --git a/tests/unit/recovery_test.cpp b/tests/unit/recovery_test.cpp
deleted file mode 100644
index f397f258e..000000000
--- a/tests/unit/recovery_test.cpp
+++ /dev/null
@@ -1,92 +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 <log/logger.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
new file mode 100644
index 000000000..ef0ee4c1d
--- /dev/null
+++ b/tests/unit/zip_test.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <otautil/SysUtil.h>
+#include <otautil/ZipUtil.h>
+#include <ziparchive/zip_archive.h>
+
+#include "common/test_constants.h"
+
+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(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);
+}
+