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; 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1211 f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) 1212 { 1213 rettv_list_set(rettv, NULL); 1214 } 1215 1216 void 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 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 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 1238 f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv) 1239 { 1240 rettv->v_type = VAR_UNKNOWN; 1241 } 1242 1243 void 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 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 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 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 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 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