xref: /vim-8.2.3635/src/testing.c (revision 5bca906b)
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 = tv_get_string_chk(&argvars[0]);
570     garray_T	ga;
571     int		save_trylevel = trylevel;
572     int		called_emsg_before = called_emsg;
573     char	*wrong_arg_msg = NULL;
574 
575     // trylevel must be zero for a ":throw" command to be considered failed
576     trylevel = 0;
577     suppress_errthrow = TRUE;
578     in_assert_fails = TRUE;
579 
580     do_cmdline_cmd(cmd);
581     if (called_emsg == called_emsg_before)
582     {
583 	prepare_assert_error(&ga);
584 	ga_concat(&ga, (char_u *)"command did not fail: ");
585 	assert_append_cmd_or_arg(&ga, argvars, cmd);
586 	assert_error(&ga);
587 	ga_clear(&ga);
588 	rettv->vval.v_number = 1;
589     }
590     else if (argvars[1].v_type != VAR_UNKNOWN)
591     {
592 	char_u	buf[NUMBUFLEN];
593 	char_u	*expected;
594 	char_u	*expected_str = NULL;
595 	int	error_found = FALSE;
596 	int	error_found_index = 1;
597 	char_u	*actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]"
598 						       : emsg_assert_fails_msg;
599 
600 	if (argvars[1].v_type == VAR_STRING)
601 	{
602 	    expected = tv_get_string_buf_chk(&argvars[1], buf);
603 	    error_found = expected == NULL
604 			   || strstr((char *)actual, (char *)expected) == NULL;
605 	}
606 	else if (argvars[1].v_type == VAR_LIST)
607 	{
608 	    list_T	*list = argvars[1].vval.v_list;
609 	    typval_T	*tv;
610 
611 	    if (list == NULL || list->lv_len < 1 || list->lv_len > 2)
612 	    {
613 		wrong_arg_msg = e_assert_fails_second_arg;
614 		goto theend;
615 	    }
616 	    CHECK_LIST_MATERIALIZE(list);
617 	    tv = &list->lv_first->li_tv;
618 	    expected = tv_get_string_buf_chk(tv, buf);
619 	    if (!pattern_match(expected, actual, FALSE))
620 	    {
621 		error_found = TRUE;
622 		expected_str = expected;
623 	    }
624 	    else if (list->lv_len == 2)
625 	    {
626 		tv = &list->lv_u.mat.lv_last->li_tv;
627 		actual = get_vim_var_str(VV_ERRMSG);
628 		expected = tv_get_string_buf_chk(tv, buf);
629 		if (!pattern_match(expected, actual, FALSE))
630 		{
631 		    error_found = TRUE;
632 		    expected_str = expected;
633 		}
634 	    }
635 	}
636 	else
637 	{
638 	    wrong_arg_msg = e_assert_fails_second_arg;
639 	    goto theend;
640 	}
641 
642 	if (!error_found && argvars[2].v_type != VAR_UNKNOWN
643 		&& argvars[3].v_type != VAR_UNKNOWN)
644 	{
645 	    if (argvars[3].v_type != VAR_NUMBER)
646 	    {
647 		wrong_arg_msg = e_assert_fails_fourth_argument;
648 		goto theend;
649 	    }
650 	    else if (argvars[3].vval.v_number >= 0
651 			 && argvars[3].vval.v_number != emsg_assert_fails_lnum)
652 	    {
653 		error_found = TRUE;
654 		error_found_index = 3;
655 	    }
656 	    if (!error_found && argvars[4].v_type != VAR_UNKNOWN)
657 	    {
658 		if (argvars[4].v_type != VAR_STRING)
659 		{
660 		    wrong_arg_msg = e_assert_fails_fifth_argument;
661 		    goto theend;
662 		}
663 		else if (argvars[4].vval.v_string != NULL
664 		    && !pattern_match(argvars[4].vval.v_string,
665 					     emsg_assert_fails_context, FALSE))
666 		{
667 		    error_found = TRUE;
668 		    error_found_index = 4;
669 		}
670 	    }
671 	}
672 
673 	if (error_found)
674 	{
675 	    typval_T actual_tv;
676 
677 	    prepare_assert_error(&ga);
678 	    if (error_found_index == 3)
679 	    {
680 		actual_tv.v_type = VAR_NUMBER;
681 		actual_tv.vval.v_number = emsg_assert_fails_lnum;
682 	    }
683 	    else if (error_found_index == 4)
684 	    {
685 		actual_tv.v_type = VAR_STRING;
686 		actual_tv.vval.v_string = emsg_assert_fails_context;
687 	    }
688 	    else
689 	    {
690 		actual_tv.v_type = VAR_STRING;
691 		actual_tv.vval.v_string = actual;
692 	    }
693 	    fill_assert_error(&ga, &argvars[2], expected_str,
694 			&argvars[error_found_index], &actual_tv, ASSERT_OTHER);
695 	    ga_concat(&ga, (char_u *)": ");
696 	    assert_append_cmd_or_arg(&ga, argvars, cmd);
697 	    assert_error(&ga);
698 	    ga_clear(&ga);
699 	    rettv->vval.v_number = 1;
700 	}
701     }
702 
703 theend:
704     trylevel = save_trylevel;
705     suppress_errthrow = FALSE;
706     in_assert_fails = FALSE;
707     did_emsg = FALSE;
708     msg_col = 0;
709     need_wait_return = FALSE;
710     emsg_on_display = FALSE;
711     msg_scrolled = 0;
712     lines_left = Rows;
713     VIM_CLEAR(emsg_assert_fails_msg);
714     set_vim_var_string(VV_ERRMSG, NULL, 0);
715     if (wrong_arg_msg != NULL)
716 	emsg(_(wrong_arg_msg));
717 }
718 
719 /*
720  * "assert_false(actual[, msg])" function
721  */
722     void
723 f_assert_false(typval_T *argvars, typval_T *rettv)
724 {
725     rettv->vval.v_number = assert_bool(argvars, FALSE);
726 }
727 
728     static int
729 assert_inrange(typval_T *argvars)
730 {
731     garray_T	ga;
732     int		error = FALSE;
733     char_u	*tofree;
734     char	msg[200];
735     char_u	numbuf[NUMBUFLEN];
736 
737 #ifdef FEAT_FLOAT
738     if (argvars[0].v_type == VAR_FLOAT
739 	    || argvars[1].v_type == VAR_FLOAT
740 	    || argvars[2].v_type == VAR_FLOAT)
741     {
742 	float_T flower = tv_get_float(&argvars[0]);
743 	float_T fupper = tv_get_float(&argvars[1]);
744 	float_T factual = tv_get_float(&argvars[2]);
745 
746 	if (factual < flower || factual > fupper)
747 	{
748 	    prepare_assert_error(&ga);
749 	    if (argvars[3].v_type != VAR_UNKNOWN)
750 	    {
751 		ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
752 		vim_free(tofree);
753 	    }
754 	    else
755 	    {
756 		vim_snprintf(msg, 200, "Expected range %g - %g, but got %g",
757 						      flower, fupper, factual);
758 		ga_concat(&ga, (char_u *)msg);
759 	    }
760 	    assert_error(&ga);
761 	    ga_clear(&ga);
762 	    return 1;
763 	}
764     }
765     else
766 #endif
767     {
768 	varnumber_T	lower = tv_get_number_chk(&argvars[0], &error);
769 	varnumber_T	upper = tv_get_number_chk(&argvars[1], &error);
770 	varnumber_T	actual = tv_get_number_chk(&argvars[2], &error);
771 
772 	if (error)
773 	    return 0;
774 	if (actual < lower || actual > upper)
775 	{
776 	    prepare_assert_error(&ga);
777 	    if (argvars[3].v_type != VAR_UNKNOWN)
778 	    {
779 		ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
780 		vim_free(tofree);
781 	    }
782 	    else
783 	    {
784 		vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld",
785 				       (long)lower, (long)upper, (long)actual);
786 		ga_concat(&ga, (char_u *)msg);
787 	    }
788 	    assert_error(&ga);
789 	    ga_clear(&ga);
790 	    return 1;
791 	}
792     }
793     return 0;
794 }
795 
796 /*
797  * "assert_inrange(lower, upper[, msg])" function
798  */
799     void
800 f_assert_inrange(typval_T *argvars, typval_T *rettv)
801 {
802     rettv->vval.v_number = assert_inrange(argvars);
803 }
804 
805 /*
806  * "assert_match(pattern, actual[, msg])" function
807  */
808     void
809 f_assert_match(typval_T *argvars, typval_T *rettv)
810 {
811     rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
812 }
813 
814 /*
815  * "assert_notmatch(pattern, actual[, msg])" function
816  */
817     void
818 f_assert_notmatch(typval_T *argvars, typval_T *rettv)
819 {
820     rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
821 }
822 
823 /*
824  * "assert_report(msg)" function
825  */
826     void
827 f_assert_report(typval_T *argvars, typval_T *rettv)
828 {
829     garray_T	ga;
830 
831     if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
832 	return;
833 
834     prepare_assert_error(&ga);
835     ga_concat(&ga, tv_get_string(&argvars[0]));
836     assert_error(&ga);
837     ga_clear(&ga);
838     rettv->vval.v_number = 1;
839 }
840 
841 /*
842  * "assert_true(actual[, msg])" function
843  */
844     void
845 f_assert_true(typval_T *argvars, typval_T *rettv)
846 {
847     rettv->vval.v_number = assert_bool(argvars, TRUE);
848 }
849 
850 /*
851  * "test_alloc_fail(id, countdown, repeat)" function
852  */
853     void
854 f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED)
855 {
856     if (argvars[0].v_type != VAR_NUMBER
857 	    || argvars[0].vval.v_number <= 0
858 	    || argvars[1].v_type != VAR_NUMBER
859 	    || argvars[1].vval.v_number < 0
860 	    || argvars[2].v_type != VAR_NUMBER)
861 	emsg(_(e_invarg));
862     else
863     {
864 	alloc_fail_id = argvars[0].vval.v_number;
865 	if (alloc_fail_id >= aid_last)
866 	    emsg(_(e_invarg));
867 	alloc_fail_countdown = argvars[1].vval.v_number;
868 	alloc_fail_repeat = argvars[2].vval.v_number;
869 	did_outofmem_msg = FALSE;
870     }
871 }
872 
873 /*
874  * "test_autochdir()"
875  */
876     void
877 f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
878 {
879 #if defined(FEAT_AUTOCHDIR)
880     test_autochdir = TRUE;
881 #endif
882 }
883 
884 /*
885  * "test_feedinput()"
886  */
887     void
888 f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED)
889 {
890 #ifdef USE_INPUT_BUF
891     char_u	*val = tv_get_string_chk(&argvars[0]);
892 
893 # ifdef VIMDLL
894     // this doesn't work in the console
895     if (!gui.in_use)
896 	return;
897 # endif
898 
899     if (val != NULL)
900     {
901 	trash_input_buf();
902 	add_to_input_buf_csi(val, (int)STRLEN(val));
903     }
904 #endif
905 }
906 
907 /*
908  * "test_getvalue({name})" function
909  */
910     void
911 f_test_getvalue(typval_T *argvars, typval_T *rettv)
912 {
913     if (argvars[0].v_type != VAR_STRING)
914 	emsg(_(e_invarg));
915     else
916     {
917 	char_u *name = tv_get_string(&argvars[0]);
918 
919 	if (STRCMP(name, (char_u *)"need_fileinfo") == 0)
920 	    rettv->vval.v_number = need_fileinfo;
921 	else
922 	    semsg(_(e_invarg2), name);
923     }
924 }
925 
926 /*
927  * "test_option_not_set({name})" function
928  */
929     void
930 f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED)
931 {
932     char_u *name = (char_u *)"";
933 
934     if (argvars[0].v_type != VAR_STRING)
935 	emsg(_(e_invarg));
936     else
937     {
938 	name = tv_get_string(&argvars[0]);
939 	if (reset_option_was_set(name) == FAIL)
940 	    semsg(_(e_invarg2), name);
941     }
942 }
943 
944 /*
945  * "test_override({name}, {val})" function
946  */
947     void
948 f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
949 {
950     char_u *name = (char_u *)"";
951     int     val;
952     static int save_starting = -1;
953 
954     if (in_vim9script()
955 	    && (check_for_string_arg(argvars, 0) == FAIL
956 		|| check_for_number_arg(argvars, 1) == FAIL))
957 	return;
958 
959     if (argvars[0].v_type != VAR_STRING
960 	    || (argvars[1].v_type) != VAR_NUMBER)
961 	emsg(_(e_invarg));
962     else
963     {
964 	name = tv_get_string(&argvars[0]);
965 	val = (int)tv_get_number(&argvars[1]);
966 
967 	if (STRCMP(name, (char_u *)"redraw") == 0)
968 	    disable_redraw_for_testing = val;
969 	else if (STRCMP(name, (char_u *)"redraw_flag") == 0)
970 	    ignore_redraw_flag_for_testing = val;
971 	else if (STRCMP(name, (char_u *)"char_avail") == 0)
972 	    disable_char_avail_for_testing = val;
973 	else if (STRCMP(name, (char_u *)"starting") == 0)
974 	{
975 	    if (val)
976 	    {
977 		if (save_starting < 0)
978 		    save_starting = starting;
979 		starting = 0;
980 	    }
981 	    else
982 	    {
983 		starting = save_starting;
984 		save_starting = -1;
985 	    }
986 	}
987 	else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
988 	    nfa_fail_for_testing = val;
989 	else if (STRCMP(name, (char_u *)"no_query_mouse") == 0)
990 	    no_query_mouse_for_testing = val;
991 	else if (STRCMP(name, (char_u *)"no_wait_return") == 0)
992 	    no_wait_return = val;
993 	else if (STRCMP(name, (char_u *)"ui_delay") == 0)
994 	    ui_delay_for_testing = val;
995 	else if (STRCMP(name, (char_u *)"term_props") == 0)
996 	    reset_term_props_on_termresponse = val;
997 	else if (STRCMP(name, (char_u *)"uptime") == 0)
998 	    override_sysinfo_uptime = val;
999 	else if (STRCMP(name, (char_u *)"ALL") == 0)
1000 	{
1001 	    disable_char_avail_for_testing = FALSE;
1002 	    disable_redraw_for_testing = FALSE;
1003 	    ignore_redraw_flag_for_testing = FALSE;
1004 	    nfa_fail_for_testing = FALSE;
1005 	    no_query_mouse_for_testing = FALSE;
1006 	    ui_delay_for_testing = 0;
1007 	    reset_term_props_on_termresponse = FALSE;
1008 	    override_sysinfo_uptime = -1;
1009 	    if (save_starting >= 0)
1010 	    {
1011 		starting = save_starting;
1012 		save_starting = -1;
1013 	    }
1014 	}
1015 	else
1016 	    semsg(_(e_invarg2), name);
1017     }
1018 }
1019 
1020 /*
1021  * "test_refcount({expr})" function
1022  */
1023     void
1024 f_test_refcount(typval_T *argvars, typval_T *rettv)
1025 {
1026     int retval = -1;
1027 
1028     switch (argvars[0].v_type)
1029     {
1030 	case VAR_UNKNOWN:
1031 	case VAR_ANY:
1032 	case VAR_VOID:
1033 	case VAR_NUMBER:
1034 	case VAR_BOOL:
1035 	case VAR_FLOAT:
1036 	case VAR_SPECIAL:
1037 	case VAR_STRING:
1038 	case VAR_INSTR:
1039 	    break;
1040 	case VAR_JOB:
1041 #ifdef FEAT_JOB_CHANNEL
1042 	    if (argvars[0].vval.v_job != NULL)
1043 		retval = argvars[0].vval.v_job->jv_refcount - 1;
1044 #endif
1045 	    break;
1046 	case VAR_CHANNEL:
1047 #ifdef FEAT_JOB_CHANNEL
1048 	    if (argvars[0].vval.v_channel != NULL)
1049 		retval = argvars[0].vval.v_channel->ch_refcount - 1;
1050 #endif
1051 	    break;
1052 	case VAR_FUNC:
1053 	    if (argvars[0].vval.v_string != NULL)
1054 	    {
1055 		ufunc_T *fp;
1056 
1057 		fp = find_func(argvars[0].vval.v_string, FALSE, NULL);
1058 		if (fp != NULL)
1059 		    retval = fp->uf_refcount;
1060 	    }
1061 	    break;
1062 	case VAR_PARTIAL:
1063 	    if (argvars[0].vval.v_partial != NULL)
1064 		retval = argvars[0].vval.v_partial->pt_refcount - 1;
1065 	    break;
1066 	case VAR_BLOB:
1067 	    if (argvars[0].vval.v_blob != NULL)
1068 		retval = argvars[0].vval.v_blob->bv_refcount - 1;
1069 	    break;
1070 	case VAR_LIST:
1071 	    if (argvars[0].vval.v_list != NULL)
1072 		retval = argvars[0].vval.v_list->lv_refcount - 1;
1073 	    break;
1074 	case VAR_DICT:
1075 	    if (argvars[0].vval.v_dict != NULL)
1076 		retval = argvars[0].vval.v_dict->dv_refcount - 1;
1077 	    break;
1078     }
1079 
1080     rettv->v_type = VAR_NUMBER;
1081     rettv->vval.v_number = retval;
1082 
1083 }
1084 
1085 /*
1086  * "test_garbagecollect_now()" function
1087  */
1088     void
1089 f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1090 {
1091     // This is dangerous, any Lists and Dicts used internally may be freed
1092     // while still in use.
1093     garbage_collect(TRUE);
1094 }
1095 
1096 /*
1097  * "test_garbagecollect_soon()" function
1098  */
1099     void
1100 f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1101 {
1102     may_garbage_collect = TRUE;
1103 }
1104 
1105 /*
1106  * "test_ignore_error()" function
1107  */
1108     void
1109 f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED)
1110 {
1111     if (argvars[0].v_type != VAR_STRING)
1112 	emsg(_(e_invarg));
1113     else
1114 	ignore_error_for_testing(tv_get_string(&argvars[0]));
1115 }
1116 
1117     void
1118 f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv)
1119 {
1120     rettv->v_type = VAR_BLOB;
1121     rettv->vval.v_blob = NULL;
1122 }
1123 
1124 #ifdef FEAT_JOB_CHANNEL
1125     void
1126 f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv)
1127 {
1128     rettv->v_type = VAR_CHANNEL;
1129     rettv->vval.v_channel = NULL;
1130 }
1131 #endif
1132 
1133     void
1134 f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv)
1135 {
1136     rettv_dict_set(rettv, NULL);
1137 }
1138 
1139 #ifdef FEAT_JOB_CHANNEL
1140     void
1141 f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv)
1142 {
1143     rettv->v_type = VAR_JOB;
1144     rettv->vval.v_job = NULL;
1145 }
1146 #endif
1147 
1148     void
1149 f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv)
1150 {
1151     rettv_list_set(rettv, NULL);
1152 }
1153 
1154     void
1155 f_test_null_function(typval_T *argvars UNUSED, typval_T *rettv)
1156 {
1157     rettv->v_type = VAR_FUNC;
1158     rettv->vval.v_string = NULL;
1159 }
1160 
1161     void
1162 f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv)
1163 {
1164     rettv->v_type = VAR_PARTIAL;
1165     rettv->vval.v_partial = NULL;
1166 }
1167 
1168     void
1169 f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
1170 {
1171     rettv->v_type = VAR_STRING;
1172     rettv->vval.v_string = NULL;
1173 }
1174 
1175     void
1176 f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv)
1177 {
1178     rettv->v_type = VAR_UNKNOWN;
1179 }
1180 
1181     void
1182 f_test_void(typval_T *argvars UNUSED, typval_T *rettv)
1183 {
1184     rettv->v_type = VAR_VOID;
1185 }
1186 
1187 #ifdef FEAT_GUI
1188     void
1189 f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED)
1190 {
1191     char_u	*which;
1192     long	value;
1193     int		dragging;
1194     scrollbar_T *sb = NULL;
1195 
1196     if (check_for_string_arg(argvars, 0) == FAIL
1197 	    || check_for_number_arg(argvars, 1) == FAIL
1198 	    || check_for_number_arg(argvars, 2) == FAIL)
1199 	return;
1200 
1201     if (argvars[0].v_type != VAR_STRING
1202 	    || (argvars[1].v_type) != VAR_NUMBER
1203 	    || (argvars[2].v_type) != VAR_NUMBER)
1204     {
1205 	emsg(_(e_invarg));
1206 	return;
1207     }
1208     which = tv_get_string(&argvars[0]);
1209     value = tv_get_number(&argvars[1]);
1210     dragging = tv_get_number(&argvars[2]);
1211 
1212     if (STRCMP(which, "left") == 0)
1213 	sb = &curwin->w_scrollbars[SBAR_LEFT];
1214     else if (STRCMP(which, "right") == 0)
1215 	sb = &curwin->w_scrollbars[SBAR_RIGHT];
1216     else if (STRCMP(which, "hor") == 0)
1217 	sb = &gui.bottom_sbar;
1218     if (sb == NULL)
1219     {
1220 	semsg(_(e_invarg2), which);
1221 	return;
1222     }
1223     gui_drag_scrollbar(sb, value, dragging);
1224 # ifndef USE_ON_FLY_SCROLL
1225     // need to loop through normal_cmd() to handle the scroll events
1226     exec_normal(FALSE, TRUE, FALSE);
1227 # endif
1228 }
1229 #endif
1230 
1231     void
1232 f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED)
1233 {
1234     if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type) != VAR_NUMBER)
1235     {
1236 	emsg(_(e_invarg));
1237 	return;
1238     }
1239 
1240     mouse_row = (time_t)tv_get_number(&argvars[0]) - 1;
1241     mouse_col = (time_t)tv_get_number(&argvars[1]) - 1;
1242 }
1243 
1244     void
1245 f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1246 {
1247 # ifdef FEAT_GUI
1248     int		button;
1249     int		row;
1250     int		col;
1251     int		repeated_click;
1252     int_u	mods;
1253 
1254     if (check_for_number_arg(argvars, 0) == FAIL
1255 	    || check_for_number_arg(argvars, 1) == FAIL
1256 	    || check_for_number_arg(argvars, 2) == FAIL
1257 	    || check_for_number_arg(argvars, 3) == FAIL
1258 	    || check_for_number_arg(argvars, 4) == FAIL)
1259 	return;
1260 
1261     button = tv_get_number(&argvars[0]);
1262     row = tv_get_number(&argvars[1]);
1263     col = tv_get_number(&argvars[2]);
1264     repeated_click = tv_get_number(&argvars[3]);
1265     mods = tv_get_number(&argvars[4]);
1266 
1267     gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods);
1268 # endif
1269 }
1270 
1271     void
1272 f_test_settime(typval_T *argvars, typval_T *rettv UNUSED)
1273 {
1274     time_for_testing = (time_t)tv_get_number(&argvars[0]);
1275 }
1276 
1277     void
1278 f_test_gui_drop_files(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1279 {
1280 #if defined(HAVE_DROP_FILE)
1281     int		row;
1282     int		col;
1283     int_u	mods;
1284     char_u	**fnames;
1285     int		count = 0;
1286     list_T	*l;
1287     listitem_T	*li;
1288 
1289     if (check_for_list_arg(argvars, 0) == FAIL
1290 	    || check_for_number_arg(argvars, 1) == FAIL
1291 	    || check_for_number_arg(argvars, 2) == FAIL
1292 	    || check_for_number_arg(argvars, 3) == FAIL)
1293 	return;
1294 
1295     row = tv_get_number(&argvars[1]);
1296     col = tv_get_number(&argvars[2]);
1297     mods = tv_get_number(&argvars[3]);
1298 
1299     l = argvars[0].vval.v_list;
1300     if (list_len(l) == 0)
1301 	return;
1302 
1303     fnames = ALLOC_MULT(char_u *, list_len(l));
1304     if (fnames == NULL)
1305 	return;
1306 
1307     FOR_ALL_LIST_ITEMS(l, li)
1308     {
1309 	// ignore non-string items
1310 	if (li->li_tv.v_type != VAR_STRING)
1311 	    continue;
1312 
1313 	fnames[count] = vim_strsave(li->li_tv.vval.v_string);
1314 	if (fnames[count] == NULL)
1315 	{
1316 	    while (--count >= 0)
1317 		vim_free(fnames[count]);
1318 	    vim_free(fnames);
1319 	    return;
1320 	}
1321 	count++;
1322     }
1323 
1324     if (count > 0)
1325 	gui_handle_drop(TEXT_X(col - 1), TEXT_Y(row - 1), mods, fnames, count);
1326     else
1327 	vim_free(fnames);
1328 # endif
1329 }
1330 
1331 #endif // defined(FEAT_EVAL)
1332