blob: 53e2736ea772f72fac353c5e67449cf28abc9629 [file] [log] [blame]
Marc Kupietzb4ade0f2024-11-23 10:12:22 +01001#include <stddef.h>
Marc Kupietz6663f112021-03-14 09:20:59 +01002#include <stdio.h>
3#include <string.h>
4#define __USE_XOPEN_EXTENDED
5#include <ftw.h>
6#include "../src/collocatordb.h"
7#include "acutest.h"
Marc Kupietz023d4592026-07-31 09:46:34 +09008#include "config.h"
Marc Kupietz6663f112021-03-14 09:20:59 +01009
10char dbpath[] = "../tests/data/wpd19_10000";
11const int testword = 10; // ist
12
13void test_open_db() {
Marc Kupietz5ffc4742024-11-15 15:45:12 +010014 COLLOCATORDB* cdb = open_collocatordb(dbpath);
Marc Kupietz6663f112021-03-14 09:20:59 +010015 TEST_ASSERT(cdb != NULL);
16}
17
18void test_get_word() {
Marc Kupietz5ffc4742024-11-15 15:45:12 +010019 COLLOCATORDB* cdb = open_collocatordb(dbpath);
Marc Kupietz6663f112021-03-14 09:20:59 +010020 TEST_ASSERT(cdb != NULL);
21 char *word = get_word(cdb, testword);
22 char *expected = "ist";
23 TEST_CHECK(strcmp(word, expected) == 0);
24 TEST_MSG("Expected: %s", expected);
25 TEST_MSG("Produced: %s", word);
26}
27
28void test_collocation_scores() {
Marc Kupietz5ffc4742024-11-15 15:45:12 +010029 COLLOCATORDB* cdb = open_collocatordb(dbpath);
Marc Kupietz6663f112021-03-14 09:20:59 +010030 TEST_ASSERT(cdb != NULL);
Marc Kupietz0fec0be2026-09-04 19:42:38 +020031 char *expected = " { \"f1\": 217,\"w1\":\"Aluminium\", \"N\": 152743, \"collocates\": [{\"word\":\"Anwendungstechnologie\",\"f2\":16,\"f\":16,\"npmi\":0.594849,\"pmi\":8.4592,\"llr\":188.227,\"lfmd\":16.4592,\"md\":12.4592,\"md_nws\":10.1373,\"dice\":0.137339,\"ld\":11.1358,\"ln_count\":16,\"rn_count\":0,\"ln_pmi\":9.4592,\"rn_pmi\":-1,\"ldaf\":11.1358,\"win\":32,\"afwin\":32}]}\n";
Marc Kupietz6663f112021-03-14 09:20:59 +010032 char *produced = get_collocation_scores_as_json(cdb, 62, 966);
33 TEST_CHECK(strcmp(produced, expected) == 0);
34 TEST_MSG("Expected: %s", expected);
35 TEST_MSG("Produced: %s", produced);
36}
37
38
39void test_collocation_analysis_as_json() {
Marc Kupietz5ffc4742024-11-15 15:45:12 +010040 COLLOCATORDB* cdb = open_collocatordb(dbpath);
Marc Kupietz6663f112021-03-14 09:20:59 +010041 TEST_ASSERT(cdb != NULL);
42 char *json = get_collocators_as_json(cdb, testword);
Marc Kupietz0fec0be2026-09-04 19:42:38 +020043 char *needle = "\"word\":\"um\",\"f2\":264,\"f\":5,\"npmi\":-0.0556349,\"pmi\":-0.958074,\"llr\":2.87723,\"lfmd\":3.68578,\"md\":1.36385,\"md_nws\":0.363854,\"dice\":0.00720461,\"ld\":6.88314,\"ln_count\":0,\"rn_count\":1,\"ln_pmi\":-1,\"rn_pmi\":-1,\"ldaf\":4.79935,\"win\":668,\"afwin\":668";
Marc Kupietz6663f112021-03-14 09:20:59 +010044 TEST_CHECK(strstr(json, needle) > 0);
45 TEST_MSG("Expected to contain: %s", needle);
Marc Kupietz6663f112021-03-14 09:20:59 +010046}
47
48void test_collocation_analysis() {
Marc Kupietz5ffc4742024-11-15 15:45:12 +010049 COLLOCATORDB* cdb = open_collocatordb(dbpath);
Marc Kupietz6663f112021-03-14 09:20:59 +010050 TEST_ASSERT(cdb != NULL);
51 char *expected = "Anwendungstechnologie";
52 const COLLOCATOR *c = get_collocators(cdb, 62);
53 char *produced = get_word(cdb,c[0].w2);
54 TEST_CHECK(strcmp(produced, expected) == 0);
55 TEST_MSG("Expected: %s", expected);
56 TEST_MSG("Produced: %s", produced);
Marc Kupietz6663f112021-03-14 09:20:59 +010057}
58
59int unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
60 int rv = remove(fpath);
61 if (rv)
62 perror(fpath);
63 return rv;
64}
65
66int rmrf(char *path) {
67 return nftw(path, unlink_cb, 64, FTW_DEPTH | FTW_PHYS);
68}
69
70void test_writing() {
Marc Kupietzb4ade0f2024-11-23 10:12:22 +010071 char tmp_template[] = "/tmp/tmpfileXXXXXX";
72 int fd = mkstemp(tmp_template);
73 if (fd == -1) {
74 perror("mkstemp");
75 exit(EXIT_FAILURE);
76 }
77 close(fd);
78 char *tmp = strdup(tmp_template);
79
Marc Kupietz6663f112021-03-14 09:20:59 +010080 long size = 0;
Marc Kupietz673bd812021-03-14 17:27:44 +010081 int i;
Marc Kupietz6663f112021-03-14 09:20:59 +010082
Marc Kupietzb4ade0f2024-11-23 10:12:22 +010083 char *rocksdbfn = malloc(strlen(tmp) + strlen(".rocksdb") + 1);
84 strcpy(rocksdbfn, tmp);
Marc Kupietz6663f112021-03-14 09:20:59 +010085 strcat(rocksdbfn, ".rocksdb");
86 COLLOCATORDB *cdb = open_collocatordb_for_write(rocksdbfn);
87
Marc Kupietzb4ade0f2024-11-23 10:12:22 +010088 char *vocabfn = malloc(strlen(tmp) + strlen(".vocab") + 1);
Marc Kupietz6663f112021-03-14 09:20:59 +010089 strcpy(vocabfn, tmp);
90 strcat(vocabfn, ".vocab");
91 FILE *h = fopen(vocabfn, "w");
92 fprintf(h, "word0 2000\n");
93 fprintf(h, "word1 2000\n");
94 fprintf(h, "word2 2000\n");
95 fclose(h);
96 read_vocab(cdb, vocabfn);
97 inc_collocator(cdb, 0, 1, 4); size++;
Marc Kupietzb4ade0f2024-11-23 10:12:22 +010098 for (i = 0; i < 1000; i++) {
Marc Kupietz6663f112021-03-14 09:20:59 +010099 inc_collocator(cdb, 0, 1, i % 5); size++;
100 inc_collocator(cdb, 0, 1, -i % 5); size++;
101 inc_collocator(cdb, 1, 0, i % 5); size++;
102 inc_collocator(cdb, 1, 0, -i % 5); size++;
103 inc_collocator(cdb, 0, 2, i % 5); size++;
104 inc_collocator(cdb, 0, 2, -i % 5); size++;
105 }
106 inc_collocator(cdb, 1, 2, 4); size++;
107 COLLOCATOR *c = get_collocators(cdb, 0);
108 TEST_ASSERT(c != NULL);
Marc Kupietz2f65bcf2026-08-02 14:04:37 +0200109 /* Both collocates of word0 have to be there, in whatever order they are
110 sorted into: word1 with 2001 and word2 with 2000. The one that came last
111 used to be dropped, which is why this only asked for the first one. */
112 int n, seen1 = 0, seen2 = 0;
113 for (n = 0; c[n].raw > 0; n++) {
114 if (c[n].w2 == 1) {
115 seen1 = 1;
116 TEST_CHECK(c[n].raw == 2001);
117 TEST_CHECK(c[n].left_raw == 200);
118 TEST_CHECK(c[n].right_raw == 200);
119 } else if (c[n].w2 == 2) {
120 seen2 = 1;
121 TEST_CHECK(c[n].raw == 2000);
122 }
123 }
124 TEST_CHECK(n == 2);
125 TEST_MSG("expected 2 collocates, got %d", n);
126 TEST_CHECK(seen1 && seen2);
127
128 rmrf(rocksdbfn);
129}
130
131/* Every increment has to end up in the database, also when rocksdb would
132 rather cancel the write because compaction is behind, and also when they are
133 still in memory when the process that wrote them is gone. Both used to lose
134 counts silently: a quarter of them under write pressure, and everything that
135 had not been flushed when the indexer ended. */
136void test_no_increment_is_lost() {
137 char tmp_template[] = "/tmp/tmpfileXXXXXX";
138 int fd = mkstemp(tmp_template);
139 if (fd == -1) {
140 perror("mkstemp");
141 exit(EXIT_FAILURE);
142 }
143 close(fd);
144 char *tmp = strdup(tmp_template);
145 char rocksdbfn[1024], vocabfn[1024];
146 const long increments = 200000;
147 const int collocates = 100;
148 long i, total = 0;
149
150 snprintf(rocksdbfn, sizeof(rocksdbfn), "%s.rocksdb", tmp);
151 snprintf(vocabfn, sizeof(vocabfn), "%s.vocab", tmp);
152 FILE *h = fopen(vocabfn, "w");
153 for (i = 0; i <= collocates + 1; i++)
154 fprintf(h, "word%ld 1000\n", i);
155 fclose(h);
156
157 /* small write buffers, so that the writer runs into flushes and stalls */
158 setenv("COLLOCATORDB_WRITE_BUFFER_MB", "1", 1);
159 setenv("COLLOCATORDB_WRITE_BUFFERS", "2", 1);
160
161 COLLOCATORDB *cdb = open_collocatordb_for_write(rocksdbfn);
162 TEST_ASSERT(cdb != NULL);
163 for (i = 0; i < increments; i++)
164 inc_collocator(cdb, 1, 2 + (i % collocates), 1); /* never w2 == w1 */
165 close_collocatordb(cdb); /* has to write what is still in memory */
166
167 unsetenv("COLLOCATORDB_WRITE_BUFFER_MB");
168 unsetenv("COLLOCATORDB_WRITE_BUFFERS");
169
170 cdb = open_collocatordb(tmp);
171 TEST_ASSERT(cdb != NULL);
172 COLLOCATOR *c = get_collocators(cdb, 1);
173 TEST_ASSERT(c != NULL);
174 for (i = 0; i < collocates && c[i].raw > 0; i++)
175 total += c[i].raw;
176 TEST_CHECK(i == collocates);
177 TEST_MSG("only %ld of %d collocates are in the database", i, collocates);
178 TEST_CHECK(total == increments);
179 TEST_MSG("%ld of %ld increments survived", total, increments);
Marc Kupietz6663f112021-03-14 09:20:59 +0100180
181 rmrf(rocksdbfn);
182}
Marc Kupietzb4ade0f2024-11-23 10:12:22 +0100183
Marc Kupietz6208fd72024-11-15 15:46:19 +0100184void test_version_function() {
185 char *version = get_version();
Marc Kupietz023d4592026-07-31 09:46:34 +0900186 /* against the version of the build, so that a version bump does not have to
187 be repeated here */
188 TEST_CHECK(strcmp(version, PROJECT_VERSION) == 0);
189 TEST_MSG("Unexpected version: %s, expected %s", version, PROJECT_VERSION);
Marc Kupietz6208fd72024-11-15 15:46:19 +0100190}
Marc Kupietz6663f112021-03-14 09:20:59 +0100191
Marc Kupietz6e2dbd12024-11-23 10:11:32 +0100192void test_get_word_id() {
193 COLLOCATORDB* cdb = open_collocatordb(dbpath);
194 TEST_ASSERT(cdb != NULL);
195 uint64_t id = get_word_id(cdb, "ist");
196 TEST_CHECK(id == 10);
197 TEST_MSG("Unexpected word id: %lu", id);
198}
199
Marc Kupietz94ea77b2024-11-23 12:32:19 +0100200void test_collocatordb_query_command_line_tool() {
201 int result = system("../build/collocatordb_query ../tests/data/wpd19_10000 ist > /dev/null 2>&1");
202 TEST_CHECK(result == 0);
203 TEST_MSG("collectordb_query command failed with result: %d", result);
204}
205
Marc Kupietzd26b1052024-12-10 16:56:39 +0100206void test_get_corpus_size() {
207 COLLOCATORDB* cdb = open_collocatordb(dbpath);
208 TEST_ASSERT(cdb != NULL);
209 uint64_t size = get_corpus_size(cdb);
210 TEST_CHECK(size == 152743);
211 TEST_MSG("Unexpected corpus size: %lu", size);
212}
213
Marc Kupietz21b964c2024-12-10 17:10:50 +0100214void test_get_word_frequency() {
215 COLLOCATORDB* cdb = open_collocatordb(dbpath);
216 TEST_ASSERT(cdb != NULL);
217 int w1 = get_word_id(cdb, "Test");
218 uint64_t freq = get_word_frequency(cdb, w1);
219 TEST_CHECK(freq == 3);
220 TEST_MSG("Unexpected word frequency: %lu", freq);
221}
222
Marc Kupietz6663f112021-03-14 09:20:59 +0100223TEST_LIST = {
224 { "open database for reading", test_open_db },
225 { "get word", test_get_word },
226 { "collocation scores", test_collocation_scores },
227 { "collocation analysis", test_collocation_analysis },
228 { "collocation analysis as json", test_collocation_analysis_as_json },
229 { "writing", test_writing },
Marc Kupietz2f65bcf2026-08-02 14:04:37 +0200230 { "no increment is lost", test_no_increment_is_lost },
Marc Kupietz6208fd72024-11-15 15:46:19 +0100231 { "version function", test_version_function },
Marc Kupietz6e2dbd12024-11-23 10:11:32 +0100232 { "get word id", test_get_word_id },
Marc Kupietzd26b1052024-12-10 16:56:39 +0100233 { "get corpus size", test_get_corpus_size},
Marc Kupietz21b964c2024-12-10 17:10:50 +0100234 { "get word frequency", test_get_word_frequency},
Marc Kupietz94ea77b2024-11-23 12:32:19 +0100235 { "collocatordb_query command line tool", test_collocatordb_query_command_line_tool},
Marc Kupietz6663f112021-03-14 09:20:59 +0100236 { NULL, NULL }
237};