From 4bc721c3bf7589b519c5730d093f704ce56e3f09 Mon Sep 17 00:00:00 2001 From: tanaka takashi Date: Mon, 29 Jun 2026 21:43:54 +0900 Subject: refactor: MCP層をmcpc APIベースに刷新 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 独自JSON-RPC処理をmcpcへ置き換え - 各MCPツールを個別ソースへ分離 - 共通コンテキストと登録処理を整理 - mcpc互換修正と統合テストを更新 --- tests/boundary_test.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 tests/boundary_test.c (limited to 'tests/boundary_test.c') diff --git a/tests/boundary_test.c b/tests/boundary_test.c new file mode 100644 index 0000000..452e2ee --- /dev/null +++ b/tests/boundary_test.c @@ -0,0 +1,275 @@ +#include +#include +#include +#include + +#include "support/test.h" + +#include "base/allocator.h" +#include "mcp/json.h" +#include "storage/codec.h" +#include "sys/encoding.h" +#include "sys/system.h" + +static void +test_allocator_boundaries(tiny_test_state_t *state) +{ + void *first; + void *resized; + size_t baseline = tiny_allocator_outstanding(); + TINY_TEST_CASE(state, "allocator overflow failure positions and balance"); + TINY_EXPECT(state, tiny_calloc(SIZE_MAX, 2) == NULL); + tiny_allocator_fail_after(0); + TINY_EXPECT(state, tiny_alloc(1) == NULL); + TINY_EXPECT_EQ_U64(state, 1, tiny_allocator_call_count()); + tiny_allocator_reset(); + first = tiny_alloc(1); + TINY_EXPECT(state, first != NULL); + tiny_allocator_fail_after(0); + resized = tiny_realloc(first, 2); + TINY_EXPECT(state, resized == NULL); + tiny_allocator_reset(); + tiny_free(first); + TINY_EXPECT_EQ_U64(state, baseline, tiny_allocator_outstanding()); + first = tiny_alloc(1); + TINY_EXPECT(state, tiny_realloc(first, 0) == NULL); + TINY_EXPECT_EQ_U64(state, baseline, tiny_allocator_outstanding()); + tiny_free(NULL); +} + +static void +test_encoding_valid(tiny_test_state_t *state) +{ + static const struct vector + { + const char *utf8; + size_t utf8_length; + uint16_t utf16[3]; + size_t utf16_length; + } vectors[] = { + { "", 0, { 0 }, 0 }, + { "\x7f", 1, { 0x007f }, 1 }, + { "\xc2\x80", 2, { 0x0080 }, 1 }, + { "\xdf\xbf", 2, { 0x07ff }, 1 }, + { "\xe0\xa0\x80", 3, { 0x0800 }, 1 }, + { "\xed\x9f\xbf", 3, { 0xd7ff }, 1 }, + { "\xee\x80\x80", 3, { 0xe000 }, 1 }, + { "\xef\xbf\xbf", 3, { 0xffff }, 1 }, + { "\xf0\x90\x80\x80", 4, { 0xd800, 0xdc00 }, 2 }, + { "\xf4\x8f\xbf\xbf", 4, { 0xdbff, 0xdfff }, 2 }, + }; + size_t index; + TINY_TEST_CASE(state, "encoding valid scalar boundaries"); + for (index = 0; index < sizeof(vectors) / sizeof(vectors[0]); ++index) + { + uint16_t *wide = NULL; + char *roundtrip = NULL; + size_t wide_length = SIZE_MAX, roundtrip_length = SIZE_MAX; + TINY_EXPECT( + state, + tiny_utf8_to_utf16(vectors[index].utf8, vectors[index].utf8_length, &wide, &wide_length) + == TINY_ENCODING_OK); + TINY_EXPECT_EQ_U64(state, vectors[index].utf16_length, wide_length); + TINY_EXPECT_BYTES(state, vectors[index].utf16, wide, wide_length * sizeof(*wide)); + TINY_EXPECT(state, + tiny_utf16_to_utf8(wide, wide_length, &roundtrip, &roundtrip_length) + == TINY_ENCODING_OK); + TINY_EXPECT_EQ_U64(state, vectors[index].utf8_length, roundtrip_length); + TINY_EXPECT_BYTES(state, vectors[index].utf8, roundtrip, roundtrip_length); + free(roundtrip); + free(wide); + } +} + +static void +test_encoding_invalid(tiny_test_state_t *state) +{ + static const unsigned char invalid[][4] = { + { 0x80, 0, 0, 0 }, { 0xc0, 0x80, 0, 0 }, { 0xe0, 0x80, 0x80, 0 }, + { 0xed, 0xa0, 0x80, 0 }, { 0xf0, 0x80, 0x80, 0x80 }, { 0xf4, 0x90, 0x80, 0x80 }, + }; + static const size_t lengths[] = { 1, 2, 3, 3, 4, 4 }; + size_t index; + TINY_TEST_CASE(state, "encoding invalid and allocation failure"); + for (index = 0; index < sizeof(lengths) / sizeof(lengths[0]); ++index) + { + uint16_t *output = (uint16_t *)(uintptr_t)1; + size_t length = SIZE_MAX; + TINY_EXPECT(state, + tiny_utf8_to_utf16((const char *)invalid[index], lengths[index], &output, &length) + == TINY_ENCODING_INVALID); + TINY_EXPECT(state, output == NULL && length == 0); + } + { + uint16_t high[] = { 0xd800 }; + uint16_t low[] = { 0xdc00 }; + char *output = NULL; + size_t length = 0; + TINY_EXPECT(state, tiny_utf16_to_utf8(high, 1, &output, &length) == TINY_ENCODING_INVALID); + TINY_EXPECT(state, tiny_utf16_to_utf8(low, 1, &output, &length) == TINY_ENCODING_INVALID); + } + TINY_EXPECT(state, tiny_utf8_to_utf16(NULL, 1, NULL, NULL) == TINY_ENCODING_INVALID); + { + uint16_t *output = NULL; + size_t length = 0; + tiny_allocator_fail_after(0); + TINY_EXPECT(state, tiny_utf8_to_utf16("a", 1, &output, &length) == TINY_ENCODING_NO_MEMORY); + TINY_EXPECT(state, output == NULL && length == 0); + tiny_allocator_reset(); + } +} + +static void +test_codec_alignment(tiny_test_state_t *state) +{ + unsigned char storage[32]; + size_t offset; + TINY_TEST_CASE(state, "codec every alignment offset"); + for (offset = 0; offset < 16; ++offset) + { + unsigned char *position = storage + offset; + memset(storage, 0, sizeof(storage)); + tiny_codec_put_u32(position, UINT32_MAX); + TINY_EXPECT_EQ_U64(state, UINT32_MAX, tiny_codec_get_u32(position)); + tiny_codec_put_u64(position, UINT64_MAX); + TINY_EXPECT_EQ_U64(state, UINT64_MAX, tiny_codec_get_u64(position)); + } + TINY_EXPECT_EQ_U64(state, 0, tiny_codec_crc32(NULL, 1)); + TINY_EXPECT_EQ_U64(state, 0, tiny_codec_crc32(NULL, 0)); + tiny_codec_put_u32(NULL, 1); + tiny_codec_put_u64(NULL, 1); + TINY_EXPECT_EQ_U64(state, 0, tiny_codec_get_u32(NULL)); + TINY_EXPECT_EQ_U64(state, 0, tiny_codec_get_u64(NULL)); + TINY_EXPECT_EQ_U64(state, 0xcbf43926U, tiny_codec_crc32((const unsigned char *)"123456789", 9)); +} + +static void +test_json_boundaries(tiny_test_state_t *state) +{ + tiny_json_buffer_t buffer; + static const char controls[] = "\b\f\n\r\t\x01"; + TINY_TEST_CASE(state, "json growth escaping integer and OOM"); + tiny_json_init(&buffer); + tiny_json_init(NULL); + tiny_json_free(NULL); + TINY_EXPECT(state, !tiny_json_raw(NULL, "x")); + TINY_EXPECT(state, !tiny_json_raw(&buffer, NULL)); + TINY_EXPECT(state, !tiny_json_raw_n(&buffer, NULL, 1)); + TINY_EXPECT(state, !tiny_json_string(&buffer, NULL)); + TINY_EXPECT(state, tiny_json_raw_n(&buffer, "", 0)); + TINY_EXPECT(state, tiny_json_string(&buffer, controls)); + TINY_EXPECT(state, strstr(buffer.data, "\\b\\f\\n\\r\\t\\u0001") != NULL); + TINY_EXPECT(state, tiny_json_i64(&buffer, LLONG_MIN)); + TINY_EXPECT(state, tiny_json_u64(&buffer, ULLONG_MAX)); + tiny_json_free(&buffer); + tiny_allocator_fail_after(0); + tiny_json_init(&buffer); + TINY_EXPECT(state, !tiny_json_raw(&buffer, "allocation")); + TINY_EXPECT(state, buffer.failed); + tiny_json_free(&buffer); + tiny_allocator_reset(); +} + +static void +test_sys_boundaries(tiny_test_state_t *state) +{ + char *joined; + char *directory; + char *path; + tiny_sys_file_t file; + uint32_t actual = UINT32_MAX; + uint64_t size = UINT64_MAX; + static const unsigned char bytes[] = { 1, 2, 3, 4 }; + unsigned char received[sizeof(bytes)] = { 0 }; + TINY_TEST_CASE(state, "sys public boundary values"); + tiny_sys_test_reset(); + TINY_EXPECT(state, tiny_sys_temp_directory_create(NULL) == NULL); + TINY_EXPECT(state, tiny_sys_path_join(NULL, "name") == NULL); + TINY_EXPECT(state, tiny_sys_path_join("directory", NULL) == NULL); + joined = tiny_sys_path_join("", "name"); + TINY_EXPECT(state, joined != NULL && strcmp(joined, "name") == 0); + free(joined); + tiny_sys_file_init(NULL); + TINY_EXPECT(state, !tiny_sys_file_valid(NULL)); + tiny_sys_file_init(&file); + TINY_EXPECT(state, !tiny_sys_file_valid(&file)); + TINY_EXPECT(state, tiny_sys_file_open(NULL, "unused", TINY_SYS_FILE_READ) == TINY_SYS_ERROR); + TINY_EXPECT(state, + tiny_sys_file_open(&file, NULL, TINY_SYS_FILE_READ) == TINY_SYS_ERROR + && !tiny_sys_file_valid(&file)); + TINY_EXPECT(state, !tiny_sys_file_seek(&file, 0, (tiny_sys_seek_origin_t)-1)); + TINY_EXPECT(state, !tiny_sys_file_size(&file, &size)); + TINY_EXPECT_EQ_U64(state, 0, size); + TINY_EXPECT(state, !tiny_sys_file_read(&file, received, 1, &actual)); + TINY_EXPECT_EQ_U64(state, 0, actual); + + directory = tiny_sys_temp_directory_create("tcm"); + TINY_EXPECT(state, directory != NULL); + path = directory == NULL ? NULL : tiny_sys_path_join(directory, "fault.bin"); + TINY_EXPECT(state, path != NULL); + if (path != NULL) + { + TINY_EXPECT(state, tiny_sys_file_open(&file, path, TINY_SYS_FILE_WRITE_NEW) == TINY_SYS_OK); + tiny_sys_test_max_transfer(1); + TINY_EXPECT(state, tiny_sys_file_write(&file, bytes, sizeof(bytes), &actual)); + TINY_EXPECT_EQ_U64(state, 1, actual); + TINY_EXPECT(state, tiny_sys_test_fail_after(TINY_SYS_TEST_FLUSH, 1)); + TINY_EXPECT(state, !tiny_sys_file_flush(&file)); + tiny_sys_test_reset(); + TINY_EXPECT(state, tiny_sys_file_flush(&file)); + tiny_sys_file_close(&file); + + TINY_EXPECT(state, tiny_sys_test_fail_after(TINY_SYS_TEST_OPEN, 1)); + TINY_EXPECT(state, + tiny_sys_file_open(&file, path, TINY_SYS_FILE_READ) == TINY_SYS_ERROR + && !tiny_sys_file_valid(&file)); + tiny_sys_test_reset(); + TINY_EXPECT(state, tiny_sys_file_open(&file, path, TINY_SYS_FILE_READ) == TINY_SYS_OK); + TINY_EXPECT(state, tiny_sys_test_fail_after(TINY_SYS_TEST_READ, 1)); + actual = UINT32_MAX; + TINY_EXPECT(state, !tiny_sys_file_read(&file, received, sizeof(received), &actual)); + TINY_EXPECT_EQ_U64(state, 0, actual); + tiny_sys_test_reset(); + tiny_sys_file_close(&file); + TINY_EXPECT(state, tiny_sys_file_remove(path)); + } + free(path); + if (directory != NULL) + { + TINY_EXPECT(state, tiny_sys_directory_remove(directory)); + } + free(directory); + tiny_sys_test_reset(); +} + +int +main(int argc, char **argv) +{ + tiny_test_state_t state = { "initial", 0, 0 }; + const char *group = argc > 1 ? argv[1] : "all"; + tiny_allocator_reset(); + if (strcmp(group, "all") == 0 || strcmp(group, "allocator") == 0) + { + test_allocator_boundaries(&state); + } + if (strcmp(group, "all") == 0 || strcmp(group, "encoding") == 0) + { + test_encoding_valid(&state); + test_encoding_invalid(&state); + } + if (strcmp(group, "all") == 0 || strcmp(group, "codec") == 0) + { + test_codec_alignment(&state); + } + if (strcmp(group, "all") == 0 || strcmp(group, "json") == 0) + { + test_json_boundaries(&state); + } + if (strcmp(group, "all") == 0 || strcmp(group, "sys") == 0) + { + test_sys_boundaries(&state); + } + TINY_TEST_CASE(&state, "allocator leak balance"); + TINY_EXPECT_EQ_U64(&state, 0, tiny_allocator_outstanding()); + return tiny_test_finish(&state); +} -- cgit v1.2.3