Day 8 之後,筆者決定以 C 語言重寫核心
第一步不是寫功能,而是把「可驗證」這件事建立起來
今天分享筆者在 Mongory-core 採用 Unity 測試框架的實戰做法,如何把系統拆成小單元,讓每一步都有落點
介面先行:先決定模組的 API 與行為,再填入實作
快速回饋:每次重構或最佳化(例如減少 callstack、調整遍歷)能立即驗證沒壞
降低恐懼:C 沒有 GC 與例外可依賴,測試是唯一「安全網」
#include "unity/unity.h"
void setUp(void) {}
void tearDown(void) {}
void test_truth(void) {
TEST_ASSERT_TRUE(1);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_truth);
return UNITY_END();
}
在 Mongory-core 專案中,Unity 已隨測試一起配置,讀者只要專注在每個模組的行為
本專案已提供自動安裝腳本與 Makefile 目標:
# 進入 C core 專案根目錄
cd mongory-core
# 方式一:用 Makefile 下載/配置 Unity
make setup-unity
# 方式二:直接執行安裝腳本(等效於上面)
chmod +x scripts/setup_unity.sh
./scripts/setup_unity.sh
# 方法三:如果是在讀者自己的C專案
# create Unity directory
mkdir -p tests/unity
# download Unity files
curl -o tests/unity/unity.h https://raw.githubusercontent.com/ThrowTheSwitch/Unity/master/src/unity.h
curl -o tests/unity/unity.c https://raw.githubusercontent.com/ThrowTheSwitch/Unity/master/src/unity.c
curl -o tests/unity/unity_internals.h https://raw.githubusercontent.com/ThrowTheSwitch/Unity/master/src/unity_internals.h
Makefile 的 setup-unity
會檢查 Unity 是否已存在,若沒有就呼叫 scripts/setup_unity.sh
幫讀者把 Unity 下載到正確位置(之後 make test
便可直接使用)
核心被拆成幾個基礎構件,每個構件都有專屬測試:
這些測試檔名(例如 mongory_array_test.c
、mongory_value_test.c
)與模組一一對應,能快速定位問題
#include "unity/unity.h"
#include "mongory-core/foundations/memory_pool.h"
#include "mongory-core/foundations/array.h"
#include "mongory-core/foundations/value.h"
void setUp(void) {}
void tearDown(void) {}
void test_pool_alloc_and_reset(void) {
mongory_memory_pool *pool = mongory_memory_pool_new();
void *p1 = pool->alloc(pool, 128);
TEST_ASSERT_NOT_NULL(p1);
pool->reset(pool); // 不應崩潰,之後仍可分配
void *p2 = pool->alloc(pool, 64);
TEST_ASSERT_NOT_NULL(p2);
pool->free(pool);
}
void test_array_push_and_get(void) {
mongory_memory_pool *pool = mongory_memory_pool_new();
mongory_array *arr = mongory_array_new(pool);
arr->push(arr, mongory_value_wrap_i(pool, 42));
arr->push(arr, mongory_value_wrap_i(pool, 7));
TEST_ASSERT_EQUAL_INT(2, (int)arr->count);
TEST_ASSERT_EQUAL_INT(42, (int)arr->get(arr, 0)->data.i);
TEST_ASSERT_EQUAL_INT(7, (int)arr->get(arr, 1)->data.i);
pool->free(pool);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_pool_alloc_and_reset);
RUN_TEST(test_array_push_and_get);
return UNITY_END();
}
重點是「行為」而不是內部實作:
在 mongory-core/
目錄:
# 方式一:使用提供的 Makefile
make test # 最簡單但限平台 macos
# 方式二:使用提供的 script
./build.sh --test
# 方式三:使用 CMake(跨平台通用,應該)
mkdir -p build && cd build
cmake .. && make && ctest --output-on-failure | cat
# 如果沒 cmake 就要裝
若讀者是在 mongory-rb
的 submodule 之內,也可進入 ext/mongory_ext/mongory-core
以相同方式執行
RUN_TEST
pool->error
),測試時別忘了驗證Day 10 會專講 memory_pool:為什麼選擇 chunk 倍增、如何在 O(log n) 控制擴容、reset 與 traced memory 的策略,並用測試帶著讀者一步步看清記憶體生命週期