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; 570 garray_T ga; 571 int save_trylevel = trylevel; 572 int called_emsg_before = called_emsg; 573 char *wrong_arg_msg = NULL; 574 575 if (check_for_string_or_number_arg(argvars, 0) == FAIL 576 || check_for_opt_string_or_list_arg(argvars, 1) == FAIL 577 || (argvars[1].v_type != VAR_UNKNOWN 578 && (argvars[2].v_type != VAR_UNKNOWN 579 && (check_for_opt_number_arg(argvars, 3) == FAIL 580 || (argvars[3].v_type != VAR_UNKNOWN 581 && check_for_opt_string_arg(argvars, 4) == FAIL))))) 582 return; 583 584 cmd = tv_get_string_chk(&argvars[0]); 585 586 // trylevel must be zero for a ":throw" command to be considered failed 587 trylevel = 0; 588 suppress_errthrow = TRUE; 589 in_assert_fails = TRUE; 590 591 do_cmdline_cmd(cmd); 592 if (called_emsg == called_emsg_before) 593 { 594 prepare_assert_error(&ga); 595 ga_concat(&ga, (char_u *)"command did not fail: "); 596 assert_append_cmd_or_arg(&ga, argvars, cmd); 597 assert_error(&ga); 598 ga_clear(&ga); 599 rettv->vval.v_number = 1; 600 } 601 else if (argvars[1].v_type != VAR_UNKNOWN) 602 { 603 char_u buf[NUMBUFLEN]; 604 char_u *expected; 605 char_u *expected_str = NULL; 606 int error_found = FALSE; 607 int error_found_index = 1; 608 char_u *actual = emsg_assert_fails_msg == NULL ? (char_u *)"[unknown]" 609 : emsg_assert_fails_msg; 610 611 if (argvars[1].v_type == VAR_STRING) 612 { 613 expected = tv_get_string_buf_chk(&argvars[1], buf); 614 error_found = expected == NULL 615 || strstr((char *)actual, (char *)expected) == NULL; 616 } 617 else if (argvars[1].v_type == VAR_LIST) 618 { 619 list_T *list = argvars[1].vval.v_list; 620 typval_T *tv; 621 622 if (list == NULL || list->lv_len < 1 || list->lv_len > 2) 623 { 624 wrong_arg_msg = e_assert_fails_second_arg; 625 goto theend; 626 } 627 CHECK_LIST_MATERIALIZE(list); 628 tv = &list->lv_first->li_tv; 629 expected = tv_get_string_buf_chk(tv, buf); 630 if (!pattern_match(expected, actual, FALSE)) 631 { 632 error_found = TRUE; 633 expected_str = expected; 634 } 635 else if (list->lv_len == 2) 636 { 637 tv = &list->lv_u.mat.lv_last->li_tv; 638 actual = get_vim_var_str(VV_ERRMSG); 639 expected = tv_get_string_buf_chk(tv, buf); 640 if (!pattern_match(expected, actual, FALSE)) 641 { 642 error_found = TRUE; 643 expected_str = expected; 644 } 645 } 646 } 647 else 648 { 649 wrong_arg_msg = e_assert_fails_second_arg; 650 goto theend; 651 } 652 653 if (!error_found && argvars[2].v_type != VAR_UNKNOWN 654 && argvars[3].v_type != VAR_UNKNOWN) 655 { 656 if (argvars[3].v_type != VAR_NUMBER) 657 { 658 wrong_arg_msg = e_assert_fails_fourth_argument; 659 goto theend; 660 } 661 else if (argvars[3].vval.v_number >= 0 662 && argvars[3].vval.v_number != emsg_assert_fails_lnum) 663 { 664 error_found = TRUE; 665 error_found_index = 3; 666 } 667 if (!error_found && argvars[4].v_type != VAR_UNKNOWN) 668 { 669 if (argvars[4].v_type != VAR_STRING) 670 { 671 wrong_arg_msg = e_assert_fails_fifth_argument; 672 goto theend; 673 } 674 else if (argvars[4].vval.v_string != NULL 675 && !pattern_match(argvars[4].vval.v_string, 676 emsg_assert_fails_context, FALSE)) 677 { 678 error_found = TRUE; 679 error_found_index = 4; 680 } 681 } 682 } 683 684 if (error_found) 685 { 686 typval_T actual_tv; 687 688 prepare_assert_error(&ga); 689 if (error_found_index == 3) 690 { 691 actual_tv.v_type = VAR_NUMBER; 692 actual_tv.vval.v_number = emsg_assert_fails_lnum; 693 } 694 else if (error_found_index == 4) 695 { 696 actual_tv.v_type = VAR_STRING; 697 actual_tv.vval.v_string = emsg_assert_fails_context; 698 } 699 else 700 { 701 actual_tv.v_type = VAR_STRING; 702 actual_tv.vval.v_string = actual; 703 } 704 fill_assert_error(&ga, &argvars[2], expected_str, 705 &argvars[error_found_index], &actual_tv, ASSERT_OTHER); 706 ga_concat(&ga, (char_u *)": "); 707 assert_append_cmd_or_arg(&ga, argvars, cmd); 708 assert_error(&ga); 709 ga_clear(&ga); 710 rettv->vval.v_number = 1; 711 } 712 } 713 714 theend: 715 trylevel = save_trylevel; 716 suppress_errthrow = FALSE; 717 in_assert_fails = FALSE; 718 did_emsg = FALSE; 719 msg_col = 0; 720 need_wait_return = FALSE; 721 emsg_on_display = FALSE; 722 msg_scrolled = 0; 723 lines_left = Rows; 724 VIM_CLEAR(emsg_assert_fails_msg); 725 set_vim_var_string(VV_ERRMSG, NULL, 0); 726 if (wrong_arg_msg != NULL) 727 emsg(_(wrong_arg_msg)); 728 } 729 730 /* 731 * "assert_false(actual[, msg])" function 732 */ 733 void 734 f_assert_false(typval_T *argvars, typval_T *rettv) 735 { 736 rettv->vval.v_number = assert_bool(argvars, FALSE); 737 } 738 739 static int 740 assert_inrange(typval_T *argvars) 741 { 742 garray_T ga; 743 int error = FALSE; 744 char_u *tofree; 745 char msg[200]; 746 char_u numbuf[NUMBUFLEN]; 747 748 #ifdef FEAT_FLOAT 749 if (argvars[0].v_type == VAR_FLOAT 750 || argvars[1].v_type == VAR_FLOAT 751 || argvars[2].v_type == VAR_FLOAT) 752 { 753 float_T flower = tv_get_float(&argvars[0]); 754 float_T fupper = tv_get_float(&argvars[1]); 755 float_T factual = tv_get_float(&argvars[2]); 756 757 if (factual < flower || factual > fupper) 758 { 759 prepare_assert_error(&ga); 760 if (argvars[3].v_type != VAR_UNKNOWN) 761 { 762 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0)); 763 vim_free(tofree); 764 } 765 else 766 { 767 vim_snprintf(msg, 200, "Expected range %g - %g, but got %g", 768 flower, fupper, factual); 769 ga_concat(&ga, (char_u *)msg); 770 } 771 assert_error(&ga); 772 ga_clear(&ga); 773 return 1; 774 } 775 } 776 else 777 #endif 778 { 779 varnumber_T lower = tv_get_number_chk(&argvars[0], &error); 780 varnumber_T upper = tv_get_number_chk(&argvars[1], &error); 781 varnumber_T actual = tv_get_number_chk(&argvars[2], &error); 782 783 if (error) 784 return 0; 785 if (actual < lower || actual > upper) 786 { 787 prepare_assert_error(&ga); 788 if (argvars[3].v_type != VAR_UNKNOWN) 789 { 790 ga_concat(&ga, tv2string(&argvars[3], &tofree, numbuf, 0)); 791 vim_free(tofree); 792 } 793 else 794 { 795 vim_snprintf(msg, 200, "Expected range %ld - %ld, but got %ld", 796 (long)lower, (long)upper, (long)actual); 797 ga_concat(&ga, (char_u *)msg); 798 } 799 assert_error(&ga); 800 ga_clear(&ga); 801 return 1; 802 } 803 } 804 return 0; 805 } 806 807 /* 808 * "assert_inrange(lower, upper[, msg])" function 809 */ 810 void 811 f_assert_inrange(typval_T *argvars, typval_T *rettv) 812 { 813 if (check_for_float_or_nr_arg(argvars, 0) == FAIL 814 || check_for_float_or_nr_arg(argvars, 1) == FAIL 815 || check_for_float_or_nr_arg(argvars, 2) == FAIL 816 || check_for_opt_string_arg(argvars, 3) == FAIL) 817 return; 818 819 rettv->vval.v_number = assert_inrange(argvars); 820 } 821 822 /* 823 * "assert_match(pattern, actual[, msg])" function 824 */ 825 void 826 f_assert_match(typval_T *argvars, typval_T *rettv) 827 { 828 rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH); 829 } 830 831 /* 832 * "assert_notmatch(pattern, actual[, msg])" function 833 */ 834 void 835 f_assert_notmatch(typval_T *argvars, typval_T *rettv) 836 { 837 rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH); 838 } 839 840 /* 841 * "assert_report(msg)" function 842 */ 843 void 844 f_assert_report(typval_T *argvars, typval_T *rettv) 845 { 846 garray_T ga; 847 848 if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) 849 return; 850 851 prepare_assert_error(&ga); 852 ga_concat(&ga, tv_get_string(&argvars[0])); 853 assert_error(&ga); 854 ga_clear(&ga); 855 rettv->vval.v_number = 1; 856 } 857 858 /* 859 * "assert_true(actual[, msg])" function 860 */ 861 void 862 f_assert_true(typval_T *argvars, typval_T *rettv) 863 { 864 rettv->vval.v_number = assert_bool(argvars, TRUE); 865 } 866 867 /* 868 * "test_alloc_fail(id, countdown, repeat)" function 869 */ 870 void 871 f_test_alloc_fail(typval_T *argvars, typval_T *rettv UNUSED) 872 { 873 if (argvars[0].v_type != VAR_NUMBER 874 || argvars[0].vval.v_number <= 0 875 || argvars[1].v_type != VAR_NUMBER 876 || argvars[1].vval.v_number < 0 877 || argvars[2].v_type != VAR_NUMBER) 878 emsg(_(e_invarg)); 879 else 880 { 881 alloc_fail_id = argvars[0].vval.v_number; 882 if (alloc_fail_id >= aid_last) 883 emsg(_(e_invarg)); 884 alloc_fail_countdown = argvars[1].vval.v_number; 885 alloc_fail_repeat = argvars[2].vval.v_number; 886 did_outofmem_msg = FALSE; 887 } 888 } 889 890 /* 891 * "test_autochdir()" 892 */ 893 void 894 f_test_autochdir(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 895 { 896 #if defined(FEAT_AUTOCHDIR) 897 test_autochdir = TRUE; 898 #endif 899 } 900 901 /* 902 * "test_feedinput()" 903 */ 904 void 905 f_test_feedinput(typval_T *argvars, typval_T *rettv UNUSED) 906 { 907 #ifdef USE_INPUT_BUF 908 char_u *val = tv_get_string_chk(&argvars[0]); 909 910 # ifdef VIMDLL 911 // this doesn't work in the console 912 if (!gui.in_use) 913 return; 914 # endif 915 916 if (val != NULL) 917 { 918 trash_input_buf(); 919 add_to_input_buf_csi(val, (int)STRLEN(val)); 920 } 921 #endif 922 } 923 924 /* 925 * "test_getvalue({name})" function 926 */ 927 void 928 f_test_getvalue(typval_T *argvars, typval_T *rettv) 929 { 930 if (argvars[0].v_type != VAR_STRING) 931 emsg(_(e_invarg)); 932 else 933 { 934 char_u *name = tv_get_string(&argvars[0]); 935 936 if (STRCMP(name, (char_u *)"need_fileinfo") == 0) 937 rettv->vval.v_number = need_fileinfo; 938 else 939 semsg(_(e_invarg2), name); 940 } 941 } 942 943 /* 944 * "test_option_not_set({name})" function 945 */ 946 void 947 f_test_option_not_set(typval_T *argvars, typval_T *rettv UNUSED) 948 { 949 char_u *name = (char_u *)""; 950 951 if (argvars[0].v_type != VAR_STRING) 952 emsg(_(e_invarg)); 953 else 954 { 955 name = tv_get_string(&argvars[0]); 956 if (reset_option_was_set(name) == FAIL) 957 semsg(_(e_invarg2), name); 958 } 959 } 960 961 /* 962 * "test_override({name}, {val})" function 963 */ 964 void 965 f_test_override(typval_T *argvars, typval_T *rettv UNUSED) 966 { 967 char_u *name = (char_u *)""; 968 int val; 969 static int save_starting = -1; 970 971 if (in_vim9script() 972 && (check_for_string_arg(argvars, 0) == FAIL 973 || check_for_number_arg(argvars, 1) == FAIL)) 974 return; 975 976 if (argvars[0].v_type != VAR_STRING 977 || (argvars[1].v_type) != VAR_NUMBER) 978 emsg(_(e_invarg)); 979 else 980 { 981 name = tv_get_string(&argvars[0]); 982 val = (int)tv_get_number(&argvars[1]); 983 984 if (STRCMP(name, (char_u *)"redraw") == 0) 985 disable_redraw_for_testing = val; 986 else if (STRCMP(name, (char_u *)"redraw_flag") == 0) 987 ignore_redraw_flag_for_testing = val; 988 else if (STRCMP(name, (char_u *)"char_avail") == 0) 989 disable_char_avail_for_testing = val; 990 else if (STRCMP(name, (char_u *)"starting") == 0) 991 { 992 if (val) 993 { 994 if (save_starting < 0) 995 save_starting = starting; 996 starting = 0; 997 } 998 else 999 { 1000 starting = save_starting; 1001 save_starting = -1; 1002 } 1003 } 1004 else if (STRCMP(name, (char_u *)"nfa_fail") == 0) 1005 nfa_fail_for_testing = val; 1006 else if (STRCMP(name, (char_u *)"no_query_mouse") == 0) 1007 no_query_mouse_for_testing = val; 1008 else if (STRCMP(name, (char_u *)"no_wait_return") == 0) 1009 no_wait_return = val; 1010 else if (STRCMP(name, (char_u *)"ui_delay") == 0) 1011 ui_delay_for_testing = val; 1012 else if (STRCMP(name, (char_u *)"term_props") == 0) 1013 reset_term_props_on_termresponse = val; 1014 else if (STRCMP(name, (char_u *)"uptime") == 0) 1015 override_sysinfo_uptime = val; 1016 else if (STRCMP(name, (char_u *)"ALL") == 0) 1017 { 1018 disable_char_avail_for_testing = FALSE; 1019 disable_redraw_for_testing = FALSE; 1020 ignore_redraw_flag_for_testing = FALSE; 1021 nfa_fail_for_testing = FALSE; 1022 no_query_mouse_for_testing = FALSE; 1023 ui_delay_for_testing = 0; 1024 reset_term_props_on_termresponse = FALSE; 1025 override_sysinfo_uptime = -1; 1026 if (save_starting >= 0) 1027 { 1028 starting = save_starting; 1029 save_starting = -1; 1030 } 1031 } 1032 else 1033 semsg(_(e_invarg2), name); 1034 } 1035 } 1036 1037 /* 1038 * "test_refcount({expr})" function 1039 */ 1040 void 1041 f_test_refcount(typval_T *argvars, typval_T *rettv) 1042 { 1043 int retval = -1; 1044 1045 switch (argvars[0].v_type) 1046 { 1047 case VAR_UNKNOWN: 1048 case VAR_ANY: 1049 case VAR_VOID: 1050 case VAR_NUMBER: 1051 case VAR_BOOL: 1052 case VAR_FLOAT: 1053 case VAR_SPECIAL: 1054 case VAR_STRING: 1055 case VAR_INSTR: 1056 break; 1057 case VAR_JOB: 1058 #ifdef FEAT_JOB_CHANNEL 1059 if (argvars[0].vval.v_job != NULL) 1060 retval = argvars[0].vval.v_job->jv_refcount - 1; 1061 #endif 1062 break; 1063 case VAR_CHANNEL: 1064 #ifdef FEAT_JOB_CHANNEL 1065 if (argvars[0].vval.v_channel != NULL) 1066 retval = argvars[0].vval.v_channel->ch_refcount - 1; 1067 #endif 1068 break; 1069 case VAR_FUNC: 1070 if (argvars[0].vval.v_string != NULL) 1071 { 1072 ufunc_T *fp; 1073 1074 fp = find_func(argvars[0].vval.v_string, FALSE, NULL); 1075 if (fp != NULL) 1076 retval = fp->uf_refcount; 1077 } 1078 break; 1079 case VAR_PARTIAL: 1080 if (argvars[0].vval.v_partial != NULL) 1081 retval = argvars[0].vval.v_partial->pt_refcount - 1; 1082 break; 1083 case VAR_BLOB: 1084 if (argvars[0].vval.v_blob != NULL) 1085 retval = argvars[0].vval.v_blob->bv_refcount - 1; 1086 break; 1087 case VAR_LIST: 1088 if (argvars[0].vval.v_list != NULL) 1089 retval = argvars[0].vval.v_list->lv_refcount - 1; 1090 break; 1091 case VAR_DICT: 1092 if (argvars[0].vval.v_dict != NULL) 1093 retval = argvars[0].vval.v_dict->dv_refcount - 1; 1094 break; 1095 } 1096 1097 rettv->v_type = VAR_NUMBER; 1098 rettv->vval.v_number = retval; 1099 1100 } 1101 1102 /* 1103 * "test_garbagecollect_now()" function 1104 */ 1105 void 1106 f_test_garbagecollect_now(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1107 { 1108 // This is dangerous, any Lists and Dicts used internally may be freed 1109 // while still in use. 1110 garbage_collect(TRUE); 1111 } 1112 1113 /* 1114 * "test_garbagecollect_soon()" function 1115 */ 1116 void 1117 f_test_garbagecollect_soon(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1118 { 1119 may_garbage_collect = TRUE; 1120 } 1121 1122 /* 1123 * "test_ignore_error()" function 1124 */ 1125 void 1126 f_test_ignore_error(typval_T *argvars, typval_T *rettv UNUSED) 1127 { 1128 if (argvars[0].v_type != VAR_STRING) 1129 emsg(_(e_invarg)); 1130 else 1131 ignore_error_for_testing(tv_get_string(&argvars[0])); 1132 } 1133 1134 void 1135 f_test_null_blob(typval_T *argvars UNUSED, typval_T *rettv) 1136 { 1137 rettv->v_type = VAR_BLOB; 1138 rettv->vval.v_blob = NULL; 1139 } 1140 1141 #ifdef FEAT_JOB_CHANNEL 1142 void 1143 f_test_null_channel(typval_T *argvars UNUSED, typval_T *rettv) 1144 { 1145 rettv->v_type = VAR_CHANNEL; 1146 rettv->vval.v_channel = NULL; 1147 } 1148 #endif 1149 1150 void 1151 f_test_null_dict(typval_T *argvars UNUSED, typval_T *rettv) 1152 { 1153 rettv_dict_set(rettv, NULL); 1154 } 1155 1156 #ifdef FEAT_JOB_CHANNEL 1157 void 1158 f_test_null_job(typval_T *argvars UNUSED, typval_T *rettv) 1159 { 1160 rettv->v_type = VAR_JOB; 1161 rettv->vval.v_job = NULL; 1162 } 1163 #endif 1164 1165 void 1166 f_test_null_list(typval_T *argvars UNUSED, typval_T *rettv) 1167 { 1168 rettv_list_set(rettv, NULL); 1169 } 1170 1171 void 1172 f_test_null_function(typval_T *argvars UNUSED, typval_T *rettv) 1173 { 1174 rettv->v_type = VAR_FUNC; 1175 rettv->vval.v_string = NULL; 1176 } 1177 1178 void 1179 f_test_null_partial(typval_T *argvars UNUSED, typval_T *rettv) 1180 { 1181 rettv->v_type = VAR_PARTIAL; 1182 rettv->vval.v_partial = NULL; 1183 } 1184 1185 void 1186 f_test_null_string(typval_T *argvars UNUSED, typval_T *rettv) 1187 { 1188 rettv->v_type = VAR_STRING; 1189 rettv->vval.v_string = NULL; 1190 } 1191 1192 void 1193 f_test_unknown(typval_T *argvars UNUSED, typval_T *rettv) 1194 { 1195 rettv->v_type = VAR_UNKNOWN; 1196 } 1197 1198 void 1199 f_test_void(typval_T *argvars UNUSED, typval_T *rettv) 1200 { 1201 rettv->v_type = VAR_VOID; 1202 } 1203 1204 #ifdef FEAT_GUI 1205 void 1206 f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED) 1207 { 1208 char_u *which; 1209 long value; 1210 int dragging; 1211 scrollbar_T *sb = NULL; 1212 1213 if (check_for_string_arg(argvars, 0) == FAIL 1214 || check_for_number_arg(argvars, 1) == FAIL 1215 || check_for_number_arg(argvars, 2) == FAIL) 1216 return; 1217 1218 if (argvars[0].v_type != VAR_STRING 1219 || (argvars[1].v_type) != VAR_NUMBER 1220 || (argvars[2].v_type) != VAR_NUMBER) 1221 { 1222 emsg(_(e_invarg)); 1223 return; 1224 } 1225 which = tv_get_string(&argvars[0]); 1226 value = tv_get_number(&argvars[1]); 1227 dragging = tv_get_number(&argvars[2]); 1228 1229 if (STRCMP(which, "left") == 0) 1230 sb = &curwin->w_scrollbars[SBAR_LEFT]; 1231 else if (STRCMP(which, "right") == 0) 1232 sb = &curwin->w_scrollbars[SBAR_RIGHT]; 1233 else if (STRCMP(which, "hor") == 0) 1234 sb = &gui.bottom_sbar; 1235 if (sb == NULL) 1236 { 1237 semsg(_(e_invarg2), which); 1238 return; 1239 } 1240 gui_drag_scrollbar(sb, value, dragging); 1241 # ifndef USE_ON_FLY_SCROLL 1242 // need to loop through normal_cmd() to handle the scroll events 1243 exec_normal(FALSE, TRUE, FALSE); 1244 # endif 1245 } 1246 #endif 1247 1248 void 1249 f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED) 1250 { 1251 if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type) != VAR_NUMBER) 1252 { 1253 emsg(_(e_invarg)); 1254 return; 1255 } 1256 1257 mouse_row = (time_t)tv_get_number(&argvars[0]) - 1; 1258 mouse_col = (time_t)tv_get_number(&argvars[1]) - 1; 1259 } 1260 1261 void 1262 f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1263 { 1264 # ifdef FEAT_GUI 1265 int button; 1266 int row; 1267 int col; 1268 int repeated_click; 1269 int_u mods; 1270 1271 if (check_for_number_arg(argvars, 0) == FAIL 1272 || check_for_number_arg(argvars, 1) == FAIL 1273 || check_for_number_arg(argvars, 2) == FAIL 1274 || check_for_number_arg(argvars, 3) == FAIL 1275 || check_for_number_arg(argvars, 4) == FAIL) 1276 return; 1277 1278 button = tv_get_number(&argvars[0]); 1279 row = tv_get_number(&argvars[1]); 1280 col = tv_get_number(&argvars[2]); 1281 repeated_click = tv_get_number(&argvars[3]); 1282 mods = tv_get_number(&argvars[4]); 1283 1284 gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods); 1285 # endif 1286 } 1287 1288 void 1289 f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) 1290 { 1291 time_for_testing = (time_t)tv_get_number(&argvars[0]); 1292 } 1293 1294 void 1295 f_test_gui_drop_files(typval_T *argvars UNUSED, typval_T *rettv UNUSED) 1296 { 1297 #if defined(HAVE_DROP_FILE) 1298 int row; 1299 int col; 1300 int_u mods; 1301 char_u **fnames; 1302 int count = 0; 1303 list_T *l; 1304 listitem_T *li; 1305 1306 if (check_for_list_arg(argvars, 0) == FAIL 1307 || check_for_number_arg(argvars, 1) == FAIL 1308 || check_for_number_arg(argvars, 2) == FAIL 1309 || check_for_number_arg(argvars, 3) == FAIL) 1310 return; 1311 1312 row = tv_get_number(&argvars[1]); 1313 col = tv_get_number(&argvars[2]); 1314 mods = tv_get_number(&argvars[3]); 1315 1316 l = argvars[0].vval.v_list; 1317 if (list_len(l) == 0) 1318 return; 1319 1320 fnames = ALLOC_MULT(char_u *, list_len(l)); 1321 if (fnames == NULL) 1322 return; 1323 1324 FOR_ALL_LIST_ITEMS(l, li) 1325 { 1326 // ignore non-string items 1327 if (li->li_tv.v_type != VAR_STRING) 1328 continue; 1329 1330 fnames[count] = vim_strsave(li->li_tv.vval.v_string); 1331 if (fnames[count] == NULL) 1332 { 1333 while (--count >= 0) 1334 vim_free(fnames[count]); 1335 vim_free(fnames); 1336 return; 1337 } 1338 count++; 1339 } 1340 1341 if (count > 0) 1342 gui_handle_drop(TEXT_X(col - 1), TEXT_Y(row - 1), mods, fnames, count); 1343 else 1344 vim_free(fnames); 1345 # endif 1346 } 1347 1348 #endif // defined(FEAT_EVAL) 1349