aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/testing.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-11-29 22:40:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-11-29 22:40:31 +0000
commit339e2d15cc26fe86988ea06468d912a46c8d6f29 (patch)
treea6167fc8fcfc6ae2dc102f57b2473858eac34063 /src/nvim/testing.c
parent067dc73729267c0262438a6fdd66e586f8496946 (diff)
parent4a8bf24ac690004aedf5540fa440e788459e5e34 (diff)
downloadrneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.gz
rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.bz2
rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.zip
Merge remote-tracking branch 'upstream/master' into fix_repeatcmdline
Diffstat (limited to 'src/nvim/testing.c')
-rw-r--r--src/nvim/testing.c206
1 files changed, 121 insertions, 85 deletions
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index edf92c78ac..cada04d276 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -1,6 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
// testing.c: Support for tests
#include <inttypes.h>
@@ -9,38 +6,50 @@
#include <stdio.h>
#include <string.h>
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.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/func_attr.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
-#include "nvim/macros.h"
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/os/os.h"
+#include "nvim/os/fs.h"
#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/testing.h"
-#include "nvim/types.h"
-#include "nvim/vim.h"
+#include "nvim/types_defs.h"
+#include "nvim/vim_defs.h"
+
+/// Type of assert_* check being performed
+typedef enum {
+ ASSERT_EQUAL,
+ ASSERT_NOTEQUAL,
+ ASSERT_MATCH,
+ ASSERT_NOTMATCH,
+ ASSERT_FAILS,
+ ASSERT_OTHER,
+} assert_type_T;
#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[]
+static const 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 const char e_assert_fails_fourth_argument[]
+ = N_("E1115: \"assert_fails()\" fourth argument must be a number");
+static const char e_assert_fails_fifth_argument[]
+ = N_("E1116: \"assert_fails()\" fifth argument must be a string");
+static const 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.
@@ -77,31 +86,32 @@ static void ga_concat_esc(garray_T *gap, const char *p, int clen)
memmove(buf, p, (size_t)clen);
buf[clen] = NUL;
ga_concat(gap, buf);
- } else {
- switch (*p) {
- case BS:
- ga_concat(gap, "\\b"); break;
- case ESC:
- ga_concat(gap, "\\e"); break;
- case FF:
- ga_concat(gap, "\\f"); break;
- case NL:
- ga_concat(gap, "\\n"); break;
- case TAB:
- ga_concat(gap, "\\t"); break;
- case CAR:
- ga_concat(gap, "\\r"); break;
- case '\\':
- ga_concat(gap, "\\\\"); break;
- default:
- if ((uint8_t)(*p) < ' ' || *p == 0x7f) {
- vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p);
- ga_concat(gap, buf);
- } else {
- ga_append(gap, (uint8_t)(*p));
- }
- break;
+ return;
+ }
+
+ switch (*p) {
+ case BS:
+ ga_concat(gap, "\\b"); break;
+ case ESC:
+ ga_concat(gap, "\\e"); break;
+ case FF:
+ ga_concat(gap, "\\f"); break;
+ case NL:
+ ga_concat(gap, "\\n"); break;
+ case TAB:
+ ga_concat(gap, "\\t"); break;
+ case CAR:
+ ga_concat(gap, "\\r"); break;
+ case '\\':
+ ga_concat(gap, "\\\\"); break;
+ default:
+ if ((uint8_t)(*p) < ' ' || *p == 0x7f) {
+ vim_snprintf(buf, NUMBUFLEN, "\\x%02x", *p);
+ ga_concat(gap, buf);
+ } else {
+ ga_append(gap, (uint8_t)(*p));
}
+ break;
}
}
@@ -120,7 +130,7 @@ static void ga_concat_shorten_esc(garray_T *gap, const char *str)
for (const char *p = str; *p != NUL; p++) {
int same_len = 1;
const char *s = p;
- const int c = mb_ptr2char_adv(&s);
+ const int c = mb_cptr2char_adv(&s);
const int clen = (int)(s - p);
while (*s != NUL && c == utf_ptr2char(s)) {
same_len++;
@@ -141,10 +151,9 @@ static void ga_concat_shorten_esc(garray_T *gap, const char *str)
}
/// Fill "gap" with information about an assert error.
-static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str,
+static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, const char *exp_str,
typval_T *exp_tv_arg, typval_T *got_tv_arg, assert_type_T atype)
{
- char *tofree;
typval_T *exp_tv = exp_tv_arg;
typval_T *got_tv = got_tv_arg;
bool did_copy = false;
@@ -154,7 +163,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str
&& !(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);
+ char *tofree = encode_tv2echo(opt_msg_tv, NULL);
ga_concat(gap, tofree);
xfree(tofree);
ga_concat(gap, ": ");
@@ -184,16 +193,14 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str
int todo = (int)exp_d->dv_hashtab.ht_used;
for (const hashitem_T *hi = exp_d->dv_hashtab.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- dictitem_T *item2 = tv_dict_find(got_d, (const char *)hi->hi_key, -1);
+ dictitem_T *item2 = tv_dict_find(got_d, hi->hi_key, -1);
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);
- tv_dict_add_tv(exp_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
- &TV_DICT_HI2DI(hi)->di_tv);
+ tv_dict_add_tv(exp_tv->vval.v_dict, hi->hi_key, key_len, &TV_DICT_HI2DI(hi)->di_tv);
if (item2 != NULL) {
- tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
- &item2->di_tv);
+ tv_dict_add_tv(got_tv->vval.v_dict, hi->hi_key, key_len, &item2->di_tv);
}
} else {
omitted++;
@@ -206,23 +213,28 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str
todo = (int)got_d->dv_hashtab.ht_used;
for (const hashitem_T *hi = got_d->dv_hashtab.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- dictitem_T *item2 = tv_dict_find(exp_d, (const char *)hi->hi_key, -1);
+ dictitem_T *item2 = tv_dict_find(exp_d, 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);
- tv_dict_add_tv(got_tv->vval.v_dict, (const char *)hi->hi_key, key_len,
- &TV_DICT_HI2DI(hi)->di_tv);
+ tv_dict_add_tv(got_tv->vval.v_dict, hi->hi_key, key_len, &TV_DICT_HI2DI(hi)->di_tv);
}
todo--;
}
}
}
- tofree = encode_tv2string(exp_tv, NULL);
+ char *tofree = encode_tv2string(exp_tv, NULL);
ga_concat_shorten_esc(gap, tofree);
xfree(tofree);
} else {
+ if (atype == ASSERT_FAILS) {
+ ga_concat(gap, "'");
+ }
ga_concat_shorten_esc(gap, exp_str);
+ if (atype == ASSERT_FAILS) {
+ ga_concat(gap, "'");
+ }
}
if (atype != ASSERT_NOTEQUAL) {
@@ -233,7 +245,7 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char *exp_str
} else {
ga_concat(gap, " but got ");
}
- tofree = encode_tv2string(got_tv, NULL);
+ char *tofree = encode_tv2string(got_tv, NULL);
ga_concat_shorten_esc(gap, tofree);
xfree(tofree);
@@ -273,11 +285,10 @@ 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 (called_emsg == called_emsg_before
+ if (pat != NULL && text != NULL
&& pattern_match(pat, text, false) != (atype == ASSERT_MATCH)) {
garray_T ga;
prepare_assert_error(&ga);
@@ -379,12 +390,10 @@ 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 (called_emsg > called_emsg_before) {
+ if (fname1 == NULL || fname2 == NULL) {
return 0;
}
@@ -394,12 +403,12 @@ static int assert_equalfile(typval_T *argvars)
char line2[200];
ptrdiff_t lineidx = 0;
if (fd1 == NULL) {
- snprintf(IObuff, IOSIZE, (char *)e_notread, fname1);
+ snprintf(IObuff, IOSIZE, e_notread, fname1);
} else {
FILE *const fd2 = os_fopen(fname2, READBIN);
if (fd2 == NULL) {
fclose(fd1);
- snprintf(IObuff, IOSIZE, (char *)e_notread, fname2);
+ snprintf(IObuff, IOSIZE, e_notread, fname2);
} else {
int64_t linecount = 1;
for (int64_t count = 0;; count++) {
@@ -407,11 +416,11 @@ static int assert_equalfile(typval_T *argvars)
const int c2 = fgetc(fd2);
if (c1 == EOF) {
if (c2 != EOF) {
- STRCPY(IObuff, "first file is shorter");
+ xstrlcpy(IObuff, "first file is shorter", IOSIZE);
}
break;
} else if (c2 == EOF) {
- STRCPY(IObuff, "second file is shorter");
+ xstrlcpy(IObuff, "second file is shorter", IOSIZE);
break;
} else {
line1[lineidx] = (char)c1;
@@ -437,7 +446,9 @@ static int assert_equalfile(typval_T *argvars)
fclose(fd2);
}
}
+
if (IObuff[0] != NUL) {
+ garray_T ga;
prepare_assert_error(&ga);
if (argvars[2].v_type != VAR_UNKNOWN) {
char *const tofree = encode_tv2echo(&argvars[2], NULL);
@@ -461,6 +472,7 @@ static int assert_equalfile(typval_T *argvars)
ga_clear(&ga);
return 1;
}
+
return 0;
}
@@ -502,11 +514,21 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "assert_fails(cmd [, error [, msg]])" function
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 save_trylevel = trylevel;
const int called_emsg_before = called_emsg;
- char *wrong_arg_msg = NULL;
+ const char *wrong_arg_msg = NULL;
+ char *tofree = NULL;
+
+ if (tv_check_for_string_or_number_arg(argvars, 0) == FAIL
+ || tv_check_for_opt_string_or_list_arg(argvars, 1) == FAIL
+ || (argvars[1].v_type != VAR_UNKNOWN
+ && (argvars[2].v_type != VAR_UNKNOWN
+ && (tv_check_for_opt_number_arg(argvars, 3) == FAIL
+ || (argvars[3].v_type != VAR_UNKNOWN
+ && tv_check_for_opt_string_arg(argvars, 4) == FAIL))))) {
+ return;
+ }
// trylevel must be zero for a ":throw" command to be considered failed
trylevel = 0;
@@ -514,7 +536,13 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
in_assert_fails = true;
no_wait_return++;
+ const char *const cmd = tv_get_string_chk(&argvars[0]);
do_cmdline_cmd(cmd);
+
+ // reset here for any errors reported below
+ trylevel = save_trylevel;
+ suppress_errthrow = false;
+
if (called_emsg == called_emsg_before) {
prepare_assert_error(&ga);
ga_concat(&ga, "command did not fail: ");
@@ -525,6 +553,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
} else if (argvars[1].v_type != VAR_UNKNOWN) {
char buf[NUMBUFLEN];
const char *expected;
+ const char *expected_str = NULL;
bool error_found = false;
int error_found_index = 1;
char *actual = emsg_assert_fails_msg == NULL ? "[unknown]" : emsg_assert_fails_msg;
@@ -540,14 +569,23 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
const typval_T *tv = TV_LIST_ITEM_TV(tv_list_first(list));
expected = tv_get_string_buf_chk(tv, buf);
+ if (expected == NULL) {
+ goto theend;
+ }
if (!pattern_match(expected, actual, false)) {
error_found = true;
+ expected_str = expected;
} else if (tv_list_len(list) == 2) {
+ // make a copy, an error in pattern_match() may free it
+ tofree = actual = xstrdup(get_vim_var_str(VV_ERRMSG));
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 (expected == NULL) {
+ goto theend;
+ }
if (!pattern_match(expected, actual, false)) {
error_found = true;
+ expected_str = expected;
}
}
} else {
@@ -591,8 +629,8 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
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);
+ fill_assert_error(&ga, &argvars[2], expected_str,
+ &argvars[error_found_index], &actual_tv, ASSERT_FAILS);
ga_concat(&ga, ": ");
assert_append_cmd_or_arg(&ga, argvars, cmd);
assert_error(&ga);
@@ -614,6 +652,7 @@ theend:
msg_reset_scroll();
lines_left = Rows;
XFREE_CLEAR(emsg_assert_fails_msg);
+ xfree(tofree);
set_vim_var_string(VV_ERRMSG, NULL, 0);
if (wrong_arg_msg != NULL) {
emsg(_(wrong_arg_msg));
@@ -641,16 +680,9 @@ static int assert_inrange(typval_T *argvars)
if (factual < flower || factual > fupper) {
garray_T ga;
prepare_assert_error(&ga);
- if (argvars[3].v_type != VAR_UNKNOWN) {
- char *const tofree = encode_tv2string(&argvars[3], NULL);
- ga_concat(&ga, tofree);
- xfree(tofree);
- } else {
- char msg[80];
- vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g",
- flower, fupper, factual);
- ga_concat(&ga, msg);
- }
+ char expected_str[200];
+ vim_snprintf(expected_str, sizeof(expected_str), "range %g - %g,", flower, fupper);
+ fill_assert_error(&ga, &argvars[3], expected_str, NULL, &argvars[2], ASSERT_OTHER);
assert_error(&ga);
ga_clear(&ga);
return 1;
@@ -666,13 +698,11 @@ static int assert_inrange(typval_T *argvars)
if (actual < lower || actual > upper) {
garray_T ga;
prepare_assert_error(&ga);
-
- char msg[55];
- vim_snprintf(msg, sizeof(msg),
+ char expected_str[200];
+ vim_snprintf(expected_str, sizeof(expected_str),
"range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",",
- lower, upper); // -V576
- fill_assert_error(&ga, &argvars[3], msg, NULL, &argvars[2],
- ASSERT_INRANGE);
+ lower, upper);
+ fill_assert_error(&ga, &argvars[3], expected_str, NULL, &argvars[2], ASSERT_OTHER);
assert_error(&ga);
ga_clear(&ga);
return 1;
@@ -684,6 +714,13 @@ static int assert_inrange(typval_T *argvars)
/// "assert_inrange(lower, upper[, msg])" function
void f_assert_inrange(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ if (tv_check_for_float_or_nr_arg(argvars, 0) == FAIL
+ || tv_check_for_float_or_nr_arg(argvars, 1) == FAIL
+ || tv_check_for_float_or_nr_arg(argvars, 2) == FAIL
+ || tv_check_for_opt_string_arg(argvars, 3) == FAIL) {
+ return;
+ }
+
rettv->vval.v_number = assert_inrange(argvars);
}
@@ -736,5 +773,4 @@ void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalF
if (fname == NULL) {
return;
}
- list_write_log(fname);
}