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