testing.c (23717B)
1 // testing.c: Support for tests 2 3 #include <inttypes.h> 4 #include <stdbool.h> 5 #include <stddef.h> 6 #include <stdio.h> 7 #include <string.h> 8 9 #include "nvim/ascii_defs.h" 10 #include "nvim/errors.h" 11 #include "nvim/eval.h" 12 #include "nvim/eval/encode.h" 13 #include "nvim/eval/typval.h" 14 #include "nvim/eval/typval_defs.h" 15 #include "nvim/eval/vars.h" 16 #include "nvim/ex_docmd.h" 17 #include "nvim/garray.h" 18 #include "nvim/garray_defs.h" 19 #include "nvim/gettext_defs.h" 20 #include "nvim/globals.h" 21 #include "nvim/hashtab.h" 22 #include "nvim/hashtab_defs.h" 23 #include "nvim/macros_defs.h" 24 #include "nvim/mbyte.h" 25 #include "nvim/memory.h" 26 #include "nvim/message.h" 27 #include "nvim/os/fs.h" 28 #include "nvim/runtime.h" 29 #include "nvim/runtime_defs.h" 30 #include "nvim/strings.h" 31 #include "nvim/testing.h" 32 #include "nvim/types_defs.h" 33 #include "nvim/vim_defs.h" 34 35 /// Type of assert_* check being performed 36 typedef enum { 37 ASSERT_EQUAL, 38 ASSERT_NOTEQUAL, 39 ASSERT_MATCH, 40 ASSERT_NOTMATCH, 41 ASSERT_FAILS, 42 ASSERT_OTHER, 43 } assert_type_T; 44 45 #include "testing.c.generated.h" 46 47 static const char e_assert_fails_second_arg[] 48 = N_( 49 "E856: \"assert_fails()\" second argument must be a string or a list with one or two strings"); 50 static const char e_assert_fails_fourth_argument[] 51 = N_("E1115: \"assert_fails()\" fourth argument must be a number"); 52 static const char e_assert_fails_fifth_argument[] 53 = N_("E1116: \"assert_fails()\" fifth argument must be a string"); 54 static const char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[] 55 = N_("E1142: Calling test_garbagecollect_now() while v:testing is not set"); 56 57 /// Prepare "gap" for an assert error and add the sourcing position. 58 static void prepare_assert_error(garray_T *gap) 59 { 60 char *sname = estack_sfile(ESTACK_NONE); 61 62 ga_init(gap, 1, 100); 63 if (sname != NULL) { 64 ga_concat(gap, sname); 65 if (SOURCING_LNUM > 0) { 66 ga_concat(gap, " "); 67 } 68 } 69 if (SOURCING_LNUM > 0) { 70 char buf[NUMBUFLEN]; 71 size_t buflen = vim_snprintf_safelen(buf, ARRAY_SIZE(buf), 72 "line %" PRId64, (int64_t)SOURCING_LNUM); 73 ga_concat_len(gap, buf, buflen); 74 } 75 if (sname != NULL || SOURCING_LNUM > 0) { 76 GA_CONCAT_LITERAL(gap, ": "); 77 } 78 xfree(sname); 79 } 80 81 /// Append "p[clen]" to "gap", escaping unprintable characters. 82 /// Changes NL to \n, CR to \r, etc. 83 static void ga_concat_esc(garray_T *gap, const char *p, int clen) 84 FUNC_ATTR_NONNULL_ALL 85 { 86 char buf[NUMBUFLEN]; 87 88 if (clen > 1) { 89 memmove(buf, p, (size_t)clen); 90 buf[clen] = NUL; 91 ga_concat_len(gap, buf, (size_t)clen); 92 return; 93 } 94 95 switch (*p) { 96 case BS: 97 GA_CONCAT_LITERAL(gap, "\\b"); break; 98 case ESC: 99 GA_CONCAT_LITERAL(gap, "\\e"); break; 100 case FF: 101 GA_CONCAT_LITERAL(gap, "\\f"); break; 102 case NL: 103 GA_CONCAT_LITERAL(gap, "\\n"); break; 104 case TAB: 105 GA_CONCAT_LITERAL(gap, "\\t"); break; 106 case CAR: 107 GA_CONCAT_LITERAL(gap, "\\r"); break; 108 case '\\': 109 GA_CONCAT_LITERAL(gap, "\\\\"); break; 110 default: 111 if ((uint8_t)(*p) < ' ' || *p == 0x7f) { 112 size_t buflen = vim_snprintf_safelen(buf, NUMBUFLEN, "\\x%02x", *p); 113 ga_concat_len(gap, buf, buflen); 114 } else { 115 ga_append(gap, (uint8_t)(*p)); 116 } 117 break; 118 } 119 } 120 121 /// Append "str" to "gap", escaping unprintable characters. 122 /// Changes NL to \n, CR to \r, etc. 123 static void ga_concat_shorten_esc(garray_T *gap, const char *str) 124 FUNC_ATTR_NONNULL_ARG(1) 125 { 126 char buf[NUMBUFLEN]; 127 128 if (str == NULL) { 129 GA_CONCAT_LITERAL(gap, "NULL"); 130 return; 131 } 132 133 for (const char *p = str; *p != NUL;) { 134 int same_len = 1; 135 const char *s = p; 136 const int c = mb_cptr2char_adv(&s); 137 const int clen = (int)(s - p); 138 while (*s != NUL && c == utf_ptr2char(s)) { 139 same_len++; 140 s += clen; 141 } 142 if (same_len > 20) { 143 GA_CONCAT_LITERAL(gap, "\\["); 144 ga_concat_esc(gap, p, clen); 145 GA_CONCAT_LITERAL(gap, " occurs "); 146 size_t buflen = vim_snprintf_safelen(buf, NUMBUFLEN, "%d", same_len); 147 ga_concat_len(gap, buf, buflen); 148 GA_CONCAT_LITERAL(gap, " times]"); 149 p = s; 150 } else { 151 ga_concat_esc(gap, p, clen); 152 p += clen; 153 } 154 } 155 } 156 157 /// Fill "gap" with information about an assert error. 158 static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, const char *exp_str, 159 typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype) 160 { 161 typval_T *exp_tv = exp_tv_arg; 162 typval_T *got_tv = got_tv_arg; 163 bool did_copy = false; 164 int omitted = 0; 165 166 if (opt_msg_tv->v_type != VAR_UNKNOWN 167 && !(opt_msg_tv->v_type == VAR_STRING 168 && (opt_msg_tv->vval.v_string == NULL 169 || *opt_msg_tv->vval.v_string == NUL))) { 170 char *tofree = encode_tv2echo(opt_msg_tv, NULL); 171 ga_concat(gap, tofree); 172 xfree(tofree); 173 GA_CONCAT_LITERAL(gap, ": "); 174 } 175 176 if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { 177 GA_CONCAT_LITERAL(gap, "Pattern "); 178 } else if (atype == ASSERT_NOTEQUAL) { 179 GA_CONCAT_LITERAL(gap, "Expected not equal to "); 180 } else { 181 GA_CONCAT_LITERAL(gap, "Expected "); 182 } 183 184 if (exp_str == NULL) { 185 // When comparing dictionaries, drop the items that are equal, so that 186 // it's a lot easier to see what differs. 187 if (atype != ASSERT_NOTEQUAL 188 && exp_tv->v_type == VAR_DICT && got_tv->v_type == VAR_DICT 189 && exp_tv->vval.v_dict != NULL && got_tv->vval.v_dict != NULL) { 190 dict_T *exp_d = exp_tv->vval.v_dict; 191 dict_T *got_d = got_tv->vval.v_dict; 192 193 did_copy = true; 194 exp_tv->vval.v_dict = tv_dict_alloc(); 195 got_tv->vval.v_dict = tv_dict_alloc(); 196 197 int todo = (int)exp_d->dv_hashtab.ht_used; 198 for (const hashitem_T *hi = exp_d->dv_hashtab.ht_array; todo > 0; hi++) { 199 if (!HASHITEM_EMPTY(hi)) { 200 dictitem_T *item2 = tv_dict_find(got_d, hi->hi_key, -1); 201 if (item2 == NULL 202 || !tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &item2->di_tv, false)) { 203 // item of exp_d not present in got_d or values differ. 204 const size_t key_len = strlen(hi->hi_key); 205 tv_dict_add_tv(exp_tv->vval.v_dict, hi->hi_key, key_len, &TV_DICT_HI2DI(hi)->di_tv); 206 if (item2 != NULL) { 207 tv_dict_add_tv(got_tv->vval.v_dict, hi->hi_key, key_len, &item2->di_tv); 208 } 209 } else { 210 omitted++; 211 } 212 todo--; 213 } 214 } 215 216 // Add items only present in got_d. 217 todo = (int)got_d->dv_hashtab.ht_used; 218 for (const hashitem_T *hi = got_d->dv_hashtab.ht_array; todo > 0; hi++) { 219 if (!HASHITEM_EMPTY(hi)) { 220 dictitem_T *item2 = tv_dict_find(exp_d, hi->hi_key, -1); 221 if (item2 == NULL) { 222 // item of got_d not present in exp_d 223 const size_t key_len = strlen(hi->hi_key); 224 tv_dict_add_tv(got_tv->vval.v_dict, hi->hi_key, key_len, &TV_DICT_HI2DI(hi)->di_tv); 225 } 226 todo--; 227 } 228 } 229 } 230 231 char *tofree = encode_tv2string(exp_tv, NULL); 232 ga_concat_shorten_esc(gap, tofree); 233 xfree(tofree); 234 } else { 235 if (atype == ASSERT_FAILS) { 236 GA_CONCAT_LITERAL(gap, "'"); 237 } 238 ga_concat_shorten_esc(gap, exp_str); 239 if (atype == ASSERT_FAILS) { 240 GA_CONCAT_LITERAL(gap, "'"); 241 } 242 } 243 244 if (atype != ASSERT_NOTEQUAL) { 245 if (atype == ASSERT_MATCH) { 246 GA_CONCAT_LITERAL(gap, " does not match "); 247 } else if (atype == ASSERT_NOTMATCH) { 248 GA_CONCAT_LITERAL(gap, " does match "); 249 } else { 250 GA_CONCAT_LITERAL(gap, " but got "); 251 } 252 char *tofree = encode_tv2string(got_tv, NULL); 253 ga_concat_shorten_esc(gap, tofree); 254 xfree(tofree); 255 256 if (omitted != 0) { 257 char buf[100]; 258 size_t buflen = vim_snprintf_safelen(buf, sizeof(buf), 259 " - %d equal item%s omitted", 260 omitted, omitted == 1 ? "" : "s"); 261 ga_concat_len(gap, buf, buflen); 262 } 263 } 264 265 if (did_copy) { 266 tv_clear(exp_tv); 267 tv_clear(got_tv); 268 } 269 } 270 271 static int assert_equal_common(typval_T *argvars, assert_type_T atype) 272 FUNC_ATTR_NONNULL_ALL 273 { 274 garray_T ga; 275 276 if (tv_equal(&argvars[0], &argvars[1], false) != (atype == ASSERT_EQUAL)) { 277 prepare_assert_error(&ga); 278 fill_assert_error(&ga, &argvars[2], NULL, 279 &argvars[0], &argvars[1], atype); 280 assert_error(&ga); 281 ga_clear(&ga); 282 return 1; 283 } 284 return 0; 285 } 286 287 static int assert_match_common(typval_T *argvars, assert_type_T atype) 288 FUNC_ATTR_NONNULL_ALL 289 { 290 char buf1[NUMBUFLEN]; 291 char buf2[NUMBUFLEN]; 292 const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); 293 const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); 294 295 if (pat != NULL && text != NULL 296 && pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) { 297 garray_T ga; 298 prepare_assert_error(&ga); 299 fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); 300 assert_error(&ga); 301 ga_clear(&ga); 302 return 1; 303 } 304 return 0; 305 } 306 307 /// Common for assert_true() and assert_false(). 308 static int assert_bool(typval_T *argvars, bool is_true) 309 FUNC_ATTR_NONNULL_ALL 310 { 311 bool error = false; 312 garray_T ga; 313 314 if ((argvars[0].v_type != VAR_NUMBER 315 || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true 316 || error) 317 && (argvars[0].v_type != VAR_BOOL 318 || (argvars[0].vval.v_bool 319 != (BoolVarValue)(is_true 320 ? kBoolVarTrue 321 : kBoolVarFalse)))) { 322 prepare_assert_error(&ga); 323 fill_assert_error(&ga, &argvars[1], 324 is_true ? "True" : "False", 325 NULL, &argvars[0], ASSERT_OTHER); 326 assert_error(&ga); 327 ga_clear(&ga); 328 return 1; 329 } 330 return 0; 331 } 332 333 static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, const char *cmd) 334 FUNC_ATTR_NONNULL_ALL 335 { 336 if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { 337 char *const tofree = encode_tv2echo(&argvars[2], NULL); 338 ga_concat(gap, tofree); 339 xfree(tofree); 340 } else { 341 ga_concat(gap, cmd); 342 } 343 } 344 345 static int assert_beeps(typval_T *argvars, bool no_beep) 346 FUNC_ATTR_NONNULL_ALL 347 { 348 const char *const cmd = tv_get_string_chk(&argvars[0]); 349 int ret = 0; 350 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 garray_T ga; 357 prepare_assert_error(&ga); 358 if (no_beep) { 359 GA_CONCAT_LITERAL(&ga, "command did beep: "); 360 } else { 361 GA_CONCAT_LITERAL(&ga, "command did not beep: "); 362 } 363 ga_concat(&ga, cmd); 364 assert_error(&ga); 365 ga_clear(&ga); 366 ret = 1; 367 } 368 369 suppress_errthrow = false; 370 emsg_on_display = false; 371 return ret; 372 } 373 374 /// "assert_beeps(cmd [, error])" function 375 void f_assert_beeps(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 376 { 377 rettv->vval.v_number = assert_beeps(argvars, false); 378 } 379 380 /// "assert_nobeep(cmd [, error])" function 381 void f_assert_nobeep(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 382 { 383 rettv->vval.v_number = assert_beeps(argvars, true); 384 } 385 386 /// "assert_equal(expected, actual[, msg])" function 387 void f_assert_equal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 388 { 389 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL); 390 } 391 392 static int assert_equalfile(typval_T *argvars) 393 FUNC_ATTR_NONNULL_ALL 394 { 395 char buf1[NUMBUFLEN]; 396 char buf2[NUMBUFLEN]; 397 const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1); 398 const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2); 399 400 if (fname1 == NULL || fname2 == NULL) { 401 return 0; 402 } 403 404 IObuff[0] = NUL; 405 size_t IObufflen = 0; 406 FILE *const fd1 = os_fopen(fname1, READBIN); 407 char line1[200]; 408 char line2[200]; 409 ptrdiff_t lineidx = 0; 410 if (fd1 == NULL) { 411 IObufflen = vim_snprintf_safelen(IObuff, IOSIZE, e_cant_read_file_str, fname1); 412 } else { 413 FILE *const fd2 = os_fopen(fname2, READBIN); 414 if (fd2 == NULL) { 415 fclose(fd1); 416 IObufflen = vim_snprintf_safelen(IObuff, IOSIZE, e_cant_read_file_str, fname2); 417 } else { 418 int64_t linecount = 1; 419 for (int64_t count = 0;; count++) { 420 const int c1 = fgetc(fd1); 421 const int c2 = fgetc(fd2); 422 if (c1 == EOF) { 423 if (c2 != EOF) { 424 IObufflen = xstrlcpy(IObuff, "first file is shorter", IOSIZE); 425 } 426 break; 427 } else if (c2 == EOF) { 428 IObufflen = xstrlcpy(IObuff, "second file is shorter", IOSIZE); 429 break; 430 } else { 431 line1[lineidx] = (char)c1; 432 line2[lineidx] = (char)c2; 433 lineidx++; 434 if (c1 != c2) { 435 IObufflen 436 = vim_snprintf_safelen(IObuff, IOSIZE, 437 "difference at byte %" PRId64 ", line %" PRId64, 438 count, linecount); 439 break; 440 } 441 } 442 if (c1 == NL) { 443 linecount++; 444 lineidx = 0; 445 } else if (lineidx + 2 == (ptrdiff_t)sizeof(line1)) { 446 memmove(line1, line1 + 100, (size_t)(lineidx - 100)); 447 memmove(line2, line2 + 100, (size_t)(lineidx - 100)); 448 lineidx -= 100; 449 } 450 } 451 fclose(fd1); 452 fclose(fd2); 453 } 454 } 455 456 if (IObufflen > 0) { 457 garray_T ga; 458 prepare_assert_error(&ga); 459 if (argvars[2].v_type != VAR_UNKNOWN) { 460 char *const tofree = encode_tv2echo(&argvars[2], NULL); 461 ga_concat(&ga, tofree); 462 xfree(tofree); 463 GA_CONCAT_LITERAL(&ga, ": "); 464 } 465 ga_concat_len(&ga, IObuff, IObufflen); 466 if (lineidx > 0) { 467 line1[lineidx] = NUL; 468 line2[lineidx] = NUL; 469 GA_CONCAT_LITERAL(&ga, " after \""); 470 ga_concat_len(&ga, line1, (size_t)lineidx); 471 if (strcmp(line1, line2) != 0) { 472 GA_CONCAT_LITERAL(&ga, "\" vs \""); 473 ga_concat_len(&ga, line2, (size_t)lineidx); 474 } 475 GA_CONCAT_LITERAL(&ga, "\""); 476 } 477 assert_error(&ga); 478 ga_clear(&ga); 479 return 1; 480 } 481 482 return 0; 483 } 484 485 /// "assert_equalfile(fname-one, fname-two[, msg])" function 486 void f_assert_equalfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 487 { 488 rettv->vval.v_number = assert_equalfile(argvars); 489 } 490 491 /// "assert_notequal(expected, actual[, msg])" function 492 void f_assert_notequal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 493 { 494 rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL); 495 } 496 497 /// "assert_exception(string[, msg])" function 498 void f_assert_exception(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 499 { 500 garray_T ga; 501 502 const char *const error = tv_get_string_chk(&argvars[0]); 503 if (*get_vim_var_str(VV_EXCEPTION) == NUL) { 504 prepare_assert_error(&ga); 505 GA_CONCAT_LITERAL(&ga, "v:exception is not set"); 506 assert_error(&ga); 507 ga_clear(&ga); 508 rettv->vval.v_number = 1; 509 } else if (error != NULL 510 && strstr(get_vim_var_str(VV_EXCEPTION), error) == NULL) { 511 prepare_assert_error(&ga); 512 fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], 513 get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER); 514 assert_error(&ga); 515 ga_clear(&ga); 516 rettv->vval.v_number = 1; 517 } 518 } 519 520 /// "assert_fails(cmd [, error [, msg]])" function 521 void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 522 { 523 garray_T ga; 524 const int save_trylevel = trylevel; 525 const int called_emsg_before = called_emsg; 526 const char *wrong_arg_msg = NULL; 527 char *tofree = NULL; 528 529 if (tv_check_for_string_or_number_arg(argvars, 0) == FAIL 530 || tv_check_for_opt_string_or_list_arg(argvars, 1) == FAIL 531 || (argvars[1].v_type != VAR_UNKNOWN 532 && (argvars[2].v_type != VAR_UNKNOWN 533 && (tv_check_for_opt_number_arg(argvars, 3) == FAIL 534 || (argvars[3].v_type != VAR_UNKNOWN 535 && tv_check_for_opt_string_arg(argvars, 4) == FAIL))))) { 536 return; 537 } 538 539 // trylevel must be zero for a ":throw" command to be considered failed 540 trylevel = 0; 541 suppress_errthrow = true; 542 in_assert_fails = true; 543 no_wait_return++; 544 545 const char *const cmd = tv_get_string_chk(&argvars[0]); 546 do_cmdline_cmd(cmd); 547 548 // reset here for any errors reported below 549 trylevel = save_trylevel; 550 suppress_errthrow = false; 551 552 if (called_emsg == called_emsg_before) { 553 prepare_assert_error(&ga); 554 GA_CONCAT_LITERAL(&ga, "command did not fail: "); 555 assert_append_cmd_or_arg(&ga, argvars, cmd); 556 assert_error(&ga); 557 ga_clear(&ga); 558 rettv->vval.v_number = 1; 559 } else if (argvars[1].v_type != VAR_UNKNOWN) { 560 char buf[NUMBUFLEN]; 561 const char *expected; 562 const char *expected_str = NULL; 563 bool error_found = false; 564 int error_found_index = 1; 565 char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg; 566 567 if (argvars[1].v_type == VAR_STRING) { 568 expected = tv_get_string_buf_chk(&argvars[1], buf); 569 error_found = expected == NULL || strstr(actual, expected) == NULL; 570 } else if (argvars[1].v_type == VAR_LIST) { 571 const list_T *const list = argvars[1].vval.v_list; 572 if (list == NULL || tv_list_len(list) < 1 || tv_list_len(list) > 2) { 573 wrong_arg_msg = e_assert_fails_second_arg; 574 goto theend; 575 } 576 const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list)); 577 expected = tv_get_string_buf_chk(tv, buf); 578 if (expected == NULL) { 579 goto theend; 580 } 581 if (!pattern_match(expected, actual, false)) { 582 error_found = true; 583 expected_str = expected; 584 } else if (tv_list_len(list) == 2) { 585 // make a copy, an error in pattern_match() may free it 586 tofree = actual = xstrdup(get_vim_var_str(VV_ERRMSG)); 587 tv = TV_LIST_ITEM_TV(tv_list_last(list)); 588 expected = tv_get_string_buf_chk(tv, buf); 589 if (expected == NULL) { 590 goto theend; 591 } 592 if (!pattern_match(expected, actual, false)) { 593 error_found = true; 594 expected_str = expected; 595 } 596 } 597 } else { 598 wrong_arg_msg = e_assert_fails_second_arg; 599 goto theend; 600 } 601 602 if (!error_found && argvars[2].v_type != VAR_UNKNOWN 603 && argvars[3].v_type != VAR_UNKNOWN) { 604 if (argvars[3].v_type != VAR_NUMBER) { 605 wrong_arg_msg = e_assert_fails_fourth_argument; 606 goto theend; 607 } else if (argvars[3].vval.v_number >= 0 608 && argvars[3].vval.v_number != emsg_assert_fails_lnum) { 609 error_found = true; 610 error_found_index = 3; 611 } 612 if (!error_found && argvars[4].v_type != VAR_UNKNOWN) { 613 if (argvars[4].v_type != VAR_STRING) { 614 wrong_arg_msg = e_assert_fails_fifth_argument; 615 goto theend; 616 } else if (argvars[4].vval.v_string != NULL 617 && !pattern_match(argvars[4].vval.v_string, 618 emsg_assert_fails_context, false)) { 619 error_found = true; 620 error_found_index = 4; 621 } 622 } 623 } 624 625 if (error_found) { 626 typval_T actual_tv; 627 prepare_assert_error(&ga); 628 if (error_found_index == 3) { 629 actual_tv.v_type = VAR_NUMBER; 630 actual_tv.vval.v_number = emsg_assert_fails_lnum; 631 } else if (error_found_index == 4) { 632 actual_tv.v_type = VAR_STRING; 633 actual_tv.vval.v_string = emsg_assert_fails_context; 634 } else { 635 actual_tv.v_type = VAR_STRING; 636 actual_tv.vval.v_string = actual; 637 } 638 fill_assert_error(&ga, &argvars[2], expected_str, 639 &argvars[error_found_index], &actual_tv, ASSERT_FAILS); 640 GA_CONCAT_LITERAL(&ga, ": "); 641 assert_append_cmd_or_arg(&ga, argvars, cmd); 642 assert_error(&ga); 643 ga_clear(&ga); 644 rettv->vval.v_number = 1; 645 } 646 } 647 648 theend: 649 trylevel = save_trylevel; 650 suppress_errthrow = false; 651 in_assert_fails = false; 652 did_emsg = false; 653 got_int = false; 654 msg_col = 0; 655 no_wait_return--; 656 need_wait_return = false; 657 emsg_on_display = false; 658 msg_reset_scroll(); 659 lines_left = Rows; 660 XFREE_CLEAR(emsg_assert_fails_msg); 661 xfree(tofree); 662 set_vim_var_string(VV_ERRMSG, NULL, 0); 663 if (wrong_arg_msg != NULL) { 664 emsg(_(wrong_arg_msg)); 665 } 666 } 667 668 // "assert_false(actual[, msg])" function 669 void f_assert_false(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 670 { 671 rettv->vval.v_number = assert_bool(argvars, false); 672 } 673 674 static int assert_inrange(typval_T *argvars) 675 FUNC_ATTR_NONNULL_ALL 676 { 677 bool error = false; 678 679 if (argvars[0].v_type == VAR_FLOAT 680 || argvars[1].v_type == VAR_FLOAT 681 || argvars[2].v_type == VAR_FLOAT) { 682 const float_T flower = tv_get_float(&argvars[0]); 683 const float_T fupper = tv_get_float(&argvars[1]); 684 const float_T factual = tv_get_float(&argvars[2]); 685 686 if (factual < flower || factual > fupper) { 687 garray_T ga; 688 prepare_assert_error(&ga); 689 char expected_str[200]; 690 vim_snprintf(expected_str, sizeof(expected_str), "range %g - %g,", flower, fupper); 691 fill_assert_error(&ga, &argvars[3], expected_str, NULL, &argvars[2], ASSERT_OTHER); 692 assert_error(&ga); 693 ga_clear(&ga); 694 return 1; 695 } 696 } else { 697 const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); 698 const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); 699 const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); 700 701 if (error) { 702 return 0; 703 } 704 if (actual < lower || actual > upper) { 705 garray_T ga; 706 prepare_assert_error(&ga); 707 char expected_str[200]; 708 vim_snprintf(expected_str, sizeof(expected_str), 709 "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", 710 lower, upper); 711 fill_assert_error(&ga, &argvars[3], expected_str, NULL, &argvars[2], ASSERT_OTHER); 712 assert_error(&ga); 713 ga_clear(&ga); 714 return 1; 715 } 716 } 717 return 0; 718 } 719 720 /// "assert_inrange(lower, upper[, msg])" function 721 void f_assert_inrange(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 722 { 723 if (tv_check_for_float_or_nr_arg(argvars, 0) == FAIL 724 || tv_check_for_float_or_nr_arg(argvars, 1) == FAIL 725 || tv_check_for_float_or_nr_arg(argvars, 2) == FAIL 726 || tv_check_for_opt_string_arg(argvars, 3) == FAIL) { 727 return; 728 } 729 730 rettv->vval.v_number = assert_inrange(argvars); 731 } 732 733 /// "assert_match(pattern, actual[, msg])" function 734 void f_assert_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 735 { 736 rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH); 737 } 738 739 /// "assert_notmatch(pattern, actual[, msg])" function 740 void f_assert_notmatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 741 { 742 rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH); 743 } 744 745 /// "assert_report(msg)" function 746 void f_assert_report(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 747 { 748 garray_T ga; 749 750 prepare_assert_error(&ga); 751 ga_concat(&ga, tv_get_string(&argvars[0])); 752 assert_error(&ga); 753 ga_clear(&ga); 754 rettv->vval.v_number = 1; 755 } 756 757 /// "assert_true(actual[, msg])" function 758 void f_assert_true(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 759 { 760 rettv->vval.v_number = assert_bool(argvars, true); 761 } 762 763 /// "test_garbagecollect_now()" function 764 void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 765 { 766 // This is dangerous, any Lists and Dicts used internally may be freed 767 // while still in use. 768 if (!get_vim_var_nr(VV_TESTING)) { 769 emsg(_(e_calling_test_garbagecollect_now_while_v_testing_is_not_set)); 770 } else { 771 garbage_collect(true); 772 } 773 } 774 775 /// "test_write_list_log()" function 776 void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalFuncData fptr) 777 { 778 const char *const fname = tv_get_string_chk(&argvars[0]); 779 if (fname == NULL) { 780 return; 781 } 782 }