/* * 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 #include #include #include #include #include #include #include #ifndef STORAGE_FAKE #include #include #include #endif #include #define TLOG_TAG "ss_unittest" #if STORAGE_UNITTEST_ON_EMULATOR #define ENABLED_ON_EMULATOR_ONLY(name) name #else #define ENABLED_ON_EMULATOR_ONLY(name) DISABLED_##name #endif typedef void (*test_body)(storage_session_t ss, storage_session_t ss_aux); static const char* storage_test_client_port; static storage_session_t storage_test_ss_persist = STORAGE_INVALID_SESSION; typedef struct { storage_session_t ss; storage_session_t ss_aux; } StorageTest_t; void StorageTest_SetUp(StorageTest_t* state) { state->ss = STORAGE_INVALID_SESSION; state->ss_aux = STORAGE_INVALID_SESSION; if (storage_test_ss_persist != STORAGE_INVALID_SESSION) { storage_close_session(storage_test_ss_persist); storage_test_ss_persist = STORAGE_INVALID_SESSION; } int rc = storage_open_session(&state->ss, storage_test_client_port); if (rc < 0) { TLOGE("failed (%d) to open session\n", rc); return; } #ifndef STORAGE_FAKE rc = storage_open_session(&state->ss_aux, storage_test_client_port); if (rc < 0) { TLOGE("failed (%d) to open session\n", rc); storage_close_session(state->ss); return; } #endif }; void StorageTest_TearDown(StorageTest_t* state) { storage_close_session(state->ss); #ifndef STORAGE_FAKE storage_close_session(state->ss_aux); #endif } #undef TEST_F #define TEST_F(suite_name, test_name) \ TEST_F_CUSTOM_ARGS(suite_name, test_name, (state.ss, state.ss_aux), \ (storage_session_t ss, storage_session_t ss_aux)) #ifndef STORAGE_FAKE TEST_FIXTURE_ALIAS(StorageInitNoCommitSmallTest, StorageTest) TEST_FIXTURE_ALIAS(StorageInitNoCommitLargeTest, StorageTest) TEST_FIXTURE_ALIAS(StorageInitNoCommitCleanupTest, StorageTest) #endif TEST_FIXTURE_ALIAS(StorageInitTest, StorageTest) TEST_FIXTURE_ALIAS(StorageCheckTest, StorageTest) TEST_FIXTURE_ALIAS(StorageCleanTest, StorageTest) static inline bool is_32bit_aligned(size_t sz) { return ((sz & 0x3) == 0); } static inline bool is_valid_size(size_t sz) { return (sz > 0) && is_32bit_aligned(sz); } static bool is_valid_offset(storage_off_t off) { return (off & 0x3) == 0ULL; } static void fill_pattern32(uint32_t* buf, size_t len, storage_off_t off) { size_t cnt = len / sizeof(uint32_t); uint32_t pattern = (uint32_t)(off / sizeof(uint32_t)); for (; cnt > 0; cnt--) { *buf++ = pattern++; } } static bool check_pattern32(const uint32_t* buf, size_t len, storage_off_t off) { size_t cnt = len / sizeof(uint32_t); uint32_t pattern = (uint32_t)(off / sizeof(uint32_t)); for (; cnt > 0; cnt--) { if (*buf != pattern) return false; buf++; pattern++; } return true; } static bool check_value32(const uint32_t* buf, size_t len, uint32_t val) { size_t cnt = len / sizeof(uint32_t); for (; cnt > 0; cnt--) { if (*buf != val) return false; buf++; } return true; } static int WriteZeroChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, uint32_t opflags) { uint32_t data_buf[chunk_len / sizeof(uint32_t)]; assert(is_valid_size(chunk_len)); assert(is_valid_offset(off)); memset(data_buf, 0, chunk_len); return storage_write(handle, off, data_buf, sizeof(data_buf), opflags); } static int WritePatternChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, uint32_t opflags) { uint32_t data_buf[chunk_len / sizeof(uint32_t)]; assert(is_valid_size(chunk_len)); assert(is_valid_offset(off)); fill_pattern32(data_buf, chunk_len, off); return storage_write(handle, off, data_buf, sizeof(data_buf), opflags); } static int WritePatternExt(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len, uint32_t extra_opflags) { size_t written = 0; assert(is_valid_size(data_len)); assert(is_valid_size(chunk_len)); while (data_len) { if (data_len < chunk_len) chunk_len = data_len; int rc = WritePatternChunk(handle, off, chunk_len, (chunk_len == data_len) ? extra_opflags : 0); if (rc < 0) return rc; if ((size_t)rc != chunk_len) return written + rc; off += chunk_len; data_len -= chunk_len; written += chunk_len; } return (int)written; } static int WritePattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len, bool complete) { return WritePatternExt(handle, off, data_len, chunk_len, complete ? STORAGE_OP_COMPLETE : 0); } static int ReadChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, size_t head_len, size_t pattern_len, size_t tail_len) { int rc; uint32_t data_buf[chunk_len / sizeof(uint32_t)]; uint8_t* data_ptr = (uint8_t*)data_buf; assert(is_valid_size(chunk_len)); assert(is_valid_offset(off)); assert((head_len + pattern_len + tail_len) == chunk_len); rc = storage_read(handle, off, data_buf, chunk_len); if ((size_t)rc != chunk_len) return rc; if (head_len) { if (!check_value32((const uint32_t*)data_ptr, head_len, 0)) return ERR_CHECKSUM_FAIL; data_ptr += head_len; off += head_len; } if (pattern_len) { if (!check_pattern32((const uint32_t*)data_ptr, pattern_len, off)) return ERR_CHECKSUM_FAIL; data_ptr += pattern_len; } if (tail_len) { if (!check_value32((const uint32_t*)data_ptr, tail_len, 0)) return ERR_CHECKSUM_FAIL; } return chunk_len; } static int ReadPattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len) { int rc; size_t bytes_read = 0; uint32_t data_buf[chunk_len / sizeof(uint32_t)]; assert(is_valid_size(chunk_len)); assert(is_valid_size(data_len)); assert(is_valid_offset(off)); while (data_len) { if (chunk_len > data_len) chunk_len = data_len; rc = storage_read(handle, off, data_buf, sizeof(data_buf)); if (rc < 0) return rc; if ((size_t)rc != chunk_len) return bytes_read + rc; if (!check_pattern32(data_buf, chunk_len, off)) return ERR_CHECKSUM_FAIL; off += chunk_len; data_len -= chunk_len; bytes_read += chunk_len; } return bytes_read; } static int ReadPatternEOF(file_handle_t handle, storage_off_t off, size_t chunk_len) { int rc; size_t bytes_read = 0; uint32_t data_buf[chunk_len / sizeof(uint32_t)]; assert(is_valid_size(chunk_len)); while (true) { rc = storage_read(handle, off, data_buf, sizeof(data_buf)); if (rc < 0) return rc; if (rc == 0) break; // end of file reached if (!is_valid_size((size_t)rc)) return ERR_BAD_LEN; if (!check_pattern32(data_buf, rc, off)) return ERR_CHECKSUM_FAIL; off += rc; bytes_read += rc; } return bytes_read; } TEST_F(StorageTest, CreateDelete) { int rc; file_handle_t handle; const char* fname = "test_create_delete_file"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc, "delete test file"); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete again"); // create file (expect 0) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "create test file"); // try to create it again while it is still opened (expect // ERR_ALREADY_EXISTS) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); // close it storage_close_file(handle); // try to create it again while it is closed (expect ERR_ALREADY_EXISTS) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); // delete file (expect 0) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "delete test file"); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete again"); test_abort:; } #ifndef STORAGE_FAKE // Moving file and opening directory is not supported in fake secure // storage implementation. static int DeleteAllFiles(storage_session_t ss) { int rc; struct storage_open_dir_state* dir; char file_name_dir[64]; uint8_t read_dir_flags; int deleted = 0; // read all test files and delete rc = storage_open_dir(ss, "", &dir); if (rc != 0) { return rc; } while (true) { rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, sizeof(file_name_dir)); if (rc == 0) break; if (rc < 0) { deleted = rc; goto cleanup; } if ((read_dir_flags & STORAGE_FILE_LIST_STATE_MASK) != STORAGE_FILE_LIST_REMOVED) { rc = storage_delete_file(ss, file_name_dir, 0); if (rc != 0) { deleted = rc; goto cleanup; } deleted++; } } rc = storage_end_transaction(ss, true); if (rc != 0) { deleted = rc; goto cleanup; } cleanup: storage_close_dir(ss, dir); return deleted; } TEST_F(StorageTest, ReadAndDelete) { int rc; file_handle_t handle; const char* fname_pat = "test_read_and_delete_%03d_file"; char file_name[64]; struct storage_open_dir_state* dir; char file_name_dir[64]; uint8_t read_dir_flags; int i; int file_count = 100; // clean up dir before the test rc = DeleteAllFiles(ss); ASSERT_LE(0, rc, "cleanup"); // make sure test files do not exist (expect ERR_NOT_FOUND) for (i = 0; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "exists before test"); } // create test files for (i = 0; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_open_file( ss, &handle, file_name, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "create test file"); // close it storage_close_file(handle); } // delete all files by reading the dir rc = DeleteAllFiles(ss); ASSERT_EQ(file_count, rc, "delete_all"); // test empty dir rc = storage_open_dir(ss, "", &dir); ASSERT_EQ(0, rc, "open_dir"); rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, sizeof(file_name_dir)); ASSERT_EQ(0, rc, "read_dir"); ASSERT_EQ(STORAGE_FILE_LIST_END, read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); storage_close_dir(ss, dir); // make sure test files do not exist (expect ERR_NOT_FOUND) for (i = 0; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete after test"); } test_abort: for (i = 0; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); } } TEST_F(StorageTest, CreateMoveDelete) { int rc; file_handle_t handle; const char* fname1 = "test_create_move_delete_1_file"; const char* fname2 = "test_create_move_delete_2_file"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc, "delete test file1"); rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc, "delete test file2"); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete 1 again"); rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete 2 again"); // create file (expect 0) rc = storage_open_file( ss, &handle, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "create test file"); // move file rc = storage_move_file(ss, handle, fname1, fname2, STORAGE_FILE_MOVE_CREATE | STORAGE_FILE_MOVE_CREATE_EXCLUSIVE | STORAGE_FILE_MOVE_OPEN_FILE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "move test file"); // try to create it again while it is still opened (expect // ERR_ALREADY_EXISTS) rc = storage_open_file( ss, &handle, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); // close it storage_close_file(handle); // try to create it again while it is closed (expect ERR_ALREADY_EXISTS) rc = storage_open_file( ss, &handle, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "create again"); // create file1 (expect 0) rc = storage_open_file( ss, &handle, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "create test file"); // move file rc = storage_move_file(ss, handle, fname1, fname2, STORAGE_FILE_MOVE_CREATE | STORAGE_FILE_MOVE_CREATE_EXCLUSIVE | STORAGE_FILE_MOVE_OPEN_FILE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_ALREADY_EXISTS, rc, "move test file"); // close it storage_close_file(handle); // delete file (expect 0) rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "delete test file"); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete again"); test_abort: storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); } TEST_F(StorageTest, FileList) { int rc; file_handle_t handle; struct storage_open_dir_state* dir; const char* fname_pat = "test_file_list_%d_file"; char file_name[64]; char file_name_dir[64]; uint8_t read_dir_flags; int i; int file_count = 100; // clean up dir before the test rc = DeleteAllFiles(ss); ASSERT_LE(0, rc, "cleanup"); // make sure test files do not exist (expect ERR_NOT_FOUND) for (i = 0; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc, "delete 1 again"); } // test empty dir rc = storage_open_dir(ss, "", &dir); ASSERT_EQ(0, rc, "open_dir"); rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, sizeof(file_name_dir)); ASSERT_EQ(0, rc, "read_dir"); ASSERT_EQ(STORAGE_FILE_LIST_END, read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); storage_close_dir(ss, dir); // create file (expect 0) snprintf(file_name, sizeof(file_name), fname_pat, 0); rc = storage_open_file( ss, &handle, file_name, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc, "create test file 1"); // close it storage_close_file(handle); for (i = 1; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_open_file( ss, &handle, file_name, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); ASSERT_EQ(0, rc, "create test file 2"); // close it storage_close_file(handle); } // test read_dir fname1 committed, fname2 added rc = storage_open_dir(ss, "", &dir); ASSERT_EQ(0, rc, "open_dir"); snprintf(file_name, sizeof(file_name), fname_pat, 0); rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, sizeof(file_name_dir)); ASSERT_EQ(strlen(file_name) + 1, rc, "read_dir"); ASSERT_EQ(STORAGE_FILE_LIST_COMMITTED, read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); ASSERT_EQ(0, strcmp(file_name, file_name_dir), "file name"); for (i = 1; i < file_count; i++) { rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, sizeof(file_name_dir)); ASSERT_EQ(strlen(file_name_dir) + 1, rc, "read_dir"); ASSERT_EQ(STORAGE_FILE_LIST_ADDED, read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "dir flags"); ASSERT_NE(0, strcmp(file_name, file_name_dir), "file name"); } rc = storage_read_dir(ss, dir, &read_dir_flags, file_name_dir, sizeof(file_name_dir)); ASSERT_EQ(0, rc, "read_dir"); ASSERT_EQ(STORAGE_FILE_LIST_END, read_dir_flags & STORAGE_FILE_LIST_STATE_MASK, "end dir flag"); storage_close_dir(ss, dir); rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc, "commit"); test_abort: for (i = 0; i < file_count; i++) { snprintf(file_name, sizeof(file_name), fname_pat, i); rc = storage_delete_file(ss, file_name, STORAGE_OP_COMPLETE); } } #endif TEST_F(StorageTest, DeleteOpened) { int rc; file_handle_t handle; const char* fname = "delete_opened_test_file"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); // open/create file (expect 0) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // delete opened file (expect 0) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); // close file storage_close_file(handle); // one more time (expect ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); test_abort:; } TEST_F(StorageTest, OpenNoCreate) { int rc; file_handle_t handle; const char* fname = "test_open_no_create_file"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc); // open non-existing file (expect ERR_NOT_FOUND) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // create file (expect 0) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_close_file(handle); // open existing file (expect 0) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // close it storage_close_file(handle); // delete file (expect 0) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); test_abort:; } TEST_F(StorageTest, OpenOrCreate) { int rc; file_handle_t handle; const char* fname = "test_open_create_file"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc); // open/create a non-existing file (expect 0) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_close_file(handle); // open/create an existing file (expect 0) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_close_file(handle); // delete file (expect 0) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); test_abort:; } TEST_F(StorageTest, OpenCreateDeleteCharset) { int rc; file_handle_t handle; const char* fname = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz_01234.56789"; // open/create file (expect 0) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_close_file(handle); // open/create an existing file (expect 0) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); storage_close_file(handle); // delete file (expect 0) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open again rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); test_abort:; } TEST_F(StorageTest, WriteReadSequential) { int rc; size_t blk = 2048; file_handle_t handle; const char* fname = "test_write_read_sequential"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc); // create file. rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write a bunch of blocks (sequentially) rc = WritePattern(handle, 0, 32 * blk, blk, true); ASSERT_EQ((int)(32 * blk), rc); rc = ReadPattern(handle, 0, 32 * blk, blk); ASSERT_EQ((int)(32 * blk), rc); // close file storage_close_file(handle); // open the same file again rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // read data back (sequentially) and check pattern again rc = ReadPattern(handle, 0, 32 * blk, blk); ASSERT_EQ((int)(32 * blk), rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, OpenTruncate) { int rc; uint32_t val; size_t blk = 2048; file_handle_t handle; const char* fname = "test_open_truncate"; // make sure test file does not exist (expect success or ERR_NOT_FOUND) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc); // create file. rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write some data and read it back rc = WritePatternChunk(handle, 0, blk, true); ASSERT_EQ((int)blk, rc); rc = ReadPattern(handle, 0, blk, blk); ASSERT_EQ((int)blk, rc); // close file storage_close_file(handle); // reopen with truncate rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); /* try to read data back (expect no data) */ rc = storage_read(handle, 0LL, &val, sizeof(val)); ASSERT_EQ(0, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, OpenSame) { int rc; file_handle_t handle1; file_handle_t handle2; file_handle_t handle3; const char* fname = "test_open_same_file"; // open/create file (expect 0) rc = storage_open_file(ss, &handle1, fname, STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_close_file(handle1); // open an existing file first time (expect 0) rc = storage_open_file(ss, &handle1, fname, 0, 0); ASSERT_EQ(0, rc); // open the same file second time (is not allowed) rc = storage_open_file(ss, &handle2, fname, 0, 0); ASSERT_EQ(ERR_NOT_ALLOWED, rc); // delete file (expect 0) rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open deleted file (expect ERR_NOT_FOUND) rc = storage_open_file(ss, &handle3, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // close file storage_close_file(handle1); test_abort:; } TEST_F(StorageTest, OpenMany) { int rc; file_handle_t handles[10]; char filename[10]; const char* fname_fmt = "mf%d"; // open or create a bunch of files (expect 0) for (uint32_t i = 0; i < countof(handles); ++i) { snprintf(filename, sizeof(filename), fname_fmt, i); rc = storage_open_file(ss, &handles[i], filename, STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); } // check that all handles are different for (uint32_t i = 0; i < countof(handles) - 1; i++) { for (uint32_t j = i + 1; j < countof(handles); j++) { ASSERT_NE(handles[i], handles[j]); } } // close them all for (uint32_t i = 0; i < countof(handles); ++i) { storage_close_file(handles[i]); } // open all files without CREATE flags (expect 0) for (uint32_t i = 0; i < countof(handles); ++i) { snprintf(filename, sizeof(filename), fname_fmt, i); rc = storage_open_file(ss, &handles[i], filename, 0, 0); ASSERT_EQ(0, rc); } // check that all handles are different for (uint32_t i = 0; i < countof(handles) - 1; i++) { for (uint32_t j = i + 1; j < countof(handles); j++) { ASSERT_NE(handles[i], handles[j]); } } // close and remove all test files for (uint32_t i = 0; i < countof(handles); ++i) { storage_close_file(handles[i]); snprintf(filename, sizeof(filename), fname_fmt, i); rc = storage_delete_file(ss, filename, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); } test_abort:; } TEST_F(StorageTest, ReadAtEOF) { int rc; uint32_t val; size_t blk = 2048; file_handle_t handle; const char* fname = "test_read_eof"; // open/create/truncate file rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write block at offset 0 rc = WritePatternChunk(handle, 0, blk, true); ASSERT_EQ((int)blk, rc); // close file storage_close_file(handle); // open same file again rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // read the whole block back and check pattern again rc = ReadPattern(handle, 0, blk, blk); ASSERT_EQ((int)blk, rc); // read at end of file (expected 0 bytes) rc = storage_read(handle, blk, &val, sizeof(val)); ASSERT_EQ(0, rc); // partial read at end of the file (expected partial data) rc = ReadPatternEOF(handle, blk / 2, blk); ASSERT_EQ((int)blk / 2, rc); // read past end of file rc = storage_read(handle, blk + 2, &val, sizeof(val)); ASSERT_EQ(ERR_NOT_VALID, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, GetFileSize) { int rc; size_t blk = 2048; storage_off_t size; file_handle_t handle; const char* fname = "test_get_file_size"; // open/create/truncate file. rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // check file size (expect success and size == 0) size = 1; rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, size); // write block rc = WritePatternChunk(handle, 0, blk, true); ASSERT_EQ((int)blk, rc); // check size rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ(blk, size); // write another block rc = WritePatternChunk(handle, blk, blk, true); ASSERT_EQ((int)blk, rc); // check size again rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ(blk * 2, size); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, SetFileSize) { int rc; size_t blk = 2048; storage_off_t size; file_handle_t handle; const char* fname = "test_set_file_size"; // open/create/truncate file. rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // check file size (expect success and size == 0) size = 1; rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, size); // write block rc = WritePatternChunk(handle, 0, blk, true); ASSERT_EQ((int)blk, rc); // check size rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ(blk, size); storage_close_file(handle); // reopen normally rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // check size again rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ(blk, size); // set file size to half rc = storage_set_file_size(handle, blk / 2, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // check size again (should be half of original size) rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ(blk / 2, size); // read data back rc = ReadPatternEOF(handle, 0, blk); ASSERT_EQ((int)blk / 2, rc); // set file size to 0 rc = storage_set_file_size(handle, 0, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // check size again (should be 0) rc = storage_get_file_size(handle, &size); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0LL, size); // try to read again rc = ReadPatternEOF(handle, 0, blk); ASSERT_EQ(0, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, WriteReadAtOffset) { int rc; file_handle_t handle; size_t blk = 2048; size_t blk_cnt = 32; const char* fname = "test_write_at_offset"; // create/truncate file. rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write a bunch of blocks filled with zeroes for (size_t i = 0; i < blk_cnt; i++) { rc = WriteZeroChunk(handle, i * blk, blk, true); ASSERT_EQ((int)blk, rc); } storage_off_t off1 = blk; storage_off_t off2 = blk * (blk_cnt - 1); // write known pattern data at non-zero offset1 rc = WritePatternChunk(handle, off1, blk, true); ASSERT_EQ((int)blk, rc); // write known pattern data at non-zero offset2 rc = WritePatternChunk(handle, off2, blk, true); ASSERT_EQ((int)blk, rc); // read data back at offset1 rc = ReadPattern(handle, off1, blk, blk); ASSERT_EQ((int)blk, rc); // read data back at offset2 rc = ReadPattern(handle, off2, blk, blk); ASSERT_EQ((int)blk, rc); // read partially written data at end of file(expect to get data only, no // padding) rc = ReadPatternEOF(handle, off2 + blk / 2, blk); ASSERT_EQ((int)blk / 2, rc); // read data at offset 0 (expect success and zero data) rc = ReadChunk(handle, 0, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // read data from gap (expect success and zero data) rc = ReadChunk(handle, off1 + blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // read partially written data (start pointing within written data) // (expect to get written data back and zeroes at the end) rc = ReadChunk(handle, off1 + blk / 2, blk, 0, blk / 2, blk / 2); ASSERT_EQ((int)blk, rc); // read partially written data (start pointing within unwritten data) // expect to get zeroes at the beginning and proper data at the end rc = ReadChunk(handle, off1 - blk / 2, blk, blk / 2, blk / 2, 0); ASSERT_EQ((int)blk, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, WriteReadAtOffsetSparse) { int rc; file_handle_t handle; size_t blk = 2048; size_t blk_cnt = 32; const char* fname = "test_write_at_offset"; // create/truncate file. rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_off_t off1 = blk; storage_off_t off2 = blk * (blk_cnt - 1); // write known pattern data at non-zero offset1 rc = WritePatternChunk(handle, off1, blk, true); ASSERT_EQ((int)blk, rc); // write known pattern data at non-zero offset2 rc = WritePatternChunk(handle, off2, blk, true); ASSERT_EQ((int)blk, rc); // read data back at offset1 rc = ReadPattern(handle, off1, blk, blk); ASSERT_EQ((int)blk, rc); // read data back at offset2 rc = ReadPattern(handle, off2, blk, blk); ASSERT_EQ((int)blk, rc); // read partially written data at end of file(expect to get data only, no // padding) rc = ReadPatternEOF(handle, off2 + blk / 2, blk); ASSERT_EQ((int)blk / 2, rc); // read data at offset 0 (expect success and zero data) rc = ReadChunk(handle, 0, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // read data from gap (expect success and zero data) rc = ReadChunk(handle, off1 + blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // read partially written data (start pointing within written data) // (expect to get written data back and zeroes at the end) rc = ReadChunk(handle, off1 + blk / 2, blk, 0, blk / 2, blk / 2); ASSERT_EQ((int)blk, rc); // read partially written data (start pointing within unwritten data) // expect to get zeroes at the beginning and proper data at the end rc = ReadChunk(handle, off1 - blk / 2, blk, blk / 2, blk / 2, 0); ASSERT_EQ((int)blk, rc); // set file size to half way into first written block rc = storage_set_file_size(handle, off1 + blk / 2, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // read partially written data at end of file(expect to get data only, no // padding) rc = ReadPatternEOF(handle, off1, blk); ASSERT_EQ((int)blk / 2, rc); // write known pattern data at non-zero offset2 rc = WritePatternChunk(handle, off2, blk, true); ASSERT_EQ((int)blk, rc); // read data back at offset1 rc = ReadPattern(handle, off1, blk / 2, blk / 2); ASSERT_EQ((int)blk / 2, rc); // read data from gap (expect success and zero data) rc = ReadChunk(handle, off1 + blk / 2, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // write known pattern data at non-zero offset1 - test again with // set_file_size rc = WritePatternChunk(handle, off1, blk, true); ASSERT_EQ((int)blk, rc); // set file size to half way into first written block rc = storage_set_file_size(handle, off1 + blk / 2, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // read partially written data at end of file(expect to get data only, no // padding) rc = ReadPatternEOF(handle, off1, blk); ASSERT_EQ((int)blk / 2, rc); // set file size to offset2 rc = storage_set_file_size(handle, off2, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write known pattern data at non-zero offset2 rc = WritePatternChunk(handle, off2, blk, true); ASSERT_EQ((int)blk, rc); // read data back at offset1 rc = ReadPattern(handle, off1, blk / 2, blk / 2); ASSERT_EQ((int)blk / 2, rc); // read data from gap (expect success and zero data) rc = ReadChunk(handle, off1 + blk / 2, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } #ifndef STORAGE_FAKE // STORAGE_FAKE does not support multiple sessionsa // Leave a small transaction open with uncommitted data blocks. This test should // fit in the cache so no data should need to be written to disk. This can be // used on a newly wiped filesystem to test the cleanup path if storageproxyd // disconnects before anything has been written. TEST_F(StorageInitNoCommitSmallTest, CreatePersistentNoCommitSmall) { int rc; file_handle_t handle; const char* fname = "test_persistent_small_uncommited_file"; rc = storage_open_session(&storage_test_ss_persist, storage_test_client_port); ASSERT_EQ(0, rc); // create/truncate file. rc = storage_open_file( storage_test_ss_persist, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // close but do not delete file storage_close_file(handle); test_abort:; } // Leave large a transaction open with uncommitted data blocks. This can be used // on a newly wiped filesystem to leave it in a state where only data blocks // have been written and expose bugs in how we auto clear the td partition. TEST_F(StorageInitNoCommitLargeTest, CreatePersistentNoCommitLarge) { int rc; file_handle_t handle; size_t blk = 2048; // Size of file should be too large to fit in block cache size_t file_size = 64 * 2048; const char* fname = "test_persistent_large_uncommited_file"; rc = storage_open_session(&storage_test_ss_persist, storage_test_client_port); ASSERT_EQ(0, rc); // create/truncate file. rc = storage_open_file( storage_test_ss_persist, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // write a bunch of blocks filled with pattern rc = WritePattern(handle, 0, file_size, blk, false); ASSERT_EQ((int)file_size, rc); // close but do not delete file storage_close_file(handle); test_abort:; } // Empty test that can be used to close the session created by // CreatePersistentNoCommitSmall or CreatePersistentNoCommitLarge. TEST_F(StorageInitNoCommitCleanupTest, NoCommitCleanup) { // Fixture setup function closes the previous persistent session. } #endif TEST_F(StorageInitTest, CreatePersistent32K) { int rc; file_handle_t handle; size_t blk = 2048; size_t file_size = 32768; const char* fname = "test_persistent_32K_file"; // create/truncate file. Don't commit until the write is complete so we // only perform a single superblock update rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // write a bunch of blocks filled with pattern rc = WritePattern(handle, 0, file_size, blk, true); ASSERT_EQ((int)file_size, rc); // close but do not delete file storage_close_file(handle); test_abort:; } TEST_F(StorageCheckTest, ReadPersistent32k) { int rc; file_handle_t handle; size_t exp_len = 32 * 1024; const char* fname = "test_persistent_32K_file"; // create/truncate file. rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); rc = ReadPatternEOF(handle, 0, 2048); ASSERT_EQ((int)exp_len, rc); rc = ReadPatternEOF(handle, 0, 1024); ASSERT_EQ((int)exp_len, rc); rc = ReadPatternEOF(handle, 0, 332); ASSERT_EQ((int)exp_len, rc); // close but do not delete file storage_close_file(handle); test_abort:; } TEST_F(StorageCleanTest, CleanUpPersistent32K) { int rc; const char* fname = "test_persistent_32K_file"; rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); rc = (rc == ERR_NOT_FOUND) ? 0 : rc; ASSERT_EQ(0, rc); test_abort:; } TEST_F(StorageTest, WriteReadLong) { int rc; file_handle_t handle; size_t wc = 10000; const char* fname = "test_write_read_long"; uint32_t* test_buf_ = malloc(wc * sizeof(uint32_t)); ASSERT_NE(NULL, test_buf_); rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); fill_pattern32(test_buf_, wc * sizeof(uint32_t), 0); rc = storage_write(handle, 0, test_buf_, wc * sizeof(uint32_t), STORAGE_OP_COMPLETE); ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc); rc = storage_read(handle, 0, test_buf_, wc * sizeof(uint32_t)); ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc); bool res = check_pattern32(test_buf_, wc * sizeof(uint32_t), 0); ASSERT_EQ(true, res); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort: if (test_buf_) free(test_buf_); } // Negative tests TEST_F(StorageTest, OpenInvalidFileName) { int rc; file_handle_t handle; const char* fname1 = ""; const char* fname2 = "ffff$ffff"; const char* fname3 = "ffff\\ffff"; char max_name[STORAGE_MAX_NAME_LENGTH_BYTES + 1]; rc = storage_open_file( ss, &handle, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); rc = storage_open_file( ss, &handle, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); rc = storage_open_file( ss, &handle, fname3, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); /* max name */ memset(max_name, 'a', sizeof(max_name)); max_name[sizeof(max_name) - 1] = 0; rc = storage_open_file( ss, &handle, max_name, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); max_name[sizeof(max_name) - 2] = 0; rc = storage_open_file( ss, &handle, max_name, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); storage_close_file(handle); storage_delete_file(ss, max_name, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, BadFileHandle) { int rc; file_handle_t handle; const char* fname = "test_invalid_file_handle"; rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write to invalid file handle uint32_t val = 0xDEDBEEF; rc = storage_write(handle + 1, 0, &val, sizeof(val), STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); // read from invalid handle rc = storage_read(handle + 1, 0, &val, sizeof(val)); ASSERT_EQ(ERR_NOT_VALID, rc); // set size rc = storage_set_file_size(handle + 1, 0, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); // get size storage_off_t fsize = (storage_off_t)(-1); rc = storage_get_file_size(handle + 1, &fsize); ASSERT_EQ(ERR_NOT_VALID, rc); // close (there is no way to check errors here) storage_close_file(handle + 1); storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, ClosedFileHandle) { int rc; file_handle_t handle1; file_handle_t handle2; const char* fname1 = "test_invalid_file_handle1"; const char* fname2 = "test_invalid_file_handle2"; rc = storage_open_file( ss, &handle1, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); rc = storage_open_file( ss, &handle2, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // close first file handle storage_close_file(handle1); // write to invalid file handle uint32_t val = 0xDEDBEEF; rc = storage_write(handle1, 0, &val, sizeof(val), STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); // read from invalid handle rc = storage_read(handle1, 0, &val, sizeof(val)); ASSERT_EQ(ERR_NOT_VALID, rc); // set size rc = storage_set_file_size(handle1, 0, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_VALID, rc); // get size storage_off_t fsize = (storage_off_t)(-1); rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(ERR_NOT_VALID, rc); // close (there is no way to check errors here) storage_close_file(handle1); // clean up storage_close_file(handle2); storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); test_abort:; } #ifndef STORAGE_FAKE TEST_F(StorageTest, TransactDiscardInactive) { int rc; // discard current transaction (there should not be any) rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // try it again rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); test_abort:; } TEST_F(StorageTest, TransactCommitInactive) { int rc; // try to commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // try it again rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); test_abort:; } TEST_F(StorageTest, TransactDiscardWrite) { int rc; file_handle_t handle; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_discard_write"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // write (without commit) rc = WritePattern(handle, 0, exp_len, blk, false); ASSERT_EQ((int)exp_len, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // abort current transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardWriteAppend) { int rc; file_handle_t handle; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_write_append"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data with commit rc = WritePattern(handle, 0, exp_len / 2, blk, true); ASSERT_EQ((int)exp_len / 2, rc); // write data without commit rc = WritePattern(handle, exp_len / 2, exp_len / 2, blk, false); ASSERT_EQ((int)exp_len / 2, rc); // check file size (should be exp_len) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // discard transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // check file size, it should be exp_len/2 rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 2, fsize); // check file data rc = ReadPatternEOF(handle, 0, blk); ASSERT_EQ((int)exp_len / 2, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardWriteRead) { int rc; file_handle_t handle; size_t blk = 2048; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_discard_write_read"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // Fill with zeroes (with commit) for (uint32_t i = 0; i < 32; i++) { rc = WriteZeroChunk(handle, i * blk, blk, true); ASSERT_EQ((int)blk, rc); } // check that test chunk is filled with zeroes rc = ReadChunk(handle, blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // write test pattern (without commit) rc = WritePattern(handle, blk, blk, blk, false); ASSERT_EQ((int)blk, rc); // read it back an check pattern rc = ReadChunk(handle, blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); // abort current transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // read same chunk back (should be filled with zeros) rc = ReadChunk(handle, blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardWriteMany) { int rc; file_handle_t handle1; file_handle_t handle2; size_t blk = 2048; size_t exp_len1 = 32 * 1024; size_t exp_len2 = 31 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname1 = "test_transact_discard_write_file1"; const char* fname2 = "test_transact_discard_write_file2"; // open create truncate (with commit) rc = storage_open_file( ss, &handle1, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open create truncate (with commit) rc = storage_open_file( ss, &handle2, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // file1: fill file with pattern (without commit) rc = WritePattern(handle1, 0, exp_len1, blk, false); ASSERT_EQ((int)exp_len1, rc); // file2: fill file with pattern (without commit) rc = WritePattern(handle2, 0, exp_len2, blk, false); ASSERT_EQ((int)exp_len2, rc); // check file size, it should be exp_len1 rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len1, fsize); // check file size, it should be exp_len2 rc = storage_get_file_size(handle2, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len2, fsize); // discard transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // check file size, it should be 0 rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // check file size, it should be 0 rc = storage_get_file_size(handle2, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // check data rc = ReadPatternEOF(handle1, 0, blk); ASSERT_EQ(0, rc); rc = ReadPatternEOF(handle2, 0, blk); ASSERT_EQ(0, rc); // cleanup storage_close_file(handle1); storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); storage_close_file(handle2); storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardTruncate) { int rc; file_handle_t handle; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_discard_truncate"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // close file storage_close_file(handle); // open truncate file (without commit) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // abort current transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // check file size (should be an oruginal size) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardSetSize) { int rc; file_handle_t handle; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_discard_set_size"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // set file size to half of original (no commit) rc = storage_set_file_size(handle, (storage_off_t)exp_len / 2, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 2, fsize); // set file size to 1/3 of original (no commit) rc = storage_set_file_size(handle, (storage_off_t)exp_len / 3, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 3, fsize); // abort current transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // check file size (should be an original size) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardDelete) { int rc; file_handle_t handle; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_discard_delete"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // close it storage_close_file(handle); // delete file (without commit) rc = storage_delete_file(ss, fname, 0); ASSERT_EQ(0, rc); // try to open it (should fail) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // abort current transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // try to open it rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // check file size (should be an original size) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactDiscardCreate) { int rc; file_handle_t handle; const char* fname = "test_transact_discard_create_excl"; // delete test file just in case storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); // create file (without commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); ASSERT_EQ(0, rc); // close it storage_close_file(handle); // open it again without create and without commit (expect success) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // close it storage_close_file(handle); // abort current transaction rc = storage_end_transaction(ss, false); ASSERT_EQ(0, rc); // open it again without create without commit (expect not found) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // cleanup storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitWrites) { int rc; file_handle_t handle; file_handle_t handle_aux; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_commit_writes"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open the same file in aux session rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); // check file size, it should be 0 rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // write data in primary session (without commit) rc = WritePattern(handle, 0, exp_len / 2, blk, false); ASSERT_EQ((int)exp_len / 2, rc); // write more data in primary (without commit) rc = WritePattern(handle, exp_len / 2, exp_len / 2, blk, false); ASSERT_EQ((int)exp_len / 2, rc); // check file size in aux session, it should still be 0 rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check file size of aux session, should fail rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(ERR_BUSY, rc); // abort transaction in aux session to recover rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // check file size in aux session, it should be exp_len rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // check file size in primary session, it should be exp_len rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // check data in primary session rc = ReadPatternEOF(handle, 0, blk); ASSERT_EQ((int)exp_len, rc); // check data in aux session rc = ReadPatternEOF(handle_aux, 0, blk); ASSERT_EQ((int)exp_len, rc); // cleanup storage_close_file(handle); storage_close_file(handle_aux); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitWrites2) { int rc; file_handle_t handle; file_handle_t handle_aux; size_t blk = 2048; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_commit_writes2"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open the same file in separate session rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // discard transaction in aux_session rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // Fill with zeroes (with commit) for (uint32_t i = 0; i < 8; i++) { rc = WriteZeroChunk(handle, i * blk, blk, true); ASSERT_EQ((int)blk, rc); } // check that test chunks is filled with zeroes rc = ReadChunk(handle, blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); rc = ReadChunk(handle, 2 * blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // write test pattern (without commit) rc = WritePattern(handle, blk, blk, blk, false); ASSERT_EQ((int)blk, rc); // write test pattern (without commit) rc = WritePattern(handle, 2 * blk, blk, blk, false); ASSERT_EQ((int)blk, rc); // read it back and check pattern rc = ReadChunk(handle, blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); rc = ReadChunk(handle, 2 * blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); // In aux session it still should be empty rc = ReadChunk(handle_aux, blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); rc = ReadChunk(handle_aux, 2 * blk, blk, blk, 0, 0); ASSERT_EQ((int)blk, rc); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // read same chunk back in primary session rc = ReadChunk(handle, blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); rc = ReadChunk(handle, 2 * blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); // read same chunk back in aux session rc = ReadChunk(handle_aux, blk, blk, 0, blk, 0); ASSERT_EQ(ERR_BUSY, rc); rc = ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0); ASSERT_EQ(ERR_BUSY, rc); // abort transaction in aux session rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // read same chunk again in aux session rc = ReadChunk(handle_aux, blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); rc = ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0); ASSERT_EQ((int)blk, rc); // cleanup storage_close_file(handle); storage_close_file(handle_aux); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitSetSize) { int rc; file_handle_t handle; file_handle_t handle_aux; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_commit_set_size"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open the same file in separate session rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // same in aux session rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // set file size to half of original (no commit) rc = storage_set_file_size(handle, (storage_off_t)exp_len / 2, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 2, fsize); rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // set file size to 1/3 of original (no commit) rc = storage_set_file_size(handle, (storage_off_t)exp_len / 3, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 3, fsize); rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check file size (should be 1/3 of an original size) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 3, fsize); // check file size from aux session rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(ERR_BUSY, rc); // abort transaction rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // check again rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 3, fsize); // write data, increasing file size to exp_len (no commit) rc = WritePattern(handle, 0, exp_len, blk, false); ASSERT_EQ((int)exp_len, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // abort aux transaction rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // check file size from aux session rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 3, fsize); // set file size without actually changing size, but ask to commit rc = storage_set_file_size(handle, (storage_off_t)exp_len, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // abort aux transaction rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // check file size from aux session rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // cleanup storage_close_file(handle); storage_close_file(handle_aux); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitDelete) { int rc; file_handle_t handle; file_handle_t handle_aux; size_t blk = 2048; size_t exp_len = 32 * 1024; const char* fname = "test_transact_commit_delete"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // close it storage_close_file(handle); // open the same file in separate session rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); storage_close_file(handle_aux); // delete file (without commit) rc = storage_delete_file(ss, fname, 0); ASSERT_EQ(0, rc); // try to open it (should fail) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // open the same file in separate session (should be fine) rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); storage_close_file(handle_aux); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // try to open it (still fails) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // open the same file in separate session (should fail) rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); test_abort:; } TEST_F(StorageTest, TransactCommitTruncate) { int rc; file_handle_t handle; file_handle_t handle_aux; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_commit_truncate"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // close file storage_close_file(handle); // check from different session rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // open truncate file (without commit) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check file size (should be 0) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // check file size in aux session (should be ERR_BUSY) rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(ERR_BUSY, rc); // abort transaction in aux session rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // check again rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // cleanup storage_close_file(handle); storage_close_file(handle_aux); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitCreate) { int rc; file_handle_t handle; file_handle_t handle_aux; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_commit_create"; // delete test file just in case storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); // check from different session rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // create file (without commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); ASSERT_EQ(0, rc); // check file size rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // close file storage_close_file(handle); // check from aux session (should fail) rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check open from normal session rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // check open from aux session (should succeed) rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); // cleanup storage_close_file(handle); storage_close_file(handle_aux); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitCreateMany) { int rc; file_handle_t handle1; file_handle_t handle2; file_handle_t handle1_aux; file_handle_t handle2_aux; storage_off_t fsize = (storage_off_t)(-1); const char* fname1 = "test_transact_commit_create1"; const char* fname2 = "test_transact_commit_create2"; // delete test file just in case storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); // create file (without commit) rc = storage_open_file( ss, &handle1, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); ASSERT_EQ(0, rc); // create file (without commit) rc = storage_open_file( ss, &handle2, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); ASSERT_EQ(0, rc); // check file sizes rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // close files storage_close_file(handle1); storage_close_file(handle2); rc = storage_open_file(ss_aux, &handle1_aux, fname1, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); rc = storage_open_file(ss_aux, &handle2_aux, fname2, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // open from primary session rc = storage_open_file(ss, &handle1, fname1, 0, 0); ASSERT_EQ(0, rc); rc = storage_open_file(ss, &handle2, fname2, 0, 0); ASSERT_EQ(0, rc); // open from aux session rc = storage_open_file(ss_aux, &handle1_aux, fname1, 0, 0); ASSERT_EQ(0, rc); rc = storage_open_file(ss_aux, &handle2_aux, fname2, 0, 0); ASSERT_EQ(0, rc); // cleanup storage_close_file(handle1); storage_close_file(handle1_aux); storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); storage_close_file(handle2); storage_close_file(handle2_aux); storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitWriteMany) { int rc; file_handle_t handle1; file_handle_t handle2; file_handle_t handle1_aux; file_handle_t handle2_aux; size_t blk = 2048; size_t exp_len1 = 32 * 1024; size_t exp_len2 = 31 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname1 = "test_transact_commit_write_file1"; const char* fname2 = "test_transact_commit_write_file2"; // open create truncate (with commit) rc = storage_open_file( ss, &handle1, fname1, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open create truncate (with commit) rc = storage_open_file( ss, &handle2, fname2, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // open same files from aux session rc = storage_open_file(ss_aux, &handle1_aux, fname1, 0, 0); ASSERT_EQ(0, rc); rc = storage_open_file(ss_aux, &handle2_aux, fname2, 0, 0); ASSERT_EQ(0, rc); // file1: fill file with pattern (without commit) rc = WritePattern(handle1, 0, exp_len1, blk, false); ASSERT_EQ((int)exp_len1, rc); // file2: fill file with pattern (without commit) rc = WritePattern(handle2, 0, exp_len2, blk, false); ASSERT_EQ((int)exp_len2, rc); // check file size, it should be exp_len1 rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len1, fsize); // check file size, it should be exp_len2 rc = storage_get_file_size(handle2, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len2, fsize); // check file sizes from aux session (should be 0) rc = storage_get_file_size(handle1_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); rc = storage_get_file_size(handle2_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)0, fsize); // commit transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check file size, it should be exp_len1 rc = storage_get_file_size(handle1, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len1, fsize); // check file size, it should be exp_len2 rc = storage_get_file_size(handle2, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len2, fsize); // from aux session rc = storage_get_file_size(handle1_aux, &fsize); ASSERT_EQ(ERR_BUSY, rc); rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); rc = storage_get_file_size(handle1_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len1, fsize); rc = storage_get_file_size(handle2_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len2, fsize); // check data rc = ReadPatternEOF(handle1, 0, blk); ASSERT_EQ((int)exp_len1, rc); rc = ReadPatternEOF(handle2, 0, blk); ASSERT_EQ((int)exp_len2, rc); rc = ReadPatternEOF(handle1_aux, 0, blk); ASSERT_EQ((int)exp_len1, rc); rc = ReadPatternEOF(handle2_aux, 0, blk); ASSERT_EQ((int)exp_len2, rc); // cleanup storage_close_file(handle1); storage_close_file(handle1_aux); storage_delete_file(ss, fname1, STORAGE_OP_COMPLETE); storage_close_file(handle2); storage_close_file(handle2_aux); storage_delete_file(ss, fname2, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactCommitDeleteCreate) { int rc; file_handle_t handle; file_handle_t handle_aux; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_delete_create"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, exp_len, blk, true); ASSERT_EQ((int)exp_len, rc); // close it storage_close_file(handle); // delete file (without commit) rc = storage_delete_file(ss, fname, 0); ASSERT_EQ(0, rc); // try to open it (should fail) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(ERR_NOT_FOUND, rc); // try to open it in aux session (should succeed) rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); // create file with the same name (no commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE, 0); ASSERT_EQ(0, rc); // write half of data (with commit) rc = WritePattern(handle, 0, exp_len / 2, blk, true); ASSERT_EQ((int)exp_len / 2, rc); // check file size (should be half) rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 2, fsize); // commit transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check data rc = ReadPatternEOF(handle, 0, blk); ASSERT_EQ((int)exp_len / 2, rc); // check from aux session rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(ERR_NOT_VALID, rc); // abort trunsaction rc = storage_end_transaction(ss_aux, false); ASSERT_EQ(0, rc); // and try again rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(ERR_NOT_VALID, rc); // close file and reopen it again storage_close_file(handle_aux); rc = storage_open_file(ss_aux, &handle_aux, fname, 0, 0); ASSERT_EQ(0, rc); rc = storage_get_file_size(handle_aux, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len / 2, fsize); rc = ReadPatternEOF(handle_aux, 0, blk); ASSERT_EQ((int)exp_len / 2, rc); // cleanup storage_close_file(handle); storage_close_file(handle_aux); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactRewriteExistingTruncate) { int rc; file_handle_t handle; size_t blk = 2048; const char* fname = "test_transact_rewrite_existing_truncate"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // close it storage_close_file(handle); // up for (uint32_t i = 1; i < 32; i++) { // open truncate (no commit) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, i * blk, blk, true); ASSERT_EQ((int)(i * blk), rc); // close storage_close_file(handle); } // down for (uint32_t i = 1; i < 32; i++) { // open truncate (no commit) rc = storage_open_file(ss, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, (32 - i) * blk, blk, true); ASSERT_EQ((int)((32 - i) * blk), rc); // close storage_close_file(handle); } // cleanup storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactRewriteExistingSetSize) { int rc; file_handle_t handle; size_t blk = 2048; const char* fname = "test_transact_rewrite_existing_set_size"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // close it storage_close_file(handle); // up for (unsigned int i = 1; i < 32; i++) { // open truncate (no commit) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, i * blk, blk, false); ASSERT_EQ((int)(i * blk), rc); // update size (with commit) rc = storage_set_file_size(handle, i * blk, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // close storage_close_file(handle); } // down for (unsigned int i = 1; i < 32; i++) { // open trancate (no commit) rc = storage_open_file(ss, &handle, fname, 0, 0); ASSERT_EQ(0, rc); // write data (with commit) rc = WritePattern(handle, 0, (32 - i) * blk, blk, false); ASSERT_EQ((int)((32 - i) * blk), rc); // update size (with commit) rc = storage_set_file_size(handle, (32 - i) * blk, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // close storage_close_file(handle); } // cleanup storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } TEST_F(StorageTest, TransactResumeAfterNonFatalError) { int rc; file_handle_t handle; file_handle_t handle1; size_t blk = 2048; size_t exp_len = 32 * 1024; storage_off_t fsize = (storage_off_t)(-1); const char* fname = "test_transact_resume_writes"; // open create truncate file (with commit) rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(0, rc); // write (without commit) rc = WritePattern(handle, 0, exp_len / 2, blk, false); ASSERT_EQ((int)exp_len / 2, rc); // issue some commands that should fail with non-fatal errors // read past end of file uint32_t val = 0xDEADBEEF; rc = storage_read(handle, exp_len / 2 + 1, &val, sizeof(val)); ASSERT_EQ(ERR_NOT_VALID, rc); // open non existing file rc = storage_open_file(ss, &handle1, "foo", STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); // delete non-existing file rc = storage_delete_file(ss, "foo", STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); // then resume writinga (without commit) rc = WritePattern(handle, exp_len / 2, exp_len / 2, blk, false); ASSERT_EQ((int)exp_len / 2, rc); // commit current transaction rc = storage_end_transaction(ss, true); ASSERT_EQ(0, rc); // check file size, it should be exp_len rc = storage_get_file_size(handle, &fsize); ASSERT_EQ(0, rc); ASSERT_EQ((storage_off_t)exp_len, fsize); // check data rc = ReadPatternEOF(handle, 0, blk); ASSERT_EQ((int)exp_len, rc); // cleanup storage_close_file(handle); storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); test_abort:; } /* * Test that every API that should allow checkpointing does (when provisioning * is enabled) */ TEST_F(StorageTest, ENABLED_ON_EMULATOR_ONLY(CheckpointApis)) { int rc; file_handle_t handle; size_t blk = 2048; size_t len = 32 * blk; const char* fname = "test_checkpoint_create"; const char* new_fname = "test_checkpoint_move"; if (!system_state_provisioning_allowed()) { trusty_unittest_printf( "[ SKIPPED ] CheckpointApis - Provisioning is not allowed\n"); return; } /* We don't allow OP_CHECKPOINT without STORAGE_OP_COMPLETE */ rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_CHECKPOINT); ASSERT_EQ(ERR_NOT_VALID, rc); /* open create truncate file (with checkpoint) */ rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ(0, rc); /* move the file and checkpoint */ rc = storage_move_file( ss, handle, fname, new_fname, STORAGE_FILE_MOVE_CREATE | STORAGE_FILE_MOVE_OPEN_FILE, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ(0, rc); /* write to the file and checkpoint */ rc = WritePatternExt(handle, 0, len, blk, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ((int)len, rc); rc = ReadPattern(handle, 0, len, blk); ASSERT_EQ((int)len, rc); /* cleanup */ storage_close_file(handle); rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); rc = storage_delete_file(ss, new_fname, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ(0, rc); test_abort:; } TEST_F(StorageTest, ENABLED_ON_EMULATOR_ONLY(CheckpointRequiresProvisioningAllowed)) { int rc; file_handle_t handle; const char* fname = "test_checkpoint_create"; const char* new_fname = "test_checkpoint_move"; if (system_state_provisioning_allowed()) { trusty_unittest_printf( "[ SKIPPED ] CheckpointRequiresProvisioningAllowed - " "Provisioning is allowed\n"); return; } /* We don't allow OP_CHECKPOINT without STORAGE_OP_COMPLETE */ rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_CHECKPOINT); ASSERT_EQ(ERR_NOT_VALID, rc); /* open create truncate file (with checkpoint) */ rc = storage_open_file( ss, &handle, fname, STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ(ERR_NOT_ALLOWED, rc); /* move the file and checkpoint */ rc = storage_move_file( ss, handle, fname, new_fname, STORAGE_FILE_MOVE_CREATE | STORAGE_FILE_MOVE_OPEN_FILE, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ(ERR_NOT_ALLOWED, rc); rc = storage_delete_file(ss, fname, STORAGE_OP_COMPLETE); ASSERT_EQ(ERR_NOT_FOUND, rc); rc = storage_delete_file(ss, new_fname, STORAGE_OP_COMPLETE | STORAGE_OP_CHECKPOINT); ASSERT_EQ(ERR_NOT_ALLOWED, rc); test_abort:; } #endif #define RUN_MODE_ALL NULL #define RUN_MODE_INIT_NO_COMMIT_SMALL "StorageInitNoCommitSmallTest" #define RUN_MODE_INIT_NO_COMMIT_LARGE "StorageInitNoCommitLargeTest" #define RUN_MODE_INIT_NO_COMMIT_CLEANUP "StorageInitNoCommitCleanupTest" #define RUN_MODE_INIT "StorageInitTest" #define RUN_MODE_CHECK "StorageCheckTest" #define RUN_MODE_CLEAN "StorageCleanTest" static bool run_all_tests(const char* port, const char* suite) { int rc; storage_session_t session; TLOGI("SS-unittest: %s: waiting for server\n", port); do { rc = storage_open_session(&session, port); if (rc < 0) { TLOGE("failed (%d) to connect to storage server - retrying\n", rc); #ifndef STORAGE_FAKE trusty_nanosleep(0, 0, 1000000); #endif } } while (rc < 0); storage_close_session(session); storage_test_client_port = port; return RUN_ALL_SUITE_TESTS(suite); } #ifdef STORAGE_FAKE int main(void) { bool passed = run_all_tests(STORAGE_CLIENT_TD_PORT, RUN_MODE_ALL); // run_all_tests(STORAGE_CLIENT_TDEA_PORT, RUN_MODE_ALL); // run_all_tests(STORAGE_CLIENT_TP_PORT, RUN_MODE_ALL); return passed ? 0 : 1; } #else struct storage_unittest { struct unittest unittest; const char* port; const char* run_mode; }; static bool run_test(struct unittest* test) { struct storage_unittest* storage_test = containerof(test, struct storage_unittest, unittest); return run_all_tests(storage_test->port, storage_test->run_mode); } #define PORT_BASE "com.android.storage-unittest." #define DEFINE_STORAGE_UNIT_TEST(fs, fs_name, run_mode_val, run_mode_name) \ { \ .unittest = \ { \ .port_name = PORT_BASE fs_name run_mode_name, \ .run_test = run_test, \ }, \ .port = (fs), .run_mode = (run_mode_val), \ } #define DEFINE_STORAGE_UNIT_TESTS_FS(fs, fs_name) \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, RUN_MODE_ALL, ""), \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, \ RUN_MODE_INIT_NO_COMMIT_SMALL, \ ".initnocommitsmall"), \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, \ RUN_MODE_INIT_NO_COMMIT_LARGE, \ ".initnocommitlarge"), \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, \ RUN_MODE_INIT_NO_COMMIT_CLEANUP, \ ".initnocommitcleanup"), \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, RUN_MODE_INIT, ".init"), \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, RUN_MODE_CHECK, ".check"), \ DEFINE_STORAGE_UNIT_TEST((fs), fs_name, RUN_MODE_CLEAN, ".clean") int main(void) { static struct storage_unittest storage_unittests[] = { DEFINE_STORAGE_UNIT_TESTS_FS(STORAGE_CLIENT_NSP_PORT, "nsp"), DEFINE_STORAGE_UNIT_TESTS_FS(STORAGE_CLIENT_TD_PORT, "td"), DEFINE_STORAGE_UNIT_TESTS_FS(STORAGE_CLIENT_TDP_PORT, "tdp"), DEFINE_STORAGE_UNIT_TESTS_FS(STORAGE_CLIENT_TDEA_PORT, "tdea"), DEFINE_STORAGE_UNIT_TESTS_FS(STORAGE_CLIENT_TP_PORT, "tp"), }; static struct unittest* unittests[countof(storage_unittests)]; for (size_t i = 0; i < countof(storage_unittests); i++) { unittests[i] = &storage_unittests[i].unittest; } return unittest_main(unittests, countof(unittests)); } #endif