1 /* vi:set ts=8 sts=4 sw=4 noet: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10 /* 11 * testing.c: Support for tests. 12 */ 13 14 #include "vim.h" 15 16 #if defined(FEAT_EVAL) || defined(PROTO) 17 18 /* 19 * Prepare "gap" for an assert error and add the sourcing position. 20 */ 21 static void 22 prepare_assert_error(garray_T *gap) 23 { 24 char buf[NUMBUFLEN]; 25 char_u *sname = estack_sfile(ESTACK_NONE); 26 27 ga_init2(gap, 1, 100); 28 if (sname != NULL) 29 { 30 ga_concat(gap, sname); 31 if (SOURCING_LNUM > 0) 32 ga_concat(gap, (char_u *)" "); 33 } 34 if (SOURCING_LNUM > 0) 35 { 36 sprintf(buf, "line %ld", (long)SOURCING_LNUM); 37 ga_concat(gap, (char_u *)buf); 38 } 39 if (sname != NULL || SOURCING_LNUM > 0) 40 ga_concat(gap, (char_u *)": "); 41 vim_free(sname); 42 } 43 44 /* 45 * Append "p[clen]" to "gap", escaping unprintable characters. 46 * Changes NL to \n, CR to \r, etc. 47 */ 48 static void 49 ga_concat_esc(garray_T *gap, char_u *p, int clen) 50 { 51 char_u buf[NUMBUFLEN]; 52 53 if (clen > 1) 54 { 55 mch_memmove(buf, p, clen); 56 buf[clen] = NUL; 57 ga_concat(gap, buf); 58 } 59 else switch (*p) 60 { 61 case BS: ga_concat(gap, (char_u *)"\\b"); break; 62 case ESC: ga_concat(gap, (char_u *)"\\e"); break; 63 case FF: ga_concat(gap, (char_u *)"\\f"); break; 64 case NL: ga_concat(gap, (char_u *)"\\n"); break; 65 case TAB: ga_concat(gap, (char_u *)"\\t"); break; 66 case CAR: ga_concat(gap, (char_u *)"\\r"); break; 67 case '\\': ga_concat(gap, (char_u *)"\\\\"); break; 68 default: 69 if (*p < ' ' || *p == 0x7f) 70 { 71 vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); 72 ga_concat(gap, buf); 73 } 74 else 75 ga_append(gap, *p); 76 break; 77 } 78 } 79 80 /* 81 * Append "str" to "gap", escaping unprintable characters. 82 * Changes NL to \n, CR to \r, etc. 83 */ 84 static void 85 ga_concat_shorten_esc(garray_T *gap, char_u *str) 86 { 87 char_u *p; 88 char_u *s; 89 int c; 90 int clen; 91 char_u buf[NUMBUFLEN]; 92 int same_len; 93 94 if (str == NULL) 95 { 96 ga_concat(gap, (char_u *)"NULL"); 97 return; 98 } 99 100 for (p = str; *p != NUL; ++p) 101 { 102 same_len = 1; 103 s = p; 104 c = mb_ptr2char_adv(&s); 105 clen = s - p; 106 while (*s != NUL && c == mb_ptr2char(s)) 107 { 108 ++same_len; 109 s += clen; 110 } 111 if (same_len > 20) 112 { 113 ga_concat(gap, (char_u *)"\\["); 114 ga_concat_esc(gap, p, clen); 115 ga_concat(gap, (char_u *)" occurs "); 116 vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len); 117 ga_concat(gap, buf); 118 ga_concat(gap, (char_u *)" times]"); 119 p = s - 1; 120 } 121 else 122 ga_concat_esc(gap, p, clen); 123 } 124 } 125 126 /* 127 * Fill "gap" with information about an assert error. 128 */ 129 static void 130 fill_assert_error( 131 garray_T *gap, 132 typval_T *opt_msg_tv, 133 char_u *exp_str, 134 typval_T *exp_tv_arg, 135 typval_T *got_tv_arg, 136 assert_type_T atype) 137 { 138 char_u numbuf[NUMBUFLEN]; 139 char_u *tofree; 140 typval_T *exp_tv = exp_tv_arg; 141 typval_T *got_tv = got_tv_arg; 142 int did_copy = FALSE; 143 int omitted = 0; 144 145 if (opt_msg_tv->v_type != VAR_UNKNOWN 146 && !(opt_msg_tv->v_type == VAR_STRING 147 && (opt_msg_tv->vval.v_string == NULL 148 || *opt_msg_tv->vval.v_string == NUL))) 149 { 150 ga_concat(gap, echo_string(opt_msg_tv, &tofree, numbuf, 0)); 151 vim_free(tofree); 152 ga_concat(gap, (char_u *)": "); 153 } 154 155 if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) 156 ga_concat(gap, (char_u *)"Pattern "); 157 else if (atype == ASSERT_NOTEQUAL) 158 ga_concat(gap, (char_u *)"Expected not equal to "); 159 else 160 ga_concat(gap, (char_u *)"Expected "); 161 if (exp_str == NULL) 162 { 163 // When comparing dictionaries, drop the items that are equal, so that 164 // it's a lot easier to see what differs. 165 if (atype != ASSERT_NOTEQUAL 166 && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT 167 && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL) 168 { 169 dict_T *exp_d = exp_tv->vval.v_dict; 170 dict_T *got_d = got_tv->vval.v_dict; 171 hashitem_T *hi; 172 dictitem_T *item2; 173 int todo; 174 175 did_copy = TRUE; 176 exp_tv->vval.v_dict = dict_alloc(); 177 got_tv->vval.v_dict = dict_alloc(); 178 if (exp_tv->vval.v_dict == NULL || got_tv->vval.v_dict == NULL) 179 return; 180 181 todo = (int)exp_d->dv_hashtab.ht_used; 182 for (hi = exp_d->dv_hashtab.ht_array; todo > 0; ++hi) 183 { 184 if (!HASHITEM_EMPTY(hi)) 185 { 186 item2 = dict_find(got_d, hi->hi_key, -1); 187 if (item2 == NULL || !tv_equal(&HI2DI(hi)->di_tv, 188 &item2->di_tv, FALSE, FALSE)) 189 { 190 // item of exp_d not present in got_d or values differ. 191 dict_add_tv(exp_tv->vval.v_dict, 192 (char *)hi->hi_key, &HI2DI(hi)->di_tv); 193 if (item2 != NULL) 194 dict_add_tv(got_tv->vval.v_dict, 195 (char *)hi->hi_key, &item2->di_tv); 196 } 197 else 198 ++omitted; 199 --todo; 200 } 201 } 202 203 // Add items only present in got_d. 204 todo = (int)got_d->dv_hashtab.ht_used; 205 for (hi = got_d->dv_hashtab.ht_array; todo > 0; ++hi) 206 { 207 if (!HASHITEM_EMPTY(hi)) 208 { 209 item2 = dict_find(exp_d, hi->hi_key, -1); 210 if (item2 == NULL) 211 // item of got_d not present in exp_d 212 dict_add_tv(got_tv->vval.v_dict, 213 (char *)hi->hi_key, &HI2DI(hi)->di_tv); 214 --todo; 215 } 216 } 217 } 218 219 ga_concat_shorten_esc(gap, tv2string(exp_tv, &tofree, numbuf, 0)); 220 vim_free(tofree); 221 } 222 else 223 { 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 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 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 = tv_get_string_buf_chk(&argvars[0], buf1); 282 char_u *text = tv_get_string_buf_chk(&argvars[1], buf2); 283 284 if (called_emsg == called_emsg_before 285 && pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH)) 286 { 287 prepare_assert_error(&ga); 288 fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], 289 atype); 290 assert_error(&ga); 291 ga_clear(&ga); 292 return 1; 293 } 294 return 0; 295 } 296 297 /* 298 * Common for assert_true() and assert_false(). 299 * Return non-zero for failure. 300 */ 301 static int 302 assert_bool(typval_T *argvars, int isTrue) 303 { 304 int error = FALSE; 305 garray_T ga; 306 307 if (argvars[0].v_type == VAR_BOOL 308 && argvars[0].vval.v_number == (isTrue ? VVAL_TRUE : VVAL_FALSE)) 309 return 0; 310 if (argvars[0].v_type != VAR_NUMBER 311 || (tv_get_number_chk(&argvars[0], &error) == 0) == isTrue 312 || error) 313 { 314 prepare_assert_error(&ga); 315 fill_assert_error(&ga, &argvars[1], 316 (char_u *)(isTrue ? "True" : "False"), 317 NULL, &argvars[0], ASSERT_OTHER); 318 assert_error(&ga); 319 ga_clear(&ga); 320 return 1; 321 } 322 return 0; 323 } 324 325 static void 326 assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, char_u *cmd) 327 { 328 char_u *tofree; 329 char_u numbuf[NUMBUFLEN]; 330 331 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) 332 { 333 ga_concat(gap, echo_string(&argvars[2], &tofree, numbuf, 0)); 334 vim_free(tofree); 335 } 336 else 337 ga_concat(gap, cmd); 338 } 339 340 static int 341 assert_beeps(typval_T *argvars, int no_beep) 342 { 343 char_u *cmd; 344 garray_T ga; 345 int ret = 0; 346 347 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 348 return 0; 349 350 cmd = tv_get_string_chk(&argvars[0]); 351 called_vim_beep = FALSE; 352 suppress_errthrow = TRUE; 353 emsg_silent = FALSE; 354 do_cmdline_cmd(cmd); 355 if (no_beep ? called_vim_beep : !called_vim_beep) 356 { 357 prepare_assert_error(&ga); 358 if (no_beep) 359 ga_concat(&ga, (char_u *)"command did beep: "); 360 else 361 ga_concat(&ga, (char_u *)"command did not beep: "); 362 ga_concat(&ga, cmd); 363 assert_error(&ga); 364 ga_clear(&ga); 365 ret = 1; 366 } 367 368 suppress_errthrow = FALSE; 369 emsg_on_display = FALSE; 370 return ret; 371 } 372 373 /* 374 * "assert_beeps(cmd)" function 375 */ 376 void 377 f_assert_beeps(typval_T *argvars, typval_T *rettv) 378 { 379 rettv->vval.v_number = assert_beeps(argvars, FALSE); 380 } 381 382 /* 383 * "assert_nobeep(cmd)" function 384 */ 385 void 386 f_assert_nobeep(typval_T *argvars, typval_T *rettv) 387 { 388 rettv->vval.v_number = assert_beeps(argvars, TRUE); 389 } 390 391 /* 392 * "assert_equal(expected, actual[, msg])" function 393 */ 394 void 395 f_assert_equal(typval_T *argvars, typval_T *rettv) 396 { 397 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL); 398 } 399 400 static int 401 assert_equalfile(typval_T *argvars) 402 { 403 char_u buf1[NUMBUFLEN]; 404 char_u buf2[NUMBUFLEN]; 405 int called_emsg_before = called_emsg; 406 char_u *fname1 = tv_get_string_buf_chk(&argvars[0], buf1); 407 char_u *fname2 = tv_get_string_buf_chk(&argvars[1], buf2); 408 garray_T ga; 409 FILE *fd1; 410 FILE *fd2; 411 char line1[200]; 412 char line2[200]; 413 int lineidx = 0; 414 415 if (called_emsg > called_emsg_before) 416 return 0; 417 418 IObuff[0] = NUL; 419 fd1 = mch_fopen((char *)fname1, READBIN); 420 if (fd1 == NULL) 421 { 422 vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1); 423 } 424 else 425 { 426 fd2 = mch_fopen((char *)fname2, READBIN); 427 if (fd2 == NULL) 428 { 429 fclose(fd1); 430 vim_snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2); 431 } 432 else 433 { 434 int c1, c2; 435 long count = 0; 436 long linecount = 1; 437 438 for (;;) 439 { 440 c1 = fgetc(fd1); 441 c2 = fgetc(fd2); 442 if (c1 == EOF) 443 { 444 if (c2 != EOF) 445 STRCPY(IObuff, "first file is shorter"); 446 break; 447 } 448 else if (c2 == EOF) 449 { 450 STRCPY(IObuff, "second file is shorter"); 451 break; 452 } 453 else 454 { 455 line1[lineidx] = c1; 456 line2[lineidx] = c2; 457 ++lineidx; 458 if (c1 != c2) 459 { 460 vim_snprintf((char *)IObuff, IOSIZE, 461 "difference at byte %ld, line %ld", 462 count, linecount); 463 break; 464 } 465 } 466 ++count; 467 if (c1 == NL) 468 { 469 ++linecount; 470 lineidx = 0; 471 } 472 else if (lineidx + 2 == (int)sizeof(line1)) 473 { 474 mch_memmove(line1, line1 + 100, lineidx - 100); 475 mch_memmove(line2, line2 + 100, lineidx - 100); 476 lineidx -= 100; 477 } 478 } 479 fclose(fd1); 480 fclose(fd2); 481 } 482 } 483 if (IObuff[0] != NUL) 484 { 485 prepare_assert_error(&ga); 486 if (argvars[2].v_type != VAR_UNKNOWN) 487 { 488 char_u numbuf[NUMBUFLEN]; 489 char_u *tofree; 490 491 ga_concat(&ga, echo_string(&argvars[2], &tofree, numbuf, 0)); 492 vim_free(tofree); 493 ga_concat(&ga, (char_u *)": "); 494 } 495 ga_concat(&ga, IObuff); 496 if (lineidx > 0) 497 { 498 line1[lineidx] = NUL; 499 line2[lineidx] = NUL; 500 ga_concat(&ga, (char_u *)" after \""); 501 ga_concat(&ga, (char_u *)line1); 502 if (STRCMP(line1, line2) != 0) 503 { 504 ga_concat(&ga, (char_u *)"\" vs \""); 505 ga_concat(&ga, (char_u *)line2); 506 } 507 ga_concat(&ga, (char_u *)"\""); 508 } 509 assert_error(&ga); 510 ga_clear(&ga); 511 return 1; 512 } 513 return 0; 514 } 515 516 /* 517 * "assert_equalfile(fname-one, fname-two[, msg])" function 518 */ 519 void 520 f_assert_equalfile(typval_T *argvars, typval_T *rettv) 521 { 522 rettv->vval.v_number = assert_equalfile(argvars); 523 } 524 525 /* 526 * "assert_notequal(expected, actual[, msg])" function 527 */ 528 void 529 f_assert_notequal(typval_T *argvars, typval_T *rettv) 530 { 531 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL); 532 } 533 534 /* 535 * "assert_exception(string[, msg])" function 536 */ 537 void 538 f_assert_exception(typval_T *argvars, typval_T *rettv) 539 { 540 garray_T ga; 541 char_u *error = tv_get_string_chk(&argvars[0]); 542 543 if (*get_vim_var_str(VV_EXCEPTION) == NUL) 544 { 545 prepare_assert_error(&ga); 546 ga_concat(&ga, (char_u *)"v:exception is not set"); 547 assert_error(&ga); 548 ga_clear(&ga); 549 rettv->vval.v_number = 1; 550 } 551 else if (error != NULL 552 && strstr((char *)get_vim_var_str(VV_EXCEPTION), (char *)error) == NULL) 553 { 554 prepare_assert_error(&ga); 555 fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], 556 get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER); 557 assert_error(&ga); 558 ga_clear(&ga); 559 rettv->vval.v_number = 1; 560 } 561 } 562 563 /* 564 * "assert_fails(cmd [, error[, msg]])" function 565 */ 566 void 567 f_assert_fails(typval_T *argvars, typval_T *rettv) 568 { 569 char_u *cmd = tv_get_string_chk(&argvars[0]); 570 garray_T ga; 571 int save_trylevel = trylevel; 572 int called_emsg_before = called_emsg; 573 char *wrong_arg_msg = NULL; 574 575 // trylevel must be zero for a ":throw" command to be considered failed 576 trylevel = 0; 577 suppress_errthrow = TRUE; 578 in_assert_fails = TRUE; 579 580 do_cmdline_cmd(cmd); 581 if (called_emsg == called_emsg_before) 582 { 583 prepare_assert_error(&ga); 584 ga_concat(&ga, (char_u *)"command did not fail: "); 585 assert_append_cmd_or_arg(&ga, argvars, cmd); 586 assert_error(&ga); 587 ga_clear(&ga); 588 rettv->vval.v_number = 1; 589 } 590 else if (argvars[1].v_type != VAR_UNKNOWN) 591 { 592 char_u buf[NUMBUFLEN]; 593 char_u *expected; 594 char_u *expected_str = NULL; 595 int error_found = FALSE; 596 int error_found_index = 1; 597 char_u *actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]" 598 : emsg_assert_fails_msg; 599 600 if (argvars[1].v_type == VAR_STRING) 601 { 602 expected = tv_get_string_buf_chk(&argvars[1], buf); 603 error_found = expected == NULL 604 || strstr((char *)actual, (char *)expected) == NULL; 605 } 606 else if (argvars[1].v_type == VAR_LIST) 607 { 608 list_T *list = argvars[1].vval.v_list; 609 typval_T *tv; 610 611 if (list == NULL || list->lv_len < 1 || list->lv_len > 2) 612 { 613 wrong_arg_msg = e_assert_fails_second_arg; 614 goto theend; 615 } 616 CHECK_LIST_MATERIALIZE(list); 617 tv = &list->lv_first->li_tv; 618 expected = tv_get_string_buf_chk(tv, buf); 619 if (!pattern_match(expected, actual, FALSE)) 620 { 621 error_found = TRUE; 622 expected_str = expected; 623 } 624 else if (list->lv_len == 2) 625 { 626 tv = &list->lv_u.mat.lv_last->li_tv; 627 actual = get_vim_var_str(VV_ERRMSG); 628 expected = tv_get_string_buf_chk(tv, buf); 629 if (!pattern_match(expected, actual, FALSE)) 630 { 631 error_found = TRUE; 632 expected_str = expected; 633 } 634 } 635 } 636 else 637 { 638 wrong_arg_msg = e_assert_fails_second_arg; 639 goto theend; 640 } 641 642 if (!error_found && argvars[2].v_type != VAR_UNKNOWN 643 && argvars[3].v_type != VAR_UNKNOWN) 644 { 645 if (argvars[3].v_type != VAR_NUMBER) 646 { 647 wrong_arg_msg = e_assert_fails_fourth_argument; 648 goto theend; 649 } 650 else if (argvars[3].vval.v_number >= 0 651 && argvars[3].vval.v_number != emsg_assert_fails_lnum) 652 { 653 error_found = TRUE; 654 error_found_index = 3; 655 } 656 if (!error_found && argvars[4].v_type != VAR_UNKNOWN) 657 { 658 if (argvars[4].v_type != VAR_STRING) 659 { 660 wrong_arg_msg = e_assert_fails_fifth_argument; 661 goto theend; 662 } 663 else if (argvars[4].vval.v_string != NULL 664 && !pattern_match(argvars[4].vval.v_string, 665 emsg_assert_fails_context, FALSE)) 666 { 667 error_found = TRUE; 668 error_found_index = 4; 669 } 670 } 671 } 672 673 if (error_found) 674 { 675 typval_T actual_tv; 676 677 prepare_assert_error(&ga); 678 if (error_found_index == 3) 679 { 680 actual_tv.v_type = VAR_NUMBER; 681 actual_tv.vval.v_number = emsg_assert_fails_lnum; 682 } 683 else if (error_found_index == 4) 684 { 685 actual_tv.v_type = VAR_STRING; 686 actual_tv.vval.v_string = emsg_assert_fails_context; 687 } 688 else 689 { 690 actual_tv.v_type = VAR_STRING; 691 actual_tv.vval.v_string = actual; 692 } 693 fill_assert_error(&ga, &argvars[2], expected_str, 694 &argvars[error_found_index], &actual_tv, ASSERT_OTHER); 695 ga_concat(&ga, (char_u *)": "); 696 assert_append_cmd_or_arg(&ga, argvars, cmd); 697 assert_error(&ga); 698 ga_clear(&ga); 699 rettv->vval.v_number = 1; 700 } 701 } 702 703 theend: 704 trylevel = save_trylevel; 705 suppress_errthrow = FALSE; 706 in_assert_fails = FALSE; 707 did_emsg = FALSE; 708 msg_col = 0; 709 need_wait_return = FALSE; 710 emsg_on_display = FALSE; 711 msg_scrolled = 0; 712 lines_left = Rows; 713 VIM_CLEAR(emsg_assert_fails_msg); 714 set_vim_var_string(VV_ERRMSG, NULL, 0); 715 if (wrong_arg_msg != NULL) 716 emsg(_(wrong_arg_msg)); 717 } 718 719 /* 720 * "assert_false(actual[, msg])" function 721 */ 722 void 723 f_assert_false(typval_T *argvars, typval_T *rettv) 724 { 725 rettv->vval.v_number = assert_bool(argvars, FALSE); 726 } 727 728 static int 729 assert_inrange(typval_T *argvars) 730 { 731 garray_T ga; 732 int error = FALSE; 733 char_u *tofree; 734 char msg[200]; 735 char_u numbuf[NUMBUFLEN]; 736 737 #ifdef FEAT_FLOAT 738 if (argvars[0].v_type == VAR_FLOAT 739 || argvars[1].v_type == VAR_FLOAT 740 || argvars[2].v_type == VAR_FLOAT) 741 { 742 float_T flower = tv_get_float(&argvars[0]); 743 float_T fupper = tv_get_float(&argvars[1]); 744 float_T factual = tv_get_float(&argvars[2]); 745 746 if (factual < flower || factual > fupper) 747 { 748 prepare_assert_error(&ga); 749 if (argvars[3].v_type != VAR_UNKNOWN) 750 { 751 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0)); 752 vim_free(tofree); 753 } 754 else 755 { 756 vim_snprintf(msg, 200, "Expected range %g - %g, but got %g", 757 flower, fupper, factual); 758 ga_concat(&ga, (char_u *)msg); 759 } 760 assert_error(&ga); 761 ga_clear(&ga); 762 return 1; 763 } 764 } 765 else 766 #endif 767 { 768 varnumber_T lower = tv_get_number_chk(&argvars[0], &error); 769 varnumber_T upper = tv_get_number_chk(&argvars[1], &error); 770 varnumber_T actual = tv_get_number_chk(&argvars[2], &error); 771 772 if (error) 773 return 0; 774 if (actual < lower || actual > upper) 775 { 776 prepare_assert_error(&ga); 777 if (argvars[3].v_type != VAR_UNKNOWN) 778 { 779 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0)); 780 vim_free(tofree); 781 } 782 else 783 { 784 vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld", 785 (long)lower, (long)upper, (long)actual); 786 ga_concat(&ga, (char_u *)msg); 787 } 788 assert_error(&ga); 789 ga_clear(&ga); 790 return 1; 791 } 792 } 793 return 0; 794 } 795 796 /* 797 * "assert_inrange(lower, upper[, msg])" function 798 */ 799 void 800 f_assert_inrange(typval_T *argvars, typval_T *rettv) 801 { 802 rettv->vval.v_number = assert_inrange(argvars); 803 } 804 805 /* 806 * "assert_match(pattern, actual[, msg])" function 807 */ 808 void 809 f_assert_match(typval_T *argvars, typval_T *rettv) 810 { 811 rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH); 812 } 813 814 /* 815 * "assert_notmatch(pattern, actual[, msg])" function 816 */ 817 void 818 f_assert_notmatch(typval_T *argvars, typval_T *rettv) 819 { 820 rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH); 821 } 822 823 /* 824 * "assert_report(msg)" function 825 */ 826 void 827 f_assert_report(typval_T *argvars, typval_T *rettv) 828 { 829 garray_T ga; 830 831 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 832 return; 833 834 prepare_assert_error(&ga); 835 ga_concat(&ga, tv_get_string(&argvars[0])); 836 assert_error(&ga); 837 ga_clear(&ga); 838 rettv->vval.v_number = 1; 839 } 840 841 /* 842 * "assert_true(actual[, msg])" function 843 */ 844 void 845 f_assert_true(typval_T *argvars, typval_T *rettv) 846 { 847 rettv->vval.v_number = assert_bool(argvars, TRUE); 848 } 849 850 /* 851 * "test_alloc_fail(id, countdown, repeat)" function 852 */ 853 void 854 f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED) 855 { 856 if (argvars[0].v_type != VAR_NUMBER 857 || argvars[0].vval.v_number <= 0 858 || argvars[1].v_type != VAR_NUMBER 859 || argvars[1].vval.v_number < 0 860 || argvars[2].v_type != VAR_NUMBER) 861 emsg(_(e_invarg)); 862 else 863 { 864 alloc_fail_id = argvars[0].vval.v_number; 865 if (alloc_fail_id >= aid_last) 866 emsg(_(e_invarg)); 867 alloc_fail_countdown = argvars[1].vval.v_number; 868 alloc_fail_repeat = argvars[2].vval.v_number; 869 did_outofmem_msg = FALSE; 870 } 871 } 872 873 /* 874 * "test_autochdir()" 875 */ 876 void 877 f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 878 { 879 #if defined(FEAT_AUTOCHDIR) 880 test_autochdir = TRUE; 881 #endif 882 } 883 884 /* 885 * "test_feedinput()" 886 */ 887 void 888 f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED) 889 { 890 #ifdef USE_INPUT_BUF 891 char_u *val = tv_get_string_chk(&argvars[0]); 892 893 # ifdef VIMDLL 894 // this doesn't work in the console 895 if (!gui.in_use) 896 return; 897 # endif 898 899 if (val != NULL) 900 { 901 trash_input_buf(); 902 add_to_input_buf_csi(val, (int)STRLEN(val)); 903 } 904 #endif 905 } 906 907 /* 908 * "test_getvalue({name})" function 909 */ 910 void 911 f_test_getvalue(typval_T *argvars, typval_T *rettv) 912 { 913 if (argvars[0].v_type != VAR_STRING) 914 emsg(_(e_invarg)); 915 else 916 { 917 char_u *name = tv_get_string(&argvars[0]); 918 919 if (STRCMP(name, (char_u *)"need_fileinfo") == 0) 920 rettv->vval.v_number = need_fileinfo; 921 else 922 semsg(_(e_invarg2), name); 923 } 924 } 925 926 /* 927 * "test_option_not_set({name})" function 928 */ 929 void 930 f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED) 931 { 932 char_u *name = (char_u *)""; 933 934 if (argvars[0].v_type != VAR_STRING) 935 emsg(_(e_invarg)); 936 else 937 { 938 name = tv_get_string(&argvars[0]); 939 if (reset_option_was_set(name) == FAIL) 940 semsg(_(e_invarg2), name); 941 } 942 } 943 944 /* 945 * "test_override({name}, {val})" function 946 */ 947 void 948 f_test_override(typval_T *argvars, typval_T *rettv UNUSED) 949 { 950 char_u *name = (char_u *)""; 951 int val; 952 static int save_starting = -1; 953 954 if (in_vim9script() 955 && (check_for_string_arg(argvars, 0) == FAIL 956 || check_for_number_arg(argvars, 1) == FAIL)) 957 return; 958 959 if (argvars[0].v_type != VAR_STRING 960 || (argvars[1].v_type) != VAR_NUMBER) 961 emsg(_(e_invarg)); 962 else 963 { 964 name = tv_get_string(&argvars[0]); 965 val = (int)tv_get_number(&argvars[1]); 966 967 if (STRCMP(name, (char_u *)"redraw") == 0) 968 disable_redraw_for_testing = val; 969 else if (STRCMP(name, (char_u *)"redraw_flag") == 0) 970 ignore_redraw_flag_for_testing = val; 971 else if (STRCMP(name, (char_u *)"char_avail") == 0) 972 disable_char_avail_for_testing = val; 973 else if (STRCMP(name, (char_u *)"starting") == 0) 974 { 975 if (val) 976 { 977 if (save_starting < 0) 978 save_starting = starting; 979 starting = 0; 980 } 981 else 982 { 983 starting = save_starting; 984 save_starting = -1; 985 } 986 } 987 else if (STRCMP(name, (char_u *)"nfa_fail") == 0) 988 nfa_fail_for_testing = val; 989 else if (STRCMP(name, (char_u *)"no_query_mouse") == 0) 990 no_query_mouse_for_testing = val; 991 else if (STRCMP(name, (char_u *)"no_wait_return") == 0) 992 no_wait_return = val; 993 else if (STRCMP(name, (char_u *)"ui_delay") == 0) 994 ui_delay_for_testing = val; 995 else if (STRCMP(name, (char_u *)"term_props") == 0) 996 reset_term_props_on_termresponse = val; 997 else if (STRCMP(name, (char_u *)"uptime") == 0) 998 override_sysinfo_uptime = val; 999 else if (STRCMP(name, (char_u *)"ALL") == 0) 1000 { 1001 disable_char_avail_for_testing = FALSE; 1002 disable_redraw_for_testing = FALSE; 1003 ignore_redraw_flag_for_testing = FALSE; 1004 nfa_fail_for_testing = FALSE; 1005 no_query_mouse_for_testing = FALSE; 1006 ui_delay_for_testing = 0; 1007 reset_term_props_on_termresponse = FALSE; 1008 override_sysinfo_uptime = -1; 1009 if (save_starting >= 0) 1010 { 1011 starting = save_starting; 1012 save_starting = -1; 1013 } 1014 } 1015 else 1016 semsg(_(e_invarg2), name); 1017 } 1018 } 1019 1020 /* 1021 * "test_refcount({expr})" function 1022 */ 1023 void 1024 f_test_refcount(typval_T *argvars, typval_T *rettv) 1025 { 1026 int retval = -1; 1027 1028 switch (argvars[0].v_type) 1029 { 1030 case VAR_UNKNOWN: 1031 case VAR_ANY: 1032 case VAR_VOID: 1033 case VAR_NUMBER: 1034 case VAR_BOOL: 1035 case VAR_FLOAT: 1036 case VAR_SPECIAL: 1037 case VAR_STRING: 1038 case VAR_INSTR: 1039 break; 1040 case VAR_JOB: 1041 #ifdef FEAT_JOB_CHANNEL 1042 if (argvars[0].vval.v_job != NULL) 1043 retval = argvars[0].vval.v_job->jv_refcount - 1; 1044 #endif 1045 break; 1046 case VAR_CHANNEL: 1047 #ifdef FEAT_JOB_CHANNEL 1048 if (argvars[0].vval.v_channel != NULL) 1049 retval = argvars[0].vval.v_channel->ch_refcount - 1; 1050 #endif 1051 break; 1052 case VAR_FUNC: 1053 if (argvars[0].vval.v_string != NULL) 1054 { 1055 ufunc_T *fp; 1056 1057 fp = find_func(argvars[0].vval.v_string, FALSE, NULL); 1058 if (fp != NULL) 1059 retval = fp->uf_refcount; 1060 } 1061 break; 1062 case VAR_PARTIAL: 1063 if (argvars[0].vval.v_partial != NULL) 1064 retval = argvars[0].vval.v_partial->pt_refcount - 1; 1065 break; 1066 case VAR_BLOB: 1067 if (argvars[0].vval.v_blob != NULL) 1068 retval = argvars[0].vval.v_blob->bv_refcount - 1; 1069 break; 1070 case VAR_LIST: 1071 if (argvars[0].vval.v_list != NULL) 1072 retval = argvars[0].vval.v_list->lv_refcount - 1; 1073 break; 1074 case VAR_DICT: 1075 if (argvars[0].vval.v_dict != NULL) 1076 retval = argvars[0].vval.v_dict->dv_refcount - 1; 1077 break; 1078 } 1079 1080 rettv->v_type = VAR_NUMBER; 1081 rettv->vval.v_number = retval; 1082 1083 } 1084 1085 /* 1086 * "test_garbagecollect_now()" function 1087 */ 1088 void 1089 f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1090 { 1091 // This is dangerous, any Lists and Dicts used internally may be freed 1092 // while still in use. 1093 garbage_collect(TRUE); 1094 } 1095 1096 /* 1097 * "test_garbagecollect_soon()" function 1098 */ 1099 void 1100 f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1101 { 1102 may_garbage_collect = TRUE; 1103 } 1104 1105 /* 1106 * "test_ignore_error()" function 1107 */ 1108 void 1109 f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED) 1110 { 1111 if (argvars[0].v_type != VAR_STRING) 1112 emsg(_(e_invarg)); 1113 else 1114 ignore_error_for_testing(tv_get_string(&argvars[0])); 1115 } 1116 1117 void 1118 f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv) 1119 { 1120 rettv->v_type = VAR_BLOB; 1121 rettv->vval.v_blob = NULL; 1122 } 1123 1124 #ifdef FEAT_JOB_CHANNEL 1125 void 1126 f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) 1127 { 1128 rettv->v_type = VAR_CHANNEL; 1129 rettv->vval.v_channel = NULL; 1130 } 1131 #endif 1132 1133 void 1134 f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) 1135 { 1136 rettv_dict_set(rettv, NULL); 1137 } 1138 1139 #ifdef FEAT_JOB_CHANNEL 1140 void 1141 f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) 1142 { 1143 rettv->v_type = VAR_JOB; 1144 rettv->vval.v_job = NULL; 1145 } 1146 #endif 1147 1148 void 1149 f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) 1150 { 1151 rettv_list_set(rettv, NULL); 1152 } 1153 1154 void 1155 f_test_null_function(typval_T *argvars UNUSED, typval_T *rettv) 1156 { 1157 rettv->v_type = VAR_FUNC; 1158 rettv->vval.v_string = NULL; 1159 } 1160 1161 void 1162 f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv) 1163 { 1164 rettv->v_type = VAR_PARTIAL; 1165 rettv->vval.v_partial = NULL; 1166 } 1167 1168 void 1169 f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv) 1170 { 1171 rettv->v_type = VAR_STRING; 1172 rettv->vval.v_string = NULL; 1173 } 1174 1175 void 1176 f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv) 1177 { 1178 rettv->v_type = VAR_UNKNOWN; 1179 } 1180 1181 void 1182 f_test_void(typval_T *argvars UNUSED, typval_T *rettv) 1183 { 1184 rettv->v_type = VAR_VOID; 1185 } 1186 1187 #ifdef FEAT_GUI 1188 void 1189 f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED) 1190 { 1191 char_u *which; 1192 long value; 1193 int dragging; 1194 scrollbar_T *sb = NULL; 1195 1196 if (check_for_string_arg(argvars, 0) == FAIL 1197 || check_for_number_arg(argvars, 1) == FAIL 1198 || check_for_number_arg(argvars, 2) == FAIL) 1199 return; 1200 1201 if (argvars[0].v_type != VAR_STRING 1202 || (argvars[1].v_type) != VAR_NUMBER 1203 || (argvars[2].v_type) != VAR_NUMBER) 1204 { 1205 emsg(_(e_invarg)); 1206 return; 1207 } 1208 which = tv_get_string(&argvars[0]); 1209 value = tv_get_number(&argvars[1]); 1210 dragging = tv_get_number(&argvars[2]); 1211 1212 if (STRCMP(which, "left") == 0) 1213 sb = &curwin->w_scrollbars[SBAR_LEFT]; 1214 else if (STRCMP(which, "right") == 0) 1215 sb = &curwin->w_scrollbars[SBAR_RIGHT]; 1216 else if (STRCMP(which, "hor") == 0) 1217 sb = &gui.bottom_sbar; 1218 if (sb == NULL) 1219 { 1220 semsg(_(e_invarg2), which); 1221 return; 1222 } 1223 gui_drag_scrollbar(sb, value, dragging); 1224 # ifndef USE_ON_FLY_SCROLL 1225 // need to loop through normal_cmd() to handle the scroll events 1226 exec_normal(FALSE, TRUE, FALSE); 1227 # endif 1228 } 1229 #endif 1230 1231 void 1232 f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED) 1233 { 1234 if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type) != VAR_NUMBER) 1235 { 1236 emsg(_(e_invarg)); 1237 return; 1238 } 1239 1240 mouse_row = (time_t)tv_get_number(&argvars[0]) - 1; 1241 mouse_col = (time_t)tv_get_number(&argvars[1]) - 1; 1242 } 1243 1244 void 1245 f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1246 { 1247 # ifdef FEAT_GUI 1248 int button; 1249 int row; 1250 int col; 1251 int repeated_click; 1252 int_u mods; 1253 1254 if (check_for_number_arg(argvars, 0) == FAIL 1255 || check_for_number_arg(argvars, 1) == FAIL 1256 || check_for_number_arg(argvars, 2) == FAIL 1257 || check_for_number_arg(argvars, 3) == FAIL 1258 || check_for_number_arg(argvars, 4) == FAIL) 1259 return; 1260 1261 button = tv_get_number(&argvars[0]); 1262 row = tv_get_number(&argvars[1]); 1263 col = tv_get_number(&argvars[2]); 1264 repeated_click = tv_get_number(&argvars[3]); 1265 mods = tv_get_number(&argvars[4]); 1266 1267 gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods); 1268 # endif 1269 } 1270 1271 void 1272 f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) 1273 { 1274 time_for_testing = (time_t)tv_get_number(&argvars[0]); 1275 } 1276 1277 void 1278 f_test_gui_drop_files(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1279 { 1280 #if defined(HAVE_DROP_FILE) 1281 int row; 1282 int col; 1283 int_u mods; 1284 char_u **fnames; 1285 int count = 0; 1286 list_T *l; 1287 listitem_T *li; 1288 1289 if (check_for_list_arg(argvars, 0) == FAIL 1290 || check_for_number_arg(argvars, 1) == FAIL 1291 || check_for_number_arg(argvars, 2) == FAIL 1292 || check_for_number_arg(argvars, 3) == FAIL) 1293 return; 1294 1295 row = tv_get_number(&argvars[1]); 1296 col = tv_get_number(&argvars[2]); 1297 mods = tv_get_number(&argvars[3]); 1298 1299 l = argvars[0].vval.v_list; 1300 if (list_len(l) == 0) 1301 return; 1302 1303 fnames = ALLOC_MULT(char_u *, list_len(l)); 1304 if (fnames == NULL) 1305 return; 1306 1307 FOR_ALL_LIST_ITEMS(l, li) 1308 { 1309 // ignore non-string items 1310 if (li->li_tv.v_type != VAR_STRING) 1311 continue; 1312 1313 fnames[count] = vim_strsave(li->li_tv.vval.v_string); 1314 if (fnames[count] == NULL) 1315 { 1316 while (--count >= 0) 1317 vim_free(fnames[count]); 1318 vim_free(fnames); 1319 return; 1320 } 1321 count++; 1322 } 1323 1324 if (count > 0) 1325 gui_handle_drop(TEXT_X(col - 1), TEXT_Y(row - 1), mods, fnames, count); 1326 else 1327 vim_free(fnames); 1328 # endif 1329 } 1330 1331 #endif // defined(FEAT_EVAL) 1332