blob: df19b9eb491a45c962d1bd61474f8244510fdea3 [file] [log] [blame]
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * testing.c: Support for tests.
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
18/*
19 * Prepare "gap" for an assert error and add the sourcing position.
20 */
21 static void
22prepare_assert_error(garray_T *gap)
23{
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010024 char buf[NUMBUFLEN];
Bram Moolenaar4f25b1a2020-09-10 19:25:05 +020025 char_u *sname = estack_sfile(ESTACK_NONE);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020026
27 ga_init2(gap, 1, 100);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010028 if (sname != NULL)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020029 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010030 ga_concat(gap, sname);
31 if (SOURCING_LNUM > 0)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020032 ga_concat(gap, (char_u *)" ");
33 }
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010034 if (SOURCING_LNUM > 0)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020035 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010036 sprintf(buf, "line %ld", (long)SOURCING_LNUM);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020037 ga_concat(gap, (char_u *)buf);
38 }
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010039 if (sname != NULL || SOURCING_LNUM > 0)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020040 ga_concat(gap, (char_u *)": ");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010041 vim_free(sname);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020042}
43
44/*
45 * Append "p[clen]" to "gap", escaping unprintable characters.
46 * Changes NL to \n, CR to \r, etc.
47 */
48 static void
49ga_concat_esc(garray_T *gap, char_u *p, int clen)
50{
51 char_u buf[NUMBUFLEN];
52
53 if (clen > 1)
54 {
55 mch_memmove(buf, p, clen);
56 buf[clen] = NUL;
57 ga_concat(gap, buf);
58 }
59 else switch (*p)
60 {
61 case BS: ga_concat(gap, (char_u *)"\\b"); break;
62 case ESC: ga_concat(gap, (char_u *)"\\e"); break;
63 case FF: ga_concat(gap, (char_u *)"\\f"); break;
64 case NL: ga_concat(gap, (char_u *)"\\n"); break;
65 case TAB: ga_concat(gap, (char_u *)"\\t"); break;
66 case CAR: ga_concat(gap, (char_u *)"\\r"); break;
67 case '\\': ga_concat(gap, (char_u *)"\\\\"); break;
68 default:
Bram Moolenaar7177da92020-07-12 23:09:20 +020069 if (*p < ' ' || *p == 0x7f)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020070 {
71 vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
72 ga_concat(gap, buf);
73 }
74 else
75 ga_append(gap, *p);
76 break;
77 }
78}
79
80/*
81 * Append "str" to "gap", escaping unprintable characters.
82 * Changes NL to \n, CR to \r, etc.
83 */
84 static void
85ga_concat_shorten_esc(garray_T *gap, char_u *str)
86{
87 char_u *p;
88 char_u *s;
89 int c;
90 int clen;
91 char_u buf[NUMBUFLEN];
92 int same_len;
93
94 if (str == NULL)
95 {
96 ga_concat(gap, (char_u *)"NULL");
97 return;
98 }
99
100 for (p = str; *p != NUL; ++p)
101 {
102 same_len = 1;
103 s = p;
104 c = mb_ptr2char_adv(&s);
105 clen = s - p;
106 while (*s != NUL && c == mb_ptr2char(s))
107 {
108 ++same_len;
109 s += clen;
110 }
111 if (same_len > 20)
112 {
113 ga_concat(gap, (char_u *)"\\[");
114 ga_concat_esc(gap, p, clen);
115 ga_concat(gap, (char_u *)" occurs ");
116 vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
117 ga_concat(gap, buf);
118 ga_concat(gap, (char_u *)" times]");
119 p = s - 1;
120 }
121 else
122 ga_concat_esc(gap, p, clen);
123 }
124}
125
126/*
127 * Fill "gap" with information about an assert error.
128 */
129 static void
130fill_assert_error(
131 garray_T *gap,
132 typval_T *opt_msg_tv,
133 char_u *exp_str,
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200134 typval_T *exp_tv_arg,
135 typval_T *got_tv_arg,
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200136 assert_type_T atype)
137{
138 char_u numbuf[NUMBUFLEN];
139 char_u *tofree;
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200140 typval_T *exp_tv = exp_tv_arg;
141 typval_T *got_tv = got_tv_arg;
142 int did_copy = FALSE;
143 int omitted = 0;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200144
Bram Moolenaar1d634542020-08-18 13:41:50 +0200145 if (opt_msg_tv->v_type != VAR_UNKNOWN
146 && !(opt_msg_tv->v_type == VAR_STRING
147 && (opt_msg_tv->vval.v_string == NULL
148 || *opt_msg_tv->vval.v_string == NUL)))
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200149 {
150 ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0));
151 vim_free(tofree);
152 ga_concat(gap, (char_u *)": ");
153 }
154
155 if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH)
156 ga_concat(gap, (char_u *)"Pattern ");
157 else if (atype == ASSERT_NOTEQUAL)
158 ga_concat(gap, (char_u *)"Expected not equal to ");
159 else
160 ga_concat(gap, (char_u *)"Expected ");
161 if (exp_str == NULL)
162 {
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200163 // When comparing dictionaries, drop the items that are equal, so that
164 // it's a lot easier to see what differs.
165 if (atype != ASSERT_NOTEQUAL
166 && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT
167 && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL)
168 {
169 dict_T *exp_d = exp_tv->vval.v_dict;
170 dict_T *got_d = got_tv->vval.v_dict;
171 hashitem_T *hi;
172 dictitem_T *item2;
173 int todo;
174
175 did_copy = TRUE;
176 exp_tv->vval.v_dict = dict_alloc();
177 got_tv->vval.v_dict = dict_alloc();
178 if (exp_tv->vval.v_dict == NULL || got_tv->vval.v_dict == NULL)
179 return;
180
181 todo = (int)exp_d->dv_hashtab.ht_used;
182 for (hi = exp_d->dv_hashtab.ht_array; todo > 0; ++hi)
183 {
184 if (!HASHITEM_EMPTY(hi))
185 {
186 item2 = dict_find(got_d, hi->hi_key, -1);
187 if (item2 == NULL || !tv_equal(&HI2DI(hi)->di_tv,
188 &item2->di_tv, FALSE, FALSE))
189 {
190 // item of exp_d not present in got_d or values differ.
191 dict_add_tv(exp_tv->vval.v_dict,
192 (char *)hi->hi_key, &HI2DI(hi)->di_tv);
193 if (item2 != NULL)
194 dict_add_tv(got_tv->vval.v_dict,
195 (char *)hi->hi_key, &item2->di_tv);
196 }
197 else
198 ++omitted;
199 --todo;
200 }
201 }
202
203 // Add items only present in got_d.
204 todo = (int)got_d->dv_hashtab.ht_used;
205 for (hi = got_d->dv_hashtab.ht_array; todo > 0; ++hi)
206 {
207 if (!HASHITEM_EMPTY(hi))
208 {
209 item2 = dict_find(exp_d, hi->hi_key, -1);
210 if (item2 == NULL)
211 // item of got_d not present in exp_d
212 dict_add_tv(got_tv->vval.v_dict,
213 (char *)hi->hi_key, &HI2DI(hi)->di_tv);
214 --todo;
215 }
216 }
217 }
218
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200219 ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0));
220 vim_free(tofree);
221 }
222 else
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100223 {
224 ga_concat(gap, (char_u *)"'");
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200225 ga_concat_shorten_esc(gap, exp_str);
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100226 ga_concat(gap, (char_u *)"'");
227 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200228 if (atype != ASSERT_NOTEQUAL)
229 {
230 if (atype == ASSERT_MATCH)
231 ga_concat(gap, (char_u *)" does not match ");
232 else if (atype == ASSERT_NOTMATCH)
233 ga_concat(gap, (char_u *)" does match ");
234 else
235 ga_concat(gap, (char_u *)" but got ");
236 ga_concat_shorten_esc(gap, tv2string(got_tv, &tofree, numbuf, 0));
237 vim_free(tofree);
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200238
239 if (omitted != 0)
240 {
241 char buf[100];
242
243 vim_snprintf(buf, 100, " - %d equal item%s omitted",
244 omitted, omitted == 1 ? "" : "s");
245 ga_concat(gap, (char_u *)buf);
246 }
247 }
248
249 if (did_copy)
250 {
251 clear_tv(exp_tv);
252 clear_tv(got_tv);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200253 }
254}
255
256 static int
257assert_equal_common(typval_T *argvars, assert_type_T atype)
258{
259 garray_T ga;
260
261 if (tv_equal(&argvars[0], &argvars[1], FALSE, FALSE)
262 != (atype == ASSERT_EQUAL))
263 {
264 prepare_assert_error(&ga);
265 fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
266 atype);
267 assert_error(&ga);
268 ga_clear(&ga);
269 return 1;
270 }
271 return 0;
272}
273
274 static int
275assert_match_common(typval_T *argvars, assert_type_T atype)
276{
277 garray_T ga;
278 char_u buf1[NUMBUFLEN];
279 char_u buf2[NUMBUFLEN];
Bram Moolenaar7177da92020-07-12 23:09:20 +0200280 int called_emsg_before = called_emsg;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200281 char_u *pat = tv_get_string_buf_chk(&argvars[0], buf1);
282 char_u *text = tv_get_string_buf_chk(&argvars[1], buf2);
283
Bram Moolenaar7177da92020-07-12 23:09:20 +0200284 if (called_emsg == called_emsg_before
285 && pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH))
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200286 {
287 prepare_assert_error(&ga);
288 fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
289 atype);
290 assert_error(&ga);
291 ga_clear(&ga);
292 return 1;
293 }
294 return 0;
295}
296
297/*
298 * Common for assert_true() and assert_false().
299 * Return non-zero for failure.
300 */
301 static int
302assert_bool(typval_T *argvars, int isTrue)
303{
304 int error = FALSE;
305 garray_T ga;
306
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100307 if (argvars[0].v_type == VAR_BOOL
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200308 && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE))
309 return 0;
310 if (argvars[0].v_type != VAR_NUMBER
311 || (tv_get_number_chk(&argvars[0], &error) == 0) == isTrue
312 || error)
313 {
314 prepare_assert_error(&ga);
315 fill_assert_error(&ga, &argvars[1],
316 (char_u *)(isTrue ? "True" : "False"),
317 NULL, &argvars[0], ASSERT_OTHER);
318 assert_error(&ga);
319 ga_clear(&ga);
320 return 1;
321 }
322 return 0;
323}
324
325 static void
326assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, char_u *cmd)
327{
328 char_u *tofree;
329 char_u numbuf[NUMBUFLEN];
330
331 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN)
332 {
333 ga_concat(gap, echo_string(&argvars[2], &tofree, numbuf, 0));
334 vim_free(tofree);
335 }
336 else
337 ga_concat(gap, cmd);
338}
339
340 static int
341assert_beeps(typval_T *argvars)
342{
343 char_u *cmd = tv_get_string_chk(&argvars[0]);
344 garray_T ga;
345 int ret = 0;
346
347 called_vim_beep = FALSE;
348 suppress_errthrow = TRUE;
349 emsg_silent = FALSE;
350 do_cmdline_cmd(cmd);
351 if (!called_vim_beep)
352 {
353 prepare_assert_error(&ga);
354 ga_concat(&ga, (char_u *)"command did not beep: ");
355 ga_concat(&ga, cmd);
356 assert_error(&ga);
357 ga_clear(&ga);
358 ret = 1;
359 }
360
361 suppress_errthrow = FALSE;
362 emsg_on_display = FALSE;
363 return ret;
364}
365
366/*
367 * "assert_beeps(cmd [, error])" function
368 */
369 void
370f_assert_beeps(typval_T *argvars, typval_T *rettv)
371{
372 rettv->vval.v_number = assert_beeps(argvars);
373}
374
375/*
376 * "assert_equal(expected, actual[, msg])" function
377 */
378 void
379f_assert_equal(typval_T *argvars, typval_T *rettv)
380{
381 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
382}
383
384 static int
385assert_equalfile(typval_T *argvars)
386{
387 char_u buf1[NUMBUFLEN];
388 char_u buf2[NUMBUFLEN];
Bram Moolenaar7177da92020-07-12 23:09:20 +0200389 int called_emsg_before = called_emsg;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200390 char_u *fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
391 char_u *fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
392 garray_T ga;
393 FILE *fd1;
394 FILE *fd2;
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200395 char line1[200];
396 char line2[200];
397 int lineidx = 0;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200398
Bram Moolenaar7177da92020-07-12 23:09:20 +0200399 if (called_emsg > called_emsg_before)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200400 return 0;
401
402 IObuff[0] = NUL;
403 fd1 = mch_fopen((char *)fname1, READBIN);
404 if (fd1 == NULL)
405 {
406 vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
407 }
408 else
409 {
410 fd2 = mch_fopen((char *)fname2, READBIN);
411 if (fd2 == NULL)
412 {
413 fclose(fd1);
414 vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
415 }
416 else
417 {
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200418 int c1, c2;
419 long count = 0;
420 long linecount = 1;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200421
422 for (;;)
423 {
424 c1 = fgetc(fd1);
425 c2 = fgetc(fd2);
426 if (c1 == EOF)
427 {
428 if (c2 != EOF)
429 STRCPY(IObuff, "first file is shorter");
430 break;
431 }
432 else if (c2 == EOF)
433 {
434 STRCPY(IObuff, "second file is shorter");
435 break;
436 }
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200437 else
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200438 {
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200439 line1[lineidx] = c1;
440 line2[lineidx] = c2;
441 ++lineidx;
442 if (c1 != c2)
443 {
444 vim_snprintf((char *)IObuff, IOSIZE,
445 "difference at byte %ld, line %ld",
446 count, linecount);
447 break;
448 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200449 }
450 ++count;
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200451 if (c1 == NL)
452 {
453 ++linecount;
454 lineidx = 0;
455 }
456 else if (lineidx + 2 == (int)sizeof(line1))
457 {
458 mch_memmove(line1, line1 + 100, lineidx - 100);
459 mch_memmove(line2, line2 + 100, lineidx - 100);
460 lineidx -= 100;
461 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200462 }
463 fclose(fd1);
464 fclose(fd2);
465 }
466 }
467 if (IObuff[0] != NUL)
468 {
469 prepare_assert_error(&ga);
Bram Moolenaarfb517ba2020-06-03 19:55:35 +0200470 if (argvars[2].v_type != VAR_UNKNOWN)
471 {
472 char_u numbuf[NUMBUFLEN];
473 char_u *tofree;
474
475 ga_concat(&ga, echo_string(&argvars[2], &tofree, numbuf, 0));
476 vim_free(tofree);
477 ga_concat(&ga, (char_u *)": ");
478 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200479 ga_concat(&ga, IObuff);
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200480 if (lineidx > 0)
481 {
482 line1[lineidx] = NUL;
483 line2[lineidx] = NUL;
484 ga_concat(&ga, (char_u *)" after \"");
485 ga_concat(&ga, (char_u *)line1);
486 if (STRCMP(line1, line2) != 0)
487 {
488 ga_concat(&ga, (char_u *)"\" vs \"");
489 ga_concat(&ga, (char_u *)line2);
490 }
491 ga_concat(&ga, (char_u *)"\"");
492 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200493 assert_error(&ga);
494 ga_clear(&ga);
495 return 1;
496 }
497 return 0;
498}
499
500/*
Bram Moolenaarfb517ba2020-06-03 19:55:35 +0200501 * "assert_equalfile(fname-one, fname-two[, msg])" function
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200502 */
503 void
504f_assert_equalfile(typval_T *argvars, typval_T *rettv)
505{
506 rettv->vval.v_number = assert_equalfile(argvars);
507}
508
509/*
510 * "assert_notequal(expected, actual[, msg])" function
511 */
512 void
513f_assert_notequal(typval_T *argvars, typval_T *rettv)
514{
515 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
516}
517
518/*
519 * "assert_exception(string[, msg])" function
520 */
521 void
522f_assert_exception(typval_T *argvars, typval_T *rettv)
523{
524 garray_T ga;
525 char_u *error = tv_get_string_chk(&argvars[0]);
526
527 if (*get_vim_var_str(VV_EXCEPTION) == NUL)
528 {
529 prepare_assert_error(&ga);
530 ga_concat(&ga, (char_u *)"v:exception is not set");
531 assert_error(&ga);
532 ga_clear(&ga);
533 rettv->vval.v_number = 1;
534 }
535 else if (error != NULL
536 && strstr((char *)get_vim_var_str(VV_EXCEPTION), (char *)error) == NULL)
537 {
538 prepare_assert_error(&ga);
539 fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
540 get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
541 assert_error(&ga);
542 ga_clear(&ga);
543 rettv->vval.v_number = 1;
544 }
545}
546
547/*
548 * "assert_fails(cmd [, error[, msg]])" function
549 */
550 void
551f_assert_fails(typval_T *argvars, typval_T *rettv)
552{
553 char_u *cmd = tv_get_string_chk(&argvars[0]);
554 garray_T ga;
555 int save_trylevel = trylevel;
Bram Moolenaar53989552019-12-23 22:59:18 +0100556 int called_emsg_before = called_emsg;
Bram Moolenaar44d66522020-09-06 22:26:57 +0200557 char *wrong_arg_msg = NULL;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200558
559 // trylevel must be zero for a ":throw" command to be considered failed
560 trylevel = 0;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200561 suppress_errthrow = TRUE;
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100562 in_assert_fails = TRUE;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200563
564 do_cmdline_cmd(cmd);
Bram Moolenaar53989552019-12-23 22:59:18 +0100565 if (called_emsg == called_emsg_before)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200566 {
567 prepare_assert_error(&ga);
568 ga_concat(&ga, (char_u *)"command did not fail: ");
569 assert_append_cmd_or_arg(&ga, argvars, cmd);
570 assert_error(&ga);
571 ga_clear(&ga);
572 rettv->vval.v_number = 1;
573 }
574 else if (argvars[1].v_type != VAR_UNKNOWN)
575 {
576 char_u buf[NUMBUFLEN];
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200577 char_u *expected;
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100578 char_u *expected_str = NULL;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200579 int error_found = FALSE;
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200580 int error_found_index = 1;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200581 char_u *actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]"
582 : emsg_assert_fails_msg;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200583
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200584 if (argvars[1].v_type == VAR_STRING)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200585 {
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200586 expected = tv_get_string_buf_chk(&argvars[1], buf);
587 error_found = expected == NULL
588 || strstr((char *)actual, (char *)expected) == NULL;
589 }
590 else if (argvars[1].v_type == VAR_LIST)
591 {
592 list_T *list = argvars[1].vval.v_list;
593 typval_T *tv;
594
595 if (list == NULL || list->lv_len < 1 || list->lv_len > 2)
596 {
Bram Moolenaar44d66522020-09-06 22:26:57 +0200597 wrong_arg_msg = e_assert_fails_second_arg;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200598 goto theend;
599 }
600 CHECK_LIST_MATERIALIZE(list);
601 tv = &list->lv_first->li_tv;
602 expected = tv_get_string_buf_chk(tv, buf);
603 if (!pattern_match(expected, actual, FALSE))
604 {
605 error_found = TRUE;
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100606 expected_str = expected;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200607 }
608 else if (list->lv_len == 2)
609 {
610 tv = &list->lv_u.mat.lv_last->li_tv;
611 actual = get_vim_var_str(VV_ERRMSG);
612 expected = tv_get_string_buf_chk(tv, buf);
613 if (!pattern_match(expected, actual, FALSE))
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100614 {
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200615 error_found = TRUE;
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100616 expected_str = expected;
617 }
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200618 }
619 }
620 else
621 {
Bram Moolenaar44d66522020-09-06 22:26:57 +0200622 wrong_arg_msg = e_assert_fails_second_arg;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200623 goto theend;
624 }
625
Bram Moolenaar9b02d642020-08-18 23:24:13 +0200626 if (!error_found && argvars[2].v_type != VAR_UNKNOWN
Bram Moolenaar44d66522020-09-06 22:26:57 +0200627 && argvars[3].v_type != VAR_UNKNOWN)
Bram Moolenaar1d634542020-08-18 13:41:50 +0200628 {
Bram Moolenaar44d66522020-09-06 22:26:57 +0200629 if (argvars[3].v_type != VAR_NUMBER)
630 {
631 wrong_arg_msg = e_assert_fails_fourth_argument;
632 goto theend;
633 }
634 else if (argvars[3].vval.v_number >= 0
635 && argvars[3].vval.v_number != emsg_assert_fails_lnum)
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200636 {
637 error_found = TRUE;
638 error_found_index = 3;
639 }
Bram Moolenaar44d66522020-09-06 22:26:57 +0200640 if (!error_found && argvars[4].v_type != VAR_UNKNOWN)
641 {
642 if (argvars[4].v_type != VAR_STRING)
643 {
644 wrong_arg_msg = e_assert_fails_fifth_argument;
645 goto theend;
646 }
647 else if (argvars[4].vval.v_string != NULL
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200648 && !pattern_match(argvars[4].vval.v_string,
649 emsg_assert_fails_context, FALSE))
Bram Moolenaar44d66522020-09-06 22:26:57 +0200650 {
651 error_found = TRUE;
652 error_found_index = 4;
653 }
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200654 }
Bram Moolenaar1d634542020-08-18 13:41:50 +0200655 }
656
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200657 if (error_found)
658 {
659 typval_T actual_tv;
660
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200661 prepare_assert_error(&ga);
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200662 if (error_found_index == 3)
Bram Moolenaar1d634542020-08-18 13:41:50 +0200663 {
664 actual_tv.v_type = VAR_NUMBER;
665 actual_tv.vval.v_number = emsg_assert_fails_lnum;
666 }
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200667 else if (error_found_index == 4)
668 {
669 actual_tv.v_type = VAR_STRING;
670 actual_tv.vval.v_string = emsg_assert_fails_context;
671 }
Bram Moolenaar1d634542020-08-18 13:41:50 +0200672 else
673 {
674 actual_tv.v_type = VAR_STRING;
675 actual_tv.vval.v_string = actual;
676 }
Bram Moolenaar631e8f92020-11-04 15:07:16 +0100677 fill_assert_error(&ga, &argvars[2], expected_str,
Bram Moolenaar9bd5d872020-09-06 21:47:48 +0200678 &argvars[error_found_index], &actual_tv, ASSERT_OTHER);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200679 ga_concat(&ga, (char_u *)": ");
680 assert_append_cmd_or_arg(&ga, argvars, cmd);
681 assert_error(&ga);
682 ga_clear(&ga);
683 rettv->vval.v_number = 1;
684 }
685 }
686
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200687theend:
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200688 trylevel = save_trylevel;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200689 suppress_errthrow = FALSE;
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100690 in_assert_fails = FALSE;
691 did_emsg = FALSE;
692 msg_col = 0;
693 need_wait_return = FALSE;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200694 emsg_on_display = FALSE;
Bram Moolenaar28ee8922020-10-28 20:20:00 +0100695 msg_scrolled = 0;
696 lines_left = Rows;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200697 VIM_CLEAR(emsg_assert_fails_msg);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200698 set_vim_var_string(VV_ERRMSG, NULL, 0);
Bram Moolenaar44d66522020-09-06 22:26:57 +0200699 if (wrong_arg_msg != NULL)
700 emsg(_(wrong_arg_msg));
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200701}
702
703/*
704 * "assert_false(actual[, msg])" function
705 */
706 void
707f_assert_false(typval_T *argvars, typval_T *rettv)
708{
709 rettv->vval.v_number = assert_bool(argvars, FALSE);
710}
711
712 static int
713assert_inrange(typval_T *argvars)
714{
715 garray_T ga;
716 int error = FALSE;
717 char_u *tofree;
718 char msg[200];
719 char_u numbuf[NUMBUFLEN];
720
721#ifdef FEAT_FLOAT
722 if (argvars[0].v_type == VAR_FLOAT
723 || argvars[1].v_type == VAR_FLOAT
724 || argvars[2].v_type == VAR_FLOAT)
725 {
726 float_T flower = tv_get_float(&argvars[0]);
727 float_T fupper = tv_get_float(&argvars[1]);
728 float_T factual = tv_get_float(&argvars[2]);
729
730 if (factual < flower || factual > fupper)
731 {
732 prepare_assert_error(&ga);
733 if (argvars[3].v_type != VAR_UNKNOWN)
734 {
735 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
736 vim_free(tofree);
737 }
738 else
739 {
740 vim_snprintf(msg, 200, "Expected range %g - %g, but got %g",
741 flower, fupper, factual);
742 ga_concat(&ga, (char_u *)msg);
743 }
744 assert_error(&ga);
745 ga_clear(&ga);
746 return 1;
747 }
748 }
749 else
750#endif
751 {
752 varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
753 varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
754 varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
755
756 if (error)
757 return 0;
758 if (actual < lower || actual > upper)
759 {
760 prepare_assert_error(&ga);
761 if (argvars[3].v_type != VAR_UNKNOWN)
762 {
763 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
764 vim_free(tofree);
765 }
766 else
767 {
768 vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld",
769 (long)lower, (long)upper, (long)actual);
770 ga_concat(&ga, (char_u *)msg);
771 }
772 assert_error(&ga);
773 ga_clear(&ga);
774 return 1;
775 }
776 }
777 return 0;
778}
779
780/*
781 * "assert_inrange(lower, upper[, msg])" function
782 */
783 void
784f_assert_inrange(typval_T *argvars, typval_T *rettv)
785{
786 rettv->vval.v_number = assert_inrange(argvars);
787}
788
789/*
790 * "assert_match(pattern, actual[, msg])" function
791 */
792 void
793f_assert_match(typval_T *argvars, typval_T *rettv)
794{
795 rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
796}
797
798/*
799 * "assert_notmatch(pattern, actual[, msg])" function
800 */
801 void
802f_assert_notmatch(typval_T *argvars, typval_T *rettv)
803{
804 rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
805}
806
807/*
808 * "assert_report(msg)" function
809 */
810 void
811f_assert_report(typval_T *argvars, typval_T *rettv)
812{
813 garray_T ga;
814
815 prepare_assert_error(&ga);
816 ga_concat(&ga, tv_get_string(&argvars[0]));
817 assert_error(&ga);
818 ga_clear(&ga);
819 rettv->vval.v_number = 1;
820}
821
822/*
823 * "assert_true(actual[, msg])" function
824 */
825 void
826f_assert_true(typval_T *argvars, typval_T *rettv)
827{
828 rettv->vval.v_number = assert_bool(argvars, TRUE);
829}
830
831/*
832 * "test_alloc_fail(id, countdown, repeat)" function
833 */
834 void
835f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED)
836{
837 if (argvars[0].v_type != VAR_NUMBER
838 || argvars[0].vval.v_number <= 0
839 || argvars[1].v_type != VAR_NUMBER
840 || argvars[1].vval.v_number < 0
841 || argvars[2].v_type != VAR_NUMBER)
842 emsg(_(e_invarg));
843 else
844 {
845 alloc_fail_id = argvars[0].vval.v_number;
846 if (alloc_fail_id >= aid_last)
847 emsg(_(e_invarg));
848 alloc_fail_countdown = argvars[1].vval.v_number;
849 alloc_fail_repeat = argvars[2].vval.v_number;
850 did_outofmem_msg = FALSE;
851 }
852}
853
854/*
855 * "test_autochdir()"
856 */
857 void
858f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
859{
860#if defined(FEAT_AUTOCHDIR)
861 test_autochdir = TRUE;
862#endif
863}
864
865/*
866 * "test_feedinput()"
867 */
868 void
869f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED)
870{
871#ifdef USE_INPUT_BUF
872 char_u *val = tv_get_string_chk(&argvars[0]);
873
Bram Moolenaar272ca952020-01-28 20:49:11 +0100874# ifdef VIMDLL
875 // this doesn't work in the console
876 if (!gui.in_use)
877 return;
878# endif
879
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200880 if (val != NULL)
881 {
882 trash_input_buf();
883 add_to_input_buf_csi(val, (int)STRLEN(val));
884 }
885#endif
886}
887
888/*
889 * "test_getvalue({name})" function
890 */
891 void
892f_test_getvalue(typval_T *argvars, typval_T *rettv)
893{
894 if (argvars[0].v_type != VAR_STRING)
895 emsg(_(e_invarg));
896 else
897 {
898 char_u *name = tv_get_string(&argvars[0]);
899
900 if (STRCMP(name, (char_u *)"need_fileinfo") == 0)
901 rettv->vval.v_number = need_fileinfo;
902 else
903 semsg(_(e_invarg2), name);
904 }
905}
906
907/*
908 * "test_option_not_set({name})" function
909 */
910 void
911f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED)
912{
913 char_u *name = (char_u *)"";
914
915 if (argvars[0].v_type != VAR_STRING)
916 emsg(_(e_invarg));
917 else
918 {
919 name = tv_get_string(&argvars[0]);
920 if (reset_option_was_set(name) == FAIL)
921 semsg(_(e_invarg2), name);
922 }
923}
924
925/*
926 * "test_override({name}, {val})" function
927 */
928 void
929f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
930{
931 char_u *name = (char_u *)"";
932 int val;
933 static int save_starting = -1;
934
935 if (argvars[0].v_type != VAR_STRING
936 || (argvars[1].v_type) != VAR_NUMBER)
937 emsg(_(e_invarg));
938 else
939 {
940 name = tv_get_string(&argvars[0]);
941 val = (int)tv_get_number(&argvars[1]);
942
943 if (STRCMP(name, (char_u *)"redraw") == 0)
944 disable_redraw_for_testing = val;
945 else if (STRCMP(name, (char_u *)"redraw_flag") == 0)
946 ignore_redraw_flag_for_testing = val;
947 else if (STRCMP(name, (char_u *)"char_avail") == 0)
948 disable_char_avail_for_testing = val;
949 else if (STRCMP(name, (char_u *)"starting") == 0)
950 {
951 if (val)
952 {
953 if (save_starting < 0)
954 save_starting = starting;
955 starting = 0;
956 }
957 else
958 {
959 starting = save_starting;
960 save_starting = -1;
961 }
962 }
963 else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
964 nfa_fail_for_testing = val;
965 else if (STRCMP(name, (char_u *)"no_query_mouse") == 0)
966 no_query_mouse_for_testing = val;
967 else if (STRCMP(name, (char_u *)"no_wait_return") == 0)
968 no_wait_return = val;
Bram Moolenaarb340bae2020-06-15 19:51:56 +0200969 else if (STRCMP(name, (char_u *)"ui_delay") == 0)
970 ui_delay_for_testing = val;
Bram Moolenaar0c0eddd2020-06-13 15:47:25 +0200971 else if (STRCMP(name, (char_u *)"term_props") == 0)
972 reset_term_props_on_termresponse = val;
Bram Moolenaarf52f0602021-03-10 21:26:37 +0100973 else if (STRCMP(name, (char_u *)"uptime") == 0)
974 override_sysinfo_uptime = val;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200975 else if (STRCMP(name, (char_u *)"ALL") == 0)
976 {
977 disable_char_avail_for_testing = FALSE;
978 disable_redraw_for_testing = FALSE;
979 ignore_redraw_flag_for_testing = FALSE;
980 nfa_fail_for_testing = FALSE;
981 no_query_mouse_for_testing = FALSE;
Bram Moolenaarb340bae2020-06-15 19:51:56 +0200982 ui_delay_for_testing = 0;
Bram Moolenaar0c0eddd2020-06-13 15:47:25 +0200983 reset_term_props_on_termresponse = FALSE;
Bram Moolenaarf52f0602021-03-10 21:26:37 +0100984 override_sysinfo_uptime = -1;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200985 if (save_starting >= 0)
986 {
987 starting = save_starting;
988 save_starting = -1;
989 }
990 }
991 else
992 semsg(_(e_invarg2), name);
993 }
994}
995
996/*
997 * "test_refcount({expr})" function
998 */
999 void
1000f_test_refcount(typval_T *argvars, typval_T *rettv)
1001{
1002 int retval = -1;
1003
1004 switch (argvars[0].v_type)
1005 {
1006 case VAR_UNKNOWN:
Bram Moolenaar4c683752020-04-05 21:38:23 +02001007 case VAR_ANY:
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001008 case VAR_VOID:
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001009 case VAR_NUMBER:
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +01001010 case VAR_BOOL:
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001011 case VAR_FLOAT:
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001012 case VAR_SPECIAL:
1013 case VAR_STRING:
1014 break;
1015 case VAR_JOB:
1016#ifdef FEAT_JOB_CHANNEL
1017 if (argvars[0].vval.v_job != NULL)
1018 retval = argvars[0].vval.v_job->jv_refcount - 1;
1019#endif
1020 break;
1021 case VAR_CHANNEL:
1022#ifdef FEAT_JOB_CHANNEL
1023 if (argvars[0].vval.v_channel != NULL)
1024 retval = argvars[0].vval.v_channel->ch_refcount - 1;
1025#endif
1026 break;
1027 case VAR_FUNC:
1028 if (argvars[0].vval.v_string != NULL)
1029 {
1030 ufunc_T *fp;
1031
Bram Moolenaar4c17ad92020-04-27 22:47:51 +02001032 fp = find_func(argvars[0].vval.v_string, FALSE, NULL);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001033 if (fp != NULL)
1034 retval = fp->uf_refcount;
1035 }
1036 break;
1037 case VAR_PARTIAL:
1038 if (argvars[0].vval.v_partial != NULL)
1039 retval = argvars[0].vval.v_partial->pt_refcount - 1;
1040 break;
1041 case VAR_BLOB:
1042 if (argvars[0].vval.v_blob != NULL)
1043 retval = argvars[0].vval.v_blob->bv_refcount - 1;
1044 break;
1045 case VAR_LIST:
1046 if (argvars[0].vval.v_list != NULL)
1047 retval = argvars[0].vval.v_list->lv_refcount - 1;
1048 break;
1049 case VAR_DICT:
1050 if (argvars[0].vval.v_dict != NULL)
1051 retval = argvars[0].vval.v_dict->dv_refcount - 1;
1052 break;
1053 }
1054
1055 rettv->v_type = VAR_NUMBER;
1056 rettv->vval.v_number = retval;
1057
1058}
1059
1060/*
1061 * "test_garbagecollect_now()" function
1062 */
1063 void
1064f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1065{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001066 // This is dangerous, any Lists and Dicts used internally may be freed
1067 // while still in use.
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001068 garbage_collect(TRUE);
1069}
1070
1071/*
1072 * "test_garbagecollect_soon()" function
1073 */
1074 void
1075f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1076{
1077 may_garbage_collect = TRUE;
1078}
1079
1080/*
1081 * "test_ignore_error()" function
1082 */
1083 void
1084f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED)
1085{
1086 ignore_error_for_testing(tv_get_string(&argvars[0]));
1087}
1088
1089 void
1090f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv)
1091{
1092 rettv->v_type = VAR_BLOB;
1093 rettv->vval.v_blob = NULL;
1094}
1095
1096#ifdef FEAT_JOB_CHANNEL
1097 void
1098f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv)
1099{
1100 rettv->v_type = VAR_CHANNEL;
1101 rettv->vval.v_channel = NULL;
1102}
1103#endif
1104
1105 void
1106f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv)
1107{
1108 rettv_dict_set(rettv, NULL);
1109}
1110
1111#ifdef FEAT_JOB_CHANNEL
1112 void
1113f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv)
1114{
1115 rettv->v_type = VAR_JOB;
1116 rettv->vval.v_job = NULL;
1117}
1118#endif
1119
1120 void
1121f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv)
1122{
1123 rettv_list_set(rettv, NULL);
1124}
1125
1126 void
Bram Moolenaare69f6d02020-04-01 22:11:01 +02001127f_test_null_function(typval_T *argvars UNUSED, typval_T *rettv)
1128{
1129 rettv->v_type = VAR_FUNC;
1130 rettv->vval.v_string = NULL;
1131}
1132
1133 void
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001134f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv)
1135{
1136 rettv->v_type = VAR_PARTIAL;
1137 rettv->vval.v_partial = NULL;
1138}
1139
1140 void
1141f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
1142{
1143 rettv->v_type = VAR_STRING;
1144 rettv->vval.v_string = NULL;
1145}
1146
Bram Moolenaar8ed04582020-02-22 19:07:28 +01001147 void
1148f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv)
1149{
1150 rettv->v_type = VAR_UNKNOWN;
1151}
1152
1153 void
1154f_test_void(typval_T *argvars UNUSED, typval_T *rettv)
1155{
1156 rettv->v_type = VAR_VOID;
1157}
1158
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001159#ifdef FEAT_GUI
1160 void
1161f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED)
1162{
1163 char_u *which;
1164 long value;
1165 int dragging;
1166 scrollbar_T *sb = NULL;
1167
1168 if (argvars[0].v_type != VAR_STRING
1169 || (argvars[1].v_type) != VAR_NUMBER
1170 || (argvars[2].v_type) != VAR_NUMBER)
1171 {
1172 emsg(_(e_invarg));
1173 return;
1174 }
1175 which = tv_get_string(&argvars[0]);
1176 value = tv_get_number(&argvars[1]);
1177 dragging = tv_get_number(&argvars[2]);
1178
1179 if (STRCMP(which, "left") == 0)
1180 sb = &curwin->w_scrollbars[SBAR_LEFT];
1181 else if (STRCMP(which, "right") == 0)
1182 sb = &curwin->w_scrollbars[SBAR_RIGHT];
1183 else if (STRCMP(which, "hor") == 0)
1184 sb = &gui.bottom_sbar;
1185 if (sb == NULL)
1186 {
1187 semsg(_(e_invarg2), which);
1188 return;
1189 }
1190 gui_drag_scrollbar(sb, value, dragging);
1191# ifndef USE_ON_FLY_SCROLL
1192 // need to loop through normal_cmd() to handle the scroll events
1193 exec_normal(FALSE, TRUE, FALSE);
1194# endif
1195}
1196#endif
1197
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001198 void
1199f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED)
1200{
1201 mouse_row = (time_t)tv_get_number(&argvars[0]) - 1;
1202 mouse_col = (time_t)tv_get_number(&argvars[1]) - 1;
1203}
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001204
1205 void
1206f_test_settime(typval_T *argvars, typval_T *rettv UNUSED)
1207{
1208 time_for_testing = (time_t)tv_get_number(&argvars[0]);
1209}
1210
1211
1212#endif // defined(FEAT_EVAL)