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