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 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 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 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 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 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 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 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 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 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 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 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 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