diff options
Diffstat (limited to '')
23 files changed, 1031 insertions, 369 deletions
diff --git a/tests/Android.bp b/tests/Android.bp index ab4d31da2..898ed7d60 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -29,6 +29,7 @@ cc_defaults { "libcutils", "liblog", "libpng", + "libprocessgroup", "libselinux", "libz", "libziparchive", @@ -45,7 +46,7 @@ cc_defaults { static_libs: [ "libutils", ], - } + }, }, } @@ -92,13 +93,16 @@ librecovery_static_libs = [ "libhidl-gen-utils", "libhidlbase", "libhidltransport", - "libhwbinder", + "libhwbinder_noltopgo", + "libbinderthreadstate", + "liblp", "libvndksupport", "libtinyxml2", ] cc_test { name: "recovery_unit_test", + isolated: true, defaults: [ "recovery_test_defaults", @@ -117,7 +121,6 @@ cc_test { "libotautil", "libupdater", "libgtest_prod", - "libBionicGtestMain", ], data: ["testdata/*"], @@ -125,6 +128,7 @@ cc_test { cc_test { name: "recovery_manual_test", + isolated: true, defaults: [ "recovery_test_defaults", @@ -135,14 +139,11 @@ cc_test { srcs: [ "manual/recovery_test.cpp", ], - - static_libs: [ - "libBionicGtestMain", - ], } cc_test { name: "recovery_component_test", + isolated: true, defaults: [ "recovery_test_defaults", @@ -159,7 +160,6 @@ cc_test { "libupdater", "libupdate_verifier", "libprotobuf-cpp-lite", - "libBionicGtestMain", ], data: [ @@ -170,6 +170,7 @@ cc_test { cc_test_host { name: "recovery_host_test", + isolated: true, defaults: [ "recovery_test_defaults", @@ -193,7 +194,6 @@ cc_test_host { "libdivsufsort64", "libdivsufsort", "libz", - "libBionicGtestMain", ], data: ["testdata/*"], diff --git a/tests/component/applypatch_modes_test.cpp b/tests/component/applypatch_modes_test.cpp index ce01f4fd5..08414b796 100644 --- a/tests/component/applypatch_modes_test.cpp +++ b/tests/component/applypatch_modes_test.cpp @@ -23,7 +23,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <bsdiff/bsdiff.h> #include <gtest/gtest.h> #include <openssl/sha.h> diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp index 6cc59a495..b005d199c 100644 --- a/tests/component/bootloader_message_test.cpp +++ b/tests/component/bootloader_message_test.cpp @@ -17,8 +17,8 @@ #include <string> #include <vector> +#include <android-base/file.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <bootloader_message/bootloader_message.h> #include <gtest/gtest.h> diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index cb4868a4a..e76ccbdfb 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -25,7 +25,6 @@ #include <android-base/memory.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <applypatch/imgdiff.h> #include <applypatch/imgdiff_image.h> #include <applypatch/imgpatch.h> diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 08b429000..969805b42 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -20,13 +20,13 @@ #include <unistd.h> #include <algorithm> +#include <random> #include <string> #include <vector> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> #include <vintf/VintfObjectRecovery.h> #include <ziparchive/zip_archive.h> @@ -36,15 +36,23 @@ #include "otautil/paths.h" #include "private/install.h" -TEST(InstallTest, verify_package_compatibility_no_entry) { - TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); +static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd, + int compression_type) { + FILE* zip_file = fdopen(fd, "w"); ZipWriter writer(zip_file); - // The archive must have something to be opened correctly. - ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); - ASSERT_EQ(0, writer.FinishEntry()); + for (const auto& [name, content] : file_map) { + ASSERT_EQ(0, writer.StartEntry(name.c_str(), compression_type)); + ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer.FinishEntry()); + } ASSERT_EQ(0, writer.Finish()); ASSERT_EQ(0, fclose(zip_file)); +} + +TEST(InstallTest, verify_package_compatibility_no_entry) { + TemporaryFile temp_file; + // The archive must have something to be opened correctly. + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); // Doesn't contain compatibility zip entry. ZipArchiveHandle zip; @@ -55,12 +63,7 @@ TEST(InstallTest, verify_package_compatibility_no_entry) { TEST(InstallTest, verify_package_compatibility_invalid_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("compatibility.zip", 0)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "compatibility.zip", "" } }, temp_file.release(), kCompressStored); // Empty compatibility zip entry. ZipArchiveHandle zip; @@ -71,77 +74,77 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) { TEST(InstallTest, read_metadata_from_package_smoke) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); - const std::string content("abcdefg"); - ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + const std::string content("abc=defg"); + BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file.release(), + kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::string metadata; - ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); - ASSERT_EQ(content, metadata); + std::map<std::string, std::string> metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ("defg", metadata["abc"]); CloseArchive(zip); TemporaryFile temp_file2; - FILE* zip_file2 = fdopen(temp_file2.release(), "w"); - ZipWriter writer2(zip_file2); - ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated)); - ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size())); - ASSERT_EQ(0, writer2.FinishEntry()); - ASSERT_EQ(0, writer2.Finish()); - ASSERT_EQ(0, fclose(zip_file2)); + BuildZipArchive({ { "META-INF/com/android/metadata", content } }, temp_file2.release(), + kCompressDeflated); ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip)); metadata.clear(); - ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); - ASSERT_EQ(content, metadata); + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ("defg", metadata["abc"]); CloseArchive(zip); } TEST(InstallTest, read_metadata_from_package_no_entry) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); - std::string metadata; - ASSERT_FALSE(read_metadata_from_package(zip, &metadata)); + std::map<std::string, std::string> metadata; + ASSERT_FALSE(ReadMetadataFromPackage(zip, &metadata)); CloseArchive(zip); } +TEST(InstallTest, read_wipe_ab_partition_list) { + std::vector<std::string> partition_list = { + "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", + "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", + "/dev/block/bootdevice/by-name/userdata", "# Wipe the boot partitions last", + "/dev/block/bootdevice/by-name/boot_a", "/dev/block/bootdevice/by-name/boot_b", + }; + TemporaryFile temp_file; + BuildZipArchive({ { "recovery.wipe", android::base::Join(partition_list, '\n') } }, + temp_file.release(), kCompressDeflated); + std::string wipe_package; + ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &wipe_package)); + + auto package = Package::CreateMemoryPackage( + std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr); + + auto read_partition_list = GetWipePartitionList(package.get()); + std::vector<std::string> expected = { + "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b", + "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b", + "/dev/block/bootdevice/by-name/userdata", "/dev/block/bootdevice/by-name/boot_a", + "/dev/block/bootdevice/by-name/boot_b", + }; + ASSERT_EQ(expected, read_partition_list); +} + TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); - ZipWriter compatibility_zip_writer(compatibility_zip); - ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); std::string malformed_xml = "malformed"; - ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(malformed_xml.data(), malformed_xml.size())); - ASSERT_EQ(0, compatibility_zip_writer.FinishEntry()); - ASSERT_EQ(0, compatibility_zip_writer.Finish()); - ASSERT_EQ(0, fclose(compatibility_zip)); + BuildZipArchive({ { "system_manifest.xml", malformed_xml } }, compatibility_zip_file.release(), + kCompressDeflated); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; ASSERT_TRUE( android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - ASSERT_EQ(0, - writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), + kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -166,27 +169,15 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml ASSERT_TRUE( android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); TemporaryFile compatibility_zip_file; - FILE* compatibility_zip = fdopen(compatibility_zip_file.release(), "w"); - ZipWriter compatibility_zip_writer(compatibility_zip); - ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); - ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(system_manifest_xml_content.data(), - system_manifest_xml_content.size())); - ASSERT_EQ(0, compatibility_zip_writer.FinishEntry()); - ASSERT_EQ(0, compatibility_zip_writer.Finish()); - ASSERT_EQ(0, fclose(compatibility_zip)); + BuildZipArchive({ { "system_manifest.xml", system_manifest_xml_content } }, + compatibility_zip_file.release(), kCompressDeflated); TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); std::string compatibility_zip_content; ASSERT_TRUE( android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); - ASSERT_EQ(0, - writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "compatibility.zip", compatibility_zip_content } }, temp_file.release(), + kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -202,13 +193,8 @@ TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml TEST(InstallTest, SetUpNonAbUpdateCommands) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; - ASSERT_EQ(0, writer.StartEntry(UPDATE_BINARY_NAME, kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { UPDATE_BINARY_NAME, "" } }, temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -246,13 +232,8 @@ TEST(InstallTest, SetUpNonAbUpdateCommands) { TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); // The archive must have something to be opened correctly. - ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + BuildZipArchive({ { "dummy_entry", "" } }, temp_file.release(), kCompressStored); // Missing update binary. ZipArchiveHandle zip; @@ -268,16 +249,8 @@ TEST(InstallTest, SetUpNonAbUpdateCommands_MissingUpdateBinary) { static void VerifyAbUpdateCommands(const std::string& serialno, bool success = true) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.StartEntry("payload_properties.txt", kCompressStored)); + const std::string properties = "some_properties"; - ASSERT_EQ(0, writer.WriteBytes(properties.data(), properties.size())); - ASSERT_EQ(0, writer.FinishEntry()); - // A metadata entry is mandatory. - ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); std::string device = android::base::GetProperty("ro.product.device", ""); ASSERT_NE("", device); std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); @@ -288,21 +261,27 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t if (!serialno.empty()) { meta.push_back("serialno=" + serialno); } - std::string metadata = android::base::Join(meta, "\n"); - ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + std::string metadata_string = android::base::Join(meta, "\n"); + + BuildZipArchive({ { "payload.bin", "" }, + { "payload_properties.txt", properties }, + { "META-INF/com/android/metadata", metadata_string } }, + temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); ZipString payload_name("payload.bin"); ZipEntry payload_entry; ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); - int status_fd = 10; - std::string package = "/path/to/update.zip"; - std::vector<std::string> cmd; + + std::map<std::string, std::string> metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); if (success) { + ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB)); + + int status_fd = 10; + std::string package = "/path/to/update.zip"; + std::vector<std::string> cmd; ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); ASSERT_EQ(5U, cmd.size()); ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]); @@ -311,7 +290,7 @@ static void VerifyAbUpdateCommands(const std::string& serialno, bool success = t ASSERT_EQ("--headers=" + properties, cmd[3]); ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); } else { - ASSERT_EQ(INSTALL_ERROR, SetUpAbUpdateCommands(package, zip, status_fd, &cmd)); + ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB)); } CloseArchive(zip); } @@ -323,13 +302,7 @@ TEST(InstallTest, SetUpAbUpdateCommands) { TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { TemporaryFile temp_file; - FILE* zip_file = fdopen(temp_file.release(), "w"); - ZipWriter writer(zip_file); - // Missing payload_properties.txt. - ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); - ASSERT_EQ(0, writer.FinishEntry()); - // A metadata entry is mandatory. - ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); + std::string device = android::base::GetProperty("ro.product.device", ""); ASSERT_NE("", device); std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); @@ -339,10 +312,13 @@ TEST(InstallTest, SetUpAbUpdateCommands_MissingPayloadPropertiesTxt) { "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, }, "\n"); - ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); - ASSERT_EQ(0, writer.FinishEntry()); - ASSERT_EQ(0, writer.Finish()); - ASSERT_EQ(0, fclose(zip_file)); + + BuildZipArchive( + { + { "payload.bin", "" }, + { "META-INF/com/android/metadata", metadata }, + }, + temp_file.release(), kCompressStored); ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); @@ -381,3 +357,241 @@ TEST(InstallTest, SetUpAbUpdateCommands_MultipleSerialnos) { // String with the matching serialno should pass the verification. VerifyAbUpdateCommands(long_serialno); } + +static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type, + int exptected_result) { + TemporaryFile temp_file; + BuildZipArchive( + { + { "META-INF/com/android/metadata", metadata_string }, + }, + temp_file.release(), kCompressStored); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + + std::map<std::string, std::string> metadata; + ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata)); + ASSERT_EQ(exptected_result, CheckPackageMetadata(metadata, ota_type)); + CloseArchive(zip); +} + +TEST(InstallTest, CheckPackageMetadata_ota_type) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // ota-type must be present + std::string metadata = android::base::Join( + std::vector<std::string>{ + "pre-device=" + device, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // Checks if ota-type matches + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_device_type) { + // device type can not be empty + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + // device type mismatches + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=dummy_device_type", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // Serial number doesn't need to exist + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); + + // Serial number mismatches + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=dummy_serial", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + serialno, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); +} + +TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string serialno = android::base::GetProperty("ro.serialno", ""); + ASSERT_NE("", serialno); + + std::vector<std::string> serial_numbers; + // Creates a dummy serial number string. + for (char c = 'a'; c <= 'z'; c++) { + serial_numbers.emplace_back(serialno.size(), c); + } + + // No matched serialno found. + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + android::base::Join(serial_numbers, '|'), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR); + + serial_numbers.emplace_back(serialno); + std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine()); + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=BRICK", + "pre-device=" + device, + "serialno=" + android::base::Join(serial_numbers, '|'), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::BRICK, 0); +} + +TEST(InstallTest, CheckPackageMetadata_ab_build_version) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string build_version = android::base::GetProperty("ro.build.version.incremental", ""); + ASSERT_NE("", build_version); + + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build-incremental=" + build_version, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build-incremental=dummy_build", + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_NE("", finger_print); + + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=" + finger_print, + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "pre-build=dummy_build_fingerprint", + "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()), + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); +} + +TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) { + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + + // post timestamp is required for upgrade. + std::string metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // post timestamp should be larger than the timestamp on device. + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + // fingerprint is required for downgrade + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + "ota-downgrade=yes", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR); + + std::string finger_print = android::base::GetProperty("ro.build.fingerprint", ""); + ASSERT_NE("", finger_print); + + metadata = android::base::Join( + std::vector<std::string>{ + "ota-type=AB", + "pre-device=" + device, + "post-timestamp=0", + "pre-build=" + finger_print, + "ota-downgrade=yes", + }, + "\n"); + test_check_package_metadata(metadata, OtaType::AB, 0); +} diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp index 54329db22..d7fdb8fa0 100644 --- a/tests/component/resources_test.cpp +++ b/tests/component/resources_test.cpp @@ -101,7 +101,7 @@ TEST_P(ResourcesTest, ValidateLocale) { EXPECT_LT(0, len) << "Locale string should be non-empty."; EXPECT_NE(0, row[5]) << "Locale string is missing."; - ASSERT_GT(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; + ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file."; char* loc = reinterpret_cast<char*>(&row[5]); if (matches_locale(loc, kLocale.c_str())) { EXPECT_TRUE(android::base::StartsWith(loc, kLocale)); diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index b7109fcc2..d5e074c63 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -21,7 +21,6 @@ #include <android-base/file.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> #include "fuse_sideload.h" diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 55baca2e3..e97d589a6 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -26,7 +26,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> -#include <android-base/test_utils.h> #include <android-base/unique_fd.h> #include <bootloader_message/bootloader_message.h> #include <gtest/gtest.h> diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index a97071635..e27e58c22 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -16,6 +16,7 @@ #include <update_verifier/update_verifier.h> +#include <functional> #include <string> #include <unordered_map> #include <vector> @@ -23,31 +24,54 @@ #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <google/protobuf/repeated_field.h> #include <gtest/gtest.h> #include "care_map.pb.h" +using namespace std::string_literals; + class UpdateVerifierTest : public ::testing::Test { protected: void SetUp() override { std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); verity_supported = android::base::EqualsIgnoreCase(verity_mode, "enforcing"); + + care_map_prefix_ = care_map_dir_.path + "/care_map"s; + care_map_pb_ = care_map_dir_.path + "/care_map.pb"s; + care_map_txt_ = care_map_dir_.path + "/care_map.txt"s; + // Overrides the the care_map_prefix. + verifier_.set_care_map_prefix(care_map_prefix_); + + property_id_ = "ro.build.fingerprint"; + fingerprint_ = android::base::GetProperty(property_id_, ""); + // Overrides the property_reader if we cannot read the given property on the device. + if (fingerprint_.empty()) { + fingerprint_ = "mock_fingerprint"; + verifier_.set_property_reader([](const std::string& /* id */) { return "mock_fingerprint"; }); + } + } + + void TearDown() override { + unlink(care_map_pb_.c_str()); + unlink(care_map_txt_.c_str()); } // Returns a serialized string of the proto3 message according to the given partition info. std::string ConstructProto( std::vector<std::unordered_map<std::string, std::string>>& partitions) { - UpdateVerifier::CareMap result; + recovery_update_verifier::CareMap result; for (const auto& partition : partitions) { - UpdateVerifier::CareMap::PartitionInfo info; + recovery_update_verifier::CareMap::PartitionInfo info; if (partition.find("name") != partition.end()) { info.set_name(partition.at("name")); } if (partition.find("ranges") != partition.end()) { info.set_ranges(partition.at("ranges")); } + if (partition.find("id") != partition.end()) { + info.set_id(partition.at("id")); + } if (partition.find("fingerprint") != partition.end()) { info.set_fingerprint(partition.at("fingerprint")); } @@ -59,15 +83,22 @@ class UpdateVerifierTest : public ::testing::Test { } bool verity_supported; - TemporaryFile care_map_file; + UpdateVerifier verifier_; + + TemporaryDir care_map_dir_; + std::string care_map_prefix_; + std::string care_map_pb_; + std::string care_map_txt_; + + std::string property_id_; + std::string fingerprint_; }; TEST_F(UpdateVerifierTest, verify_image_no_care_map) { - // Non-existing care_map is allowed. - ASSERT_TRUE(verify_image("/doesntexist")); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_smoke) { +TEST_F(UpdateVerifierTest, verify_image_text_format) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -75,52 +106,58 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { } std::string content = "system\n2,0,1"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); - - // Leading and trailing newlines should be accepted. - ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_)); + // CareMap in text format is no longer supported. + ASSERT_FALSE(verifier_.ParseCareMap()); } TEST_F(UpdateVerifierTest, verify_image_empty_care_map) { - ASSERT_FALSE(verify_image(care_map_file.path)); -} - -TEST_F(UpdateVerifierTest, verify_image_wrong_lines) { - // The care map file can have only 2 / 4 / 6 lines. - ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); - - ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; return; } - std::string content = "system\n2,1,0"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + std::vector<std::unordered_map<std::string, std::string>> partitions = { + { + { "name", "system" }, + { "ranges", "2,0,1" }, + { "id", property_id_ }, + { "fingerprint", fingerprint_ }, + }, + }; + + std::string proto = ConstructProto(partitions); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_TRUE(verifier_.ParseCareMap()); + ASSERT_TRUE(verifier_.VerifyPartitions()); } -TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; return; } - std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); + std::vector<std::unordered_map<std::string, std::string>> partitions = { + { + { "ranges", "2,0,1" }, + { "id", property_id_ }, + { "fingerprint", fingerprint_ }, + }, + }; + + std::string proto = ConstructProto(partitions); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -128,15 +165,20 @@ TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { } std::vector<std::unordered_map<std::string, std::string>> partitions = { - { { "name", "system" }, { "ranges", "2,0,1" } }, + { + { "name", "system" }, + { "ranges", "3,0,1" }, + { "id", property_id_ }, + { "fingerprint", fingerprint_ }, + }, }; std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path)); - ASSERT_TRUE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_empty_fingerprint) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -144,15 +186,18 @@ TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_missing_name) { } std::vector<std::unordered_map<std::string, std::string>> partitions = { - { { "ranges", "2,0,1" } }, + { + { "name", "system" }, + { "ranges", "2,0,1" }, + }, }; std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { +TEST_F(UpdateVerifierTest, verify_image_protobuf_fingerprint_mismatch) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -160,10 +205,15 @@ TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_bad_ranges) { } std::vector<std::unordered_map<std::string, std::string>> partitions = { - { { "name", "system" }, { "ranges", "3,0,1" } }, + { + { "name", "system" }, + { "ranges", "2,0,1" }, + { "id", property_id_ }, + { "fingerprint", "unsupported_fingerprint" }, + }, }; std::string proto = ConstructProto(partitions); - ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_file.path)); - ASSERT_FALSE(verify_image(care_map_file.path)); + ASSERT_TRUE(android::base::WriteStringToFile(proto, care_map_pb_)); + ASSERT_FALSE(verifier_.ParseCareMap()); } diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 24c63e776..a0a7b66ab 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -23,6 +23,7 @@ #include <algorithm> #include <memory> #include <string> +#include <string_view> #include <unordered_map> #include <vector> @@ -32,7 +33,6 @@ #include <android-base/properties.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <android-base/test_utils.h> #include <bootloader_message/bootloader_message.h> #include <brotli/encode.h> #include <bsdiff/bsdiff.h> @@ -134,9 +134,9 @@ static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries, CloseArchive(handle); } -static std::string get_sha1(const std::string& content) { +static std::string GetSha1(std::string_view content) { uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(reinterpret_cast<const uint8_t*>(content.c_str()), content.size(), digest); + SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest); return print_sha1(digest); } @@ -187,7 +187,7 @@ class UpdaterTest : public ::testing::Test { // Clear partition updated marker if any. std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + get_sha1(image_temp_file_.path) + ".UPDATED"; + updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); } @@ -223,14 +223,14 @@ TEST_F(UpdaterTest, patch_partition_check) { std::string source_content; ASSERT_TRUE(android::base::ReadFileToString(source_file, &source_content)); size_t source_size = source_content.size(); - std::string source_hash = get_sha1(source_content); + std::string source_hash = GetSha1(source_content); Partition source(source_file, source_size, source_hash); std::string target_file = from_testdata_base("recovery.img"); std::string target_content; ASSERT_TRUE(android::base::ReadFileToString(target_file, &target_content)); size_t target_size = target_content.size(); - std::string target_hash = get_sha1(target_content); + std::string target_hash = GetSha1(target_content); Partition target(target_file, target_size, target_hash); // One argument is not valid. @@ -619,54 +619,100 @@ TEST_F(UpdaterTest, block_image_update_parsing_error) { RunBlockImageUpdate(false, entries, image_file_, "", kArgsParsingFailure); } -TEST_F(UpdaterTest, block_image_update_patch_data) { - std::string src_content = std::string(4096, 'a') + std::string(4096, 'c'); - std::string tgt_content = std::string(4096, 'b') + std::string(4096, 'd'); - +// Generates the bsdiff of the given source and target images, and writes the result entries. +// target_blocks specifies the block count to be written into the `bsdiff` command, which may be +// different from the given target size in order to trigger overrun / underrun paths. +static void GetEntriesForBsdiff(std::string_view source, std::string_view target, + size_t target_blocks, PackageEntries* entries) { // Generate the patch data. TemporaryFile patch_file; - ASSERT_EQ(0, - bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src_content.data()), src_content.size(), - reinterpret_cast<const uint8_t*>(tgt_content.data()), tgt_content.size(), - patch_file.path, nullptr)); + ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(source.data()), source.size(), + reinterpret_cast<const uint8_t*>(target.data()), target.size(), + patch_file.path, nullptr)); std::string patch_content; ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch_content)); // Create the transfer list that contains a bsdiff. - std::string src_hash = get_sha1(src_content); - std::string tgt_hash = get_sha1(tgt_content); + std::string src_hash = GetSha1(source); + std::string tgt_hash = GetSha1(target); + size_t source_blocks = source.size() / 4096; std::vector<std::string> transfer_list{ // clang-format off "4", - "2", + std::to_string(target_blocks), "0", - "2", - "stash " + src_hash + " 2,0,2", - android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,2 2 - %s:2,0,2", patch_content.size(), - src_hash.c_str(), tgt_hash.c_str(), src_hash.c_str()), - "free " + src_hash, + "0", + // bsdiff patch_offset patch_length source_hash target_hash target_range source_block_count + // source_range + android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,%zu %zu 2,0,%zu", patch_content.size(), + src_hash.c_str(), tgt_hash.c_str(), target_blocks, source_blocks, + source_blocks), // clang-format on }; - PackageEntries entries{ + *entries = { { "new_data", "" }, { "patch_data", patch_content }, { "transfer_list", android::base::Join(transfer_list, '\n') }, }; +} - ASSERT_TRUE(android::base::WriteStringToFile(src_content, image_file_)); - +TEST_F(UpdaterTest, block_image_update_patch_data) { + // Both source and target images have 10 blocks. + std::string source = + std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); + std::string target = + std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); + + PackageEntries entries; + GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), + std::string_view(target).substr(0, 4096 * 2), 2, &entries); RunBlockImageUpdate(false, entries, image_file_, "t"); // The update_file should be patched correctly. - std::string updated_content; - ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated_content)); - ASSERT_EQ(tgt_content, updated_content); + std::string updated; + ASSERT_TRUE(android::base::ReadFileToString(image_file_, &updated)); + ASSERT_EQ(target, updated); +} + +TEST_F(UpdaterTest, block_image_update_patch_overrun) { + // Both source and target images have 10 blocks. + std::string source = + std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); + std::string target = + std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); + + // Provide one less block to trigger the overrun path. + PackageEntries entries; + GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), + std::string_view(target).substr(0, 4096 * 2), 1, &entries); + + // The update should fail due to overrun. + RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure); +} + +TEST_F(UpdaterTest, block_image_update_patch_underrun) { + // Both source and target images have 10 blocks. + std::string source = + std::string(4096, 'a') + std::string(4096, 'c') + std::string(4096 * 3, '\0'); + std::string target = + std::string(4096, 'b') + std::string(4096, 'd') + std::string(4096 * 3, '\0'); + ASSERT_TRUE(android::base::WriteStringToFile(source, image_file_)); + + // Provide one more block to trigger the overrun path. + PackageEntries entries; + GetEntriesForBsdiff(std::string_view(source).substr(0, 4096 * 2), + std::string_view(target).substr(0, 4096 * 2), 3, &entries); + + // The update should fail due to underrun. + RunBlockImageUpdate(false, entries, image_file_, "", kPatchApplicationFailure); } TEST_F(UpdaterTest, block_image_update_fail) { std::string src_content(4096 * 2, 'e'); - std::string src_hash = get_sha1(src_content); + std::string src_hash = GetSha1(src_content); // Stash and free some blocks, then fail the update intentionally. std::vector<std::string> transfer_list{ // clang-format off @@ -692,7 +738,7 @@ TEST_F(UpdaterTest, block_image_update_fail) { RunBlockImageUpdate(false, entries, image_file_, ""); // Updater generates the stash name based on the input file name. - std::string name_digest = get_sha1(image_file_); + std::string name_digest = GetSha1(image_file_); std::string stash_base = std::string(temp_stash_base_.path) + "/" + name_digest; ASSERT_EQ(0, access(stash_base.c_str(), F_OK)); // Expect the stashed blocks to be freed. @@ -796,9 +842,9 @@ TEST_F(UpdaterTest, last_command_update) { std::string block1(4096, '1'); std::string block2(4096, '2'); std::string block3(4096, '3'); - std::string block1_hash = get_sha1(block1); - std::string block2_hash = get_sha1(block2); - std::string block3_hash = get_sha1(block3); + std::string block1_hash = GetSha1(block1); + std::string block2_hash = GetSha1(block2); + std::string block3_hash = GetSha1(block3); // Compose the transfer list to fail the first update. std::vector<std::string> transfer_list_fail{ @@ -864,8 +910,8 @@ TEST_F(UpdaterTest, last_command_update) { TEST_F(UpdaterTest, last_command_update_unresumable) { std::string block1(4096, '1'); std::string block2(4096, '2'); - std::string block1_hash = get_sha1(block1); - std::string block2_hash = get_sha1(block2); + std::string block1_hash = GetSha1(block1); + std::string block2_hash = GetSha1(block2); // Construct an unresumable update with source blocks mismatch. std::vector<std::string> transfer_list_unresumable{ @@ -901,9 +947,9 @@ TEST_F(UpdaterTest, last_command_verify) { std::string block1(4096, '1'); std::string block2(4096, '2'); std::string block3(4096, '3'); - std::string block1_hash = get_sha1(block1); - std::string block2_hash = get_sha1(block2); - std::string block3_hash = get_sha1(block3); + std::string block1_hash = GetSha1(block1); + std::string block2_hash = GetSha1(block2); + std::string block3_hash = GetSha1(block3); std::vector<std::string> transfer_list_verify{ // clang-format off @@ -972,7 +1018,7 @@ class ResumableUpdaterTest : public testing::TestWithParam<size_t> { // Clear partition updated marker if any. std::string updated_marker{ temp_stash_base_.path }; - updated_marker += "/" + get_sha1(image_temp_file_.path) + ".UPDATED"; + updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED"; ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker)); } @@ -1003,10 +1049,10 @@ static std::vector<std::string> GenerateTransferList() { std::string i(4096, 'i'); std::string zero(4096, '\0'); - std::string a_hash = get_sha1(a); - std::string b_hash = get_sha1(b); - std::string c_hash = get_sha1(c); - std::string e_hash = get_sha1(e); + std::string a_hash = GetSha1(a); + std::string b_hash = GetSha1(b); + std::string c_hash = GetSha1(c); + std::string e_hash = GetSha1(e); auto loc = [](const std::string& range_text) { std::vector<std::string> pieces = android::base::Split(range_text, "-"); @@ -1027,8 +1073,8 @@ static std::vector<std::string> GenerateTransferList() { // patch 1: "b d c" -> "g" TemporaryFile patch_file_bdc_g; std::string bdc = b + d + c; - std::string bdc_hash = get_sha1(bdc); - std::string g_hash = get_sha1(g); + std::string bdc_hash = GetSha1(bdc); + std::string g_hash = GetSha1(g); CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(bdc.data()), bdc.size(), reinterpret_cast<const uint8_t*>(g.data()), g.size(), patch_file_bdc_g.path, nullptr)); @@ -1038,9 +1084,9 @@ static std::vector<std::string> GenerateTransferList() { // patch 2: "a b c d" -> "d c b" TemporaryFile patch_file_abcd_dcb; std::string abcd = a + b + c + d; - std::string abcd_hash = get_sha1(abcd); + std::string abcd_hash = GetSha1(abcd); std::string dcb = d + c + b; - std::string dcb_hash = get_sha1(dcb); + std::string dcb_hash = GetSha1(dcb); CHECK_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(abcd.data()), abcd.size(), reinterpret_cast<const uint8_t*>(dcb.data()), dcb.size(), patch_file_abcd_dcb.path, nullptr)); diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 3246ecdbc..c26d76d73 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -26,32 +26,232 @@ #include <android-base/file.h> #include <android-base/stringprintf.h> -#include <android-base/test_utils.h> +#include <android-base/strings.h> +#include <android-base/unique_fd.h> #include <gtest/gtest.h> +#include <openssl/bn.h> +#include <openssl/ec.h> +#include <openssl/nid.h> +#include <ziparchive/zip_writer.h> #include "common/test_constants.h" #include "otautil/sysutil.h" +#include "package.h" #include "verifier.h" using namespace std::string_literals; +static void LoadKeyFromFile(const std::string& file_name, Certificate* cert) { + std::string testkey_string; + ASSERT_TRUE(android::base::ReadFileToString(file_name, &testkey_string)); + ASSERT_TRUE(LoadCertificateFromBuffer( + std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert)); +} + +static void VerifyFile(const std::string& content, const std::vector<Certificate>& keys, + int expected) { + auto package = + Package::CreateMemoryPackage(std::vector<uint8_t>(content.begin(), content.end()), nullptr); + ASSERT_NE(nullptr, package); + + ASSERT_EQ(expected, verify_file(package.get(), keys)); +} + +static void VerifyPackageWithCertificates(const std::string& name, + const std::vector<Certificate>& certs) { + std::string path = from_testdata_base(name); + auto package = Package::CreateMemoryPackage(path, nullptr); + ASSERT_NE(nullptr, package); + + ASSERT_EQ(VERIFY_SUCCESS, verify_file(package.get(), certs)); +} + +static void VerifyPackageWithSingleCertificate(const std::string& name, Certificate&& cert) { + std::vector<Certificate> certs; + certs.emplace_back(std::move(cert)); + VerifyPackageWithCertificates(name, certs); +} + +static void BuildCertificateArchive(const std::vector<std::string>& file_names, int fd) { + FILE* zip_file_ptr = fdopen(fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + for (const auto& name : file_names) { + std::string content; + ASSERT_TRUE(android::base::ReadFileToString(name, &content)); + + // Makes sure the zip entry name has the correct suffix. + std::string entry_name = name; + if (!android::base::EndsWith(entry_name, "x509.pem")) { + entry_name += "x509.pem"; + } + ASSERT_EQ(0, zip_writer.StartEntry(entry_name.c_str(), ZipWriter::kCompress)); + ASSERT_EQ(0, zip_writer.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + } + + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_failure) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + std::string testkey_string; + ASSERT_TRUE( + android::base::ReadFileToString(from_testdata_base("testkey_v1.txt"), &testkey_string)); + ASSERT_FALSE(LoadCertificateFromBuffer( + std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), &cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent3) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v1.x509.pem"), &cert); + + ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v1.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha1_exponent65537) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v2.x509.pem"), &cert); + + ASSERT_EQ(SHA_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v2.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent3) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &cert); + + ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v3.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha256_exponent65537) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v4.x509.pem"), &cert); + + ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type); + ASSERT_EQ(nullptr, cert.ec); + + VerifyPackageWithSingleCertificate("otasigned_v4.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_sha256_ec256bits) { + Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v5.x509.pem"), &cert); + + ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len); + ASSERT_EQ(Certificate::KEY_TYPE_EC, cert.key_type); + ASSERT_EQ(nullptr, cert.rsa); + + VerifyPackageWithSingleCertificate("otasigned_v5.zip", std::move(cert)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_check_rsa_keys) { + std::unique_ptr<RSA, RSADeleter> rsa(RSA_new()); + std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free); + BN_set_word(exponent.get(), 3); + RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); + ASSERT_TRUE(CheckRSAKey(rsa)); + + // Exponent is expected to be 3 or 65537 + BN_set_word(exponent.get(), 17); + RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr); + ASSERT_FALSE(CheckRSAKey(rsa)); + + // Modulus is expected to be 2048. + BN_set_word(exponent.get(), 3); + RSA_generate_key_ex(rsa.get(), 1024, exponent.get(), nullptr); + ASSERT_FALSE(CheckRSAKey(rsa)); +} + +TEST(VerifierTest, LoadCertificateFromBuffer_check_ec_keys) { + std::unique_ptr<EC_KEY, ECKEYDeleter> ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + ASSERT_EQ(1, EC_KEY_generate_key(ec.get())); + ASSERT_TRUE(CheckECKey(ec)); + + // Expects 256-bit EC key with curve NIST P-256 + ec.reset(EC_KEY_new_by_curve_name(NID_secp224r1)); + ASSERT_EQ(1, EC_KEY_generate_key(ec.get())); + ASSERT_FALSE(CheckECKey(ec)); +} + +TEST(VerifierTest, LoadKeysFromZipfile_empty_archive) { + TemporaryFile otacerts; + BuildCertificateArchive({}, otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_TRUE(certs.empty()); +} + +TEST(VerifierTest, LoadKeysFromZipfile_single_key) { + TemporaryFile otacerts; + BuildCertificateArchive({ from_testdata_base("testkey_v1.x509.pem") }, otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_EQ(1, certs.size()); + + VerifyPackageWithCertificates("otasigned_v1.zip", certs); +} + +TEST(VerifierTest, LoadKeysFromZipfile_corrupted_key) { + TemporaryFile corrupted_key; + std::string content; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v1.x509.pem"), &content)); + content = "random-contents" + content; + ASSERT_TRUE(android::base::WriteStringToFd(content, corrupted_key.release())); + + TemporaryFile otacerts; + BuildCertificateArchive({ from_testdata_base("testkey_v2.x509.pem"), corrupted_key.path }, + otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_EQ(0, certs.size()); +} + +TEST(VerifierTest, LoadKeysFromZipfile_multiple_key) { + TemporaryFile otacerts; + BuildCertificateArchive( + { + from_testdata_base("testkey_v3.x509.pem"), + from_testdata_base("testkey_v4.x509.pem"), + from_testdata_base("testkey_v5.x509.pem"), + + }, + otacerts.release()); + std::vector<Certificate> certs = LoadKeysFromZipfile(otacerts.path); + ASSERT_EQ(3, certs.size()); + + VerifyPackageWithCertificates("otasigned_v3.zip", certs); + VerifyPackageWithCertificates("otasigned_v4.zip", certs); + VerifyPackageWithCertificates("otasigned_v5.zip", certs); +} + class VerifierTest : public testing::TestWithParam<std::vector<std::string>> { protected: void SetUp() override { std::vector<std::string> args = GetParam(); - std::string package = from_testdata_base(args[0]); - if (!memmap.MapFile(package)) { - FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; - } + std::string path = from_testdata_base(args[0]); + package_ = Package::CreateMemoryPackage(path, nullptr); + ASSERT_NE(nullptr, package_); for (auto it = ++args.cbegin(); it != args.cend(); ++it) { - std::string public_key_file = from_testdata_base("testkey_" + *it + ".txt"); - ASSERT_TRUE(load_keys(public_key_file.c_str(), certs)); + std::string public_key_file = from_testdata_base("testkey_" + *it + ".x509.pem"); + certs_.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(public_key_file, &certs_.back()); } } - MemMapping memmap; - std::vector<Certificate> certs; + std::unique_ptr<Package> package_; + std::vector<Certificate> certs_; }; class VerifierSuccessTest : public VerifierTest { @@ -60,70 +260,10 @@ class VerifierSuccessTest : public VerifierTest { class VerifierFailureTest : public VerifierTest { }; -TEST(VerifierTest, load_keys_multiple_keys) { - std::string testkey_v4; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v4.txt"), &testkey_v4)); - - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - - std::string keys = testkey_v4 + "," + testkey_v3 + "," + testkey_v4; - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(keys, key_file1.path)); - std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); - ASSERT_EQ(3U, certs.size()); -} - -TEST(VerifierTest, load_keys_invalid_keys) { - std::vector<Certificate> certs; - ASSERT_FALSE(load_keys("/doesntexist", certs)); - - // Empty file. - TemporaryFile key_file1; - ASSERT_FALSE(load_keys(key_file1.path, certs)); - - // Invalid contents. - ASSERT_TRUE(android::base::WriteStringToFile("invalid", key_file1.path)); - ASSERT_FALSE(load_keys(key_file1.path, certs)); - - std::string testkey_v4; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v4.txt"), &testkey_v4)); - - // Invalid key version: "v4 ..." => "v6 ...". - std::string invalid_key2(testkey_v4); - invalid_key2[1] = '6'; - TemporaryFile key_file2; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key2, key_file2.path)); - ASSERT_FALSE(load_keys(key_file2.path, certs)); - - // Invalid key content: inserted extra bytes ",2209831334". - std::string invalid_key3(testkey_v4); - invalid_key3.insert(invalid_key2.size() - 2, ",2209831334"); - TemporaryFile key_file3; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key3, key_file3.path)); - ASSERT_FALSE(load_keys(key_file3.path, certs)); - - // Invalid key: the last key must not end with an extra ','. - std::string invalid_key4 = testkey_v4 + ","; - TemporaryFile key_file4; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key4, key_file4.path)); - ASSERT_FALSE(load_keys(key_file4.path, certs)); - - // Invalid key separator. - std::string invalid_key5 = testkey_v4 + ";" + testkey_v4; - TemporaryFile key_file5; - ASSERT_TRUE(android::base::WriteStringToFile(invalid_key5, key_file5.path)); - ASSERT_FALSE(load_keys(key_file5.path, certs)); -} - TEST(VerifierTest, BadPackage_AlteredFooter) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); std::string package; ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); @@ -131,18 +271,13 @@ TEST(VerifierTest, BadPackage_AlteredFooter) { // Alter the footer. package[package.size() - 5] = '\x05'; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast<const unsigned char*>(package.data()), package.size(), - certs)); + VerifyFile(package, certs, VERIFY_FAILURE); } TEST(VerifierTest, BadPackage_AlteredContent) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); std::string package; ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); @@ -151,38 +286,29 @@ TEST(VerifierTest, BadPackage_AlteredContent) { // Alter the content. std::string altered1(package); altered1[50] += 1; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast<const unsigned char*>(altered1.data()), altered1.size(), - certs)); + VerifyFile(altered1, certs, VERIFY_FAILURE); std::string altered2(package); altered2[10] += 1; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast<const unsigned char*>(altered2.data()), altered2.size(), - certs)); + VerifyFile(altered2, certs, VERIFY_FAILURE); } TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - - TemporaryFile key_file; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file.path)); std::vector<Certificate> certs; - ASSERT_TRUE(load_keys(key_file.path, certs)); + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + LoadKeyFromFile(from_testdata_base("testkey_v3.x509.pem"), &certs.back()); // Signature start is 65535 (0xffff) while comment size is 0 (Bug: 31914369). std::string package = "\x50\x4b\x05\x06"s + std::string(12, '\0') + "\xff\xff\xff\xff\x00\x00"s; - ASSERT_EQ(VERIFY_FAILURE, verify_file(reinterpret_cast<const unsigned char*>(package.data()), - package.size(), certs)); + VerifyFile(package, certs, VERIFY_FAILURE); } TEST_P(VerifierSuccessTest, VerifySucceed) { - ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS); + ASSERT_EQ(VERIFY_SUCCESS, verify_file(package_.get(), certs_)); } TEST_P(VerifierFailureTest, VerifyFailure) { - ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_FAILURE); + ASSERT_EQ(VERIFY_FAILURE, verify_file(package_.get(), certs_)); } INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest, diff --git a/tests/testdata/battery_scale.png b/tests/testdata/battery_scale.png Binary files differnew file mode 100644 index 000000000..2ae8f0fd7 --- /dev/null +++ b/tests/testdata/battery_scale.png diff --git a/tests/testdata/jarsigned.zip b/tests/testdata/jarsigned.zip Binary files differdeleted file mode 100644 index 8b1ef8bdd..000000000 --- a/tests/testdata/jarsigned.zip +++ /dev/null diff --git a/tests/testdata/patch.bsdiff b/tests/testdata/patch.bsdiff Binary files differdeleted file mode 100644 index b78d38573..000000000 --- a/tests/testdata/patch.bsdiff +++ /dev/null diff --git a/tests/testdata/unsigned.zip b/tests/testdata/unsigned.zip Binary files differdeleted file mode 100644 index 24e3eadac..000000000 --- a/tests/testdata/unsigned.zip +++ /dev/null diff --git a/tests/unit/applypatch_test.cpp b/tests/unit/applypatch_test.cpp index 066f981b4..794f2c103 100644 --- a/tests/unit/applypatch_test.cpp +++ b/tests/unit/applypatch_test.cpp @@ -32,7 +32,6 @@ #include <android-base/file.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> -#include <android-base/test_utils.h> #include <android-base/unique_fd.h> #include <gtest/gtest.h> diff --git a/tests/unit/dirutil_test.cpp b/tests/unit/dirutil_test.cpp index 1ca786c28..4dd111a70 100644 --- a/tests/unit/dirutil_test.cpp +++ b/tests/unit/dirutil_test.cpp @@ -20,7 +20,7 @@ #include <string> -#include <android-base/test_utils.h> +#include <android-base/file.h> #include <gtest/gtest.h> #include "otautil/dirutil.h" diff --git a/tests/unit/minui_test.cpp b/tests/unit/minui_test.cpp new file mode 100644 index 000000000..c7d7f7eef --- /dev/null +++ b/tests/unit/minui_test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 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 <stdint.h> +#include <stdlib.h> + +#include <limits> +#include <vector> + +#include <gtest/gtest.h> + +#include "minui/minui.h" + +TEST(GRSurfaceTest, Create_aligned) { + auto surface = GRSurface::Create(9, 11, 9, 1); + ASSERT_TRUE(surface); + ASSERT_EQ(0, reinterpret_cast<uintptr_t>(surface->data()) % GRSurface::kSurfaceDataAlignment); + // data_size will be rounded up to the next multiple of GRSurface::kSurfaceDataAlignment. + ASSERT_EQ(0, surface->data_size() % GRSurface::kSurfaceDataAlignment); + ASSERT_GE(surface->data_size(), 11 * 9); +} + +TEST(GRSurfaceTest, Create_invalid_inputs) { + ASSERT_FALSE(GRSurface::Create(9, 11, 0, 1)); + ASSERT_FALSE(GRSurface::Create(9, 0, 9, 1)); + ASSERT_FALSE(GRSurface::Create(0, 11, 9, 1)); + ASSERT_FALSE(GRSurface::Create(9, 11, 9, 0)); + ASSERT_FALSE(GRSurface::Create(9, 101, std::numeric_limits<size_t>::max() / 100, 1)); +} + +TEST(GRSurfaceTest, Clone) { + auto image = GRSurface::Create(50, 10, 50, 1); + ASSERT_GE(image->data_size(), 10 * 50); + for (auto i = 0; i < image->data_size(); i++) { + image->data()[i] = rand() % 128; + } + auto image_copy = image->Clone(); + ASSERT_EQ(image->data_size(), image_copy->data_size()); + ASSERT_EQ(std::vector(image->data(), image->data() + image->data_size()), + std::vector(image_copy->data(), image_copy->data() + image->data_size())); +} diff --git a/tests/unit/parse_install_logs_test.cpp b/tests/unit/parse_install_logs_test.cpp new file mode 100644 index 000000000..72169a0c6 --- /dev/null +++ b/tests/unit/parse_install_logs_test.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2018 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 <map> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/strings.h> +#include <gtest/gtest.h> + +#include "otautil/parse_install_logs.h" + +TEST(ParseInstallLogsTest, EmptyFile) { + TemporaryFile last_install; + + auto metrics = ParseLastInstall(last_install.path); + ASSERT_TRUE(metrics.empty()); +} + +TEST(ParseInstallLogsTest, SideloadSmoke) { + TemporaryFile last_install; + ASSERT_TRUE(android::base::WriteStringToFile("/cache/recovery/ota.zip\n0\n", last_install.path)); + auto metrics = ParseLastInstall(last_install.path); + ASSERT_EQ(metrics.end(), metrics.find("ota_sideload")); + + ASSERT_TRUE(android::base::WriteStringToFile("/sideload/package.zip\n0\n", last_install.path)); + metrics = ParseLastInstall(last_install.path); + ASSERT_NE(metrics.end(), metrics.find("ota_sideload")); +} + +TEST(ParseInstallLogsTest, ParseRecoveryUpdateMetrics) { + std::vector<std::string> lines = { + "/sideload/package.zip", + "0", + "time_total: 300", + "uncrypt_time: 40", + "source_build: 4973410", + "bytes_written_system: " + std::to_string(1200 * 1024 * 1024), + "bytes_stashed_system: " + std::to_string(300 * 1024 * 1024), + "bytes_written_vendor: " + std::to_string(40 * 1024 * 1024), + "bytes_stashed_vendor: " + std::to_string(50 * 1024 * 1024), + "temperature_start: 37000", + "temperature_end: 38000", + "temperature_max: 39000", + "error: 22", + "cause: 55", + }; + + auto metrics = ParseRecoveryUpdateMetrics(lines); + + std::map<std::string, int64_t> expected_result = { + { "ota_time_total", 300 }, { "ota_uncrypt_time", 40 }, + { "ota_source_version", 4973410 }, { "ota_written_in_MiBs", 1240 }, + { "ota_stashed_in_MiBs", 350 }, { "ota_temperature_start", 37000 }, + { "ota_temperature_end", 38000 }, { "ota_temperature_max", 39000 }, + { "ota_non_ab_error_code", 22 }, { "ota_non_ab_cause_code", 55 }, + }; + + ASSERT_EQ(expected_result, metrics); +} diff --git a/tests/unit/resources_test.cpp b/tests/unit/resources_test.cpp new file mode 100644 index 000000000..c3f72718f --- /dev/null +++ b/tests/unit/resources_test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 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 "common/test_constants.h" +#include "minui/minui.h" + +TEST(ResourcesTest, res_create_multi_display_surface) { + GRSurface** frames; + int frame_count; + int fps; + ASSERT_EQ(0, res_create_multi_display_surface(from_testdata_base("battery_scale.png").c_str(), + &frame_count, &fps, &frames)); + ASSERT_EQ(6, frame_count); + ASSERT_EQ(20, fps); + + for (auto i = 0; i < frame_count; i++) { + free(frames[i]); + } + free(frames); +} diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp index 7d97a006b..647c7b2d3 100644 --- a/tests/unit/screen_ui_test.cpp +++ b/tests/unit/screen_ui_test.cpp @@ -23,10 +23,11 @@ #include <string> #include <vector> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/stringprintf.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> +#include <gtest/gtest_prod.h> #include "common/test_constants.h" #include "device.h" @@ -38,8 +39,39 @@ static const std::vector<std::string> HEADERS{ "header" }; static const std::vector<std::string> ITEMS{ "item1", "item2", "item3", "item4", "1234567890" }; -TEST(ScreenUITest, StartPhoneMenuSmoke) { - Menu menu(false, 10, 20, HEADERS, ITEMS, 0); +// TODO(xunchang) check if some draw functions are called when drawing menus. +class MockDrawFunctions : public DrawInterface { + void SetColor(UIElement /* element */) const override {} + void DrawHighlightBar(int /* x */, int /* y */, int /* width */, + int /* height */) const override {} + int DrawHorizontalRule(int /* y */) const override { + return 0; + } + int DrawTextLine(int /* x */, int /* y */, const std::string& /* line */, + bool /* bold */) const override { + return 0; + } + void DrawSurface(const GRSurface* /* surface */, int /* sx */, int /* sy */, int /* w */, + int /* h */, int /* dx */, int /* dy */) const override {} + void DrawFill(int /* x */, int /* y */, int /* w */, int /* h */) const override {} + void DrawTextIcon(int /* x */, int /* y */, const GRSurface* /* surface */) const override {} + int DrawTextLines(int /* x */, int /* y */, + const std::vector<std::string>& /* lines */) const override { + return 0; + } + int DrawWrappedTextLines(int /* x */, int /* y */, + const std::vector<std::string>& /* lines */) const override { + return 0; + } +}; + +class ScreenUITest : public testing::Test { + protected: + MockDrawFunctions draw_funcs_; +}; + +TEST_F(ScreenUITest, StartPhoneMenuSmoke) { + TextMenu menu(false, 10, 20, HEADERS, ITEMS, 0, 20, draw_funcs_); ASSERT_FALSE(menu.scrollable()); ASSERT_EQ(HEADERS[0], menu.text_headers()[0]); ASSERT_EQ(5u, menu.ItemsCount()); @@ -53,8 +85,8 @@ TEST(ScreenUITest, StartPhoneMenuSmoke) { ASSERT_EQ(0, menu.selection()); } -TEST(ScreenUITest, StartWearMenuSmoke) { - Menu menu(true, 10, 8, HEADERS, ITEMS, 1); +TEST_F(ScreenUITest, StartWearMenuSmoke) { + TextMenu menu(true, 10, 8, HEADERS, ITEMS, 1, 20, draw_funcs_); ASSERT_TRUE(menu.scrollable()); ASSERT_EQ(HEADERS[0], menu.text_headers()[0]); ASSERT_EQ(5u, menu.ItemsCount()); @@ -69,8 +101,8 @@ TEST(ScreenUITest, StartWearMenuSmoke) { ASSERT_EQ(1, menu.selection()); } -TEST(ScreenUITest, StartPhoneMenuItemsOverflow) { - Menu menu(false, 1, 20, HEADERS, ITEMS, 0); +TEST_F(ScreenUITest, StartPhoneMenuItemsOverflow) { + TextMenu menu(false, 1, 20, HEADERS, ITEMS, 0, 20, draw_funcs_); ASSERT_FALSE(menu.scrollable()); ASSERT_EQ(1u, menu.ItemsCount()); @@ -84,8 +116,8 @@ TEST(ScreenUITest, StartPhoneMenuItemsOverflow) { ASSERT_EQ(1u, menu.MenuEnd()); } -TEST(ScreenUITest, StartWearMenuItemsOverflow) { - Menu menu(true, 1, 20, HEADERS, ITEMS, 0); +TEST_F(ScreenUITest, StartWearMenuItemsOverflow) { + TextMenu menu(true, 1, 20, HEADERS, ITEMS, 0, 20, draw_funcs_); ASSERT_TRUE(menu.scrollable()); ASSERT_EQ(5u, menu.ItemsCount()); @@ -101,9 +133,9 @@ TEST(ScreenUITest, StartWearMenuItemsOverflow) { ASSERT_EQ(1u, menu.MenuEnd()); } -TEST(ScreenUITest, PhoneMenuSelectSmoke) { +TEST_F(ScreenUITest, PhoneMenuSelectSmoke) { int sel = 0; - Menu menu(false, 10, 20, HEADERS, ITEMS, sel); + TextMenu menu(false, 10, 20, HEADERS, ITEMS, sel, 20, draw_funcs_); // Mimic down button 10 times (2 * items size) for (int i = 0; i < 10; i++) { sel = menu.Select(++sel); @@ -130,9 +162,9 @@ TEST(ScreenUITest, PhoneMenuSelectSmoke) { } } -TEST(ScreenUITest, WearMenuSelectSmoke) { +TEST_F(ScreenUITest, WearMenuSelectSmoke) { int sel = 0; - Menu menu(true, 10, 20, HEADERS, ITEMS, sel); + TextMenu menu(true, 10, 20, HEADERS, ITEMS, sel, 20, draw_funcs_); // Mimic pressing down button 10 times (2 * items size) for (int i = 0; i < 10; i++) { sel = menu.Select(++sel); @@ -159,9 +191,9 @@ TEST(ScreenUITest, WearMenuSelectSmoke) { } } -TEST(ScreenUITest, WearMenuSelectItemsOverflow) { +TEST_F(ScreenUITest, WearMenuSelectItemsOverflow) { int sel = 1; - Menu menu(true, 3, 20, HEADERS, ITEMS, sel); + TextMenu menu(true, 3, 20, HEADERS, ITEMS, sel, 20, draw_funcs_); ASSERT_EQ(5u, menu.ItemsCount()); // Scroll the menu to the end, and check the start & end of menu. @@ -198,6 +230,53 @@ TEST(ScreenUITest, WearMenuSelectItemsOverflow) { ASSERT_EQ(3u, menu.MenuEnd()); } +TEST_F(ScreenUITest, GraphicMenuSelection) { + auto image = GRSurface::Create(50, 50, 50, 1); + auto header = image->Clone(); + std::vector<const GRSurface*> items = { + image.get(), + image.get(), + image.get(), + }; + GraphicMenu menu(header.get(), items, 0, draw_funcs_); + + ASSERT_EQ(0, menu.selection()); + + int sel = 0; + for (int i = 0; i < 3; i++) { + sel = menu.Select(++sel); + ASSERT_EQ((i + 1) % 3, sel); + ASSERT_EQ(sel, menu.selection()); + } + + sel = 0; + for (int i = 0; i < 3; i++) { + sel = menu.Select(--sel); + ASSERT_EQ(2 - i, sel); + ASSERT_EQ(sel, menu.selection()); + } +} + +TEST_F(ScreenUITest, GraphicMenuValidate) { + auto image = GRSurface::Create(50, 50, 50, 1); + auto header = image->Clone(); + std::vector<const GRSurface*> items = { + image.get(), + image.get(), + image.get(), + }; + + ASSERT_TRUE(GraphicMenu::Validate(200, 200, header.get(), items)); + + // Menu exceeds the horizontal boundary. + auto wide_surface = GRSurface::Create(300, 50, 300, 1); + ASSERT_FALSE(GraphicMenu::Validate(299, 200, wide_surface.get(), items)); + + // Menu exceeds the vertical boundary. + items.emplace_back(image.get()); + ASSERT_FALSE(GraphicMenu::Validate(200, 249, header.get(), items)); +} + static constexpr int kMagicAction = 101; enum class KeyCode : int { @@ -228,24 +307,13 @@ class TestableScreenRecoveryUI : public ScreenRecoveryUI { int KeyHandler(int key, bool visible) const; - // The following functions expose the protected members for test purpose. - void RunLoadAnimation() { - LoadAnimation(); - } - - size_t GetLoopFrames() const { - return loop_frames; - } - - size_t GetIntroFrames() const { - return intro_frames; - } - - bool GetRtlLocale() const { - return rtl_locale_; - } - private: + FRIEND_TEST(DISABLED_ScreenRecoveryUITest, Init); + FRIEND_TEST(DISABLED_ScreenRecoveryUITest, RtlLocale); + FRIEND_TEST(DISABLED_ScreenRecoveryUITest, RtlLocaleWithSuffix); + FRIEND_TEST(DISABLED_ScreenRecoveryUITest, LoadAnimation); + FRIEND_TEST(DISABLED_ScreenRecoveryUITest, LoadAnimation_MissingAnimation); + std::vector<KeyCode> key_buffer_; size_t key_buffer_index_; }; @@ -272,7 +340,7 @@ int TestableScreenRecoveryUI::WaitKey() { return static_cast<int>(key_buffer_[key_buffer_index_++]); } -class ScreenRecoveryUITest : public ::testing::Test { +class DISABLED_ScreenRecoveryUITest : public ::testing::Test { protected: const std::string kTestLocale = "en-US"; const std::string kTestRtlLocale = "ar"; @@ -304,22 +372,22 @@ class ScreenRecoveryUITest : public ::testing::Test { } \ } while (false) -TEST_F(ScreenRecoveryUITest, Init) { +TEST_F(DISABLED_ScreenRecoveryUITest, Init) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); ASSERT_EQ(kTestLocale, ui_->GetLocale()); - ASSERT_FALSE(ui_->GetRtlLocale()); + ASSERT_FALSE(ui_->rtl_locale_); ASSERT_FALSE(ui_->IsTextVisible()); ASSERT_FALSE(ui_->WasTextEverVisible()); } -TEST_F(ScreenRecoveryUITest, dtor_NotCallingInit) { +TEST_F(DISABLED_ScreenRecoveryUITest, dtor_NotCallingInit) { ui_.reset(); ASSERT_FALSE(ui_); } -TEST_F(ScreenRecoveryUITest, ShowText) { +TEST_F(DISABLED_ScreenRecoveryUITest, ShowText) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -333,21 +401,21 @@ TEST_F(ScreenRecoveryUITest, ShowText) { ASSERT_TRUE(ui_->WasTextEverVisible()); } -TEST_F(ScreenRecoveryUITest, RtlLocale) { +TEST_F(DISABLED_ScreenRecoveryUITest, RtlLocale) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestRtlLocale)); - ASSERT_TRUE(ui_->GetRtlLocale()); + ASSERT_TRUE(ui_->rtl_locale_); } -TEST_F(ScreenRecoveryUITest, RtlLocaleWithSuffix) { +TEST_F(DISABLED_ScreenRecoveryUITest, RtlLocaleWithSuffix) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestRtlLocaleWithSuffix)); - ASSERT_TRUE(ui_->GetRtlLocale()); + ASSERT_TRUE(ui_->rtl_locale_); } -TEST_F(ScreenRecoveryUITest, ShowMenu) { +TEST_F(DISABLED_ScreenRecoveryUITest, ShowMenu) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -375,7 +443,7 @@ TEST_F(ScreenRecoveryUITest, ShowMenu) { std::placeholders::_1, std::placeholders::_2))); } -TEST_F(ScreenRecoveryUITest, ShowMenu_NotMenuOnly) { +TEST_F(DISABLED_ScreenRecoveryUITest, ShowMenu_NotMenuOnly) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -388,7 +456,7 @@ TEST_F(ScreenRecoveryUITest, ShowMenu_NotMenuOnly) { std::placeholders::_1, std::placeholders::_2))); } -TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut) { +TEST_F(DISABLED_ScreenRecoveryUITest, ShowMenu_TimedOut) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -399,7 +467,7 @@ TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut) { ui_->ShowMenu(HEADERS, ITEMS, 3, true, nullptr)); } -TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) { +TEST_F(DISABLED_ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -417,7 +485,7 @@ TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) { std::placeholders::_1, std::placeholders::_2))); } -TEST_F(ScreenRecoveryUITest, ShowMenuWithInterrupt) { +TEST_F(DISABLED_ScreenRecoveryUITest, ShowMenuWithInterrupt) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -449,7 +517,7 @@ TEST_F(ScreenRecoveryUITest, ShowMenuWithInterrupt) { std::placeholders::_1, std::placeholders::_2))); } -TEST_F(ScreenRecoveryUITest, LoadAnimation) { +TEST_F(DISABLED_ScreenRecoveryUITest, LoadAnimation) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -469,17 +537,17 @@ TEST_F(ScreenRecoveryUITest, LoadAnimation) { } Paths::Get().set_resource_dir(resource_dir.path); - ui_->RunLoadAnimation(); + ui_->LoadAnimation(); - ASSERT_EQ(2u, ui_->GetIntroFrames()); - ASSERT_EQ(3u, ui_->GetLoopFrames()); + ASSERT_EQ(2u, ui_->intro_frames_.size()); + ASSERT_EQ(3u, ui_->loop_frames_.size()); for (const auto& name : tempfiles) { ASSERT_EQ(0, unlink(name.c_str())); } } -TEST_F(ScreenRecoveryUITest, LoadAnimation_MissingAnimation) { +TEST_F(DISABLED_ScreenRecoveryUITest, LoadAnimation_MissingAnimation) { RETURN_IF_NO_GRAPHICS; ASSERT_TRUE(ui_->Init(kTestLocale)); @@ -488,7 +556,7 @@ TEST_F(ScreenRecoveryUITest, LoadAnimation_MissingAnimation) { Paths::Get().set_resource_dir("/proc/self"); ::testing::FLAGS_gtest_death_test_style = "threadsafe"; - ASSERT_EXIT(ui_->RunLoadAnimation(), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(ui_->LoadAnimation(), ::testing::KilledBySignal(SIGABRT), ""); } #undef RETURN_IF_NO_GRAPHICS diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp index de8ff7065..77625dbe9 100644 --- a/tests/unit/sysutil_test.cpp +++ b/tests/unit/sysutil_test.cpp @@ -17,7 +17,6 @@ #include <string> #include <android-base/file.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> #include "otautil/sysutil.h" diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index 47f33d9ea..dfe617ebe 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -21,7 +21,6 @@ #include <vector> #include <android-base/file.h> -#include <android-base/test_utils.h> #include <gtest/gtest.h> #include <ziparchive/zip_archive.h> |