1 /* $OpenBSD: history.c,v 1.7 2017/07/05 15:31:45 bluhm Exp $ */
2 /*
3 * Copyright (c) 2016 Bastian Maerkisch <bmaerkisch@web.de>
4 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute these tests for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THESE TESTS ARE PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include <err.h>
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include <string.h>
22
23 #ifdef READLINE
24 #include <readline/history.h>
25 #else
26 #include <readline/readline.h>
27 #endif
28
29
30 /*
31 * Test infrastructure function.
32 * At the beginning of each test, call as "msg(__func__);".
33 * Upon failure, call as "msg(fmt, ...);".
34 * At the end of each test, call as "return msg(NULL);".
35 */
36 int
msg(const char * fmt,...)37 msg(const char *fmt, ...)
38 {
39 static const char *testname = NULL;
40 static int failed = 0;
41 va_list ap;
42
43 if (testname == NULL) {
44 using_history();
45 unstifle_history();
46 testname = fmt;
47 return 0;
48 }
49
50 if (fmt == NULL) {
51 clear_history();
52 unstifle_history();
53 testname = NULL;
54 if (failed == 0)
55 return 0;
56 failed = 0;
57 return 1;
58 }
59
60 fprintf(stderr, "%s: ", testname);
61 va_start(ap, fmt);
62 vfprintf(stderr, fmt, ap);
63 va_end(ap);
64 fputc('\n', stderr);
65 failed = 1;
66 return 1;
67 }
68
69 void
check_current(const char * descr,const char * want)70 check_current(const char *descr, const char *want)
71 {
72 HIST_ENTRY *he;
73
74 he = current_history();
75
76 if (want == NULL) {
77 if (he != NULL)
78 msg("Failed to move beyond the newest entry.");
79 return;
80 }
81
82 if (he == NULL)
83 msg("%s is NULL.", descr);
84 else if (strcmp(he->line, want) != 0)
85 msg("%s is \"%s\" instead of \"%s\".", descr, he->line, want);
86 }
87
88 void
check_get(int idx,const char * want)89 check_get(int idx, const char *want)
90 {
91 HIST_ENTRY *he;
92
93 if ((he = history_get(history_base + idx)) == NULL)
94 msg("Get %d+%d returned NULL.", history_base, idx);
95 else if (he->line == NULL)
96 msg("Get %d+%d returned line == NULL.", history_base, idx);
97 else if (strcmp(he->line, want) != 0)
98 msg("Get %d+%d returned \"%s\" instead of \"%s\".",
99 history_base, idx, he->line, want);
100 }
101
102
103 /* Fails if previous and next are interchanged. */
104 int
test_movement_direction(void)105 test_movement_direction(void)
106 {
107 msg(__func__);
108 add_history("111");
109 add_history("222");
110
111 while (previous_history() != NULL);
112 check_current("Oldest entry", "111");
113
114 /*
115 * Move to the most recent end of the history.
116 * This moves past the newest entry.
117 */
118 while (next_history() != NULL);
119 check_current(NULL, NULL);
120
121 return msg(NULL);
122 }
123
124
125 /* Fails if the position is counted from the recent end. */
126 int
test_where(void)127 test_where(void)
128 {
129 int ret;
130
131 msg(__func__);
132
133 /*
134 * Adding four elements since set_pos(0) doesn't work
135 * for some versions of libedit.
136 */
137 add_history("111");
138 add_history("222");
139 add_history("333");
140 add_history("444");
141
142 /* Set the pointer to the element "222". */
143 history_set_pos(1);
144 if ((ret = where_history()) != 1)
145 msg("Where returned %d instead of 1.", ret);
146
147 return msg(NULL);
148 }
149
150
151 /*
152 * Fails if the argument of history_get()
153 * does not refer to the zero-based index + history_base.
154 */
155 int
test_get(void)156 test_get(void)
157 {
158 msg(__func__);
159 add_history("111");
160 add_history("222");
161 add_history("333");
162 add_history("444");
163
164 /* Try to retrieve second element. */
165 check_get(1, "222");
166
167 return msg(NULL);
168 }
169
170
171 /* Fails if set_pos returns 0 for success and -1 for failure. */
172 int
test_set_pos_return_values(void)173 test_set_pos_return_values(void)
174 {
175 int ret;
176
177 msg(__func__);
178 add_history("111");
179 add_history("222");
180
181 /* This should fail. */
182 if ((ret = history_set_pos(-1)) != 0)
183 msg("Set_pos(-1) returned %d instead of 0.", ret);
184
185 /*
186 * This should succeed.
187 * Note that we do not use the index 0 here, since that
188 * actually fails for some versions of libedit.
189 */
190 if ((ret = history_set_pos(1)) != 1)
191 msg("Set_pos(1) returned %d instead of 1.", ret);
192
193 return msg(NULL);
194 }
195
196
197 /* Fails if the index is one-based. */
198 int
test_set_pos_index(void)199 test_set_pos_index(void)
200 {
201 msg(__func__);
202 add_history("111");
203 add_history("222");
204
205 /* Do not test return value here since that might be broken, too. */
206 history_set_pos(0);
207 check_current("Entry 0", "111");
208
209 history_set_pos(1);
210 check_current("Entry 1", "222");
211
212 return msg(NULL);
213 }
214
215
216 /* Fails if remove does not renumber. */
217 int
test_remove(void)218 test_remove(void)
219 {
220 msg(__func__);
221 add_history("111");
222 add_history("222");
223 add_history("333");
224 add_history("444");
225
226 /* Remove the second item "222"; the index is zero-based. */
227 remove_history(1);
228
229 /*
230 * Try to get the new second element using history_get.
231 * The argument of get is based on history_base.
232 */
233 check_get(1, "333");
234
235 /*
236 * Try to get the second element using set_pos/current.
237 * The index is zero-based.
238 */
239 history_set_pos(1);
240 check_current("Entry 1", "333");
241
242 /* Remove the new second item "333". */
243 remove_history(1);
244 check_get(1, "444");
245
246 return msg(NULL);
247 }
248
249
250 /* Fails if stifle doesn't discard existing entries. */
251 int
test_stifle_size(void)252 test_stifle_size(void)
253 {
254 msg(__func__);
255 add_history("111");
256 add_history("222");
257 add_history("333");
258
259 /* Reduce the size of the history. */
260 stifle_history(2);
261 if (history_length != 2)
262 msg("Length is %d instead of 2.", history_length);
263
264 return msg(NULL);
265 }
266
267
268 /* Fails if add doesn't increase history_base if the history is full. */
269 int
test_stifle_base(void)270 test_stifle_base(void)
271 {
272 msg(__func__);
273 stifle_history(2);
274
275 /* Add one more than the maximum size. */
276 add_history("111");
277 add_history("222");
278 add_history("333");
279
280 /* The history_base should have changed. */
281 if (history_base != 2)
282 msg("Base is %d instead of 2.", history_base);
283
284 return msg(NULL);
285 }
286
287
288 int
main(void)289 main(void)
290 {
291 int fail = 0;
292
293 fail += test_movement_direction();
294 fail += test_where();
295 fail += test_get();
296 fail += test_set_pos_return_values();
297 fail += test_set_pos_index();
298 fail += test_remove();
299 fail += test_stifle_size();
300 fail += test_stifle_base();
301
302 if (fail)
303 errx(1, "%d test(s) failed.", fail);
304 return 0;
305 }
306