blob: de89bd9fd7fe9811bfbfb3d68a03c5191ec6f3da [file] [log] [blame]
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001/* 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
22prepare_assert_error(garray_T *gap)
23{
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010024 char buf[NUMBUFLEN];
25 char_u *sname = estack_sfile();
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020026
27 ga_init2(gap, 1, 100);
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010028 if (sname != NULL)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020029 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010030 ga_concat(gap, sname);
31 if (SOURCING_LNUM > 0)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020032 ga_concat(gap, (char_u *)" ");
33 }
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010034 if (SOURCING_LNUM > 0)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020035 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010036 sprintf(buf, "line %ld", (long)SOURCING_LNUM);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020037 ga_concat(gap, (char_u *)buf);
38 }
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010039 if (sname != NULL || SOURCING_LNUM > 0)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020040 ga_concat(gap, (char_u *)": ");
Bram Moolenaar1a47ae32019-12-29 23:04:25 +010041 vim_free(sname);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020042}
43
44/*
45 * Append "p[clen]" to "gap", escaping unprintable characters.
46 * Changes NL to \n, CR to \r, etc.
47 */
48 static void
49ga_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:
Bram Moolenaar7177da92020-07-12 23:09:20 +020069 if (*p < ' ' || *p == 0x7f)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +020070 {
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
85ga_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
130fill_assert_error(
131 garray_T *gap,
132 typval_T *opt_msg_tv,
133 char_u *exp_str,
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200134 typval_T *exp_tv_arg,
135 typval_T *got_tv_arg,
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200136 assert_type_T atype)
137{
138 char_u numbuf[NUMBUFLEN];
139 char_u *tofree;
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200140 typval_T *exp_tv = exp_tv_arg;
141 typval_T *got_tv = got_tv_arg;
142 int did_copy = FALSE;
143 int omitted = 0;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200144
145 if (opt_msg_tv->v_type != VAR_UNKNOWN)
146 {
147 ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0));
148 vim_free(tofree);
149 ga_concat(gap, (char_u *)": ");
150 }
151
152 if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH)
153 ga_concat(gap, (char_u *)"Pattern ");
154 else if (atype == ASSERT_NOTEQUAL)
155 ga_concat(gap, (char_u *)"Expected not equal to ");
156 else
157 ga_concat(gap, (char_u *)"Expected ");
158 if (exp_str == NULL)
159 {
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200160 // When comparing dictionaries, drop the items that are equal, so that
161 // it's a lot easier to see what differs.
162 if (atype != ASSERT_NOTEQUAL
163 && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT
164 && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL)
165 {
166 dict_T *exp_d = exp_tv->vval.v_dict;
167 dict_T *got_d = got_tv->vval.v_dict;
168 hashitem_T *hi;
169 dictitem_T *item2;
170 int todo;
171
172 did_copy = TRUE;
173 exp_tv->vval.v_dict = dict_alloc();
174 got_tv->vval.v_dict = dict_alloc();
175 if (exp_tv->vval.v_dict == NULL || got_tv->vval.v_dict == NULL)
176 return;
177
178 todo = (int)exp_d->dv_hashtab.ht_used;
179 for (hi = exp_d->dv_hashtab.ht_array; todo > 0; ++hi)
180 {
181 if (!HASHITEM_EMPTY(hi))
182 {
183 item2 = dict_find(got_d, hi->hi_key, -1);
184 if (item2 == NULL || !tv_equal(&HI2DI(hi)->di_tv,
185 &item2->di_tv, FALSE, FALSE))
186 {
187 // item of exp_d not present in got_d or values differ.
188 dict_add_tv(exp_tv->vval.v_dict,
189 (char *)hi->hi_key, &HI2DI(hi)->di_tv);
190 if (item2 != NULL)
191 dict_add_tv(got_tv->vval.v_dict,
192 (char *)hi->hi_key, &item2->di_tv);
193 }
194 else
195 ++omitted;
196 --todo;
197 }
198 }
199
200 // Add items only present in got_d.
201 todo = (int)got_d->dv_hashtab.ht_used;
202 for (hi = got_d->dv_hashtab.ht_array; todo > 0; ++hi)
203 {
204 if (!HASHITEM_EMPTY(hi))
205 {
206 item2 = dict_find(exp_d, hi->hi_key, -1);
207 if (item2 == NULL)
208 // item of got_d not present in exp_d
209 dict_add_tv(got_tv->vval.v_dict,
210 (char *)hi->hi_key, &HI2DI(hi)->di_tv);
211 --todo;
212 }
213 }
214 }
215
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200216 ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0));
217 vim_free(tofree);
218 }
219 else
220 ga_concat_shorten_esc(gap, exp_str);
221 if (atype != ASSERT_NOTEQUAL)
222 {
223 if (atype == ASSERT_MATCH)
224 ga_concat(gap, (char_u *)" does not match ");
225 else if (atype == ASSERT_NOTMATCH)
226 ga_concat(gap, (char_u *)" does match ");
227 else
228 ga_concat(gap, (char_u *)" but got ");
229 ga_concat_shorten_esc(gap, tv2string(got_tv, &tofree, numbuf, 0));
230 vim_free(tofree);
Bram Moolenaar4a021df2020-06-13 15:13:38 +0200231
232 if (omitted != 0)
233 {
234 char buf[100];
235
236 vim_snprintf(buf, 100, " - %d equal item%s omitted",
237 omitted, omitted == 1 ? "" : "s");
238 ga_concat(gap, (char_u *)buf);
239 }
240 }
241
242 if (did_copy)
243 {
244 clear_tv(exp_tv);
245 clear_tv(got_tv);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200246 }
247}
248
249 static int
250assert_equal_common(typval_T *argvars, assert_type_T atype)
251{
252 garray_T ga;
253
254 if (tv_equal(&argvars[0], &argvars[1], FALSE, FALSE)
255 != (atype == ASSERT_EQUAL))
256 {
257 prepare_assert_error(&ga);
258 fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
259 atype);
260 assert_error(&ga);
261 ga_clear(&ga);
262 return 1;
263 }
264 return 0;
265}
266
267 static int
268assert_match_common(typval_T *argvars, assert_type_T atype)
269{
270 garray_T ga;
271 char_u buf1[NUMBUFLEN];
272 char_u buf2[NUMBUFLEN];
Bram Moolenaar7177da92020-07-12 23:09:20 +0200273 int called_emsg_before = called_emsg;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200274 char_u *pat = tv_get_string_buf_chk(&argvars[0], buf1);
275 char_u *text = tv_get_string_buf_chk(&argvars[1], buf2);
276
Bram Moolenaar7177da92020-07-12 23:09:20 +0200277 if (called_emsg == called_emsg_before
278 && pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH))
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200279 {
280 prepare_assert_error(&ga);
281 fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
282 atype);
283 assert_error(&ga);
284 ga_clear(&ga);
285 return 1;
286 }
287 return 0;
288}
289
290/*
291 * Common for assert_true() and assert_false().
292 * Return non-zero for failure.
293 */
294 static int
295assert_bool(typval_T *argvars, int isTrue)
296{
297 int error = FALSE;
298 garray_T ga;
299
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100300 if (argvars[0].v_type == VAR_BOOL
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200301 && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE))
302 return 0;
303 if (argvars[0].v_type != VAR_NUMBER
304 || (tv_get_number_chk(&argvars[0], &error) == 0) == isTrue
305 || error)
306 {
307 prepare_assert_error(&ga);
308 fill_assert_error(&ga, &argvars[1],
309 (char_u *)(isTrue ? "True" : "False"),
310 NULL, &argvars[0], ASSERT_OTHER);
311 assert_error(&ga);
312 ga_clear(&ga);
313 return 1;
314 }
315 return 0;
316}
317
318 static void
319assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, char_u *cmd)
320{
321 char_u *tofree;
322 char_u numbuf[NUMBUFLEN];
323
324 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN)
325 {
326 ga_concat(gap, echo_string(&argvars[2], &tofree, numbuf, 0));
327 vim_free(tofree);
328 }
329 else
330 ga_concat(gap, cmd);
331}
332
333 static int
334assert_beeps(typval_T *argvars)
335{
336 char_u *cmd = tv_get_string_chk(&argvars[0]);
337 garray_T ga;
338 int ret = 0;
339
340 called_vim_beep = FALSE;
341 suppress_errthrow = TRUE;
342 emsg_silent = FALSE;
343 do_cmdline_cmd(cmd);
344 if (!called_vim_beep)
345 {
346 prepare_assert_error(&ga);
347 ga_concat(&ga, (char_u *)"command did not beep: ");
348 ga_concat(&ga, cmd);
349 assert_error(&ga);
350 ga_clear(&ga);
351 ret = 1;
352 }
353
354 suppress_errthrow = FALSE;
355 emsg_on_display = FALSE;
356 return ret;
357}
358
359/*
360 * "assert_beeps(cmd [, error])" function
361 */
362 void
363f_assert_beeps(typval_T *argvars, typval_T *rettv)
364{
365 rettv->vval.v_number = assert_beeps(argvars);
366}
367
368/*
369 * "assert_equal(expected, actual[, msg])" function
370 */
371 void
372f_assert_equal(typval_T *argvars, typval_T *rettv)
373{
374 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
375}
376
377 static int
378assert_equalfile(typval_T *argvars)
379{
380 char_u buf1[NUMBUFLEN];
381 char_u buf2[NUMBUFLEN];
Bram Moolenaar7177da92020-07-12 23:09:20 +0200382 int called_emsg_before = called_emsg;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200383 char_u *fname1 = tv_get_string_buf_chk(&argvars[0], buf1);
384 char_u *fname2 = tv_get_string_buf_chk(&argvars[1], buf2);
385 garray_T ga;
386 FILE *fd1;
387 FILE *fd2;
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200388 char line1[200];
389 char line2[200];
390 int lineidx = 0;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200391
Bram Moolenaar7177da92020-07-12 23:09:20 +0200392 if (called_emsg > called_emsg_before)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200393 return 0;
394
395 IObuff[0] = NUL;
396 fd1 = mch_fopen((char *)fname1, READBIN);
397 if (fd1 == NULL)
398 {
399 vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1);
400 }
401 else
402 {
403 fd2 = mch_fopen((char *)fname2, READBIN);
404 if (fd2 == NULL)
405 {
406 fclose(fd1);
407 vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2);
408 }
409 else
410 {
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200411 int c1, c2;
412 long count = 0;
413 long linecount = 1;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200414
415 for (;;)
416 {
417 c1 = fgetc(fd1);
418 c2 = fgetc(fd2);
419 if (c1 == EOF)
420 {
421 if (c2 != EOF)
422 STRCPY(IObuff, "first file is shorter");
423 break;
424 }
425 else if (c2 == EOF)
426 {
427 STRCPY(IObuff, "second file is shorter");
428 break;
429 }
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200430 else
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200431 {
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200432 line1[lineidx] = c1;
433 line2[lineidx] = c2;
434 ++lineidx;
435 if (c1 != c2)
436 {
437 vim_snprintf((char *)IObuff, IOSIZE,
438 "difference at byte %ld, line %ld",
439 count, linecount);
440 break;
441 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200442 }
443 ++count;
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200444 if (c1 == NL)
445 {
446 ++linecount;
447 lineidx = 0;
448 }
449 else if (lineidx + 2 == (int)sizeof(line1))
450 {
451 mch_memmove(line1, line1 + 100, lineidx - 100);
452 mch_memmove(line2, line2 + 100, lineidx - 100);
453 lineidx -= 100;
454 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200455 }
456 fclose(fd1);
457 fclose(fd2);
458 }
459 }
460 if (IObuff[0] != NUL)
461 {
462 prepare_assert_error(&ga);
Bram Moolenaarfb517ba2020-06-03 19:55:35 +0200463 if (argvars[2].v_type != VAR_UNKNOWN)
464 {
465 char_u numbuf[NUMBUFLEN];
466 char_u *tofree;
467
468 ga_concat(&ga, echo_string(&argvars[2], &tofree, numbuf, 0));
469 vim_free(tofree);
470 ga_concat(&ga, (char_u *)": ");
471 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200472 ga_concat(&ga, IObuff);
Bram Moolenaar30cc44a2020-06-04 16:52:40 +0200473 if (lineidx > 0)
474 {
475 line1[lineidx] = NUL;
476 line2[lineidx] = NUL;
477 ga_concat(&ga, (char_u *)" after \"");
478 ga_concat(&ga, (char_u *)line1);
479 if (STRCMP(line1, line2) != 0)
480 {
481 ga_concat(&ga, (char_u *)"\" vs \"");
482 ga_concat(&ga, (char_u *)line2);
483 }
484 ga_concat(&ga, (char_u *)"\"");
485 }
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200486 assert_error(&ga);
487 ga_clear(&ga);
488 return 1;
489 }
490 return 0;
491}
492
493/*
Bram Moolenaarfb517ba2020-06-03 19:55:35 +0200494 * "assert_equalfile(fname-one, fname-two[, msg])" function
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200495 */
496 void
497f_assert_equalfile(typval_T *argvars, typval_T *rettv)
498{
499 rettv->vval.v_number = assert_equalfile(argvars);
500}
501
502/*
503 * "assert_notequal(expected, actual[, msg])" function
504 */
505 void
506f_assert_notequal(typval_T *argvars, typval_T *rettv)
507{
508 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
509}
510
511/*
512 * "assert_exception(string[, msg])" function
513 */
514 void
515f_assert_exception(typval_T *argvars, typval_T *rettv)
516{
517 garray_T ga;
518 char_u *error = tv_get_string_chk(&argvars[0]);
519
520 if (*get_vim_var_str(VV_EXCEPTION) == NUL)
521 {
522 prepare_assert_error(&ga);
523 ga_concat(&ga, (char_u *)"v:exception is not set");
524 assert_error(&ga);
525 ga_clear(&ga);
526 rettv->vval.v_number = 1;
527 }
528 else if (error != NULL
529 && strstr((char *)get_vim_var_str(VV_EXCEPTION), (char *)error) == NULL)
530 {
531 prepare_assert_error(&ga);
532 fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
533 get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
534 assert_error(&ga);
535 ga_clear(&ga);
536 rettv->vval.v_number = 1;
537 }
538}
539
540/*
541 * "assert_fails(cmd [, error[, msg]])" function
542 */
543 void
544f_assert_fails(typval_T *argvars, typval_T *rettv)
545{
546 char_u *cmd = tv_get_string_chk(&argvars[0]);
547 garray_T ga;
548 int save_trylevel = trylevel;
Bram Moolenaar53989552019-12-23 22:59:18 +0100549 int called_emsg_before = called_emsg;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200550 int wrong_arg = FALSE;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200551
552 // trylevel must be zero for a ":throw" command to be considered failed
553 trylevel = 0;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200554 suppress_errthrow = TRUE;
555 emsg_silent = TRUE;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200556 emsg_assert_fails_used = TRUE;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200557
558 do_cmdline_cmd(cmd);
Bram Moolenaar53989552019-12-23 22:59:18 +0100559 if (called_emsg == called_emsg_before)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200560 {
561 prepare_assert_error(&ga);
562 ga_concat(&ga, (char_u *)"command did not fail: ");
563 assert_append_cmd_or_arg(&ga, argvars, cmd);
564 assert_error(&ga);
565 ga_clear(&ga);
566 rettv->vval.v_number = 1;
567 }
568 else if (argvars[1].v_type != VAR_UNKNOWN)
569 {
570 char_u buf[NUMBUFLEN];
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200571 char_u *expected;
572 int error_found = FALSE;
573 char_u *actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]"
574 : emsg_assert_fails_msg;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200575
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200576 if (argvars[1].v_type == VAR_STRING)
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200577 {
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200578 expected = tv_get_string_buf_chk(&argvars[1], buf);
579 error_found = expected == NULL
580 || strstr((char *)actual, (char *)expected) == NULL;
581 }
582 else if (argvars[1].v_type == VAR_LIST)
583 {
584 list_T *list = argvars[1].vval.v_list;
585 typval_T *tv;
586
587 if (list == NULL || list->lv_len < 1 || list->lv_len > 2)
588 {
589 wrong_arg = TRUE;
590 goto theend;
591 }
592 CHECK_LIST_MATERIALIZE(list);
593 tv = &list->lv_first->li_tv;
594 expected = tv_get_string_buf_chk(tv, buf);
595 if (!pattern_match(expected, actual, FALSE))
596 {
597 error_found = TRUE;
598 }
599 else if (list->lv_len == 2)
600 {
601 tv = &list->lv_u.mat.lv_last->li_tv;
602 actual = get_vim_var_str(VV_ERRMSG);
603 expected = tv_get_string_buf_chk(tv, buf);
604 if (!pattern_match(expected, actual, FALSE))
605 error_found = TRUE;
606 }
607 }
608 else
609 {
610 wrong_arg = TRUE;
611 goto theend;
612 }
613
614 if (error_found)
615 {
616 typval_T actual_tv;
617
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200618 prepare_assert_error(&ga);
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200619 actual_tv.v_type = VAR_STRING;
620 actual_tv.vval.v_string = actual;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200621 fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200622 &actual_tv, ASSERT_OTHER);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200623 ga_concat(&ga, (char_u *)": ");
624 assert_append_cmd_or_arg(&ga, argvars, cmd);
625 assert_error(&ga);
626 ga_clear(&ga);
627 rettv->vval.v_number = 1;
628 }
629 }
630
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200631theend:
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200632 trylevel = save_trylevel;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200633 suppress_errthrow = FALSE;
634 emsg_silent = FALSE;
635 emsg_on_display = FALSE;
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200636 emsg_assert_fails_used = FALSE;
637 VIM_CLEAR(emsg_assert_fails_msg);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200638 set_vim_var_string(VV_ERRMSG, NULL, 0);
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200639 if (wrong_arg)
640 emsg(_("E856: assert_fails() second argument must be a string or a list with one or two strings"));
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200641}
642
643/*
644 * "assert_false(actual[, msg])" function
645 */
646 void
647f_assert_false(typval_T *argvars, typval_T *rettv)
648{
649 rettv->vval.v_number = assert_bool(argvars, FALSE);
650}
651
652 static int
653assert_inrange(typval_T *argvars)
654{
655 garray_T ga;
656 int error = FALSE;
657 char_u *tofree;
658 char msg[200];
659 char_u numbuf[NUMBUFLEN];
660
661#ifdef FEAT_FLOAT
662 if (argvars[0].v_type == VAR_FLOAT
663 || argvars[1].v_type == VAR_FLOAT
664 || argvars[2].v_type == VAR_FLOAT)
665 {
666 float_T flower = tv_get_float(&argvars[0]);
667 float_T fupper = tv_get_float(&argvars[1]);
668 float_T factual = tv_get_float(&argvars[2]);
669
670 if (factual < flower || factual > fupper)
671 {
672 prepare_assert_error(&ga);
673 if (argvars[3].v_type != VAR_UNKNOWN)
674 {
675 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
676 vim_free(tofree);
677 }
678 else
679 {
680 vim_snprintf(msg, 200, "Expected range %g - %g, but got %g",
681 flower, fupper, factual);
682 ga_concat(&ga, (char_u *)msg);
683 }
684 assert_error(&ga);
685 ga_clear(&ga);
686 return 1;
687 }
688 }
689 else
690#endif
691 {
692 varnumber_T lower = tv_get_number_chk(&argvars[0], &error);
693 varnumber_T upper = tv_get_number_chk(&argvars[1], &error);
694 varnumber_T actual = tv_get_number_chk(&argvars[2], &error);
695
696 if (error)
697 return 0;
698 if (actual < lower || actual > upper)
699 {
700 prepare_assert_error(&ga);
701 if (argvars[3].v_type != VAR_UNKNOWN)
702 {
703 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0));
704 vim_free(tofree);
705 }
706 else
707 {
708 vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld",
709 (long)lower, (long)upper, (long)actual);
710 ga_concat(&ga, (char_u *)msg);
711 }
712 assert_error(&ga);
713 ga_clear(&ga);
714 return 1;
715 }
716 }
717 return 0;
718}
719
720/*
721 * "assert_inrange(lower, upper[, msg])" function
722 */
723 void
724f_assert_inrange(typval_T *argvars, typval_T *rettv)
725{
726 rettv->vval.v_number = assert_inrange(argvars);
727}
728
729/*
730 * "assert_match(pattern, actual[, msg])" function
731 */
732 void
733f_assert_match(typval_T *argvars, typval_T *rettv)
734{
735 rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
736}
737
738/*
739 * "assert_notmatch(pattern, actual[, msg])" function
740 */
741 void
742f_assert_notmatch(typval_T *argvars, typval_T *rettv)
743{
744 rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
745}
746
747/*
748 * "assert_report(msg)" function
749 */
750 void
751f_assert_report(typval_T *argvars, typval_T *rettv)
752{
753 garray_T ga;
754
755 prepare_assert_error(&ga);
756 ga_concat(&ga, tv_get_string(&argvars[0]));
757 assert_error(&ga);
758 ga_clear(&ga);
759 rettv->vval.v_number = 1;
760}
761
762/*
763 * "assert_true(actual[, msg])" function
764 */
765 void
766f_assert_true(typval_T *argvars, typval_T *rettv)
767{
768 rettv->vval.v_number = assert_bool(argvars, TRUE);
769}
770
771/*
772 * "test_alloc_fail(id, countdown, repeat)" function
773 */
774 void
775f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED)
776{
777 if (argvars[0].v_type != VAR_NUMBER
778 || argvars[0].vval.v_number <= 0
779 || argvars[1].v_type != VAR_NUMBER
780 || argvars[1].vval.v_number < 0
781 || argvars[2].v_type != VAR_NUMBER)
782 emsg(_(e_invarg));
783 else
784 {
785 alloc_fail_id = argvars[0].vval.v_number;
786 if (alloc_fail_id >= aid_last)
787 emsg(_(e_invarg));
788 alloc_fail_countdown = argvars[1].vval.v_number;
789 alloc_fail_repeat = argvars[2].vval.v_number;
790 did_outofmem_msg = FALSE;
791 }
792}
793
794/*
795 * "test_autochdir()"
796 */
797 void
798f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
799{
800#if defined(FEAT_AUTOCHDIR)
801 test_autochdir = TRUE;
802#endif
803}
804
805/*
806 * "test_feedinput()"
807 */
808 void
809f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED)
810{
811#ifdef USE_INPUT_BUF
812 char_u *val = tv_get_string_chk(&argvars[0]);
813
Bram Moolenaar272ca952020-01-28 20:49:11 +0100814# ifdef VIMDLL
815 // this doesn't work in the console
816 if (!gui.in_use)
817 return;
818# endif
819
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200820 if (val != NULL)
821 {
822 trash_input_buf();
823 add_to_input_buf_csi(val, (int)STRLEN(val));
824 }
825#endif
826}
827
828/*
829 * "test_getvalue({name})" function
830 */
831 void
832f_test_getvalue(typval_T *argvars, typval_T *rettv)
833{
834 if (argvars[0].v_type != VAR_STRING)
835 emsg(_(e_invarg));
836 else
837 {
838 char_u *name = tv_get_string(&argvars[0]);
839
840 if (STRCMP(name, (char_u *)"need_fileinfo") == 0)
841 rettv->vval.v_number = need_fileinfo;
842 else
843 semsg(_(e_invarg2), name);
844 }
845}
846
847/*
848 * "test_option_not_set({name})" function
849 */
850 void
851f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED)
852{
853 char_u *name = (char_u *)"";
854
855 if (argvars[0].v_type != VAR_STRING)
856 emsg(_(e_invarg));
857 else
858 {
859 name = tv_get_string(&argvars[0]);
860 if (reset_option_was_set(name) == FAIL)
861 semsg(_(e_invarg2), name);
862 }
863}
864
865/*
866 * "test_override({name}, {val})" function
867 */
868 void
869f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
870{
871 char_u *name = (char_u *)"";
872 int val;
873 static int save_starting = -1;
874
875 if (argvars[0].v_type != VAR_STRING
876 || (argvars[1].v_type) != VAR_NUMBER)
877 emsg(_(e_invarg));
878 else
879 {
880 name = tv_get_string(&argvars[0]);
881 val = (int)tv_get_number(&argvars[1]);
882
883 if (STRCMP(name, (char_u *)"redraw") == 0)
884 disable_redraw_for_testing = val;
885 else if (STRCMP(name, (char_u *)"redraw_flag") == 0)
886 ignore_redraw_flag_for_testing = val;
887 else if (STRCMP(name, (char_u *)"char_avail") == 0)
888 disable_char_avail_for_testing = val;
889 else if (STRCMP(name, (char_u *)"starting") == 0)
890 {
891 if (val)
892 {
893 if (save_starting < 0)
894 save_starting = starting;
895 starting = 0;
896 }
897 else
898 {
899 starting = save_starting;
900 save_starting = -1;
901 }
902 }
903 else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
904 nfa_fail_for_testing = val;
905 else if (STRCMP(name, (char_u *)"no_query_mouse") == 0)
906 no_query_mouse_for_testing = val;
907 else if (STRCMP(name, (char_u *)"no_wait_return") == 0)
908 no_wait_return = val;
Bram Moolenaarb340bae2020-06-15 19:51:56 +0200909 else if (STRCMP(name, (char_u *)"ui_delay") == 0)
910 ui_delay_for_testing = val;
Bram Moolenaar0c0eddd2020-06-13 15:47:25 +0200911 else if (STRCMP(name, (char_u *)"term_props") == 0)
912 reset_term_props_on_termresponse = val;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200913 else if (STRCMP(name, (char_u *)"ALL") == 0)
914 {
915 disable_char_avail_for_testing = FALSE;
916 disable_redraw_for_testing = FALSE;
917 ignore_redraw_flag_for_testing = FALSE;
918 nfa_fail_for_testing = FALSE;
919 no_query_mouse_for_testing = FALSE;
Bram Moolenaarb340bae2020-06-15 19:51:56 +0200920 ui_delay_for_testing = 0;
Bram Moolenaar0c0eddd2020-06-13 15:47:25 +0200921 reset_term_props_on_termresponse = FALSE;
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200922 if (save_starting >= 0)
923 {
924 starting = save_starting;
925 save_starting = -1;
926 }
927 }
928 else
929 semsg(_(e_invarg2), name);
930 }
931}
932
933/*
934 * "test_refcount({expr})" function
935 */
936 void
937f_test_refcount(typval_T *argvars, typval_T *rettv)
938{
939 int retval = -1;
940
941 switch (argvars[0].v_type)
942 {
943 case VAR_UNKNOWN:
Bram Moolenaar4c683752020-04-05 21:38:23 +0200944 case VAR_ANY:
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100945 case VAR_VOID:
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200946 case VAR_NUMBER:
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100947 case VAR_BOOL:
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100948 case VAR_FLOAT:
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200949 case VAR_SPECIAL:
950 case VAR_STRING:
951 break;
952 case VAR_JOB:
953#ifdef FEAT_JOB_CHANNEL
954 if (argvars[0].vval.v_job != NULL)
955 retval = argvars[0].vval.v_job->jv_refcount - 1;
956#endif
957 break;
958 case VAR_CHANNEL:
959#ifdef FEAT_JOB_CHANNEL
960 if (argvars[0].vval.v_channel != NULL)
961 retval = argvars[0].vval.v_channel->ch_refcount - 1;
962#endif
963 break;
964 case VAR_FUNC:
965 if (argvars[0].vval.v_string != NULL)
966 {
967 ufunc_T *fp;
968
Bram Moolenaar4c17ad92020-04-27 22:47:51 +0200969 fp = find_func(argvars[0].vval.v_string, FALSE, NULL);
Bram Moolenaarecaa70e2019-07-14 14:55:39 +0200970 if (fp != NULL)
971 retval = fp->uf_refcount;
972 }
973 break;
974 case VAR_PARTIAL:
975 if (argvars[0].vval.v_partial != NULL)
976 retval = argvars[0].vval.v_partial->pt_refcount - 1;
977 break;
978 case VAR_BLOB:
979 if (argvars[0].vval.v_blob != NULL)
980 retval = argvars[0].vval.v_blob->bv_refcount - 1;
981 break;
982 case VAR_LIST:
983 if (argvars[0].vval.v_list != NULL)
984 retval = argvars[0].vval.v_list->lv_refcount - 1;
985 break;
986 case VAR_DICT:
987 if (argvars[0].vval.v_dict != NULL)
988 retval = argvars[0].vval.v_dict->dv_refcount - 1;
989 break;
990 }
991
992 rettv->v_type = VAR_NUMBER;
993 rettv->vval.v_number = retval;
994
995}
996
997/*
998 * "test_garbagecollect_now()" function
999 */
1000 void
1001f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1002{
Bram Moolenaar0d6f5d92019-12-05 21:33:15 +01001003 // This is dangerous, any Lists and Dicts used internally may be freed
1004 // while still in use.
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001005 garbage_collect(TRUE);
1006}
1007
1008/*
1009 * "test_garbagecollect_soon()" function
1010 */
1011 void
1012f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
1013{
1014 may_garbage_collect = TRUE;
1015}
1016
1017/*
1018 * "test_ignore_error()" function
1019 */
1020 void
1021f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED)
1022{
1023 ignore_error_for_testing(tv_get_string(&argvars[0]));
1024}
1025
1026 void
1027f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv)
1028{
1029 rettv->v_type = VAR_BLOB;
1030 rettv->vval.v_blob = NULL;
1031}
1032
1033#ifdef FEAT_JOB_CHANNEL
1034 void
1035f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv)
1036{
1037 rettv->v_type = VAR_CHANNEL;
1038 rettv->vval.v_channel = NULL;
1039}
1040#endif
1041
1042 void
1043f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv)
1044{
1045 rettv_dict_set(rettv, NULL);
1046}
1047
1048#ifdef FEAT_JOB_CHANNEL
1049 void
1050f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv)
1051{
1052 rettv->v_type = VAR_JOB;
1053 rettv->vval.v_job = NULL;
1054}
1055#endif
1056
1057 void
1058f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv)
1059{
1060 rettv_list_set(rettv, NULL);
1061}
1062
1063 void
Bram Moolenaare69f6d02020-04-01 22:11:01 +02001064f_test_null_function(typval_T *argvars UNUSED, typval_T *rettv)
1065{
1066 rettv->v_type = VAR_FUNC;
1067 rettv->vval.v_string = NULL;
1068}
1069
1070 void
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001071f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv)
1072{
1073 rettv->v_type = VAR_PARTIAL;
1074 rettv->vval.v_partial = NULL;
1075}
1076
1077 void
1078f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv)
1079{
1080 rettv->v_type = VAR_STRING;
1081 rettv->vval.v_string = NULL;
1082}
1083
Bram Moolenaar8ed04582020-02-22 19:07:28 +01001084 void
1085f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv)
1086{
1087 rettv->v_type = VAR_UNKNOWN;
1088}
1089
1090 void
1091f_test_void(typval_T *argvars UNUSED, typval_T *rettv)
1092{
1093 rettv->v_type = VAR_VOID;
1094}
1095
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001096#ifdef FEAT_GUI
1097 void
1098f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED)
1099{
1100 char_u *which;
1101 long value;
1102 int dragging;
1103 scrollbar_T *sb = NULL;
1104
1105 if (argvars[0].v_type != VAR_STRING
1106 || (argvars[1].v_type) != VAR_NUMBER
1107 || (argvars[2].v_type) != VAR_NUMBER)
1108 {
1109 emsg(_(e_invarg));
1110 return;
1111 }
1112 which = tv_get_string(&argvars[0]);
1113 value = tv_get_number(&argvars[1]);
1114 dragging = tv_get_number(&argvars[2]);
1115
1116 if (STRCMP(which, "left") == 0)
1117 sb = &curwin->w_scrollbars[SBAR_LEFT];
1118 else if (STRCMP(which, "right") == 0)
1119 sb = &curwin->w_scrollbars[SBAR_RIGHT];
1120 else if (STRCMP(which, "hor") == 0)
1121 sb = &gui.bottom_sbar;
1122 if (sb == NULL)
1123 {
1124 semsg(_(e_invarg2), which);
1125 return;
1126 }
1127 gui_drag_scrollbar(sb, value, dragging);
1128# ifndef USE_ON_FLY_SCROLL
1129 // need to loop through normal_cmd() to handle the scroll events
1130 exec_normal(FALSE, TRUE, FALSE);
1131# endif
1132}
1133#endif
1134
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001135 void
1136f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED)
1137{
1138 mouse_row = (time_t)tv_get_number(&argvars[0]) - 1;
1139 mouse_col = (time_t)tv_get_number(&argvars[1]) - 1;
1140}
Bram Moolenaarecaa70e2019-07-14 14:55:39 +02001141
1142 void
1143f_test_settime(typval_T *argvars, typval_T *rettv UNUSED)
1144{
1145 time_for_testing = (time_t)tv_get_number(&argvars[0]);
1146}
1147
1148
1149#endif // defined(FEAT_EVAL)