From 7ad75127117d1804fbf29adbe381131f1e2d63cd Mon Sep 17 00:00:00 2001 From: danielhanchen Date: Thu, 3 Sep 2026 22:09:07 +0000 Subject: [PATCH] mtmd: test that every projector is registered and uniquely named Nothing checks the projector registry, and it needs no model file to check. Two ways it breaks, both of which compile and neither of which is visible until someone loads a model: * a projector's enum value survives an edit while its PROJECTOR_TYPE_NAMES entry is lost. clip_projector_type_from_string then returns PROJECTOR_TYPE_UNKNOWN for it, and every model using that tower fails to load with no indication of why. * two projectors end up sharing a name, which routes one model to the other's graph. Deleting one line to demonstrate: { PROJECTOR_TYPE_GLM4V, "kimik25"} ASSERT EQUAL FAILED : "kimik25" resolves to its own projector expected: 46 actual : 43 So: walk the enum, and for each value assert it has a name, that the name resolves back through the same lookup a model load goes through, and that no name is claimed twice. Also assert an unregistered string still lands on UNKNOWN, since clip reads that string straight out of the GGUF and a typo must not become a wrong graph. PROJECTOR_TYPE_MLP_NORM is exempt and listed as such: clip assigns it from the tensor shapes for Yi-type llava and never reads it from a file, so it is unnameable by construction rather than by omission. It is the only one, and the comment says what adding a second would mean. 185 assertions, no I/O. --- tests/test-mtmd-impl.cpp | 61 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/test-mtmd-impl.cpp b/tests/test-mtmd-impl.cpp index 2ec6b239158f..f20e571341eb 100644 --- a/tests/test-mtmd-impl.cpp +++ b/tests/test-mtmd-impl.cpp @@ -1,9 +1,11 @@ #include "testing.h" +#include "clip-impl.h" #include "mtmd-image.h" #include "mtmd-internal.h" #include +#include #include #include #include @@ -137,6 +139,65 @@ MAKE_TEST(test_temporal_merge_grouping) { } } +// +// projector registry +// + +// Every projector the enum declares must have a name, and that name must map +// back to it. Both halves matter, and neither needs a model file. +// +// A projector whose enum value survives a merge while its PROJECTOR_TYPE_NAMES +// entry is lost still compiles, and every model using it then loads as +// PROJECTOR_TYPE_UNKNOWN. Two projectors sharing a name compiles too, and +// silently routes one model to the other's graph. +MAKE_TEST(test_projector_registry) { + // Projectors that clip assigns to itself and never reads from a GGUF, so + // they are unnameable by construction rather than by omission. Keep this + // list at exactly the ones that are: an entry added here to quiet a + // failure is a projector no model can ever select. + const std::set internal_only = { + PROJECTOR_TYPE_MLP_NORM, // set from the tensor shapes, for Yi-type llava + }; + + std::map seen; + + for (int i = 0; i < PROJECTOR_TYPE_UNKNOWN; i++) { + const projector_type ty = static_cast(i); + + const auto it = PROJECTOR_TYPE_NAMES.find(ty); + if (it == PROJECTOR_TYPE_NAMES.end()) { + t.assert_equal("projector " + std::to_string(i) + " has a name", + std::string(internal_only.count(ty) ? "internal-only" : "named"), + std::string(internal_only.count(ty) ? "internal-only" + : "missing from PROJECTOR_TYPE_NAMES")); + continue; + } + t.assert_equal("\"" + it->second + "\" is not an internal-only projector", + std::string("selectable"), + std::string(internal_only.count(ty) ? "named but listed internal-only" + : "selectable")); + + // The lookup every model load goes through, not the table read backwards. + t.assert_equal("\"" + it->second + "\" resolves to its own projector", + std::to_string(static_cast(ty)), + std::to_string(static_cast(clip_projector_type_from_string(it->second)))); + + const auto dup = seen.find(it->second); + t.assert_equal("\"" + it->second + "\" names exactly one projector", + std::string("unique"), + dup == seen.end() ? std::string("unique") + : std::string("also names projector " + + std::to_string(static_cast(dup->second)))); + seen[it->second] = ty; + } + + // An unknown string must not resolve to a real projector: clip reads this + // straight out of the GGUF, so anything else turns a typo into a wrong graph. + t.assert_equal("an unregistered name resolves to UNKNOWN", + std::to_string(static_cast(PROJECTOR_TYPE_UNKNOWN)), + std::to_string(static_cast(clip_projector_type_from_string("no-such-projector")))); +} + // // main //