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