summaryrefslogtreecommitdiffstats
path: root/src/message_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/message_test.c')
-rw-r--r--src/message_test.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/src/message_test.c b/src/message_test.c
new file mode 100644
index 0000000..21ec4db
--- /dev/null
+++ b/src/message_test.c
@@ -0,0 +1,327 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * message_test.c: Unittests for message.c
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+// Must include main.c because it contains much more than just main()
+#define NO_VIM_MAIN
+#include "main.c"
+
+// This file has to be included because some of the tested functions are
+// static.
+#include "message.c"
+
+#ifndef MIN
+# define MIN(x,y) ((x) < (y) ? (x) : (y))
+#endif
+
+// These formats are not standard in C printf() function.
+// Use a global variable rather than a literal format to disable
+// -Wformat compiler warnings:
+//
+// - warning: '0' flag used with ‘%p’ gnu_printf format
+// - warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 4 has type ‘char *’
+// - warning: unknown conversion type character ‘b’ in format
+//
+// These formats are in practise only used from vim script printf()
+// function and never as literals in C code.
+char *fmt_012p = "%012p";
+char *fmt_5S = "%5S";
+char *fmt_06b = "%06b";
+
+/*
+ * Test trunc_string().
+ */
+ static void
+test_trunc_string(void)
+{
+ char_u *buf; /*allocated every time to find uninit errors */
+ char_u *s;
+
+ // Should not write anything to destination if buflen is 0.
+ trunc_string((char_u *)"", NULL, 1, 0);
+
+ // Truncating an empty string does nothing.
+ buf = alloc(1);
+ trunc_string((char_u *)"", buf, 1, 1);
+ assert(buf[0] == NUL);
+ vim_free(buf);
+
+ // in place
+ buf = alloc(40);
+ STRCPY(buf, "text");
+ trunc_string(buf, buf, 20, 40);
+ assert(STRCMP(buf, "text") == 0);
+ vim_free(buf);
+
+ buf = alloc(40);
+ STRCPY(buf, "a short text");
+ trunc_string(buf, buf, 20, 40);
+ assert(STRCMP(buf, "a short text") == 0);
+ vim_free(buf);
+
+ buf = alloc(40);
+ STRCPY(buf, "a text tha just fits");
+ trunc_string(buf, buf, 20, 40);
+ assert(STRCMP(buf, "a text tha just fits") == 0);
+ vim_free(buf);
+
+ buf = alloc(40);
+ STRCPY(buf, "a text that nott fits");
+ trunc_string(buf, buf, 20, 40);
+ assert(STRCMP(buf, "a text t...nott fits") == 0);
+ vim_free(buf);
+
+ // copy from string to buf
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"text");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "text") == 0);
+ vim_free(buf);
+ vim_free(s);
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"a text that fits");
+ trunc_string(s, buf, 34, 40);
+ assert(STRCMP(buf, "a text that fits") == 0);
+ vim_free(buf);
+ vim_free(s);
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"a short text");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "a short text") == 0);
+ vim_free(buf);
+ vim_free(s);
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"a text tha just fits");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "a text tha just fits") == 0);
+ vim_free(buf);
+ vim_free(s);
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"a text that nott fits");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "a text t...nott fits") == 0);
+ vim_free(buf);
+ vim_free(s);
+}
+
+/*
+ * Test trunc_string() with mbyte chars.
+ */
+ static void
+test_trunc_string_mbyte(void)
+{
+ char_u *buf; // allocated every time to find uninit errors
+ char_u *s;
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"Ä text tha just fits");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "Ä text tha just fits") == 0);
+ vim_free(buf);
+ vim_free(s);
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"a text ÄÖÜä nott fits");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "a text Ä...nott fits") == 0);
+ vim_free(buf);
+ vim_free(s);
+
+ buf = alloc(40);
+ s = vim_strsave((char_u *)"a text that not fitsÄ");
+ trunc_string(s, buf, 20, 40);
+ assert(STRCMP(buf, "a text t...not fitsÄ") == 0);
+ vim_free(buf);
+ vim_free(s);
+}
+
+/*
+ * Test vim_snprintf() with a focus on checking that truncation is
+ * correct when buffer is small, since it cannot be tested from
+ * vim script tests. Check that:
+ * - no buffer overflows happens (with valgrind or asan)
+ * - output string is always NUL terminated.
+ *
+ * Not all formats of vim_snprintf() are checked here. They are
+ * checked more exhaustively in Test_printf*() vim script tests.
+ */
+ static void
+test_vim_snprintf(void)
+{
+ int n;
+ size_t bsize;
+ int bsize_int;
+ void *ptr = (void *)0x87654321;
+
+ // Loop on various buffer sizes to make sure that truncation of
+ // vim_snprintf() is correct.
+ for (bsize = 0; bsize < 15; ++bsize)
+ {
+ bsize_int = (int)bsize - 1;
+
+ // buf is the heap rather than in the stack
+ // so valgrind can detect buffer overflows if any.
+ // Use malloc() rather than alloc() as test checks with 0-size
+ // buffer and its content should then never be used.
+ char *buf = malloc(bsize);
+
+ n = vim_snprintf(buf, bsize, "%d", 1234567);
+ assert(n == 7);
+ assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%ld", 1234567L);
+ assert(n == 7);
+ assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%9ld", 1234567L);
+ assert(n == 9);
+ assert(bsize == 0 || STRNCMP(buf, " 1234567", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%-9ld", 1234567L);
+ assert(n == 9);
+ assert(bsize == 0 || STRNCMP(buf, "1234567 ", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%x", 0xdeadbeef);
+ assert(n == 8);
+ assert(bsize == 0 || STRNCMP(buf, "deadbeef", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, fmt_06b, (uvarnumber_T)12);
+ assert(n == 6);
+ assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%f", 1.234);
+ assert(n == 8);
+ assert(bsize == 0 || STRNCMP(buf, "1.234000", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%e", 1.234);
+ assert(n == 12);
+ assert(bsize == 0 || STRNCMP(buf, "1.234000e+00", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%f", 0.0/0.0);
+ assert(n == 3);
+ assert(bsize == 0 || STRNCMP(buf, "nan", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%f", 1.0/0.0);
+ assert(n == 3);
+ assert(bsize == 0 || STRNCMP(buf, "inf", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%f", -1.0/0.0);
+ assert(n == 4);
+ assert(bsize == 0 || STRNCMP(buf, "-inf", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%f", -0.0);
+ assert(n == 9);
+ assert(bsize == 0 || STRNCMP(buf, "-0.000000", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%s", "漢語");
+ assert(n == 6);
+ assert(bsize == 0 || STRNCMP(buf, "漢語", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%8s", "漢語");
+ assert(n == 8);
+ assert(bsize == 0 || STRNCMP(buf, " 漢語", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%-8s", "漢語");
+ assert(n == 8);
+ assert(bsize == 0 || STRNCMP(buf, "漢語 ", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%.3s", "漢語");
+ assert(n == 3);
+ assert(bsize == 0 || STRNCMP(buf, "漢", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, fmt_5S, "foo");
+ assert(n == 5);
+ assert(bsize == 0 || STRNCMP(buf, " foo", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%%%%%%");
+ assert(n == 3);
+ assert(bsize == 0 || STRNCMP(buf, "%%%", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, "%c%c", 1, 2);
+ assert(n == 2);
+ assert(bsize == 0 || STRNCMP(buf, "\x01\x02", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ // %p format is not tested in vim script tests Test_printf*()
+ // as it only makes sense in C code.
+ // NOTE: SunOS libc doesn't use the prefix "0x" on %p.
+#ifdef SUN_SYSTEM
+# define PREFIX_LEN 0
+# define PREFIX_STR1 ""
+# define PREFIX_STR2 "00"
+#else
+# define PREFIX_LEN 2
+# define PREFIX_STR1 "0x"
+# define PREFIX_STR2 "0x"
+#endif
+ n = vim_snprintf(buf, bsize, "%p", ptr);
+ assert(n == 8 + PREFIX_LEN);
+ assert(bsize == 0
+ || STRNCMP(buf, PREFIX_STR1 "87654321", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ n = vim_snprintf(buf, bsize, fmt_012p, ptr);
+ assert(n == 12);
+ assert(bsize == 0
+ || STRNCMP(buf, PREFIX_STR2 "0087654321", bsize_int) == 0);
+ assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
+
+ free(buf);
+ }
+}
+
+ int
+main(int argc, char **argv)
+{
+ CLEAR_FIELD(params);
+ params.argc = argc;
+ params.argv = argv;
+ common_init(&params);
+
+ set_option_value_give_err((char_u *)"encoding", 0, (char_u *)"utf-8", 0);
+ init_chartab();
+ test_trunc_string();
+ test_trunc_string_mbyte();
+ test_vim_snprintf();
+
+ set_option_value_give_err((char_u *)"encoding", 0, (char_u *)"latin1", 0);
+ init_chartab();
+ test_trunc_string();
+ test_vim_snprintf();
+
+ return 0;
+}