Skip to content

Commit 95c6a1e

Browse files
committed
Add unit test cases for ART prefix search
1 parent f16ddf7 commit 95c6a1e

2 files changed

Lines changed: 287 additions & 1 deletion

File tree

test/CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,15 @@ if (check)
118118
target_include_directories(pgmoneta-test PRIVATE
119119
${CMAKE_SOURCE_DIR}/src/include
120120
${CMAKE_SOURCE_DIR}/test/include
121-
${CMAKE_SOURCE_DIR}/test/libpgmonetatest)
121+
${CMAKE_SOURCE_DIR}/test/libpgmonetatest
122+
${LIBEV_INCLUDE_DIRS}
123+
${OPENSSL_INCLUDE_DIR}
124+
${CURSES_INCLUDE_DIRS}
125+
${LIBYAML_INCLUDE_DIRS}
126+
${LIBSSH_INCLUDE_DIRS}
127+
${ZSTD_INCLUDE_DIRS}
128+
${LZ4_INCLUDE_DIRS}
129+
${LibArchive_INCLUDE_DIRS})
122130

123131
if(EXISTS "/etc/debian_version")
124132
target_link_libraries(pgmoneta-test pthread rt m pgmoneta)

test/testcases/test_art.c

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,284 @@ MCTF_TEST(test_art_random_delete)
665665
MCTF_FINISH();
666666
}
667667

668+
MCTF_TEST(test_art_prefix_search)
669+
{
670+
struct art* t = NULL;
671+
char** matches = NULL;
672+
int count = 0;
673+
674+
pgmoneta_test_setup();
675+
676+
pgmoneta_art_create(&t);
677+
MCTF_ASSERT_PTR_NONNULL(t, cleanup, "ART creation failed");
678+
679+
MCTF_ASSERT(!pgmoneta_art_insert(t, "apple", 1, ValueInt32), cleanup, "Insert apple failed");
680+
MCTF_ASSERT(!pgmoneta_art_insert(t, "applet", 2, ValueInt32), cleanup, "Insert applet failed");
681+
MCTF_ASSERT(!pgmoneta_art_insert(t, "apply", 3, ValueInt32), cleanup, "Insert apply failed");
682+
MCTF_ASSERT(!pgmoneta_art_insert(t, "ball", 4, ValueInt32), cleanup, "Insert ball failed");
683+
MCTF_ASSERT(!pgmoneta_art_insert(t, "bat", 5, ValueInt32), cleanup, "Insert bat failed");
684+
685+
count = pgmoneta_art_prefix_search(t, "app", &matches, 10);
686+
MCTF_ASSERT_INT_EQ(count, 3, cleanup, "Prefix search 'app' should return 3 matches");
687+
MCTF_ASSERT_STR_EQ(matches[0], "apple", cleanup, "First match mismatch");
688+
MCTF_ASSERT_STR_EQ(matches[1], "applet", cleanup, "Second match mismatch");
689+
MCTF_ASSERT_STR_EQ(matches[2], "apply", cleanup, "Third match mismatch");
690+
for (int i = 0; i < count; i++)
691+
{
692+
free(matches[i]);
693+
}
694+
free(matches);
695+
matches = NULL;
696+
697+
count = pgmoneta_art_prefix_search(t, "cat", &matches, 10);
698+
MCTF_ASSERT_INT_EQ(count, 0, cleanup, "Prefix search 'cat' should return 0 matches");
699+
free(matches);
700+
matches = NULL;
701+
702+
count = pgmoneta_art_prefix_search(t, "apple", &matches, 10);
703+
MCTF_ASSERT_INT_EQ(count, 2, cleanup, "Prefix search 'apple' should return 2 matches");
704+
MCTF_ASSERT_STR_EQ(matches[0], "apple", cleanup, "First match mismatch");
705+
MCTF_ASSERT_STR_EQ(matches[1], "applet", cleanup, "Second match mismatch");
706+
for (int i = 0; i < count; i++)
707+
{
708+
free(matches[i]);
709+
}
710+
free(matches);
711+
matches = NULL;
712+
713+
count = pgmoneta_art_prefix_search(t, "", &matches, 10);
714+
MCTF_ASSERT_INT_EQ(count, 5, cleanup, "Empty prefix should return all matches");
715+
MCTF_ASSERT_STR_EQ(matches[0], "apple", cleanup, "Match 0 mismatch");
716+
MCTF_ASSERT_STR_EQ(matches[1], "applet", cleanup, "Match 1 mismatch");
717+
MCTF_ASSERT_STR_EQ(matches[2], "apply", cleanup, "Match 2 mismatch");
718+
MCTF_ASSERT_STR_EQ(matches[3], "ball", cleanup, "Match 3 mismatch");
719+
MCTF_ASSERT_STR_EQ(matches[4], "bat", cleanup, "Match 4 mismatch");
720+
for (int i = 0; i < count; i++)
721+
{
722+
free(matches[i]);
723+
}
724+
free(matches);
725+
matches = NULL;
726+
727+
count = pgmoneta_art_prefix_search(t, "app", &matches, 2);
728+
MCTF_ASSERT_INT_EQ(count, 2, cleanup, "Max matches should limit results to 2");
729+
MCTF_ASSERT_STR_EQ(matches[0], "apple", cleanup, "Match 0 mismatch");
730+
MCTF_ASSERT_STR_EQ(matches[1], "applet", cleanup, "Match 1 mismatch");
731+
for (int i = 0; i < count; i++)
732+
{
733+
free(matches[i]);
734+
}
735+
free(matches);
736+
matches = NULL;
737+
738+
count = pgmoneta_art_prefix_search(t, "applet", &matches, 10);
739+
MCTF_ASSERT_INT_EQ(count, 1, cleanup, "Prefix search 'applet' should return exactly 1 match");
740+
MCTF_ASSERT_STR_EQ(matches[0], "applet", cleanup, "Exact match mismatch");
741+
for (int i = 0; i < count; i++)
742+
{
743+
free(matches[i]);
744+
}
745+
free(matches);
746+
matches = NULL;
747+
748+
MCTF_ASSERT(!pgmoneta_art_delete(t, "applet"), cleanup, "Delete applet failed");
749+
count = pgmoneta_art_prefix_search(t, "app", &matches, 10);
750+
MCTF_ASSERT_INT_EQ(count, 2, cleanup, "Prefix search 'app' after delete should return 2 matches");
751+
MCTF_ASSERT_STR_EQ(matches[0], "apple", cleanup, "Match 0 mismatch after delete");
752+
MCTF_ASSERT_STR_EQ(matches[1], "apply", cleanup, "Match 1 mismatch after delete");
753+
for (int i = 0; i < count; i++)
754+
{
755+
free(matches[i]);
756+
}
757+
free(matches);
758+
matches = NULL;
759+
760+
count = pgmoneta_art_prefix_search(NULL, "app", &matches, 10);
761+
MCTF_ASSERT_INT_EQ(count, -1, cleanup, "Prefix search with NULL tree should return -1");
762+
763+
count = pgmoneta_art_prefix_search(t, "app", NULL, 10);
764+
MCTF_ASSERT_INT_EQ(count, -1, cleanup, "Prefix search with NULL matches pointer should return -1");
765+
766+
count = pgmoneta_art_prefix_search(t, "app", &matches, 0);
767+
MCTF_ASSERT_INT_EQ(count, -1, cleanup, "Prefix search with 0 max_matches should return -1");
768+
769+
cleanup:
770+
if (matches)
771+
{
772+
for (int i = 0; i < count; i++)
773+
{
774+
free(matches[i]);
775+
}
776+
free(matches);
777+
}
778+
pgmoneta_art_destroy(t);
779+
pgmoneta_test_teardown();
780+
MCTF_FINISH();
781+
}
782+
783+
MCTF_TEST(test_art_prefix_search_nested)
784+
{
785+
struct art* t = NULL;
786+
char** matches = NULL;
787+
int count = 0;
788+
789+
pgmoneta_test_setup();
790+
pgmoneta_art_create(&t);
791+
MCTF_ASSERT_PTR_NONNULL(t, cleanup, "ART creation failed");
792+
793+
MCTF_ASSERT(!pgmoneta_art_insert(t, "api.foo.bar", 1, ValueInt32), cleanup, "Insert failed");
794+
MCTF_ASSERT(!pgmoneta_art_insert(t, "api.foo.baz", 2, ValueInt32), cleanup, "Insert failed");
795+
MCTF_ASSERT(!pgmoneta_art_insert(t, "api.foe.fum", 3, ValueInt32), cleanup, "Insert failed");
796+
MCTF_ASSERT(!pgmoneta_art_insert(t, "abc.123.456", 4, ValueInt32), cleanup, "Insert failed");
797+
MCTF_ASSERT(!pgmoneta_art_insert(t, "api.foo", 5, ValueInt32), cleanup, "Insert failed");
798+
MCTF_ASSERT(!pgmoneta_art_insert(t, "api", 6, ValueInt32), cleanup, "Insert failed");
799+
800+
count = pgmoneta_art_prefix_search(t, "api", &matches, 10);
801+
MCTF_ASSERT_INT_EQ(count, 5, cleanup, "Prefix 'api' should return 5 matches");
802+
MCTF_ASSERT_STR_EQ(matches[0], "api", cleanup, "Match 0 mismatch");
803+
MCTF_ASSERT_STR_EQ(matches[1], "api.foe.fum", cleanup, "Match 1 mismatch");
804+
MCTF_ASSERT_STR_EQ(matches[2], "api.foo", cleanup, "Match 2 mismatch");
805+
MCTF_ASSERT_STR_EQ(matches[3], "api.foo.bar", cleanup, "Match 3 mismatch");
806+
MCTF_ASSERT_STR_EQ(matches[4], "api.foo.baz", cleanup, "Match 4 mismatch");
807+
for (int i = 0; i < count; i++)
808+
{
809+
free(matches[i]);
810+
}
811+
free(matches);
812+
matches = NULL;
813+
814+
count = pgmoneta_art_prefix_search(t, "a", &matches, 10);
815+
MCTF_ASSERT_INT_EQ(count, 6, cleanup, "Prefix 'a' should return 6 matches");
816+
for (int i = 0; i < count; i++)
817+
{
818+
free(matches[i]);
819+
}
820+
free(matches);
821+
matches = NULL;
822+
823+
count = pgmoneta_art_prefix_search(t, "b", &matches, 10);
824+
MCTF_ASSERT_INT_EQ(count, 0, cleanup, "Prefix 'b' should return 0 matches");
825+
free(matches);
826+
matches = NULL;
827+
828+
count = pgmoneta_art_prefix_search(t, "api.", &matches, 10);
829+
MCTF_ASSERT_INT_EQ(count, 4, cleanup, "Prefix 'api.' should return 4 matches");
830+
for (int i = 0; i < count; i++)
831+
{
832+
free(matches[i]);
833+
}
834+
free(matches);
835+
matches = NULL;
836+
837+
count = pgmoneta_art_prefix_search(t, "api.foo.ba", &matches, 10);
838+
MCTF_ASSERT_INT_EQ(count, 2, cleanup, "Prefix 'api.foo.ba' should return 2 matches");
839+
MCTF_ASSERT_STR_EQ(matches[0], "api.foo.bar", cleanup, "Match 0 mismatch");
840+
MCTF_ASSERT_STR_EQ(matches[1], "api.foo.baz", cleanup, "Match 1 mismatch");
841+
for (int i = 0; i < count; i++)
842+
{
843+
free(matches[i]);
844+
}
845+
free(matches);
846+
matches = NULL;
847+
848+
count = pgmoneta_art_prefix_search(t, "api.end", &matches, 10);
849+
MCTF_ASSERT_INT_EQ(count, 0, cleanup, "Prefix 'api.end' should return 0 matches");
850+
free(matches);
851+
matches = NULL;
852+
853+
cleanup:
854+
if (matches)
855+
{
856+
for (int i = 0; i < count; i++)
857+
{
858+
free(matches[i]);
859+
}
860+
free(matches);
861+
}
862+
pgmoneta_art_destroy(t);
863+
pgmoneta_test_teardown();
864+
MCTF_FINISH();
865+
}
866+
867+
MCTF_TEST(test_art_prefix_search_long_prefix)
868+
{
869+
struct art* t = NULL;
870+
char** matches = NULL;
871+
int count = 0;
872+
873+
pgmoneta_test_setup();
874+
pgmoneta_art_create(&t);
875+
MCTF_ASSERT_PTR_NONNULL(t, cleanup, "ART creation failed");
876+
877+
MCTF_ASSERT(!pgmoneta_art_insert(t, "this:key:has:a:long:prefix:3", 3, ValueInt32), cleanup, "Insert failed");
878+
MCTF_ASSERT(!pgmoneta_art_insert(t, "this:key:has:a:long:common:prefix:2", 2, ValueInt32), cleanup, "Insert failed");
879+
MCTF_ASSERT(!pgmoneta_art_insert(t, "this:key:has:a:long:common:prefix:1", 1, ValueInt32), cleanup, "Insert failed");
880+
881+
count = pgmoneta_art_prefix_search(t, "this:key:has", &matches, 10);
882+
MCTF_ASSERT_INT_EQ(count, 3, cleanup, "Prefix 'this:key:has' should return 3 matches");
883+
MCTF_ASSERT_STR_EQ(matches[0], "this:key:has:a:long:common:prefix:1", cleanup, "Match 0 mismatch");
884+
MCTF_ASSERT_STR_EQ(matches[1], "this:key:has:a:long:common:prefix:2", cleanup, "Match 1 mismatch");
885+
MCTF_ASSERT_STR_EQ(matches[2], "this:key:has:a:long:prefix:3", cleanup, "Match 2 mismatch");
886+
for (int i = 0; i < count; i++)
887+
{
888+
free(matches[i]);
889+
}
890+
free(matches);
891+
matches = NULL;
892+
893+
cleanup:
894+
if (matches)
895+
{
896+
for (int i = 0; i < count; i++)
897+
{
898+
free(matches[i]);
899+
}
900+
free(matches);
901+
}
902+
pgmoneta_art_destroy(t);
903+
pgmoneta_test_teardown();
904+
MCTF_FINISH();
905+
}
906+
907+
MCTF_TEST(test_art_prefix_search_max_prefix_len)
908+
{
909+
struct art* t = NULL;
910+
char** matches = NULL;
911+
int count = 0;
912+
913+
pgmoneta_test_setup();
914+
pgmoneta_art_create(&t);
915+
MCTF_ASSERT_PTR_NONNULL(t, cleanup, "ART creation failed");
916+
917+
MCTF_ASSERT(!pgmoneta_art_insert(t, "foobarbaz1-test1-foo", 1, ValueInt32), cleanup, "Insert failed");
918+
MCTF_ASSERT(!pgmoneta_art_insert(t, "foobarbaz1-test1-bar", 2, ValueInt32), cleanup, "Insert failed");
919+
MCTF_ASSERT(!pgmoneta_art_insert(t, "foobarbaz1-test2-foo", 3, ValueInt32), cleanup, "Insert failed");
920+
921+
count = pgmoneta_art_prefix_search(t, "foobarbaz1-test1", &matches, 10);
922+
MCTF_ASSERT_INT_EQ(count, 2, cleanup, "Prefix 'foobarbaz1-test1' should return 2 matches");
923+
MCTF_ASSERT_STR_EQ(matches[0], "foobarbaz1-test1-bar", cleanup, "Match 0 mismatch");
924+
MCTF_ASSERT_STR_EQ(matches[1], "foobarbaz1-test1-foo", cleanup, "Match 1 mismatch");
925+
for (int i = 0; i < count; i++)
926+
{
927+
free(matches[i]);
928+
}
929+
free(matches);
930+
matches = NULL;
931+
932+
cleanup:
933+
if (matches)
934+
{
935+
for (int i = 0; i < count; i++)
936+
{
937+
free(matches[i]);
938+
}
939+
free(matches);
940+
}
941+
pgmoneta_art_destroy(t);
942+
pgmoneta_test_teardown();
943+
MCTF_FINISH();
944+
}
945+
668946
MCTF_TEST(test_art_insert_index_out_of_range)
669947
{
670948
struct art* t = NULL;

0 commit comments

Comments
 (0)