summaryrefslogtreecommitdiff
path: root/tests/boundary_test.c
diff options
context:
space:
mode:
authortanaka takashi <isazaka@gmail.com>2026-06-29 21:43:54 +0900
committertanaka takashi <isazaka@gmail.com>2026-06-30 01:23:51 +0900
commit4bc721c3bf7589b519c5730d093f704ce56e3f09 (patch)
tree38da055ab15fd70f5c92584560ea73486e5ddce0 /tests/boundary_test.c
parent4c6a831aa2a57e2bcc20aa3fc2f1b7172f5be285 (diff)
refactor: MCP層をmcpc APIベースに刷新
- 独自JSON-RPC処理をmcpcへ置き換え - 各MCPツールを個別ソースへ分離 - 共通コンテキストと登録処理を整理 - mcpc互換修正と統合テストを更新
Diffstat (limited to 'tests/boundary_test.c')
-rw-r--r--tests/boundary_test.c275
1 files changed, 275 insertions, 0 deletions
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 <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}