#include "storage/store.h" #include "sys/system.h" #include "mcp/tools/common/history.h" #include #include #include #include "base/allocator.h" static int failures; #define CHECK(value) \ do \ { \ if (!(value)) \ { \ fprintf(stderr, "CHECK failed at line %d: %s\n", __LINE__, #value); \ failures++; \ } \ } \ while (0) static char * duplicate_string(const char *value) { const size_t length = strlen(value); char *copy = malloc(length + 1); if (copy != NULL) { memcpy(copy, value, length + 1); } return copy; } int main(void) { char *temporary_root = tiny_sys_temp_directory_create("tcm"); char *directory; char *log_path; char *temporary_path; char *lock_path; char *snapshot_path; tiny_store_t *store = NULL, *second = NULL; tiny_message_t input, first, second_message; tiny_store_status_t status; tiny_sys_file_t log; tiny_rebuild_report_t report; unsigned char byte; uint32_t count; tiny_store_health_t health; size_t operation; static const tiny_sys_test_operation_t rebuild_failures[] = { TINY_SYS_TEST_OPEN, TINY_SYS_TEST_RANDOM, TINY_SYS_TEST_WRITE, TINY_SYS_TEST_FLUSH, TINY_SYS_TEST_REPLACE }; CHECK(tiny_store_open(NULL, "unused") == TINY_STORE_IO_ERROR); store = (tiny_store_t *)(uintptr_t)1; CHECK(tiny_store_open(&store, NULL) == TINY_STORE_IO_ERROR && store == NULL); CHECK(tiny_store_entry_count(NULL) == 0); CHECK(tiny_store_entry(NULL, 0) == NULL); memset(&health, 0xff, sizeof(health)); tiny_store_health(NULL, &health); CHECK(health.active_messages == 0 && health.log_size == 0); CHECK(temporary_root != NULL); if (temporary_root == NULL) { return 1; } directory = tiny_sys_path_join(temporary_root, "日本語-memory"); CHECK(directory != NULL && tiny_sys_directory_create(directory) == TINY_SYS_OK); if (directory == NULL) { free(temporary_root); return 1; } log_path = tiny_sys_path_join(directory, "active.log"); temporary_path = tiny_sys_path_join(directory, "active.tmp"); lock_path = tiny_sys_path_join(directory, "store.lock"); snapshot_path = tiny_sys_path_join(directory, "snapshot.bin"); CHECK(log_path != NULL && temporary_path != NULL && lock_path != NULL && snapshot_path != NULL); status = tiny_store_open(&store, directory); CHECK(status == TINY_STORE_OK); CHECK(tiny_store_open(&second, directory) == TINY_STORE_BUSY); tiny_message_init(&input); tiny_message_init(&first); tiny_message_init(&second_message); input.session_id = duplicate_string("unit"); input.content = duplicate_string("first"); input.role = TINY_ROLE_USER; CHECK(tiny_store_write(store, &input, &first) == TINY_STORE_OK); free(input.content); input.content = duplicate_string("second"); CHECK(tiny_store_write(store, &input, &second_message) == TINY_STORE_OK); CHECK(strcmp(first.message_id, second_message.message_id) < 0); { tiny_message_patch_t patch; tiny_message_t updated; tiny_metadata_t metadata = first.metadata; tiny_sys_io_counters_t counters; bool changed = false; metadata.importance = 80; memset(&patch, 0, sizeof(patch)); patch.metadata = &metadata; patch.metadata_mask = TINY_METADATA_IMPORTANCE; patch.expected_revision = 1; patch.has_expected_revision = true; tiny_message_init(&updated); tiny_sys_io_counters_reset(); CHECK(tiny_store_update(store, first.message_id, &patch, &updated, &changed) == TINY_STORE_OK); tiny_sys_io_counters_get(&counters); CHECK(changed && updated.revision == 2 && updated.metadata.importance == 80); CHECK(counters.flush_calls == 1 && counters.write_calls == 2); CHECK(counters.write_bytes < 8192); tiny_message_free(&updated); patch.expected_revision = 2; changed = true; tiny_sys_io_counters_reset(); CHECK(tiny_store_update(store, first.message_id, &patch, &updated, &changed) == TINY_STORE_OK); tiny_sys_io_counters_get(&counters); CHECK(!changed && counters.write_bytes == 0 && counters.write_calls == 0 && counters.flush_calls == 0); tiny_message_free(&updated); patch.expected_revision = 1; CHECK(tiny_store_update(store, first.message_id, &patch, &updated, &changed) == TINY_STORE_CONFLICT); } tiny_message_free(&input); tiny_message_free(&first); tiny_message_free(&second_message); tiny_store_close(store); store = NULL; CHECK(tiny_store_open(&store, directory) == TINY_STORE_OK); CHECK(tiny_store_entry_count(store) == 2); CHECK(tiny_store_entry(store, 0)->message.revision == 2); CHECK(tiny_store_entry(store, 0)->message.metadata.importance == 80); { size_t failure_index; bool built = false; for (failure_index = 0; failure_index < 64; ++failure_index) { tiny_history_index_t *history; size_t baseline = tiny_allocator_outstanding(); tiny_allocator_fail_after(failure_index); history = tiny_history_index_build(store); tiny_allocator_reset(); if (history) { const size_t *entries = NULL; size_t entry_count = 0; CHECK(tiny_history_index_session(history, "unit", &entries, &entry_count)); CHECK(entry_count == 2 && entries[0] == 0 && entries[1] == 1); tiny_history_index_free(history); CHECK(tiny_allocator_outstanding() == baseline); built = true; break; } CHECK(tiny_allocator_outstanding() == baseline); } CHECK(built); } for (operation = 0; operation < sizeof(rebuild_failures) / sizeof(rebuild_failures[0]); ++operation) { tiny_sys_test_reset(); CHECK(tiny_sys_test_fail_after(rebuild_failures[operation], 1)); CHECK(tiny_store_rebuild(store, &report) != TINY_STORE_OK); CHECK(tiny_store_entry_count(store) == 2); tiny_sys_test_reset(); (void)tiny_sys_file_remove(temporary_path); } CHECK(tiny_sys_directory_create(temporary_path) == TINY_SYS_OK); CHECK(tiny_store_rebuild(store, &report) == TINY_STORE_IO_ERROR); CHECK(tiny_store_entry_count(store) == 2); CHECK(tiny_sys_directory_remove(temporary_path)); tiny_store_close(store); store = NULL; CHECK(tiny_sys_file_open(&log, log_path, TINY_SYS_FILE_READ_WRITE) == TINY_SYS_OK); CHECK(tiny_sys_file_seek(&log, -1, TINY_SYS_SEEK_END)); CHECK(tiny_sys_file_read(&log, &byte, 1, &count) && count == 1); byte ^= 0x5a; CHECK(tiny_sys_file_seek(&log, -1, TINY_SYS_SEEK_CURRENT)); CHECK(tiny_sys_file_write(&log, &byte, 1, &count) && count == 1); tiny_sys_file_close(&log); CHECK(tiny_store_open(&store, directory) == TINY_STORE_CORRUPT); (void)tiny_sys_file_remove(log_path); (void)tiny_sys_file_remove(temporary_path); (void)tiny_sys_file_remove(lock_path); (void)tiny_sys_file_remove(snapshot_path); (void)tiny_sys_directory_remove(directory); (void)tiny_sys_directory_remove(temporary_root); free(snapshot_path); free(lock_path); free(temporary_path); free(log_path); free(directory); free(temporary_root); return failures == 0 ? 0 : 1; }