Skip to content

Commit b330512

Browse files
committed
test: Add unit tests for mmlora functionality
Branch: ModalityConditionalAdapters AI-usage: full (Bob, OpenCode + qwen3.5:122b) Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
1 parent f52409d commit b330512

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ endif()
261261
set(LLAMA_TEST_NAME test-mtmd-c-api)
262262
llama_build_and_test(test-mtmd-c-api.c)
263263
target_link_libraries(${LLAMA_TEST_NAME} PRIVATE mtmd)
264+
set(LLAMA_TEST_NAME test-mmlora)
265+
llama_build_and_test(test-mmlora.cpp)
266+
target_link_libraries(${LLAMA_TEST_NAME} PRIVATE mtmd)
264267
unset(LLAMA_TEST_NAME)
265268

266269
# GGUF model data fetcher library for tests that need real model metadata

tests/test-mmlora.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Tests for Multi-Modal LoRA (MMLoRA) functionality
2+
#include "mtmd.h"
3+
#include "mtmd-helper.h"
4+
#include "common.h"
5+
6+
#include <cstdio>
7+
#include <cstdlib>
8+
#include <vector>
9+
#include <string>
10+
11+
#undef NDEBUG
12+
#include <cassert>
13+
14+
#define LOG_INF(...) printf(__VA_ARGS__)
15+
16+
#define TEST(cond) do { \
17+
if (!(cond)) { \
18+
fprintf(stderr, "TEST FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
19+
return 1; \
20+
} \
21+
} while(0)
22+
23+
// Test string↔enum conversion functions
24+
static int test_mmlora_modality_type_conversion() {
25+
LOG_INF("Testing modality type string conversions...\n");
26+
27+
// Test enum to string
28+
TEST(strcmp(mtmd_modality_type_to_str(MTMD_INPUT_CHUNK_TYPE_IMAGE), "image") == 0);
29+
TEST(strcmp(mtmd_modality_type_to_str(MTMD_INPUT_CHUNK_TYPE_AUDIO), "audio") == 0);
30+
TEST(strcmp(mtmd_modality_type_to_str(MTMD_INPUT_CHUNK_TYPE_TEXT), "text") == 0);
31+
TEST(strcmp(mtmd_modality_type_to_str(MTMD_INPUT_CHUNK_TYPE_UNKNOWN), "unknown") == 0);
32+
TEST(strcmp(mtmd_modality_type_to_str((enum mtmd_input_chunk_type)999), "unknown") == 0);
33+
34+
// Test string to enum
35+
TEST(mtmd_modality_type_from_str("image") == MTMD_INPUT_CHUNK_TYPE_IMAGE);
36+
TEST(mtmd_modality_type_from_str("audio") == MTMD_INPUT_CHUNK_TYPE_AUDIO);
37+
TEST(mtmd_modality_type_from_str("text") == MTMD_INPUT_CHUNK_TYPE_TEXT);
38+
TEST(mtmd_modality_type_from_str("unknown") == MTMD_INPUT_CHUNK_TYPE_UNKNOWN);
39+
TEST(mtmd_modality_type_from_str("invalid") == MTMD_INPUT_CHUNK_TYPE_UNKNOWN);
40+
41+
// Test validation
42+
TEST(mtmd_is_valid_modality_str("image") == true);
43+
TEST(mtmd_is_valid_modality_str("audio") == true);
44+
TEST(mtmd_is_valid_modality_str("text") == false);
45+
TEST(mtmd_is_valid_modality_str("unknown") == false);
46+
TEST(mtmd_is_valid_modality_str("invalid") == false);
47+
TEST(mtmd_is_valid_modality_str(NULL) == false);
48+
49+
LOG_INF(" Passed modality type conversions\n");
50+
return 0;
51+
}
52+
53+
// Test common_adapter_lora_info MMLoRA helpers
54+
static int test_mmlora_info_helpers() {
55+
LOG_INF("Testing MMLoRA info helpers...\n");
56+
57+
// Test is_mmlora() with empty vector (not MMLoRA)
58+
common_adapter_lora_info regular_lora;
59+
TEST(regular_lora.is_mmlora() == false);
60+
TEST(regular_lora.mmlora_modality_types.empty() == true);
61+
62+
// Test is_mmlora() with single modality
63+
common_adapter_lora_info image_lora;
64+
image_lora.mmlora_modality_types.push_back("image");
65+
TEST(image_lora.is_mmlora() == true);
66+
TEST(image_lora.mmlora_modality_types.size() == 1);
67+
68+
// Test is_mmlora() with multiple modalities
69+
common_adapter_lora_info multi_lora;
70+
multi_lora.mmlora_modality_types.push_back("image");
71+
multi_lora.mmlora_modality_types.push_back("audio");
72+
TEST(multi_lora.is_mmlora() == true);
73+
TEST(multi_lora.mmlora_modality_types.size() == 2);
74+
75+
LOG_INF(" Passed MMLoRA info helpers\n");
76+
return 0;
77+
}
78+
79+
// Test CLI argument parsing (simulated)
80+
static int test_mmlora_arg_parsing() {LOG_INF("Testing MMLoRA argument parsing logic...\n");
81+
82+
// Simulate parsing "0:image,audio"
83+
std::string value = "0:image,audio";
84+
size_t colon_pos = value.find(':');
85+
TEST(colon_pos != std::string::npos);
86+
87+
std::string index_str = value.substr(0, colon_pos);
88+
std::string modalities_str = value.substr(colon_pos + 1);
89+
90+
TEST(index_str == "0");
91+
std::vector<std::string> modality_strs = string_split<std::string>(modalities_str, ',');
92+
TEST(modality_strs.size() == 2);
93+
TEST(modality_strs[0] == "image");
94+
TEST(modality_strs[1] == "audio");
95+
96+
// Validate modalities
97+
for (const auto & mod : modality_strs) {
98+
TEST(mod == "image" || mod == "audio");
99+
}
100+
101+
LOG_INF(" Passed argument parsing logic\n");
102+
return 0;
103+
}
104+
105+
int main(int argc, char ** argv) {
106+
(void)argc;
107+
(void)argv;
108+
109+
LOG_INF("MMLoRA unit tests starting...\n");
110+
111+
int result = 0;
112+
result += test_mmlora_modality_type_conversion();
113+
result += test_mmlora_info_helpers();
114+
result += test_mmlora_arg_parsing();
115+
116+
LOG_INF("MMLoRA unit tests %s!\n", result == 0 ? "passed" : "failed");
117+
return result;
118+
}

0 commit comments

Comments
 (0)