xref: /vim-8.2.3635/src/testing.c (revision a764e73d)
1 /* 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
22 prepare_assert_error(garray_T *gap)
23 {
24     char    buf[NUMBUFLEN];
25     char_u  *sname = estack_sfile(ESTACK_NONE);
26 
27     ga_init2(gap, 1, 100);
28     if (sname != NULL)
29     {
30 	ga_concat(gap, sname);
31 	if (SOURCING_LNUM > 0)
32 	    ga_concat(gap, (char_u *)" ");
33     }
34     if (SOURCING_LNUM > 0)
35     {
36 	sprintf(buf, "line %ld", (long)SOURCING_LNUM);
37 	ga_concat(gap, (char_u *)buf);
38     }
39     if (sname != NULL || SOURCING_LNUM > 0)
40 	ga_concat(gap, (char_u *)": ");
41     vim_free(sname);
42 }
43 
44 /*
45  * Append "p[clen]" to "gap", escaping unprintable characters.
46  * Changes NL to \n, CR to \r, etc.
47  */
48     static void
49 ga_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:
69 		   if (*p < ' ' || *p == 0x7f)
70 		   {
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
85 ga_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
130 fill_assert_error(
131     garray_T	*gap,
132     typval_T	*opt_msg_tv,
133     char_u      *exp_str,
134     typval_T	*exp_tv_arg,
135     typval_T	*got_tv_arg,
136     assert_type_T atype)
137 {
138     char_u	numbuf[NUMBUFLEN];
139     char_u	*tofree;
140     typval_T	*exp_tv = exp_tv_arg;
141     typval_T	*got_tv = got_tv_arg;
142     int		did_copy = FALSE;
143     int		omitted = 0;
144 
145     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)))
149     {
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     {
163 	// 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 
219 	ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0));
220 	vim_free(tofree);
221     }
222     else
223     {
224 	ga_concat(gap, (char_u *)"'");
225 	ga_concat_shorten_esc(gap, exp_str);
226 	ga_concat(gap, (char_u *)"'");
227     }
228     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);
238 
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);
253     }
254 }
255 
256     static int
257 assert_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
275 assert_match_common(typval_T *argvars, assert_type_T atype)
276 {
277     garray_T	ga;
278     char_u	buf1[NUMBUFLEN];
279     char_u	buf2[NUMBUFLEN];
280     int		called_emsg_before = called_emsg;
281     char_u	*pat = tv_get_string_buf_chk(&argvars[0], buf1);
282     char_u	*text = tv_get_string_buf_chk(&argvars[1], buf2);
283 
284     if (called_emsg == called_emsg_before
285 		 && pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH))
286     {
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
302 assert_bool(typval_T *argvars, int isTrue)
303 {
304     int		error = FALSE;
305     garray_T	ga;
306 
307     if (argvars[0].v_type == VAR_BOOL
308 	    && 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
326 assert_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
341 assert_beeps(typval_T *argvars, int no_beep)
342 {
343     char_u	*cmd;
344     garray_T	ga;
345     int		ret = 0;
346 
347     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
348 	return 0;
349 
350     cmd = tv_get_string_chk(&argvars[0]);
351     called_vim_beep = FALSE;
352     suppress_errthrow = TRUE;
353     emsg_silent = FALSE;
354     do_cmdline_cmd(cmd);
355     if (no_beep ? called_vim_beep : !called_vim_beep)
356     {
357 	prepare_assert_error(&ga);
358 	if (no_beep)
359 	    ga_concat(&ga, (char_u *)"command did beep: ");
360 	else
361 	    ga_concat(&ga, (char_u *)"command did not beep: ");
362 	ga_concat(&ga, cmd);
363 	assert_error(&ga);
364 	ga_clear(&ga);
365 	ret = 1;
366     }
367 
368     suppress_errthrow = FALSE;
369     emsg_on_display = FALSE;
370     return ret;
371 }
372 
373 /*
374  * "assert_beeps(cmd)" function
375  */
376     void
377 f_assert_beeps(typval_T *argvars, typval_T *rettv)
378 {
379     rettv->vval.v_number = assert_beeps(argvars, FALSE);
380 }
381 
382 /*
383  * "assert_nobeep(cmd)" function
384  */
385     void
386 f_assert_nobeep(typval_T *argvars, typval_T *rettv)
387 {
388     rettv->vval.v_number = assert_beeps(argvars, TRUE);
389 }
390 
391 /*
392  * "assert_equal(expected, actual[, msg])" function
393  */
394     void
395 f_assert_equal(typval_T *argvars, typval_T *rettv)
396 {
397     rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
398 }
399 
400     static int
401 assert_equalfile(typval_T *argvars)
402 {
403     char_u	buf1[NUMBUFLEN];
404     char_u	buf2[NUMBUFLEN];
405     int		called_emsg_before = called_emsg;
406     char_u	*fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
407     char_u	*fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
408     garray_T	ga;
409     FILE	*fd1;
410     FILE	*fd2;
411     char	line1[200];
412     char	line2[200];
413     int		lineidx = 0;
414 
415     if (called_emsg > called_emsg_before)
416 	return 0;
417 
418     IObuff[0] = NUL;
419     fd1 = mch_fopen((char *)fname1, READBIN);
420     if (fd1 == NULL)
421     {
422 	vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
423     }
424     else
425     {
426 	fd2 = mch_fopen((char *)fname2, READBIN);
427 	if (fd2 == NULL)
428 	{
429 	    fclose(fd1);
430 	    vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
431 	}
432 	else
433 	{
434 	    int	    c1, c2;
435 	    long    count = 0;
436 	    long    linecount = 1;
437 
438 	    for (;;)
439 	    {
440 		c1 = fgetc(fd1);
441 		c2 = fgetc(fd2);
442 		if (c1 == EOF)
443 		{
444 		    if (c2 != EOF)
445 			STRCPY(IObuff, "first file is shorter");
446 		    break;
447 		}
448 		else if (c2 == EOF)
449 		{
450 		    STRCPY(IObuff, "second file is shorter");
451 		    break;
452 		}
453 		else
454 		{
455 		    line1[lineidx] = c1;
456 		    line2[lineidx] = c2;
457 		    ++lineidx;
458 		    if (c1 != c2)
459 		    {
460 			vim_snprintf((char *)IObuff, IOSIZE,
461 					    "difference at byte %ld, line %ld",
462 							     count, linecount);
463 			break;
464 		    }
465 		}
466 		++count;
467 		if (c1 == NL)
468 		{
469 		    ++linecount;
470 		    lineidx = 0;
471 		}
472 		else if (lineidx + 2 == (int)sizeof(line1))
473 		{
474 		    mch_memmove(line1, line1 + 100, lineidx - 100);
475 		    mch_memmove(line2, line2 + 100, lineidx - 100);
476 		    lineidx -= 100;
477 		}
478 	    }
479 	    fclose(fd1);
480 	    fclose(fd2);
481 	}
482     }
483     if (IObuff[0] != NUL)
484     {
485 	prepare_assert_error(&ga);
486 	if (argvars[2].v_type != VAR_UNKNOWN)
487 	{
488 	    char_u	numbuf[NUMBUFLEN];
489 	    char_u	*tofree;
490 
491 	    ga_concat(&ga, echo_string(&argvars[2], &tofree, numbuf, 0));
492 	    vim_free(tofree);
493 	    ga_concat(&ga, (char_u *)": ");
494 	}
495 	ga_concat(&ga, IObuff);
496 	if (lineidx > 0)
497 	{
498 	    line1[lineidx] = NUL;
499 	    line2[lineidx] = NUL;
500 	    ga_concat(&ga, (char_u *)" after \"");
501 	    ga_concat(&ga, (char_u *)line1);
502 	    if (STRCMP(line1, line2) != 0)
503 	    {
504 		ga_concat(&ga, (char_u *)"\" vs \"");
505 		ga_concat(&ga, (char_u *)line2);
506 	    }
507 	    ga_concat(&ga, (char_u *)"\"");
508 	}
509 	assert_error(&ga);
510 	ga_clear(&ga);
511 	return 1;
512     }
513     return 0;
514 }
515 
516 /*
517  * "assert_equalfile(fname-one, fname-two[, msg])" function
518  */
519     void
520 f_assert_equalfile(typval_T *argvars, typval_T *rettv)
521 {
522     rettv->vval.v_number = assert_equalfile(argvars);
523 }
524 
525 /*
526  * "assert_notequal(expected, actual[, msg])" function
527  */
528     void
529 f_assert_notequal(typval_T *argvars, typval_T *rettv)
530 {
531     rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
532 }
533 
534 /*
535  * "assert_exception(string[, msg])" function
536  */
537     void
538 f_assert_exception(typval_T *argvars, typval_T *rettv)
539 {
540     garray_T	ga;
541     char_u	*error = tv_get_string_chk(&argvars[0]);
542 
543     if (*get_vim_var_str(VV_EXCEPTION) == NUL)
544     {
545 	prepare_assert_error(&ga);
546 	ga_concat(&ga, (char_u *)"v:exception is not set");
547 	assert_error(&ga);
548 	ga_clear(&ga);
549 	rettv->vval.v_number = 1;
550     }
551     else if (error != NULL
552 	&& strstr((char *)get_vim_var_str(VV_EXCEPTION), (char *)error) == NULL)
553     {
554 	prepare_assert_error(&ga);
555 	fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
556 				  get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
557 	assert_error(&ga);
558 	ga_clear(&ga);
559 	rettv->vval.v_number = 1;
560     }
561 }
562 
563 /*
564  * "assert_fails(cmd [, error[, msg]])" function
565  */
566     void
567 f_assert_fails(typval_T *argvars, typval_T *rettv)
568 {
569     char_u	*cmd;
570     garray_T	ga;
571     int		save_trylevel = trylevel;
572     int		called_emsg_before = called_emsg;
573     char	*wrong_arg_msg = NULL;
574 
575     if (check_for_string_or_number_arg(argvars, 0) == FAIL
576 	    || check_for_opt_string_or_list_arg(argvars, 1) == FAIL
577 	    || (argvars[1].v_type != VAR_UNKNOWN
578 		&& (argvars[2].v_type != VAR_UNKNOWN
579 		    && (check_for_opt_number_arg(argvars, 3) == FAIL
580 			|| (argvars[3].v_type != VAR_UNKNOWN
581 			    && check_for_opt_string_arg(argvars, 4) == FAIL)))))
582 	return;
583 
584     cmd = tv_get_string_chk(&argvars[0]);
585 
586     // trylevel must be zero for a ":throw" command to be considered failed
587     trylevel = 0;
588     suppress_errthrow = TRUE;
589     in_assert_fails = TRUE;
590 
591     do_cmdline_cmd(cmd);
592     if (called_emsg == called_emsg_before)
593     {
594 	prepare_assert_error(&ga);
595 	ga_concat(&ga, (char_u *)"command did not fail: ");
596 	assert_append_cmd_or_arg(&ga, argvars, cmd);
597 	assert_error(&ga);
598 	ga_clear(&ga);
599 	rettv->vval.v_number = 1;
600     }
601     else if (argvars[1].v_type != VAR_UNKNOWN)
602     {
603 	char_u	buf[NUMBUFLEN];
604 	char_u	*expected;
605 	char_u	*expected_str = NULL;
606 	int	error_found = FALSE;
607 	int	error_found_index = 1;
608 	char_u	*actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]"
609 						       : emsg_assert_fails_msg;
610 
611 	if (argvars[1].v_type == VAR_STRING)
612 	{
613 	    expected = tv_get_string_buf_chk(&argvars[1], buf);
614 	    error_found = expected == NULL
615 			   || strstr((char *)actual, (char *)expected) == NULL;
616 	}
617 	else if (argvars[1].v_type == VAR_LIST)
618 	{
619 	    list_T	*list = argvars[1].vval.v_list;
620 	    typval_T	*tv;
621 
622 	    if (list == NULL || list->lv_len < 1 || list->lv_len > 2)
623 	    {
624 		wrong_arg_msg = e_assert_fails_second_arg;
625 		goto theend;
626 	    }
627 	    CHECK_LIST_MATERIALIZE(list);
628 	    tv = &list->lv_first->li_tv;
629 	    expected = tv_get_string_buf_chk(tv, buf);
630 	    if (!pattern_match(expected, actual, FALSE))
631 	    {
632 		error_found = TRUE;
633 		expected_str = expected;
634 	    }
635 	    else if (list->lv_len == 2)
636 	    {
637 		tv = &list->lv_u.mat.lv_last->li_tv;
638 		actual = get_vim_var_str(VV_ERRMSG);
639 		expected = tv_get_string_buf_chk(tv, buf);
640 		if (!pattern_match(expected, actual, FALSE))
641 		{
642 		    error_found = TRUE;
643 		    expected_str = expected;
644 		}
645 	    }
646 	}
647 	else
648 	{
649 	    wrong_arg_msg = e_assert_fails_second_arg;
650 	    goto theend;
651 	}
652 
653 	if (!error_found && argvars[2].v_type != VAR_UNKNOWN
654 		&& argvars[3].v_type != VAR_UNKNOWN)
655 	{
656 	    if (argvars[3].v_type != VAR_NUMBER)
657 	    {
658 		wrong_arg_msg = e_assert_fails_fourth_argument;
659 		goto theend;
660 	    }
661 	    else if (argvars[3].vval.v_number >= 0
662 			 && argvars[3].vval.v_number != emsg_assert_fails_lnum)
663 	    {
664 		error_found = TRUE;
665 		error_found_index = 3;
666 	    }
667 	    if (!error_found && argvars[4].v_type != VAR_UNKNOWN)
668 	    {
669 		if (argvars[4].v_type != VAR_STRING)
670 		{
671 		    wrong_arg_msg = e_assert_fails_fifth_argument;
672 		    goto theend;
673 		}
674 		else if (argvars[4].vval.v_string != NULL
675 		    && !pattern_match(argvars[4].vval.v_string,
676 					     emsg_assert_fails_context, FALSE))
677 		{
678 		    error_found = TRUE;
679 		    error_found_index = 4;
680 		}
681 	    }
682 	}
683 
684 	if (error_found)
685 	{
686 	    typval_T actual_tv;
687 
688 	    prepare_assert_error(&ga);
689 	    if (error_found_index == 3)
690 	    {
691 		actual_tv.v_type = VAR_NUMBER;
692 		actual_tv.vval.v_number = emsg_assert_fails_lnum;
693 	    }
694 	    else if (error_found_index == 4)
695 	    {
696 		actual_tv.v_type = VAR_STRING;
697 		actual_tv.vval.v_string = emsg_assert_fails_context;
698 	    }
699 	    else
700 	    {
701 		actual_tv.v_type = VAR_STRING;
702 		actual_tv.vval.v_string = actual;
703 	    }
704 	    fill_assert_error(&ga, &argvars[2], expected_str,
705 			&argvars[error_found_index], &actual_tv, ASSERT_OTHER);
706 	    ga_concat(&ga, (char_u *)": ");
707 	    assert_append_cmd_or_arg(&ga, argvars, cmd);
708 	    assert_error(&ga);
709 	    ga_clear(&ga);
710 	    rettv->vval.v_number = 1;
711 	}
712     }
713 
714 theend:
715     trylevel = save_trylevel;
716     suppress_errthrow = FALSE;
717     in_assert_fails = FALSE;
718     did_emsg = FALSE;
719     msg_col = 0;
720     need_wait_return = FALSE;
721     emsg_on_display = FALSE;
722     msg_scrolled = 0;
723     lines_left = Rows;
724     VIM_CLEAR(emsg_assert_fails_msg);
725     set_vim_var_string(VV_ERRMSG, NULL, 0);
726     if (wrong_arg_msg != NULL)
727 	emsg(_(wrong_arg_msg));
728 }
729 
730 /*
731  * "assert_false(actual[, msg])" function
732  */
733     void
734 f_assert_false(typval_T *argvars, typval_T *rettv)
735 {
736     rettv->vval.v_number = assert_bool(argvars, FALSE);
737 }
738 
739     static int
740 assert_inrange(typval_T *argvars)
741 {
742     garray_T	ga;
743     int		error = FALSE;
744     char_u	*tofree;
745     char	msg[200];
746     char_u	numbuf[NUMBUFLEN];
747 
748 #ifdef FEAT_FLOAT
749     if (argvars[0].v_type == VAR_FLOAT
750 	    || argvars[1].v_type == VAR_FLOAT
751 	    || argvars[2].v_type == VAR_FLOAT)
752     {
753 	float_T flower = tv_get_float(&argvars[0]);
754 	float_T fupper = tv_get_float(&argvars[1]);
755 	float_T factual = tv_get_float(&argvars[2]);
756 
757 	if (factual < flower || factual > fupper)
758 	{
759 	    prepare_assert_error(&ga);
760 	    if (argvars[3].v_type != VAR_UNKNOWN)
761 	    {
762 		ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
763 		vim_free(tofree);
764 	    }
765 	    else
766 	    {
767 		vim_snprintf(msg, 200, "Expected range %g - %g, but got %g",
768 						      flower, fupper, factual);
769 		ga_concat(&ga, (char_u *)msg);
770 	    }
771 	    assert_error(&ga);
772 	    ga_clear(&ga);
773 	    return 1;
774 	}
775     }
776     else
777 #endif
778     {
779 	varnumber_T	lower = tv_get_number_chk(&argvars[0], &error);
780 	varnumber_T	upper = tv_get_number_chk(&argvars[1], &error);
781 	varnumber_T	actual = tv_get_number_chk(&argvars[2], &error);
782 
783 	if (error)
784 	    return 0;
785 	if (actual < lower || actual > upper)
786 	{
787 	    prepare_assert_error(&ga);
788 	    if (argvars[3].v_type != VAR_UNKNOWN)
789 	    {
790 		ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
791 		vim_free(tofree);
792 	    }
793 	    else
794 	    {
795 		vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld",
796 				       (long)lower, (long)upper, (long)actual);
797 		ga_concat(&ga, (char_u *)msg);
798 	    }
799 	    assert_error(&ga);
800 	    ga_clear(&ga);
801 	    return 1;
802 	}
803     }
804     return 0;
805 }
806 
807 /*
808  * "assert_inrange(lower, upper[, msg])" function
809  */
810     void
811 f_assert_inrange(typval_T *argvars, typval_T *rettv)
812 {
813     if (check_for_float_or_nr_arg(argvars, 0) == FAIL
814 	    || check_for_float_or_nr_arg(argvars, 1) == FAIL
815 	    || check_for_float_or_nr_arg(argvars, 2) == FAIL
816 	    || check_for_opt_string_arg(argvars, 3) == FAIL)
817 	return;
818 
819     rettv->vval.v_number = assert_inrange(argvars);
820 }
821 
822 /*
823  * "assert_match(pattern, actual[, msg])" function
824  */
825     void
826 f_assert_match(typval_T *argvars, typval_T *rettv)
827 {
828     rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
829 }
830 
831 /*
832  * "assert_notmatch(pattern, actual[, msg])" function
833  */
834     void
835 f_assert_notmatch(typval_T *argvars, typval_T *rettv)
836 {
837     rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
838 }
839 
840 /*
841  * "assert_report(msg)" function
842  */
843     void
844 f_assert_report(typval_T *argvars, typval_T *rettv)
845 {
846     garray_T	ga;
847 
848     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
849 	return;
850 
851     prepare_assert_error(&ga);
852     ga_concat(&ga, tv_get_string(&argvars[0]));
853     assert_error(&ga);
854     ga_clear(&ga);
855     rettv->vval.v_number = 1;
856 }
857 
858 /*
859  * "assert_true(actual[, msg])" function
860  */
861     void
862 f_assert_true(typval_T *argvars, typval_T *rettv)
863 {
864     rettv->vval.v_number = assert_bool(argvars, TRUE);
865 }
866 
867 /*
868  * "test_alloc_fail(id, countdown, repeat)" function
869  */
870     void
871 f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED)
872 {
873     if (argvars[0].v_type != VAR_NUMBER
874 	    || argvars[0].vval.v_number <= 0
875 	    || argvars[1].v_type != VAR_NUMBER
876 	    || argvars[1].vval.v_number < 0
877 	    || argvars[2].v_type != VAR_NUMBER)
878 	emsg(_(e_invarg));
879     else
880     {
881 	alloc_fail_id = argvars[0].vval.v_number;
882 	if (alloc_fail_id >= aid_last)
883 	    emsg(_(e_invarg));
884 	alloc_fail_countdown = argvars[1].vval.v_number;
885 	alloc_fail_repeat = argvars[2].vval.v_number;
886 	did_outofmem_msg = FALSE;
887     }
888 }
889 
890 /*
891  * "test_autochdir()"
892  */
893     void
894 f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
895 {
896 #if defined(FEAT_AUTOCHDIR)
897     test_autochdir = TRUE;
898 #endif
899 }
900 
901 /*
902  * "test_feedinput()"
903  */
904     void
905 f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED)
906 {
907 #ifdef USE_INPUT_BUF
908     char_u	*val = tv_get_string_chk(&argvars[0]);
909 
910 # ifdef VIMDLL
911     // this doesn't work in the console
912     if (!gui.in_use)
913 	return;
914 # endif
915 
916     if (val != NULL)
917     {
918 	trash_input_buf();
919 	add_to_input_buf_csi(val, (int)STRLEN(val));
920     }
921 #endif
922 }
923 
924 /*
925  * "test_getvalue({name})" function
926  */
927     void
928 f_test_getvalue(typval_T *argvars, typval_T *rettv)
929 {
930     if (argvars[0].v_type != VAR_STRING)
931 	emsg(_(e_invarg));
932     else
933     {
934 	char_u *name = tv_get_string(&argvars[0]);
935 
936 	if (STRCMP(name, (char_u *)"need_fileinfo") == 0)
937 	    rettv->vval.v_number = need_fileinfo;
938 	else
939 	    semsg(_(e_invarg2), name);
940     }
941 }
942 
943 /*
944  * "test_option_not_set({name})" function
945  */
946     void
947 f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED)
948 {
949     char_u *name = (char_u *)"";
950 
951     if (argvars[0].v_type != VAR_STRING)
952 	emsg(_(e_invarg));
953     else
954     {
955 	name = tv_get_string(&argvars[0]);
956 	if (reset_option_was_set(name) == FAIL)
957 	    semsg(_(e_invarg2), name);
958     }
959 }
960 
961 /*
962  * "test_override({name}, {val})" function
963  */
964     void
965 f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
966 {
967     char_u *name = (char_u *)"";
968     int     val;
969     static int save_starting = -1;
970 
971     if (in_vim9script()
972 	    && (check_for_string_arg(argvars, 0) == FAIL
973 		|| check_for_number_arg(argvars, 1) == FAIL))
974 	return;
975 
976     if (argvars[0].v_type != VAR_STRING
977 	    || (argvars[1].v_type) != VAR_NUMBER)
978 	emsg(_(e_invarg));
979     else
980     {
981 	name = tv_get_string(&argvars[0]);
982 	val = (int)tv_get_number(&argvars[1]);
983 
984 	if (STRCMP(name, (char_u *)"redraw") == 0)
985 	    disable_redraw_for_testing = val;
986 	else if (STRCMP(name, (char_u *)"redraw_flag") == 0)
987 	    ignore_redraw_flag_for_testing = val;
988 	else if (STRCMP(name, (char_u *)"char_avail") == 0)
989 	    disable_char_avail_for_testing = val;
990 	else if (STRCMP(name, (char_u *)"starting") == 0)
991 	{
992 	    if (val)
993 	    {
994 		if (save_starting < 0)
995 		    save_starting = starting;
996 		starting = 0;
997 	    }
998 	    else
999 	    {
1000 		starting = save_starting;
1001 		save_starting = -1;
1002 	    }
1003 	}
1004 	else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
1005 	    nfa_fail_for_testing = val;
1006 	else if (STRCMP(name, (char_u *)"no_query_mouse") == 0)
1007 	    no_query_mouse_for_testing = val;
1008 	else if (STRCMP(name, (char_u *)"no_wait_return") == 0)
1009 	    no_wait_return = val;
1010 	else if (STRCMP(name, (char_u *)"ui_delay") == 0)
1011 	    ui_delay_for_testing = val;
1012 	else if (STRCMP(name, (char_u *)"term_props") == 0)
1013 	    reset_term_props_on_termresponse = val;
1014 	else if (STRCMP(name, (char_u *)"uptime") == 0)
1015 	    override_sysinfo_uptime = val;
1016 	else if (STRCMP(name, (char_u *)"ALL") == 0)
1017 	{
1018 	    disable_char_avail_for_testing = FALSE;
1019 	    disable_redraw_for_testing = FALSE;
1020 	    ignore_redraw_flag_for_testing = FALSE;
1021 	    nfa_fail_for_testing = FALSE;
1022 	    no_query_mouse_for_testing = FALSE;
1023 	    ui_delay_for_testing = 0;
1024 	    reset_term_props_on_termresponse = FALSE;
1025 	    override_sysinfo_uptime = -1;
1026 	    if (save_starting >= 0)
1027 	    {
1028 		starting = save_starting;
1029 		save_starting = -1;
1030 	    }
1031 	}
1032 	else
1033 	    semsg(_(e_invarg2), name);
1034     }
1035 }
1036 
1037 /*
1038  * "test_refcount({expr})" function
1039  */
1040     void
1041 f_test_refcount(typval_T *argvars, typval_T *rettv)
1042 {
1043     int retval = -1;
1044 
1045     switch (argvars[0].v_type)
1046     {
1047 	case VAR_UNKNOWN:
1048 	case VAR_ANY:
1049 	case VAR_VOID:
1050 	case VAR_NUMBER:
1051 	case VAR_BOOL:
1052 	case VAR_FLOAT:
1053 	case VAR_SPECIAL:
1054 	case VAR_STRING:
1055 	case VAR_INSTR:
1056 	    break;
1057 	case VAR_JOB:
1058 #ifdef FEAT_JOB_CHANNEL
1059 	    if (argvars[0].vval.v_job != NULL)
1060 		retval = argvars[0].vval.v_job->jv_refcount - 1;
1061 #endif
1062 	    break;
1063 	case VAR_CHANNEL:
1064 #ifdef FEAT_JOB_CHANNEL
1065 	    if (argvars[0].vval.v_channel != NULL)
1066 		retval = argvars[0].vval.v_channel->ch_refcount - 1;
1067 #endif
1068 	    break;
1069 	case VAR_FUNC:
1070 	    if (argvars[0].vval.v_string != NULL)
1071 	    {
1072 		ufunc_T *fp;
1073 
1074 		fp = find_func(argvars[0].vval.v_string, FALSE, NULL);
1075 		if (fp != NULL)
1076 		    retval = fp->uf_refcount;
1077 	    }
1078 	    break;
1079 	case VAR_PARTIAL:
1080 	    if (argvars[0].vval.v_partial != NULL)
1081 		retval = argvars[0].vval.v_partial->pt_refcount - 1;
1082 	    break;
1083 	case VAR_BLOB:
1084 	    if (argvars[0].vval.v_blob != NULL)
1085 		retval = argvars[0].vval.v_blob->bv_refcount - 1;
1086 	    break;
1087 	case VAR_LIST:
1088 	    if (argvars[0].vval.v_list != NULL)
1089 		retval = argvars[0].vval.v_list->lv_refcount - 1;
1090 	    break;
1091 	case VAR_DICT:
1092 	    if (argvars[0].vval.v_dict != NULL)
1093 		retval = argvars[0].vval.v_dict->dv_refcount - 1;
1094 	    break;
1095     }
1096 
1097     rettv->v_type = VAR_NUMBER;
1098     rettv->vval.v_number = retval;
1099 
1100 }
1101 
1102 /*
1103  * "test_garbagecollect_now()" function
1104  */
1105     void
1106 f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1107 {
1108     // This is dangerous, any Lists and Dicts used internally may be freed
1109     // while still in use.
1110     garbage_collect(TRUE);
1111 }
1112 
1113 /*
1114  * "test_garbagecollect_soon()" function
1115  */
1116     void
1117 f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1118 {
1119     may_garbage_collect = TRUE;
1120 }
1121 
1122 /*
1123  * "test_ignore_error()" function
1124  */
1125     void
1126 f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED)
1127 {
1128     if (argvars[0].v_type != VAR_STRING)
1129 	emsg(_(e_invarg));
1130     else
1131 	ignore_error_for_testing(tv_get_string(&argvars[0]));
1132 }
1133 
1134     void
1135 f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv)
1136 {
1137     rettv->v_type = VAR_BLOB;
1138     rettv->vval.v_blob = NULL;
1139 }
1140 
1141 #ifdef FEAT_JOB_CHANNEL
1142     void
1143 f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv)
1144 {
1145     rettv->v_type = VAR_CHANNEL;
1146     rettv->vval.v_channel = NULL;
1147 }
1148 #endif
1149 
1150     void
1151 f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv)
1152 {
1153     rettv_dict_set(rettv, NULL);
1154 }
1155 
1156 #ifdef FEAT_JOB_CHANNEL
1157     void
1158 f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv)
1159 {
1160     rettv->v_type = VAR_JOB;
1161     rettv->vval.v_job = NULL;
1162 }
1163 #endif
1164 
1165     void
1166 f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv)
1167 {
1168     rettv_list_set(rettv, NULL);
1169 }
1170 
1171     void
1172 f_test_null_function(typval_T *argvars UNUSED, typval_T *rettv)
1173 {
1174     rettv->v_type = VAR_FUNC;
1175     rettv->vval.v_string = NULL;
1176 }
1177 
1178     void
1179 f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv)
1180 {
1181     rettv->v_type = VAR_PARTIAL;
1182     rettv->vval.v_partial = NULL;
1183 }
1184 
1185     void
1186 f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
1187 {
1188     rettv->v_type = VAR_STRING;
1189     rettv->vval.v_string = NULL;
1190 }
1191 
1192     void
1193 f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv)
1194 {
1195     rettv->v_type = VAR_UNKNOWN;
1196 }
1197 
1198     void
1199 f_test_void(typval_T *argvars UNUSED, typval_T *rettv)
1200 {
1201     rettv->v_type = VAR_VOID;
1202 }
1203 
1204 #ifdef FEAT_GUI
1205     void
1206 f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED)
1207 {
1208     char_u	*which;
1209     long	value;
1210     int		dragging;
1211     scrollbar_T *sb = NULL;
1212 
1213     if (check_for_string_arg(argvars, 0) == FAIL
1214 	    || check_for_number_arg(argvars, 1) == FAIL
1215 	    || check_for_number_arg(argvars, 2) == FAIL)
1216 	return;
1217 
1218     if (argvars[0].v_type != VAR_STRING
1219 	    || (argvars[1].v_type) != VAR_NUMBER
1220 	    || (argvars[2].v_type) != VAR_NUMBER)
1221     {
1222 	emsg(_(e_invarg));
1223 	return;
1224     }
1225     which = tv_get_string(&argvars[0]);
1226     value = tv_get_number(&argvars[1]);
1227     dragging = tv_get_number(&argvars[2]);
1228 
1229     if (STRCMP(which, "left") == 0)
1230 	sb = &curwin->w_scrollbars[SBAR_LEFT];
1231     else if (STRCMP(which, "right") == 0)
1232 	sb = &curwin->w_scrollbars[SBAR_RIGHT];
1233     else if (STRCMP(which, "hor") == 0)
1234 	sb = &gui.bottom_sbar;
1235     if (sb == NULL)
1236     {
1237 	semsg(_(e_invarg2), which);
1238 	return;
1239     }
1240     gui_drag_scrollbar(sb, value, dragging);
1241 # ifndef USE_ON_FLY_SCROLL
1242     // need to loop through normal_cmd() to handle the scroll events
1243     exec_normal(FALSE, TRUE, FALSE);
1244 # endif
1245 }
1246 #endif
1247 
1248     void
1249 f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED)
1250 {
1251     if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type) != VAR_NUMBER)
1252     {
1253 	emsg(_(e_invarg));
1254 	return;
1255     }
1256 
1257     mouse_row = (time_t)tv_get_number(&argvars[0]) - 1;
1258     mouse_col = (time_t)tv_get_number(&argvars[1]) - 1;
1259 }
1260 
1261     void
1262 f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1263 {
1264 # ifdef FEAT_GUI
1265     int		button;
1266     int		row;
1267     int		col;
1268     int		repeated_click;
1269     int_u	mods;
1270 
1271     if (check_for_number_arg(argvars, 0) == FAIL
1272 	    || check_for_number_arg(argvars, 1) == FAIL
1273 	    || check_for_number_arg(argvars, 2) == FAIL
1274 	    || check_for_number_arg(argvars, 3) == FAIL
1275 	    || check_for_number_arg(argvars, 4) == FAIL)
1276 	return;
1277 
1278     button = tv_get_number(&argvars[0]);
1279     row = tv_get_number(&argvars[1]);
1280     col = tv_get_number(&argvars[2]);
1281     repeated_click = tv_get_number(&argvars[3]);
1282     mods = tv_get_number(&argvars[4]);
1283 
1284     gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods);
1285 # endif
1286 }
1287 
1288     void
1289 f_test_settime(typval_T *argvars, typval_T *rettv UNUSED)
1290 {
1291     time_for_testing = (time_t)tv_get_number(&argvars[0]);
1292 }
1293 
1294     void
1295 f_test_gui_drop_files(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1296 {
1297 #if defined(HAVE_DROP_FILE)
1298     int		row;
1299     int		col;
1300     int_u	mods;
1301     char_u	**fnames;
1302     int		count = 0;
1303     list_T	*l;
1304     listitem_T	*li;
1305 
1306     if (check_for_list_arg(argvars, 0) == FAIL
1307 	    || check_for_number_arg(argvars, 1) == FAIL
1308 	    || check_for_number_arg(argvars, 2) == FAIL
1309 	    || check_for_number_arg(argvars, 3) == FAIL)
1310 	return;
1311 
1312     row = tv_get_number(&argvars[1]);
1313     col = tv_get_number(&argvars[2]);
1314     mods = tv_get_number(&argvars[3]);
1315 
1316     l = argvars[0].vval.v_list;
1317     if (list_len(l) == 0)
1318 	return;
1319 
1320     fnames = ALLOC_MULT(char_u *, list_len(l));
1321     if (fnames == NULL)
1322 	return;
1323 
1324     FOR_ALL_LIST_ITEMS(l, li)
1325     {
1326 	// ignore non-string items
1327 	if (li->li_tv.v_type != VAR_STRING)
1328 	    continue;
1329 
1330 	fnames[count] = vim_strsave(li->li_tv.vval.v_string);
1331 	if (fnames[count] == NULL)
1332 	{
1333 	    while (--count >= 0)
1334 		vim_free(fnames[count]);
1335 	    vim_free(fnames);
1336 	    return;
1337 	}
1338 	count++;
1339     }
1340 
1341     if (count > 0)
1342 	gui_handle_drop(TEXT_X(col - 1), TEXT_Y(row - 1), mods, fnames, count);
1343     else
1344 	vim_free(fnames);
1345 # endif
1346 }
1347 
1348 #endif // defined(FEAT_EVAL)
1349