diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-01-25 18:31:31 +0000 |
commit | 9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch) | |
tree | 607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/testing.c | |
parent | 9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff) | |
parent | 3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff) | |
download | rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.gz rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.bz2 rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.zip |
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/testing.c')
-rw-r--r-- | src/nvim/testing.c | 234 |
1 files changed, 173 insertions, 61 deletions
diff --git a/src/nvim/testing.c b/src/nvim/testing.c index e70e9f2cbd..edf92c78ac 100644 --- a/src/nvim/testing.c +++ b/src/nvim/testing.c @@ -3,17 +3,46 @@ // testing.c: Support for tests +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +#include "nvim/ascii.h" #include "nvim/eval.h" #include "nvim/eval/encode.h" +#include "nvim/eval/typval.h" +#include "nvim/eval/typval_defs.h" #include "nvim/ex_docmd.h" +#include "nvim/garray.h" +#include "nvim/gettext.h" +#include "nvim/globals.h" +#include "nvim/hashtab.h" +#include "nvim/macros.h" +#include "nvim/mbyte.h" +#include "nvim/memory.h" +#include "nvim/message.h" #include "nvim/os/os.h" #include "nvim/runtime.h" +#include "nvim/strings.h" #include "nvim/testing.h" +#include "nvim/types.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "testing.c.generated.h" #endif +static char e_assert_fails_second_arg[] + = N_("E856: assert_fails() second argument must be a string or a list with one or two strings"); +static char e_assert_fails_fourth_argument[] + = N_("E1115: assert_fails() fourth argument must be a number"); +static char e_assert_fails_fifth_argument[] + = N_("E1116: assert_fails() fifth argument must be a string"); +static char e_calling_test_garbagecollect_now_while_v_testing_is_not_set[] + = N_("E1142: Calling test_garbagecollect_now() while v:testing is not set"); + /// Prepare "gap" for an assert error and add the sourcing position. static void prepare_assert_error(garray_T *gap) { @@ -39,15 +68,15 @@ static void prepare_assert_error(garray_T *gap) /// Append "p[clen]" to "gap", escaping unprintable characters. /// Changes NL to \n, CR to \r, etc. -static void ga_concat_esc(garray_T *gap, const char_u *p, int clen) +static void ga_concat_esc(garray_T *gap, const char *p, int clen) FUNC_ATTR_NONNULL_ALL { - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; if (clen > 1) { memmove(buf, p, (size_t)clen); buf[clen] = NUL; - ga_concat(gap, (char *)buf); + ga_concat(gap, buf); } else { switch (*p) { case BS: @@ -65,11 +94,11 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen) case '\\': ga_concat(gap, "\\\\"); break; default: - if (*p < ' ') { - vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); - ga_concat(gap, (char *)buf); + if ((uint8_t)(*p) < ' ' || *p == 0x7f) { + vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p); + ga_concat(gap, buf); } else { - ga_append(gap, (char)(*p)); + ga_append(gap, (uint8_t)(*p)); } break; } @@ -78,22 +107,22 @@ static void ga_concat_esc(garray_T *gap, const char_u *p, int clen) /// Append "str" to "gap", escaping unprintable characters. /// Changes NL to \n, CR to \r, etc. -static void ga_concat_shorten_esc(garray_T *gap, const char_u *str) +static void ga_concat_shorten_esc(garray_T *gap, const char *str) FUNC_ATTR_NONNULL_ARG(1) { - char_u buf[NUMBUFLEN]; + char buf[NUMBUFLEN]; if (str == NULL) { ga_concat(gap, "NULL"); return; } - for (const char_u *p = str; *p != NUL; p++) { + for (const char *p = str; *p != NUL; p++) { int same_len = 1; - const char_u *s = p; + const char *s = p; const int c = mb_ptr2char_adv(&s); const int clen = (int)(s - p); - while (*s != NUL && c == utf_ptr2char((char *)s)) { + while (*s != NUL && c == utf_ptr2char(s)) { same_len++; s += clen; } @@ -101,8 +130,8 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str) ga_concat(gap, "\\["); ga_concat_esc(gap, p, clen); ga_concat(gap, " occurs "); - vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len); - ga_concat(gap, (char *)buf); + vim_snprintf(buf, NUMBUFLEN, "%d", same_len); + ga_concat(gap, buf); ga_concat(gap, " times]"); p = s - 1; } else { @@ -112,18 +141,21 @@ static void ga_concat_shorten_esc(garray_T *gap, const char_u *str) } /// Fill "gap" with information about an assert error. -static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, +static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str, typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype) { - char_u *tofree; + char *tofree; typval_T *exp_tv = exp_tv_arg; typval_T *got_tv = got_tv_arg; bool did_copy = false; int omitted = 0; - if (opt_msg_tv->v_type != VAR_UNKNOWN) { - tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL); - ga_concat(gap, (char *)tofree); + if (opt_msg_tv->v_type != VAR_UNKNOWN + && !(opt_msg_tv->v_type == VAR_STRING + && (opt_msg_tv->vval.v_string == NULL + || *opt_msg_tv->vval.v_string == NUL))) { + tofree = encode_tv2echo(opt_msg_tv, NULL); + ga_concat(gap, tofree); xfree(tofree); ga_concat(gap, ": "); } @@ -156,7 +188,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s if (item2 == NULL || !tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &item2->di_tv, false, false)) { // item of exp_d not present in got_d or values differ. - const size_t key_len = STRLEN(hi->hi_key); + const size_t key_len = strlen(hi->hi_key); tv_dict_add_tv(exp_tv->vval.v_dict, (const char *)hi->hi_key, key_len, &TV_DICT_HI2DI(hi)->di_tv); if (item2 != NULL) { @@ -177,7 +209,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s dictitem_T *item2 = tv_dict_find(exp_d, (const char *)hi->hi_key, -1); if (item2 == NULL) { // item of got_d not present in exp_d - const size_t key_len = STRLEN(hi->hi_key); + const size_t key_len = strlen(hi->hi_key); tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len, &TV_DICT_HI2DI(hi)->di_tv); } @@ -186,7 +218,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s } } - tofree = (char_u *)encode_tv2string(exp_tv, NULL); + tofree = encode_tv2string(exp_tv, NULL); ga_concat_shorten_esc(gap, tofree); xfree(tofree); } else { @@ -201,7 +233,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_s } else { ga_concat(gap, " but got "); } - tofree = (char_u *)encode_tv2string(got_tv, NULL); + tofree = encode_tv2string(got_tv, NULL); ga_concat_shorten_esc(gap, tofree); xfree(tofree); @@ -241,13 +273,12 @@ static int assert_match_common(typval_T *argvars, assert_type_T atype) { char buf1[NUMBUFLEN]; char buf2[NUMBUFLEN]; + const int called_emsg_before = called_emsg; const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); - if (pat == NULL || text == NULL) { - emsg(_(e_invarg)); - } else if (pattern_match((char *)pat, (char *)text, false) - != (atype == ASSERT_MATCH)) { + if (called_emsg == called_emsg_before + && pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) { garray_T ga; prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); @@ -275,7 +306,7 @@ static int assert_bool(typval_T *argvars, bool is_true) : kBoolVarFalse)))) { prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[1], - (char_u *)(is_true ? "True" : "False"), + is_true ? "True" : "False", NULL, &argvars[0], ASSERT_OTHER); assert_error(&ga); ga_clear(&ga); @@ -326,19 +357,19 @@ static int assert_beeps(typval_T *argvars, bool no_beep) } /// "assert_beeps(cmd [, error])" function -void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_beeps(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_beeps(argvars, false); } /// "assert_nobeep(cmd [, error])" function -void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_nobeep(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_beeps(argvars, true); } /// "assert_equal(expected, actual[, msg])" function -void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_equal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL); } @@ -348,11 +379,12 @@ static int assert_equalfile(typval_T *argvars) { char buf1[NUMBUFLEN]; char buf2[NUMBUFLEN]; + const int called_emsg_before = called_emsg; const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1); const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2); garray_T ga; - if (fname1 == NULL || fname2 == NULL) { + if (called_emsg > called_emsg_before) { return 0; } @@ -362,12 +394,12 @@ static int assert_equalfile(typval_T *argvars) char line2[200]; ptrdiff_t lineidx = 0; if (fd1 == NULL) { - snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1); + snprintf(IObuff, IOSIZE, (char *)e_notread, fname1); } else { FILE *const fd2 = os_fopen(fname2, READBIN); if (fd2 == NULL) { fclose(fd1); - snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2); + snprintf(IObuff, IOSIZE, (char *)e_notread, fname2); } else { int64_t linecount = 1; for (int64_t count = 0;; count++) { @@ -386,7 +418,7 @@ static int assert_equalfile(typval_T *argvars) line2[lineidx] = (char)c2; lineidx++; if (c1 != c2) { - snprintf((char *)IObuff, IOSIZE, + snprintf(IObuff, IOSIZE, "difference at byte %" PRId64 ", line %" PRId64, count, linecount); break; @@ -413,13 +445,13 @@ static int assert_equalfile(typval_T *argvars) xfree(tofree); ga_concat(&ga, ": "); } - ga_concat(&ga, (char *)IObuff); + ga_concat(&ga, IObuff); if (lineidx > 0) { line1[lineidx] = NUL; line2[lineidx] = NUL; ga_concat(&ga, " after \""); ga_concat(&ga, line1); - if (STRCMP(line1, line2) != 0) { + if (strcmp(line1, line2) != 0) { ga_concat(&ga, "\" vs \""); ga_concat(&ga, line2); } @@ -433,19 +465,19 @@ static int assert_equalfile(typval_T *argvars) } /// "assert_equalfile(fname-one, fname-two[, msg])" function -void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_equalfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_equalfile(argvars); } /// "assert_notequal(expected, actual[, msg])" function -void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_notequal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL); } /// "assert_exception(string[, msg])" function -void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_exception(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { garray_T ga; @@ -457,7 +489,7 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) ga_clear(&ga); rettv->vval.v_number = 1; } else if (error != NULL - && strstr((char *)get_vim_var_str(VV_EXCEPTION), error) == NULL) { + && strstr(get_vim_var_str(VV_EXCEPTION), error) == NULL) { prepare_assert_error(&ga); fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER); @@ -468,17 +500,19 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "assert_fails(cmd [, error [, msg]])" function -void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const char *const cmd = tv_get_string_chk(&argvars[0]); garray_T ga; int save_trylevel = trylevel; const int called_emsg_before = called_emsg; + char *wrong_arg_msg = NULL; // trylevel must be zero for a ":throw" command to be considered failed trylevel = 0; suppress_errthrow = true; - emsg_silent = true; + in_assert_fails = true; + no_wait_return++; do_cmdline_cmd(cmd); if (called_emsg == called_emsg_before) { @@ -490,13 +524,75 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } else if (argvars[1].v_type != VAR_UNKNOWN) { char buf[NUMBUFLEN]; - const char *const error = tv_get_string_buf_chk(&argvars[1], buf); + const char *expected; + bool error_found = false; + int error_found_index = 1; + char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg; + + if (argvars[1].v_type == VAR_STRING) { + expected = tv_get_string_buf_chk(&argvars[1], buf); + error_found = expected == NULL || strstr(actual, expected) == NULL; + } else if (argvars[1].v_type == VAR_LIST) { + const list_T *const list = argvars[1].vval.v_list; + if (list == NULL || tv_list_len(list) < 1 || tv_list_len(list) > 2) { + wrong_arg_msg = e_assert_fails_second_arg; + goto theend; + } + const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list)); + expected = tv_get_string_buf_chk(tv, buf); + if (!pattern_match(expected, actual, false)) { + error_found = true; + } else if (tv_list_len(list) == 2) { + tv = TV_LIST_ITEM_TV(tv_list_last(list)); + actual = get_vim_var_str(VV_ERRMSG); + expected = tv_get_string_buf_chk(tv, buf); + if (!pattern_match(expected, actual, false)) { + error_found = true; + } + } + } else { + wrong_arg_msg = e_assert_fails_second_arg; + goto theend; + } + + if (!error_found && argvars[2].v_type != VAR_UNKNOWN + && argvars[3].v_type != VAR_UNKNOWN) { + if (argvars[3].v_type != VAR_NUMBER) { + wrong_arg_msg = e_assert_fails_fourth_argument; + goto theend; + } else if (argvars[3].vval.v_number >= 0 + && argvars[3].vval.v_number != emsg_assert_fails_lnum) { + error_found = true; + error_found_index = 3; + } + if (!error_found && argvars[4].v_type != VAR_UNKNOWN) { + if (argvars[4].v_type != VAR_STRING) { + wrong_arg_msg = e_assert_fails_fifth_argument; + goto theend; + } else if (argvars[4].vval.v_string != NULL + && !pattern_match(argvars[4].vval.v_string, + emsg_assert_fails_context, false)) { + error_found = true; + error_found_index = 4; + } + } + } - if (error == NULL - || strstr((char *)get_vim_var_str(VV_ERRMSG), error) == NULL) { + if (error_found) { + typval_T actual_tv; prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], - get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER); + if (error_found_index == 3) { + actual_tv.v_type = VAR_NUMBER; + actual_tv.vval.v_number = emsg_assert_fails_lnum; + } else if (error_found_index == 4) { + actual_tv.v_type = VAR_STRING; + actual_tv.vval.v_string = emsg_assert_fails_context; + } else { + actual_tv.v_type = VAR_STRING; + actual_tv.vval.v_string = actual; + } + fill_assert_error(&ga, &argvars[2], NULL, + &argvars[error_found_index], &actual_tv, ASSERT_OTHER); ga_concat(&ga, ": "); assert_append_cmd_or_arg(&ga, argvars, cmd); assert_error(&ga); @@ -505,15 +601,27 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +theend: trylevel = save_trylevel; suppress_errthrow = false; - emsg_silent = false; + in_assert_fails = false; + did_emsg = false; + got_int = false; + msg_col = 0; + no_wait_return--; + need_wait_return = false; emsg_on_display = false; + msg_reset_scroll(); + lines_left = Rows; + XFREE_CLEAR(emsg_assert_fails_msg); set_vim_var_string(VV_ERRMSG, NULL, 0); + if (wrong_arg_msg != NULL) { + emsg(_(wrong_arg_msg)); + } } // "assert_false(actual[, msg])" function -void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_false(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_bool(argvars, false); } @@ -534,8 +642,8 @@ static int assert_inrange(typval_T *argvars) garray_T ga; prepare_assert_error(&ga); if (argvars[3].v_type != VAR_UNKNOWN) { - char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL); - ga_concat(&ga, (char *)tofree); + char *const tofree = encode_tv2string(&argvars[3], NULL); + ga_concat(&ga, tofree); xfree(tofree); } else { char msg[80]; @@ -563,7 +671,7 @@ static int assert_inrange(typval_T *argvars) vim_snprintf(msg, sizeof(msg), "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", lower, upper); // -V576 - fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], + fill_assert_error(&ga, &argvars[3], msg, NULL, &argvars[2], ASSERT_INRANGE); assert_error(&ga); ga_clear(&ga); @@ -574,25 +682,25 @@ static int assert_inrange(typval_T *argvars) } /// "assert_inrange(lower, upper[, msg])" function -void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_inrange(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_inrange(argvars); } /// "assert_match(pattern, actual[, msg])" function -void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH); } /// "assert_notmatch(pattern, actual[, msg])" function -void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_notmatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH); } /// "assert_report(msg)" function -void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_report(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { garray_T ga; @@ -604,21 +712,25 @@ void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr) } /// "assert_true(actual[, msg])" function -void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_assert_true(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = assert_bool(argvars, true); } /// "test_garbagecollect_now()" function -void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr) +void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { // This is dangerous, any Lists and Dicts used internally may be freed // while still in use. - garbage_collect(true); + if (!get_vim_var_nr(VV_TESTING)) { + emsg(_(e_calling_test_garbagecollect_now_while_v_testing_is_not_set)); + } else { + garbage_collect(true); + } } /// "test_write_list_log()" function -void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr) +void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalFuncData fptr) { const char *const fname = tv_get_string_chk(&argvars[0]); if (fname == NULL) { |