xref: /vim-8.2.3635/src/testing.c (revision 5be4ceec)
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 
26     ga_init2(gap, 1, 100);
27     if (sourcing_name != NULL)
28     {
29 	ga_concat(gap, sourcing_name);
30 	if (sourcing_lnum > 0)
31 	    ga_concat(gap, (char_u *)" ");
32     }
33     if (sourcing_lnum > 0)
34     {
35 	sprintf(buf, "line %ld", (long)sourcing_lnum);
36 	ga_concat(gap, (char_u *)buf);
37     }
38     if (sourcing_name != NULL || sourcing_lnum > 0)
39 	ga_concat(gap, (char_u *)": ");
40 }
41 
42 /*
43  * Append "p[clen]" to "gap", escaping unprintable characters.
44  * Changes NL to \n, CR to \r, etc.
45  */
46     static void
47 ga_concat_esc(garray_T *gap, char_u *p, int clen)
48 {
49     char_u  buf[NUMBUFLEN];
50 
51     if (clen > 1)
52     {
53 	mch_memmove(buf, p, clen);
54 	buf[clen] = NUL;
55 	ga_concat(gap, buf);
56     }
57     else switch (*p)
58     {
59 	case BS: ga_concat(gap, (char_u *)"\\b"); break;
60 	case ESC: ga_concat(gap, (char_u *)"\\e"); break;
61 	case FF: ga_concat(gap, (char_u *)"\\f"); break;
62 	case NL: ga_concat(gap, (char_u *)"\\n"); break;
63 	case TAB: ga_concat(gap, (char_u *)"\\t"); break;
64 	case CAR: ga_concat(gap, (char_u *)"\\r"); break;
65 	case '\\': ga_concat(gap, (char_u *)"\\\\"); break;
66 	default:
67 		   if (*p < ' ')
68 		   {
69 		       vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p);
70 		       ga_concat(gap, buf);
71 		   }
72 		   else
73 		       ga_append(gap, *p);
74 		   break;
75     }
76 }
77 
78 /*
79  * Append "str" to "gap", escaping unprintable characters.
80  * Changes NL to \n, CR to \r, etc.
81  */
82     static void
83 ga_concat_shorten_esc(garray_T *gap, char_u *str)
84 {
85     char_u  *p;
86     char_u  *s;
87     int	    c;
88     int	    clen;
89     char_u  buf[NUMBUFLEN];
90     int	    same_len;
91 
92     if (str == NULL)
93     {
94 	ga_concat(gap, (char_u *)"NULL");
95 	return;
96     }
97 
98     for (p = str; *p != NUL; ++p)
99     {
100 	same_len = 1;
101 	s = p;
102 	c = mb_ptr2char_adv(&s);
103 	clen = s - p;
104 	while (*s != NUL && c == mb_ptr2char(s))
105 	{
106 	    ++same_len;
107 	    s += clen;
108 	}
109 	if (same_len > 20)
110 	{
111 	    ga_concat(gap, (char_u *)"\\[");
112 	    ga_concat_esc(gap, p, clen);
113 	    ga_concat(gap, (char_u *)" occurs ");
114 	    vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
115 	    ga_concat(gap, buf);
116 	    ga_concat(gap, (char_u *)" times]");
117 	    p = s - 1;
118 	}
119 	else
120 	    ga_concat_esc(gap, p, clen);
121     }
122 }
123 
124 /*
125  * Fill "gap" with information about an assert error.
126  */
127     static void
128 fill_assert_error(
129     garray_T	*gap,
130     typval_T	*opt_msg_tv,
131     char_u      *exp_str,
132     typval_T	*exp_tv,
133     typval_T	*got_tv,
134     assert_type_T atype)
135 {
136     char_u	numbuf[NUMBUFLEN];
137     char_u	*tofree;
138 
139     if (opt_msg_tv->v_type != VAR_UNKNOWN)
140     {
141 	ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0));
142 	vim_free(tofree);
143 	ga_concat(gap, (char_u *)": ");
144     }
145 
146     if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH)
147 	ga_concat(gap, (char_u *)"Pattern ");
148     else if (atype == ASSERT_NOTEQUAL)
149 	ga_concat(gap, (char_u *)"Expected not equal to ");
150     else
151 	ga_concat(gap, (char_u *)"Expected ");
152     if (exp_str == NULL)
153     {
154 	ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0));
155 	vim_free(tofree);
156     }
157     else
158 	ga_concat_shorten_esc(gap, exp_str);
159     if (atype != ASSERT_NOTEQUAL)
160     {
161 	if (atype == ASSERT_MATCH)
162 	    ga_concat(gap, (char_u *)" does not match ");
163 	else if (atype == ASSERT_NOTMATCH)
164 	    ga_concat(gap, (char_u *)" does match ");
165 	else
166 	    ga_concat(gap, (char_u *)" but got ");
167 	ga_concat_shorten_esc(gap, tv2string(got_tv, &tofree, numbuf, 0));
168 	vim_free(tofree);
169     }
170 }
171 
172     static int
173 assert_equal_common(typval_T *argvars, assert_type_T atype)
174 {
175     garray_T	ga;
176 
177     if (tv_equal(&argvars[0], &argvars[1], FALSE, FALSE)
178 						   != (atype == ASSERT_EQUAL))
179     {
180 	prepare_assert_error(&ga);
181 	fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
182 								       atype);
183 	assert_error(&ga);
184 	ga_clear(&ga);
185 	return 1;
186     }
187     return 0;
188 }
189 
190     static int
191 assert_match_common(typval_T *argvars, assert_type_T atype)
192 {
193     garray_T	ga;
194     char_u	buf1[NUMBUFLEN];
195     char_u	buf2[NUMBUFLEN];
196     char_u	*pat = tv_get_string_buf_chk(&argvars[0], buf1);
197     char_u	*text = tv_get_string_buf_chk(&argvars[1], buf2);
198 
199     if (pat == NULL || text == NULL)
200 	emsg(_(e_invarg));
201     else if (pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH))
202     {
203 	prepare_assert_error(&ga);
204 	fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
205 									atype);
206 	assert_error(&ga);
207 	ga_clear(&ga);
208 	return 1;
209     }
210     return 0;
211 }
212 
213 /*
214  * Common for assert_true() and assert_false().
215  * Return non-zero for failure.
216  */
217     static int
218 assert_bool(typval_T *argvars, int isTrue)
219 {
220     int		error = FALSE;
221     garray_T	ga;
222 
223     if (argvars[0].v_type == VAR_SPECIAL
224 	    && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE))
225 	return 0;
226     if (argvars[0].v_type != VAR_NUMBER
227 	    || (tv_get_number_chk(&argvars[0], &error) == 0) == isTrue
228 	    || error)
229     {
230 	prepare_assert_error(&ga);
231 	fill_assert_error(&ga, &argvars[1],
232 		(char_u *)(isTrue ? "True" : "False"),
233 		NULL, &argvars[0], ASSERT_OTHER);
234 	assert_error(&ga);
235 	ga_clear(&ga);
236 	return 1;
237     }
238     return 0;
239 }
240 
241     static void
242 assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, char_u *cmd)
243 {
244     char_u	*tofree;
245     char_u	numbuf[NUMBUFLEN];
246 
247     if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN)
248     {
249 	ga_concat(gap, echo_string(&argvars[2], &tofree, numbuf, 0));
250 	vim_free(tofree);
251     }
252     else
253 	ga_concat(gap, cmd);
254 }
255 
256     static int
257 assert_beeps(typval_T *argvars)
258 {
259     char_u	*cmd = tv_get_string_chk(&argvars[0]);
260     garray_T	ga;
261     int		ret = 0;
262 
263     called_vim_beep = FALSE;
264     suppress_errthrow = TRUE;
265     emsg_silent = FALSE;
266     do_cmdline_cmd(cmd);
267     if (!called_vim_beep)
268     {
269 	prepare_assert_error(&ga);
270 	ga_concat(&ga, (char_u *)"command did not beep: ");
271 	ga_concat(&ga, cmd);
272 	assert_error(&ga);
273 	ga_clear(&ga);
274 	ret = 1;
275     }
276 
277     suppress_errthrow = FALSE;
278     emsg_on_display = FALSE;
279     return ret;
280 }
281 
282 /*
283  * "assert_beeps(cmd [, error])" function
284  */
285     void
286 f_assert_beeps(typval_T *argvars, typval_T *rettv)
287 {
288     rettv->vval.v_number = assert_beeps(argvars);
289 }
290 
291 /*
292  * "assert_equal(expected, actual[, msg])" function
293  */
294     void
295 f_assert_equal(typval_T *argvars, typval_T *rettv)
296 {
297     rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
298 }
299 
300     static int
301 assert_equalfile(typval_T *argvars)
302 {
303     char_u	buf1[NUMBUFLEN];
304     char_u	buf2[NUMBUFLEN];
305     char_u	*fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
306     char_u	*fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
307     garray_T	ga;
308     FILE	*fd1;
309     FILE	*fd2;
310 
311     if (fname1 == NULL || fname2 == NULL)
312 	return 0;
313 
314     IObuff[0] = NUL;
315     fd1 = mch_fopen((char *)fname1, READBIN);
316     if (fd1 == NULL)
317     {
318 	vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
319     }
320     else
321     {
322 	fd2 = mch_fopen((char *)fname2, READBIN);
323 	if (fd2 == NULL)
324 	{
325 	    fclose(fd1);
326 	    vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
327 	}
328 	else
329 	{
330 	    int c1, c2;
331 	    long count = 0;
332 
333 	    for (;;)
334 	    {
335 		c1 = fgetc(fd1);
336 		c2 = fgetc(fd2);
337 		if (c1 == EOF)
338 		{
339 		    if (c2 != EOF)
340 			STRCPY(IObuff, "first file is shorter");
341 		    break;
342 		}
343 		else if (c2 == EOF)
344 		{
345 		    STRCPY(IObuff, "second file is shorter");
346 		    break;
347 		}
348 		else if (c1 != c2)
349 		{
350 		    vim_snprintf((char *)IObuff, IOSIZE,
351 					      "difference at byte %ld", count);
352 		    break;
353 		}
354 		++count;
355 	    }
356 	    fclose(fd1);
357 	    fclose(fd2);
358 	}
359     }
360     if (IObuff[0] != NUL)
361     {
362 	prepare_assert_error(&ga);
363 	ga_concat(&ga, IObuff);
364 	assert_error(&ga);
365 	ga_clear(&ga);
366 	return 1;
367     }
368     return 0;
369 }
370 
371 /*
372  * "assert_equalfile(fname-one, fname-two)" function
373  */
374     void
375 f_assert_equalfile(typval_T *argvars, typval_T *rettv)
376 {
377     rettv->vval.v_number = assert_equalfile(argvars);
378 }
379 
380 /*
381  * "assert_notequal(expected, actual[, msg])" function
382  */
383     void
384 f_assert_notequal(typval_T *argvars, typval_T *rettv)
385 {
386     rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
387 }
388 
389 /*
390  * "assert_exception(string[, msg])" function
391  */
392     void
393 f_assert_exception(typval_T *argvars, typval_T *rettv)
394 {
395     garray_T	ga;
396     char_u	*error = tv_get_string_chk(&argvars[0]);
397 
398     if (*get_vim_var_str(VV_EXCEPTION) == NUL)
399     {
400 	prepare_assert_error(&ga);
401 	ga_concat(&ga, (char_u *)"v:exception is not set");
402 	assert_error(&ga);
403 	ga_clear(&ga);
404 	rettv->vval.v_number = 1;
405     }
406     else if (error != NULL
407 	&& strstr((char *)get_vim_var_str(VV_EXCEPTION), (char *)error) == NULL)
408     {
409 	prepare_assert_error(&ga);
410 	fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
411 				  get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
412 	assert_error(&ga);
413 	ga_clear(&ga);
414 	rettv->vval.v_number = 1;
415     }
416 }
417 
418 /*
419  * "assert_fails(cmd [, error[, msg]])" function
420  */
421     void
422 f_assert_fails(typval_T *argvars, typval_T *rettv)
423 {
424     char_u	*cmd = tv_get_string_chk(&argvars[0]);
425     garray_T	ga;
426     int		save_trylevel = trylevel;
427 
428     // trylevel must be zero for a ":throw" command to be considered failed
429     trylevel = 0;
430     called_emsg = FALSE;
431     suppress_errthrow = TRUE;
432     emsg_silent = TRUE;
433 
434     do_cmdline_cmd(cmd);
435     if (!called_emsg)
436     {
437 	prepare_assert_error(&ga);
438 	ga_concat(&ga, (char_u *)"command did not fail: ");
439 	assert_append_cmd_or_arg(&ga, argvars, cmd);
440 	assert_error(&ga);
441 	ga_clear(&ga);
442 	rettv->vval.v_number = 1;
443     }
444     else if (argvars[1].v_type != VAR_UNKNOWN)
445     {
446 	char_u	buf[NUMBUFLEN];
447 	char	*error = (char *)tv_get_string_buf_chk(&argvars[1], buf);
448 
449 	if (error == NULL
450 		  || strstr((char *)get_vim_var_str(VV_ERRMSG), error) == NULL)
451 	{
452 	    prepare_assert_error(&ga);
453 	    fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
454 				      get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
455 	    ga_concat(&ga, (char_u *)": ");
456 	    assert_append_cmd_or_arg(&ga, argvars, cmd);
457 	    assert_error(&ga);
458 	    ga_clear(&ga);
459 	    rettv->vval.v_number = 1;
460 	}
461     }
462 
463     trylevel = save_trylevel;
464     called_emsg = FALSE;
465     suppress_errthrow = FALSE;
466     emsg_silent = FALSE;
467     emsg_on_display = FALSE;
468     set_vim_var_string(VV_ERRMSG, NULL, 0);
469 }
470 
471 /*
472  * "assert_false(actual[, msg])" function
473  */
474     void
475 f_assert_false(typval_T *argvars, typval_T *rettv)
476 {
477     rettv->vval.v_number = assert_bool(argvars, FALSE);
478 }
479 
480     static int
481 assert_inrange(typval_T *argvars)
482 {
483     garray_T	ga;
484     int		error = FALSE;
485     char_u	*tofree;
486     char	msg[200];
487     char_u	numbuf[NUMBUFLEN];
488 
489 #ifdef FEAT_FLOAT
490     if (argvars[0].v_type == VAR_FLOAT
491 	    || argvars[1].v_type == VAR_FLOAT
492 	    || argvars[2].v_type == VAR_FLOAT)
493     {
494 	float_T flower = tv_get_float(&argvars[0]);
495 	float_T fupper = tv_get_float(&argvars[1]);
496 	float_T factual = tv_get_float(&argvars[2]);
497 
498 	if (factual < flower || factual > fupper)
499 	{
500 	    prepare_assert_error(&ga);
501 	    if (argvars[3].v_type != VAR_UNKNOWN)
502 	    {
503 		ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
504 		vim_free(tofree);
505 	    }
506 	    else
507 	    {
508 		vim_snprintf(msg, 200, "Expected range %g - %g, but got %g",
509 						      flower, fupper, factual);
510 		ga_concat(&ga, (char_u *)msg);
511 	    }
512 	    assert_error(&ga);
513 	    ga_clear(&ga);
514 	    return 1;
515 	}
516     }
517     else
518 #endif
519     {
520 	varnumber_T	lower = tv_get_number_chk(&argvars[0], &error);
521 	varnumber_T	upper = tv_get_number_chk(&argvars[1], &error);
522 	varnumber_T	actual = tv_get_number_chk(&argvars[2], &error);
523 
524 	if (error)
525 	    return 0;
526 	if (actual < lower || actual > upper)
527 	{
528 	    prepare_assert_error(&ga);
529 	    if (argvars[3].v_type != VAR_UNKNOWN)
530 	    {
531 		ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
532 		vim_free(tofree);
533 	    }
534 	    else
535 	    {
536 		vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld",
537 				       (long)lower, (long)upper, (long)actual);
538 		ga_concat(&ga, (char_u *)msg);
539 	    }
540 	    assert_error(&ga);
541 	    ga_clear(&ga);
542 	    return 1;
543 	}
544     }
545     return 0;
546 }
547 
548 /*
549  * "assert_inrange(lower, upper[, msg])" function
550  */
551     void
552 f_assert_inrange(typval_T *argvars, typval_T *rettv)
553 {
554     rettv->vval.v_number = assert_inrange(argvars);
555 }
556 
557 /*
558  * "assert_match(pattern, actual[, msg])" function
559  */
560     void
561 f_assert_match(typval_T *argvars, typval_T *rettv)
562 {
563     rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
564 }
565 
566 /*
567  * "assert_notmatch(pattern, actual[, msg])" function
568  */
569     void
570 f_assert_notmatch(typval_T *argvars, typval_T *rettv)
571 {
572     rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
573 }
574 
575 /*
576  * "assert_report(msg)" function
577  */
578     void
579 f_assert_report(typval_T *argvars, typval_T *rettv)
580 {
581     garray_T	ga;
582 
583     prepare_assert_error(&ga);
584     ga_concat(&ga, tv_get_string(&argvars[0]));
585     assert_error(&ga);
586     ga_clear(&ga);
587     rettv->vval.v_number = 1;
588 }
589 
590 /*
591  * "assert_true(actual[, msg])" function
592  */
593     void
594 f_assert_true(typval_T *argvars, typval_T *rettv)
595 {
596     rettv->vval.v_number = assert_bool(argvars, TRUE);
597 }
598 
599 /*
600  * "test_alloc_fail(id, countdown, repeat)" function
601  */
602     void
603 f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED)
604 {
605     if (argvars[0].v_type != VAR_NUMBER
606 	    || argvars[0].vval.v_number <= 0
607 	    || argvars[1].v_type != VAR_NUMBER
608 	    || argvars[1].vval.v_number < 0
609 	    || argvars[2].v_type != VAR_NUMBER)
610 	emsg(_(e_invarg));
611     else
612     {
613 	alloc_fail_id = argvars[0].vval.v_number;
614 	if (alloc_fail_id >= aid_last)
615 	    emsg(_(e_invarg));
616 	alloc_fail_countdown = argvars[1].vval.v_number;
617 	alloc_fail_repeat = argvars[2].vval.v_number;
618 	did_outofmem_msg = FALSE;
619     }
620 }
621 
622 /*
623  * "test_autochdir()"
624  */
625     void
626 f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
627 {
628 #if defined(FEAT_AUTOCHDIR)
629     test_autochdir = TRUE;
630 #endif
631 }
632 
633 /*
634  * "test_feedinput()"
635  */
636     void
637 f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED)
638 {
639 #ifdef USE_INPUT_BUF
640     char_u	*val = tv_get_string_chk(&argvars[0]);
641 
642     if (val != NULL)
643     {
644 	trash_input_buf();
645 	add_to_input_buf_csi(val, (int)STRLEN(val));
646     }
647 #endif
648 }
649 
650 /*
651  * "test_getvalue({name})" function
652  */
653     void
654 f_test_getvalue(typval_T *argvars, typval_T *rettv)
655 {
656     if (argvars[0].v_type != VAR_STRING)
657 	emsg(_(e_invarg));
658     else
659     {
660 	char_u *name = tv_get_string(&argvars[0]);
661 
662 	if (STRCMP(name, (char_u *)"need_fileinfo") == 0)
663 	    rettv->vval.v_number = need_fileinfo;
664 	else
665 	    semsg(_(e_invarg2), name);
666     }
667 }
668 
669 /*
670  * "test_option_not_set({name})" function
671  */
672     void
673 f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED)
674 {
675     char_u *name = (char_u *)"";
676 
677     if (argvars[0].v_type != VAR_STRING)
678 	emsg(_(e_invarg));
679     else
680     {
681 	name = tv_get_string(&argvars[0]);
682 	if (reset_option_was_set(name) == FAIL)
683 	    semsg(_(e_invarg2), name);
684     }
685 }
686 
687 /*
688  * "test_override({name}, {val})" function
689  */
690     void
691 f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
692 {
693     char_u *name = (char_u *)"";
694     int     val;
695     static int save_starting = -1;
696 
697     if (argvars[0].v_type != VAR_STRING
698 	    || (argvars[1].v_type) != VAR_NUMBER)
699 	emsg(_(e_invarg));
700     else
701     {
702 	name = tv_get_string(&argvars[0]);
703 	val = (int)tv_get_number(&argvars[1]);
704 
705 	if (STRCMP(name, (char_u *)"redraw") == 0)
706 	    disable_redraw_for_testing = val;
707 	else if (STRCMP(name, (char_u *)"redraw_flag") == 0)
708 	    ignore_redraw_flag_for_testing = val;
709 	else if (STRCMP(name, (char_u *)"char_avail") == 0)
710 	    disable_char_avail_for_testing = val;
711 	else if (STRCMP(name, (char_u *)"starting") == 0)
712 	{
713 	    if (val)
714 	    {
715 		if (save_starting < 0)
716 		    save_starting = starting;
717 		starting = 0;
718 	    }
719 	    else
720 	    {
721 		starting = save_starting;
722 		save_starting = -1;
723 	    }
724 	}
725 	else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
726 	    nfa_fail_for_testing = val;
727 	else if (STRCMP(name, (char_u *)"no_query_mouse") == 0)
728 	    no_query_mouse_for_testing = val;
729 	else if (STRCMP(name, (char_u *)"no_wait_return") == 0)
730 	    no_wait_return = val;
731 	else if (STRCMP(name, (char_u *)"ALL") == 0)
732 	{
733 	    disable_char_avail_for_testing = FALSE;
734 	    disable_redraw_for_testing = FALSE;
735 	    ignore_redraw_flag_for_testing = FALSE;
736 	    nfa_fail_for_testing = FALSE;
737 	    no_query_mouse_for_testing = FALSE;
738 	    if (save_starting >= 0)
739 	    {
740 		starting = save_starting;
741 		save_starting = -1;
742 	    }
743 	}
744 	else
745 	    semsg(_(e_invarg2), name);
746     }
747 }
748 
749 /*
750  * "test_refcount({expr})" function
751  */
752     void
753 f_test_refcount(typval_T *argvars, typval_T *rettv)
754 {
755     int retval = -1;
756 
757     switch (argvars[0].v_type)
758     {
759 	case VAR_UNKNOWN:
760 	case VAR_NUMBER:
761 	case VAR_FLOAT:
762 	case VAR_SPECIAL:
763 	case VAR_STRING:
764 	    break;
765 	case VAR_JOB:
766 #ifdef FEAT_JOB_CHANNEL
767 	    if (argvars[0].vval.v_job != NULL)
768 		retval = argvars[0].vval.v_job->jv_refcount - 1;
769 #endif
770 	    break;
771 	case VAR_CHANNEL:
772 #ifdef FEAT_JOB_CHANNEL
773 	    if (argvars[0].vval.v_channel != NULL)
774 		retval = argvars[0].vval.v_channel->ch_refcount - 1;
775 #endif
776 	    break;
777 	case VAR_FUNC:
778 	    if (argvars[0].vval.v_string != NULL)
779 	    {
780 		ufunc_T *fp;
781 
782 		fp = find_func(argvars[0].vval.v_string);
783 		if (fp != NULL)
784 		    retval = fp->uf_refcount;
785 	    }
786 	    break;
787 	case VAR_PARTIAL:
788 	    if (argvars[0].vval.v_partial != NULL)
789 		retval = argvars[0].vval.v_partial->pt_refcount - 1;
790 	    break;
791 	case VAR_BLOB:
792 	    if (argvars[0].vval.v_blob != NULL)
793 		retval = argvars[0].vval.v_blob->bv_refcount - 1;
794 	    break;
795 	case VAR_LIST:
796 	    if (argvars[0].vval.v_list != NULL)
797 		retval = argvars[0].vval.v_list->lv_refcount - 1;
798 	    break;
799 	case VAR_DICT:
800 	    if (argvars[0].vval.v_dict != NULL)
801 		retval = argvars[0].vval.v_dict->dv_refcount - 1;
802 	    break;
803     }
804 
805     rettv->v_type = VAR_NUMBER;
806     rettv->vval.v_number = retval;
807 
808 }
809 
810 /*
811  * "test_garbagecollect_now()" function
812  */
813     void
814 f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
815 {
816     /* This is dangerous, any Lists and Dicts used internally may be freed
817      * while still in use. */
818     garbage_collect(TRUE);
819 }
820 
821 /*
822  * "test_garbagecollect_soon()" function
823  */
824     void
825 f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
826 {
827     may_garbage_collect = TRUE;
828 }
829 
830 /*
831  * "test_ignore_error()" function
832  */
833     void
834 f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED)
835 {
836      ignore_error_for_testing(tv_get_string(&argvars[0]));
837 }
838 
839     void
840 f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv)
841 {
842     rettv->v_type = VAR_BLOB;
843     rettv->vval.v_blob = NULL;
844 }
845 
846 #ifdef FEAT_JOB_CHANNEL
847     void
848 f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv)
849 {
850     rettv->v_type = VAR_CHANNEL;
851     rettv->vval.v_channel = NULL;
852 }
853 #endif
854 
855     void
856 f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv)
857 {
858     rettv_dict_set(rettv, NULL);
859 }
860 
861 #ifdef FEAT_JOB_CHANNEL
862     void
863 f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv)
864 {
865     rettv->v_type = VAR_JOB;
866     rettv->vval.v_job = NULL;
867 }
868 #endif
869 
870     void
871 f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv)
872 {
873     rettv_list_set(rettv, NULL);
874 }
875 
876     void
877 f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv)
878 {
879     rettv->v_type = VAR_PARTIAL;
880     rettv->vval.v_partial = NULL;
881 }
882 
883     void
884 f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
885 {
886     rettv->v_type = VAR_STRING;
887     rettv->vval.v_string = NULL;
888 }
889 
890 #ifdef FEAT_GUI
891     void
892 f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED)
893 {
894     char_u	*which;
895     long	value;
896     int		dragging;
897     scrollbar_T *sb = NULL;
898 
899     if (argvars[0].v_type != VAR_STRING
900 	    || (argvars[1].v_type) != VAR_NUMBER
901 	    || (argvars[2].v_type) != VAR_NUMBER)
902     {
903 	emsg(_(e_invarg));
904 	return;
905     }
906     which = tv_get_string(&argvars[0]);
907     value = tv_get_number(&argvars[1]);
908     dragging = tv_get_number(&argvars[2]);
909 
910     if (STRCMP(which, "left") == 0)
911 	sb = &curwin->w_scrollbars[SBAR_LEFT];
912     else if (STRCMP(which, "right") == 0)
913 	sb = &curwin->w_scrollbars[SBAR_RIGHT];
914     else if (STRCMP(which, "hor") == 0)
915 	sb = &gui.bottom_sbar;
916     if (sb == NULL)
917     {
918 	semsg(_(e_invarg2), which);
919 	return;
920     }
921     gui_drag_scrollbar(sb, value, dragging);
922 # ifndef USE_ON_FLY_SCROLL
923     // need to loop through normal_cmd() to handle the scroll events
924     exec_normal(FALSE, TRUE, FALSE);
925 # endif
926 }
927 #endif
928 
929 #ifdef FEAT_MOUSE
930     void
931 f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED)
932 {
933     mouse_row = (time_t)tv_get_number(&argvars[0]) - 1;
934     mouse_col = (time_t)tv_get_number(&argvars[1]) - 1;
935 }
936 #endif
937 
938     void
939 f_test_settime(typval_T *argvars, typval_T *rettv UNUSED)
940 {
941     time_for_testing = (time_t)tv_get_number(&argvars[0]);
942 }
943 
944 
945 #endif // defined(FEAT_EVAL)
946