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 | ErrorBuffer ebuf = {.print_to_stderr = false}; | |
113 | 1 | const char *filename = "test/data/history"; | |
114 | 1 | History h = {.max_entries = 64}; | |
115 | 1 | history_load(&h, &ebuf, xstrdup(filename), 4096); | |
116 | 1 | EXPECT_EQ(h.entries.count, 3); | |
117 | 1 | EXPECT_STREQ(h.filename, filename); | |
118 | 1 | ASSERT_NONNULL(h.first); | |
119 | 1 | EXPECT_STREQ(h.first->text, "one"); | |
120 | 1 | ASSERT_NONNULL(h.last); | |
121 | 1 | EXPECT_STREQ(h.last->text, "three"); | |
122 | 1 | ASSERT_NONNULL(h.first->next); | |
123 | 1 | EXPECT_STREQ(h.first->next->text, "two"); | |
124 | 1 | ASSERT_NONNULL(h.last->prev); | |
125 | 1 | EXPECT_STREQ(h.last->prev->text, "two"); | |
126 | 1 | EXPECT_NULL(h.first->prev); | |
127 | 1 | EXPECT_NULL(h.last->next); | |
128 | |||
129 | 1 | const HistoryEntry *e = h.last; | |
130 | 1 | EXPECT_STREQ(e->text, "three"); | |
131 | 1 | EXPECT_TRUE(history_search_forward(&h, &e, "")); | |
132 | 1 | EXPECT_STREQ(e->text, "two"); | |
133 | 1 | EXPECT_TRUE(history_search_forward(&h, &e, "")); | |
134 | 1 | EXPECT_STREQ(e->text, "one"); | |
135 | 1 | EXPECT_FALSE(history_search_forward(&h, &e, "")); | |
136 | |||
137 | 1 | EXPECT_STREQ(e->text, "one"); | |
138 | 1 | EXPECT_TRUE(history_search_backward(&h, &e, "")); | |
139 | 1 | EXPECT_STREQ(e->text, "two"); | |
140 | 1 | EXPECT_TRUE(history_search_backward(&h, &e, "")); | |
141 | 1 | EXPECT_STREQ(e->text, "three"); | |
142 | 1 | EXPECT_FALSE(history_search_backward(&h, &e, "")); | |
143 | |||
144 | 1 | EXPECT_STREQ(e->text, "three"); | |
145 | 1 | EXPECT_TRUE(history_search_forward(&h, &e, "o")); | |
146 | 1 | EXPECT_STREQ(e->text, "one"); | |
147 | 1 | EXPECT_TRUE(history_search_backward(&h, &e, "th")); | |
148 | 1 | EXPECT_STREQ(e->text, "three"); | |
149 | |||
150 | 1 | free(h.filename); | |
151 | 1 | filename = "build/test/saved_history"; | |
152 | 1 | h.filename = xstrdup(filename); | |
153 | 1 | history_save(&h, &ebuf); | |
154 | 1 | history_free(&h); | |
155 | 1 | char *buf = NULL; | |
156 | 1 | ssize_t n = read_file(filename, &buf, 4096); | |
157 | 1 | EXPECT_EQ(n, 14); | |
158 | 1 | EXPECT_STREQ(buf, "one\ntwo\nthree\n"); | |
159 | 1 | free(buf); | |
160 | 1 | } | |
161 | |||
162 | // This test is done to ensure the HashMap can handle the constant | ||
163 | // churn from insertions and removals (i.e. that it rehashes the | ||
164 | // table to clean out tombstones, even when the number of real | ||
165 | // entries stops growing). | ||
166 | 1 | static void test_history_tombstones(TestContext *ctx) | |
167 | { | ||
168 | 1 | History h = {.max_entries = 512}; | |
169 |
2/2✓ Branch 0 (6→3) taken 12000 times.
✓ Branch 1 (6→7) taken 1 times.
|
12001 | for (unsigned int i = 0; i < 12000; i++) { |
170 | 12000 | history_append(&h, uint_to_str(i)); | |
171 | } | ||
172 | |||
173 | 1 | EXPECT_EQ(h.entries.count, h.max_entries); | |
174 | 1 | EXPECT_TRUE(h.entries.mask + 1 <= 2048); | |
175 | 1 | history_free(&h); | |
176 | 1 | } | |
177 | |||
178 | 1 | static void test_file_history_find(TestContext *ctx) | |
179 | { | ||
180 | 1 | ErrorBuffer ebuf = {.print_to_stderr = false}; | |
181 | 1 | const char fh_filename[] = "test/data/file-history"; | |
182 | 1 | FileHistory h = {.filename = NULL}; | |
183 | 1 | file_history_load(&h, &ebuf, xstrdup(fh_filename), 4096); | |
184 | 1 | EXPECT_STREQ(h.filename, fh_filename); | |
185 | 1 | EXPECT_EQ(h.entries.count, 3); | |
186 | |||
187 | 1 | const FileHistoryEntry *first = h.first; | |
188 | 1 | ASSERT_NONNULL(first); | |
189 | 1 | ASSERT_NONNULL(first->next); | |
190 | 1 | EXPECT_NULL(first->prev); | |
191 | 1 | EXPECT_STREQ(first->filename, "/etc/hosts"); | |
192 | 1 | EXPECT_EQ(first->row, 3); | |
193 | 1 | EXPECT_EQ(first->col, 42); | |
194 | |||
195 | 1 | const FileHistoryEntry *last = h.last; | |
196 | 1 | ASSERT_NONNULL(last); | |
197 | 1 | ASSERT_NONNULL(last->prev); | |
198 | 1 | EXPECT_NULL(last->next); | |
199 | 1 | EXPECT_STREQ(last->filename, "/home/user/file.txt"); | |
200 | 1 | EXPECT_EQ(last->row, 4521); | |
201 | 1 | EXPECT_EQ(last->col, 1); | |
202 | |||
203 | 1 | const FileHistoryEntry *mid = first->next; | |
204 | 1 | ASSERT_NONNULL(mid); | |
205 | 1 | EXPECT_PTREQ(mid, last->prev); | |
206 | 1 | EXPECT_PTREQ(mid->prev, first); | |
207 | 1 | EXPECT_PTREQ(mid->next, last); | |
208 | 1 | EXPECT_STREQ(mid->filename, "/tmp/foo"); | |
209 | 1 | EXPECT_EQ(mid->row, 123); | |
210 | 1 | EXPECT_EQ(mid->col, 456); | |
211 | |||
212 | 1 | unsigned long row = 0, col = 0; | |
213 | 1 | EXPECT_TRUE(file_history_find(&h, first->filename, &row, &col)); | |
214 | 1 | EXPECT_EQ(row, first->row); | |
215 | 1 | EXPECT_EQ(col, first->col); | |
216 | 1 | EXPECT_TRUE(file_history_find(&h, mid->filename, &row, &col)); | |
217 | 1 | EXPECT_EQ(row, mid->row); | |
218 | 1 | EXPECT_EQ(col, mid->col); | |
219 | 1 | EXPECT_TRUE(file_history_find(&h, last->filename, &row, &col)); | |
220 | 1 | EXPECT_EQ(row, last->row); | |
221 | 1 | EXPECT_EQ(col, last->col); | |
222 | |||
223 | 1 | row = col = 99; | |
224 | 1 | EXPECT_FALSE(file_history_find(&h, "/tmp/_", &row, &col)); | |
225 | 1 | EXPECT_EQ(row, 99); | |
226 | 1 | EXPECT_EQ(col, 99); | |
227 | |||
228 | 1 | file_history_free(&h); | |
229 | 1 | } | |
230 | |||
231 | static const TestEntry tests[] = { | ||
232 | TEST(test_history_append), | ||
233 | TEST(test_history_search), | ||
234 | TEST(test_history_tombstones), | ||
235 | TEST(test_file_history_find), | ||
236 | }; | ||
237 | |||
238 | const TestGroup history_tests = TEST_GROUP(tests); | ||
239 |