xref: /vim-8.2.3635/src/message_test.c (revision 9faec4e3)
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  * message_test.c: Unittests for message.c
12  */
13 
14 #undef NDEBUG
15 #include <assert.h>
16 
17 // Must include main.c because it contains much more than just main()
18 #define NO_VIM_MAIN
19 #include "main.c"
20 
21 // This file has to be included because some of the tested functions are
22 // static.
23 #include "message.c"
24 
25 #ifndef MIN
26 # define MIN(x,y) ((x) < (y) ? (x) : (y))
27 #endif
28 
29 // These formats are not standard in C printf() function.
30 // Use a global variable rather than a literal format to disable
31 // -Wformat compiler warnings:
32 //
33 // - warning: '0' flag used with ‘%p’ gnu_printf format
34 // - warning: format ‘%S’ expects argument of type ‘wchar_t *’, but argument 4 has type ‘char *’
35 // - warning: unknown conversion type character ‘b’ in format
36 //
37 // These formats are in practise only used from vim script printf()
38 // function and never as literals in C code.
39 char *fmt_012p = "%012p";
40 char *fmt_5S   = "%5S";
41 char *fmt_06b  = "%06b";
42 
43 /*
44  * Test trunc_string().
45  */
46     static void
47 test_trunc_string(void)
48 {
49     char_u  *buf; /*allocated every time to find uninit errors */
50     char_u  *s;
51 
52     // Should not write anything to destination if buflen is 0.
53     trunc_string((char_u *)"", NULL, 1, 0);
54 
55     // Truncating an empty string does nothing.
56     buf = alloc(1);
57     trunc_string((char_u *)"", buf, 1, 1);
58     assert(buf[0] == NUL);
59     vim_free(buf);
60 
61     // in place
62     buf = alloc(40);
63     STRCPY(buf, "text");
64     trunc_string(buf, buf, 20, 40);
65     assert(STRCMP(buf, "text") == 0);
66     vim_free(buf);
67 
68     buf = alloc(40);
69     STRCPY(buf, "a short text");
70     trunc_string(buf, buf, 20, 40);
71     assert(STRCMP(buf, "a short text") == 0);
72     vim_free(buf);
73 
74     buf = alloc(40);
75     STRCPY(buf, "a text tha just fits");
76     trunc_string(buf, buf, 20, 40);
77     assert(STRCMP(buf, "a text tha just fits") == 0);
78     vim_free(buf);
79 
80     buf = alloc(40);
81     STRCPY(buf, "a text that nott fits");
82     trunc_string(buf, buf, 20, 40);
83     assert(STRCMP(buf, "a text t...nott fits") == 0);
84     vim_free(buf);
85 
86     // copy from string to buf
87     buf = alloc(40);
88     s = vim_strsave((char_u *)"text");
89     trunc_string(s, buf, 20, 40);
90     assert(STRCMP(buf, "text") == 0);
91     vim_free(buf);
92     vim_free(s);
93 
94     buf = alloc(40);
95     s = vim_strsave((char_u *)"a text that fits");
96     trunc_string(s, buf, 34, 40);
97     assert(STRCMP(buf, "a text that fits") == 0);
98     vim_free(buf);
99     vim_free(s);
100 
101     buf = alloc(40);
102     s = vim_strsave((char_u *)"a short text");
103     trunc_string(s, buf, 20, 40);
104     assert(STRCMP(buf, "a short text") == 0);
105     vim_free(buf);
106     vim_free(s);
107 
108     buf = alloc(40);
109     s = vim_strsave((char_u *)"a text tha just fits");
110     trunc_string(s, buf, 20, 40);
111     assert(STRCMP(buf, "a text tha just fits") == 0);
112     vim_free(buf);
113     vim_free(s);
114 
115     buf = alloc(40);
116     s = vim_strsave((char_u *)"a text that nott fits");
117     trunc_string(s, buf, 20, 40);
118     assert(STRCMP(buf, "a text t...nott fits") == 0);
119     vim_free(buf);
120     vim_free(s);
121 }
122 
123 /*
124  * Test vim_snprintf() with a focus on checking that truncation is
125  * correct when buffer is small, since it cannot be tested from
126  * vim scrip tests. Check that:
127  * - no buffer overflows happens (with valgrind or asan)
128  * - output string is always NUL terminated.
129  *
130  * Not all formats of vim_snprintf() are checked here. They are
131  * checked more exhaustively in Test_printf*() vim script tests.
132  */
133     static void
134 test_vim_snprintf(void)
135 {
136     int		n;
137     size_t	bsize;
138     int		bsize_int;
139     void	*ptr = (void *)0x87654321;
140 
141     // Loop on various buffer sizes to make sure that truncation of
142     // vim_snprintf() is correct.
143     for (bsize = 0; bsize < 15; ++bsize)
144     {
145 	bsize_int = (int)bsize - 1;
146 
147 	// buf is the heap rather than in the stack
148 	// so valgrind can detect buffer overflows if any.
149 	// Use malloc() rather than alloc() as test checks with 0-size
150 	// buffer and its content should then never be used.
151 	char *buf = malloc(bsize);
152 
153 	n = vim_snprintf(buf, bsize, "%d", 1234567);
154 	assert(n == 7);
155 	assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
156 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
157 
158 	n = vim_snprintf(buf, bsize, "%ld", 1234567L);
159 	assert(n == 7);
160 	assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
161 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
162 
163 	n = vim_snprintf(buf, bsize, "%9ld", 1234567L);
164 	assert(n == 9);
165 	assert(bsize == 0 || STRNCMP(buf, "  1234567", bsize_int) == 0);
166 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
167 
168 	n = vim_snprintf(buf, bsize, "%-9ld", 1234567L);
169 	assert(n == 9);
170 	assert(bsize == 0 || STRNCMP(buf, "1234567  ", bsize_int) == 0);
171 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
172 
173 	n = vim_snprintf(buf, bsize, "%x", 0xdeadbeef);
174 	assert(n == 8);
175 	assert(bsize == 0 || STRNCMP(buf, "deadbeef", bsize_int) == 0);
176 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
177 
178 	n = vim_snprintf(buf, bsize, fmt_06b, (uvarnumber_T)12);
179 	assert(n == 6);
180 	assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0);
181 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
182 
183 #ifdef FEAT_FLOAT
184 	n = vim_snprintf(buf, bsize, "%f", 1.234);
185 	assert(n == 8);
186 	assert(bsize == 0 || STRNCMP(buf, "1.234000", bsize_int) == 0);
187 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
188 
189 	n = vim_snprintf(buf, bsize, "%e", 1.234);
190 	assert(n == 12);
191 	assert(bsize == 0 || STRNCMP(buf, "1.234000e+00", bsize_int) == 0);
192 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
193 
194 	n = vim_snprintf(buf, bsize, "%f", 0.0/0.0);
195 	assert(n == 3);
196 	assert(bsize == 0 || STRNCMP(buf, "nan", bsize_int) == 0);
197 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
198 
199 	n = vim_snprintf(buf, bsize, "%f", 1.0/0.0);
200 	assert(n == 3);
201 	assert(bsize == 0 || STRNCMP(buf, "inf", bsize_int) == 0);
202 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
203 
204 	n = vim_snprintf(buf, bsize, "%f", -1.0/0.0);
205 	assert(n == 4);
206 	assert(bsize == 0 || STRNCMP(buf, "-inf", bsize_int) == 0);
207 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
208 
209 	n = vim_snprintf(buf, bsize, "%f", -0.0);
210 	assert(n == 9);
211 	assert(bsize == 0 || STRNCMP(buf, "-0.000000", bsize_int) == 0);
212 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
213 #endif
214 
215 	n = vim_snprintf(buf, bsize, "%s", "漢語");
216 	assert(n == 6);
217 	assert(bsize == 0 || STRNCMP(buf, "漢語", bsize_int) == 0);
218 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
219 
220 	n = vim_snprintf(buf, bsize, "%8s", "漢語");
221 	assert(n == 8);
222 	assert(bsize == 0 || STRNCMP(buf, "  漢語", bsize_int) == 0);
223 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
224 
225 	n = vim_snprintf(buf, bsize, "%-8s", "漢語");
226 	assert(n == 8);
227 	assert(bsize == 0 || STRNCMP(buf, "漢語  ", bsize_int) == 0);
228 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
229 
230 	n = vim_snprintf(buf, bsize, "%.3s", "漢語");
231 	assert(n == 3);
232 	assert(bsize == 0 || STRNCMP(buf, "漢", bsize_int) == 0);
233 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
234 
235 	n = vim_snprintf(buf, bsize, fmt_5S, "foo");
236 	assert(n == 5);
237 	assert(bsize == 0 || STRNCMP(buf, "  foo", bsize_int) == 0);
238 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
239 
240 	n = vim_snprintf(buf, bsize, "%%%%%%");
241 	assert(n == 3);
242 	assert(bsize == 0 || STRNCMP(buf, "%%%", bsize_int) == 0);
243 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
244 
245 	n = vim_snprintf(buf, bsize, "%c%c", 1, 2);
246 	assert(n == 2);
247 	assert(bsize == 0 || STRNCMP(buf, "\x01\x02", bsize_int) == 0);
248 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
249 
250 	// %p format is not tested in vim script tests Test_printf*()
251 	// as it only makes sense in C code.
252 	// NOTE: SunOS libc doesn't use the prefix "0x" on %p.
253 #ifdef SUN_SYSTEM
254 # define PREFIX_LEN  0
255 # define PREFIX_STR1 ""
256 # define PREFIX_STR2 "00"
257 #else
258 # define PREFIX_LEN  2
259 # define PREFIX_STR1 "0x"
260 # define PREFIX_STR2 "0x"
261 #endif
262 	n = vim_snprintf(buf, bsize, "%p", ptr);
263 	assert(n == 8 + PREFIX_LEN);
264 	assert(bsize == 0
265 		     || STRNCMP(buf, PREFIX_STR1 "87654321", bsize_int) == 0);
266 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
267 
268 	n = vim_snprintf(buf, bsize, fmt_012p, ptr);
269 	assert(n == 12);
270 	assert(bsize == 0
271 		   || STRNCMP(buf, PREFIX_STR2 "0087654321", bsize_int) == 0);
272 	assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
273 
274 	free(buf);
275     }
276 }
277 
278     int
279 main(int argc, char **argv)
280 {
281     CLEAR_FIELD(params);
282     params.argc = argc;
283     params.argv = argv;
284     common_init(&params);
285 
286     set_option_value((char_u *)"encoding", 0, (char_u *)"utf-8", 0);
287     init_chartab();
288     test_trunc_string();
289     test_vim_snprintf();
290 
291     set_option_value((char_u *)"encoding", 0, (char_u *)"latin1", 0);
292     init_chartab();
293     test_trunc_string();
294     test_vim_snprintf();
295 
296     return 0;
297 }
298