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