Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <stdlib.h> | ||
2 | #include "test.h" | ||
3 | #include "file-history.h" | ||
4 | #include "history.h" | ||
5 | #include "util/numtostr.h" | ||
6 | #include "util/readfile.h" | ||
7 | #include "util/xmalloc.h" | ||
8 | |||
9 | 1 | static void test_history_append(TestContext *ctx) | |
10 | { | ||
11 | 1 | History h = {.max_entries = 7}; | |
12 | 1 | history_append(&h, "A"); | |
13 | 1 | EXPECT_EQ(h.entries.count, 1); | |
14 | 1 | EXPECT_STREQ(h.first->text, "A"); | |
15 | 1 | EXPECT_NULL(h.first->prev); | |
16 | 1 | EXPECT_NULL(h.first->next); | |
17 | 1 | EXPECT_PTREQ(h.first, h.last); | |
18 | |||
19 | 1 | history_append(&h, "A"); | |
20 | 1 | EXPECT_EQ(h.entries.count, 1); | |
21 | 1 | EXPECT_PTREQ(h.first, h.last); | |
22 | |||
23 | 1 | history_append(&h, "B"); | |
24 | 1 | EXPECT_EQ(h.entries.count, 2); | |
25 | 1 | EXPECT_STREQ(h.first->text, "A"); | |
26 | 1 | EXPECT_STREQ(h.last->text, "B"); | |
27 | 1 | EXPECT_NULL(h.first->prev); | |
28 | 1 | EXPECT_NULL(h.last->next); | |
29 | 1 | EXPECT_PTREQ(h.first->next, h.last); | |
30 | 1 | EXPECT_PTREQ(h.last->prev, h.first); | |
31 | |||
32 | 1 | history_append(&h, "C"); | |
33 | 1 | EXPECT_EQ(h.entries.count, 3); | |
34 | 1 | EXPECT_STREQ(h.first->text, "A"); | |
35 | 1 | EXPECT_STREQ(h.first->next->text, "B"); | |
36 | 1 | EXPECT_STREQ(h.last->prev->text, "B"); | |
37 | 1 | EXPECT_STREQ(h.last->text, "C"); | |
38 | 1 | EXPECT_NULL(h.first->prev); | |
39 | 1 | EXPECT_NULL(h.last->next); | |
40 | |||
41 | 1 | history_append(&h, "A"); | |
42 | 1 | EXPECT_EQ(h.entries.count, 3); | |
43 | 1 | EXPECT_STREQ(h.last->text, "A"); | |
44 | 1 | EXPECT_STREQ(h.first->text, "B"); | |
45 | 1 | EXPECT_NULL(h.first->prev); | |
46 | 1 | EXPECT_NULL(h.last->next); | |
47 | 1 | EXPECT_STREQ(h.first->next->text, "C"); | |
48 | 1 | EXPECT_STREQ(h.last->prev->text, "C"); | |
49 | |||
50 | 1 | history_append(&h, "C"); | |
51 | 1 | EXPECT_EQ(h.entries.count, 3); | |
52 | 1 | EXPECT_STREQ(h.last->text, "C"); | |
53 | 1 | EXPECT_STREQ(h.first->text, "B"); | |
54 | 1 | EXPECT_NULL(h.first->prev); | |
55 | 1 | EXPECT_NULL(h.last->next); | |
56 | 1 | EXPECT_STREQ(h.first->next->text, "A"); | |
57 | 1 | EXPECT_STREQ(h.last->prev->text, "A"); | |
58 | |||
59 | 1 | history_append(&h, "B"); | |
60 | 1 | history_append(&h, "C"); | |
61 | 1 | EXPECT_EQ(h.entries.count, 3); | |
62 | 1 | EXPECT_STREQ(h.first->text, "A"); | |
63 | 1 | EXPECT_STREQ(h.first->next->text, "B"); | |
64 | 1 | EXPECT_STREQ(h.last->prev->text, "B"); | |
65 | 1 | EXPECT_STREQ(h.last->text, "C"); | |
66 | |||
67 | 1 | history_append(&h, "D"); | |
68 | 1 | history_append(&h, "E"); | |
69 | 1 | history_append(&h, "F"); | |
70 | 1 | history_append(&h, "G"); | |
71 | 1 | EXPECT_EQ(h.entries.count, 7); | |
72 | 1 | EXPECT_STREQ(h.last->text, "G"); | |
73 | 1 | EXPECT_STREQ(h.first->text, "A"); | |
74 | 1 | EXPECT_STREQ(h.last->prev->text, "F"); | |
75 | |||
76 | 1 | history_append(&h, "H"); | |
77 | 1 | EXPECT_EQ(h.entries.count, 7); | |
78 | 1 | EXPECT_STREQ(h.last->text, "H"); | |
79 | 1 | EXPECT_STREQ(h.first->text, "B"); | |
80 | 1 | EXPECT_STREQ(h.last->prev->text, "G"); | |
81 | |||
82 | 1 | history_append(&h, "I"); | |
83 | 1 | EXPECT_EQ(h.entries.count, 7); | |
84 | 1 | EXPECT_STREQ(h.last->text, "I"); | |
85 | 1 | EXPECT_STREQ(h.first->text, "C"); | |
86 | 1 | EXPECT_STREQ(h.last->prev->text, "H"); | |
87 | |||
88 | 1 | history_free(&h); | |
89 | 1 | h = (History){.max_entries = 2}; | |
90 | 1 | EXPECT_EQ(h.entries.count, 0); | |
91 | |||
92 | 1 | history_append(&h, "1"); | |
93 | 1 | EXPECT_EQ(h.entries.count, 1); | |
94 | 1 | EXPECT_STREQ(h.last->text, "1"); | |
95 | 1 | EXPECT_STREQ(h.first->text, "1"); | |
96 | |||
97 | 1 | history_append(&h, "2"); | |
98 | 1 | EXPECT_EQ(h.entries.count, 2); | |
99 | 1 | EXPECT_STREQ(h.last->text, "2"); | |
100 | 1 | EXPECT_STREQ(h.first->text, "1"); | |
101 | |||
102 | 1 | history_append(&h, "3"); | |
103 | 1 | EXPECT_EQ(h.entries.count, 2); | |
104 | 1 | EXPECT_STREQ(h.last->text, "3"); | |
105 | 1 | EXPECT_STREQ(h.first->text, "2"); | |
106 | |||
107 | 1 | history_free(&h); | |
108 | 1 | } | |
109 | |||
110 | 1 | static void test_history_search(TestContext *ctx) | |
111 | { | ||
112 | 1 | const char *filename = "test/data/history"; | |
113 | 1 | History h = {.max_entries = 64}; | |
114 | 1 | history_load(&h, xstrdup(filename), 4096); | |
115 | 1 | EXPECT_EQ(h.entries.count, 3); | |
116 | 1 | EXPECT_STREQ(h.filename, filename); | |
117 | 1 | ASSERT_NONNULL(h.first); | |
118 | 1 | EXPECT_STREQ(h.first->text, "one"); | |
119 | 1 | ASSERT_NONNULL(h.last); | |
120 | 1 | EXPECT_STREQ(h.last->text, "three"); | |
121 | 1 | ASSERT_NONNULL(h.first->next); | |
122 | 1 | EXPECT_STREQ(h.first->next->text, "two"); | |
123 | 1 | ASSERT_NONNULL(h.last->prev); | |
124 | 1 | EXPECT_STREQ(h.last->prev->text, "two"); | |
125 | 1 | EXPECT_NULL(h.first->prev); | |
126 | 1 | EXPECT_NULL(h.last->next); | |
127 | |||
128 | 1 | const HistoryEntry *e = h.last; | |
129 | 1 | EXPECT_STREQ(e->text, "three"); | |
130 | 1 | EXPECT_TRUE(history_search_forward(&h, &e, "")); | |
131 | 1 | EXPECT_STREQ(e->text, "two"); | |
132 | 1 | EXPECT_TRUE(history_search_forward(&h, &e, "")); | |
133 | 1 | EXPECT_STREQ(e->text, "one"); | |
134 | 1 | EXPECT_FALSE(history_search_forward(&h, &e, "")); | |
135 | |||
136 | 1 | EXPECT_STREQ(e->text, "one"); | |
137 | 1 | EXPECT_TRUE(history_search_backward(&h, &e, "")); | |
138 | 1 | EXPECT_STREQ(e->text, "two"); | |
139 | 1 | EXPECT_TRUE(history_search_backward(&h, &e, "")); | |
140 | 1 | EXPECT_STREQ(e->text, "three"); | |
141 | 1 | EXPECT_FALSE(history_search_backward(&h, &e, "")); | |
142 | |||
143 | 1 | EXPECT_STREQ(e->text, "three"); | |
144 | 1 | EXPECT_TRUE(history_search_forward(&h, &e, "o")); | |
145 | 1 | EXPECT_STREQ(e->text, "one"); | |
146 | 1 | EXPECT_TRUE(history_search_backward(&h, &e, "th")); | |
147 | 1 | EXPECT_STREQ(e->text, "three"); | |
148 | |||
149 | 1 | free(h.filename); | |
150 | 1 | filename = "build/test/saved_history"; | |
151 | 1 | h.filename = xstrdup(filename); | |
152 | 1 | history_save(&h); | |
153 | 1 | history_free(&h); | |
154 | 1 | char *buf = NULL; | |
155 | 1 | ssize_t n = read_file(filename, &buf, 4096); | |
156 | 1 | EXPECT_EQ(n, 14); | |
157 | 1 | EXPECT_STREQ(buf, "one\ntwo\nthree\n"); | |
158 | 1 | free(buf); | |
159 | 1 | } | |
160 | |||
161 | // This test is done to ensure the HashMap can handle the constant | ||
162 | // churn from insertions and removals (i.e. that it rehashes the | ||
163 | // table to clean out tombstones, even when the number of real | ||
164 | // entries stops growing). | ||
165 | 1 | static void test_history_tombstone_pressure(TestContext *ctx) | |
166 | { | ||
167 | 1 | History h = {.max_entries = 512}; | |
168 |
2/2✓ Branch 0 taken 12000 times.
✓ Branch 1 taken 1 times.
|
12001 | for (unsigned int i = 0; i < 12000; i++) { |
169 | 12000 | history_append(&h, uint_to_str(i)); | |
170 | } | ||
171 | |||
172 | 1 | EXPECT_EQ(h.entries.count, h.max_entries); | |
173 | 1 | EXPECT_TRUE(h.entries.mask + 1 <= 2048); | |
174 | 1 | history_free(&h); | |
175 | 1 | } | |
176 | |||
177 | 1 | static void test_file_history_find(TestContext *ctx) | |
178 | { | ||
179 | 1 | const char fh_filename[] = "test/data/file-history"; | |
180 | 1 | FileHistory h = {.filename = NULL}; | |
181 | 1 | file_history_load(&h, xstrdup(fh_filename), 4096); | |
182 | 1 | EXPECT_STREQ(h.filename, fh_filename); | |
183 | 1 | EXPECT_EQ(h.entries.count, 3); | |
184 | |||
185 | 1 | const FileHistoryEntry *first = h.first; | |
186 | 1 | ASSERT_NONNULL(first); | |
187 | 1 | ASSERT_NONNULL(first->next); | |
188 | 1 | EXPECT_NULL(first->prev); | |
189 | 1 | EXPECT_STREQ(first->filename, "/etc/hosts"); | |
190 | 1 | EXPECT_EQ(first->row, 3); | |
191 | 1 | EXPECT_EQ(first->col, 42); | |
192 | |||
193 | 1 | const FileHistoryEntry *last = h.last; | |
194 | 1 | ASSERT_NONNULL(last); | |
195 | 1 | ASSERT_NONNULL(last->prev); | |
196 | 1 | EXPECT_NULL(last->next); | |
197 | 1 | EXPECT_STREQ(last->filename, "/home/user/file.txt"); | |
198 | 1 | EXPECT_EQ(last->row, 4521); | |
199 | 1 | EXPECT_EQ(last->col, 1); | |
200 | |||
201 | 1 | const FileHistoryEntry *mid = first->next; | |
202 | 1 | ASSERT_NONNULL(mid); | |
203 | 1 | EXPECT_PTREQ(mid, last->prev); | |
204 | 1 | EXPECT_PTREQ(mid->prev, first); | |
205 | 1 | EXPECT_PTREQ(mid->next, last); | |
206 | 1 | EXPECT_STREQ(mid->filename, "/tmp/foo"); | |
207 | 1 | EXPECT_EQ(mid->row, 123); | |
208 | 1 | EXPECT_EQ(mid->col, 456); | |
209 | |||
210 | 1 | unsigned long row = 0, col = 0; | |
211 | 1 | EXPECT_TRUE(file_history_find(&h, first->filename, &row, &col)); | |
212 | 1 | EXPECT_EQ(row, first->row); | |
213 | 1 | EXPECT_EQ(col, first->col); | |
214 | 1 | EXPECT_TRUE(file_history_find(&h, mid->filename, &row, &col)); | |
215 | 1 | EXPECT_EQ(row, mid->row); | |
216 | 1 | EXPECT_EQ(col, mid->col); | |
217 | 1 | EXPECT_TRUE(file_history_find(&h, last->filename, &row, &col)); | |
218 | 1 | EXPECT_EQ(row, last->row); | |
219 | 1 | EXPECT_EQ(col, last->col); | |
220 | |||
221 | 1 | row = col = 99; | |
222 | 1 | EXPECT_FALSE(file_history_find(&h, "/tmp/_", &row, &col)); | |
223 | 1 | EXPECT_EQ(row, 99); | |
224 | 1 | EXPECT_EQ(col, 99); | |
225 | |||
226 | 1 | file_history_free(&h); | |
227 | 1 | } | |
228 | |||
229 | static const TestEntry tests[] = { | ||
230 | TEST(test_history_append), | ||
231 | TEST(test_history_search), | ||
232 | TEST(test_history_tombstone_pressure), | ||
233 | TEST(test_file_history_find), | ||
234 | }; | ||
235 | |||
236 | const TestGroup history_tests = TEST_GROUP(tests); | ||
237 |