From d5fbcc1ba9f5ff81ca67e80b552ee02d285ef536 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 11 Apr 2018 14:38:09 -0700 Subject: Remove the old log files if cache space is insufficient for OTA We set the limit of the max stash size to 80% of cache size. But the cache space can still be insufficient for the update if the log files occupy a large chunk of /cache. So remove the old logs for now to make room for the update. Bug: 77528881 Test: unit tests pass Change-Id: Ia8bcb0ace11f8164ad9290bfb360e08e31d282cb --- tests/component/applypatch_test.cpp | 129 ++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) (limited to 'tests/component') diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 916028594..aa0959bf5 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#include #include #include +#include #include #include #include @@ -23,6 +25,7 @@ #include #include +#include #include #include #include @@ -30,6 +33,7 @@ #include #include #include +#include #include #include @@ -110,6 +114,52 @@ class ApplyPatchModesTest : public ::testing::Test { TemporaryFile cache_source; }; +class FreeCacheTest : public ::testing::Test { + protected: + static constexpr size_t PARTITION_SIZE = 4096 * 10; + + // Returns a sorted list of files in |dirname|. + static std::vector FindFilesInDir(const std::string& dirname) { + std::vector file_list; + + std::unique_ptr d(opendir(dirname.c_str()), closedir); + struct dirent* de; + while ((de = readdir(d.get())) != 0) { + std::string path = dirname + "/" + de->d_name; + + struct stat st; + if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) { + file_list.emplace_back(de->d_name); + } + } + + std::sort(file_list.begin(), file_list.end()); + return file_list; + } + + static void AddFilesToDir(const std::string& dir, const std::vector& files) { + std::string zeros(4096, 0); + for (const auto& file : files) { + std::string path = dir + "/" + file; + ASSERT_TRUE(android::base::WriteStringToFile(zeros, path)); + } + } + + void SetUp() override { + CacheLocation::location().set_cache_log_directory(mock_log_dir.path); + } + + // A mock method to calculate the free space. It assumes the partition has a total size of 40960 + // bytes and all files are 4096 bytes in size. + size_t MockFreeSpaceChecker(const std::string& dirname) { + std::vector files = FindFilesInDir(dirname); + return PARTITION_SIZE - 4096 * files.size(); + } + + TemporaryDir mock_cache; + TemporaryDir mock_log_dir; +}; + TEST_F(ApplyPatchTest, CheckModeSkip) { std::vector sha1s; ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); @@ -414,3 +464,82 @@ TEST_F(ApplyPatchModesTest, CheckModeInvalidArgs) { TEST_F(ApplyPatchModesTest, ShowLicenses) { ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" })); } + +TEST_F(FreeCacheTest, FreeCacheSmoke) { + std::vector files = { "file1", "file2", "file3" }; + AddFilesToDir(mock_cache.path, files); + ASSERT_EQ(files, FindFilesInDir(mock_cache.path)); + ASSERT_EQ(4096 * 7, MockFreeSpaceChecker(mock_cache.path)); + + ASSERT_TRUE(RemoveFilesInDirectory(4096 * 9, mock_cache.path, [&](const std::string& dir) { + return this->MockFreeSpaceChecker(dir); + })); + + ASSERT_EQ(std::vector{ "file3" }, FindFilesInDir(mock_cache.path)); + ASSERT_EQ(4096 * 9, MockFreeSpaceChecker(mock_cache.path)); +} + +TEST_F(FreeCacheTest, FreeCacheOpenFile) { + std::vector files = { "file1", "file2" }; + AddFilesToDir(mock_cache.path, files); + ASSERT_EQ(files, FindFilesInDir(mock_cache.path)); + ASSERT_EQ(4096 * 8, MockFreeSpaceChecker(mock_cache.path)); + + std::string file1_path = mock_cache.path + "/file1"s; + android::base::unique_fd fd(open(file1_path.c_str(), O_RDONLY)); + + // file1 can't be deleted as it's opened by us. + ASSERT_FALSE(RemoveFilesInDirectory(4096 * 10, mock_cache.path, [&](const std::string& dir) { + return this->MockFreeSpaceChecker(dir); + })); + + ASSERT_EQ(std::vector{ "file1" }, FindFilesInDir(mock_cache.path)); +} + +TEST_F(FreeCacheTest, FreeCacheLogsSmoke) { + std::vector log_files = { "last_log", "last_log.1", "last_kmsg.2", "last_log.5", + "last_log.10" }; + AddFilesToDir(mock_log_dir.path, log_files); + ASSERT_EQ(4096 * 5, MockFreeSpaceChecker(mock_log_dir.path)); + + ASSERT_TRUE(RemoveFilesInDirectory(4096 * 8, mock_log_dir.path, [&](const std::string& dir) { + return this->MockFreeSpaceChecker(dir); + })); + + // Logs with a higher index will be deleted first + std::vector expected = { "last_log", "last_log.1" }; + ASSERT_EQ(expected, FindFilesInDir(mock_log_dir.path)); + ASSERT_EQ(4096 * 8, MockFreeSpaceChecker(mock_log_dir.path)); +} + +TEST_F(FreeCacheTest, FreeCacheLogsStringComparison) { + std::vector log_files = { "last_log.1", "last_kmsg.1", "last_log.not_number", + "last_kmsgrandom" }; + AddFilesToDir(mock_log_dir.path, log_files); + ASSERT_EQ(4096 * 6, MockFreeSpaceChecker(mock_log_dir.path)); + + ASSERT_TRUE(RemoveFilesInDirectory(4096 * 9, mock_log_dir.path, [&](const std::string& dir) { + return this->MockFreeSpaceChecker(dir); + })); + + // Logs with incorrect format will be deleted first; and the last_kmsg with the same index is + // deleted before last_log. + std::vector expected = { "last_log.1" }; + ASSERT_EQ(expected, FindFilesInDir(mock_log_dir.path)); + ASSERT_EQ(4096 * 9, MockFreeSpaceChecker(mock_log_dir.path)); +} + +TEST_F(FreeCacheTest, FreeCacheLogsOtherFiles) { + std::vector log_files = { "last_install", "command", "block.map", "last_log", + "last_kmsg.1" }; + AddFilesToDir(mock_log_dir.path, log_files); + ASSERT_EQ(4096 * 5, MockFreeSpaceChecker(mock_log_dir.path)); + + ASSERT_FALSE(RemoveFilesInDirectory(4096 * 8, mock_log_dir.path, [&](const std::string& dir) { + return this->MockFreeSpaceChecker(dir); + })); + + // Non log files in /cache/recovery won't be deleted. + std::vector expected = { "block.map", "command", "last_install" }; + ASSERT_EQ(expected, FindFilesInDir(mock_log_dir.path)); +} -- cgit v1.2.3