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
test_trunc_string(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 trunc_string() with mbyte chars.
125 */
126 static void
test_trunc_string_mbyte(void)127 test_trunc_string_mbyte(void)
128 {
129 char_u *buf; // allocated every time to find uninit errors
130 char_u *s;
131
132 buf = alloc(40);
133 s = vim_strsave((char_u *)"Ä text tha just fits");
134 trunc_string(s, buf, 20, 40);
135 assert(STRCMP(buf, "Ä text tha just fits") == 0);
136 vim_free(buf);
137 vim_free(s);
138
139 buf = alloc(40);
140 s = vim_strsave((char_u *)"a text ÄÖÜä nott fits");
141 trunc_string(s, buf, 20, 40);
142 assert(STRCMP(buf, "a text Ä...nott fits") == 0);
143 vim_free(buf);
144 vim_free(s);
145
146 buf = alloc(40);
147 s = vim_strsave((char_u *)"a text that not fitsÄ");
148 trunc_string(s, buf, 20, 40);
149 assert(STRCMP(buf, "a text t...not fitsÄ") == 0);
150 vim_free(buf);
151 vim_free(s);
152 }
153
154 /*
155 * Test vim_snprintf() with a focus on checking that truncation is
156 * correct when buffer is small, since it cannot be tested from
157 * vim scrip tests. Check that:
158 * - no buffer overflows happens (with valgrind or asan)
159 * - output string is always NUL terminated.
160 *
161 * Not all formats of vim_snprintf() are checked here. They are
162 * checked more exhaustively in Test_printf*() vim script tests.
163 */
164 static void
test_vim_snprintf(void)165 test_vim_snprintf(void)
166 {
167 int n;
168 size_t bsize;
169 int bsize_int;
170 void *ptr = (void *)0x87654321;
171
172 // Loop on various buffer sizes to make sure that truncation of
173 // vim_snprintf() is correct.
174 for (bsize = 0; bsize < 15; ++bsize)
175 {
176 bsize_int = (int)bsize - 1;
177
178 // buf is the heap rather than in the stack
179 // so valgrind can detect buffer overflows if any.
180 // Use malloc() rather than alloc() as test checks with 0-size
181 // buffer and its content should then never be used.
182 char *buf = malloc(bsize);
183
184 n = vim_snprintf(buf, bsize, "%d", 1234567);
185 assert(n == 7);
186 assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
187 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
188
189 n = vim_snprintf(buf, bsize, "%ld", 1234567L);
190 assert(n == 7);
191 assert(bsize == 0 || STRNCMP(buf, "1234567", bsize_int) == 0);
192 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
193
194 n = vim_snprintf(buf, bsize, "%9ld", 1234567L);
195 assert(n == 9);
196 assert(bsize == 0 || STRNCMP(buf, " 1234567", bsize_int) == 0);
197 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
198
199 n = vim_snprintf(buf, bsize, "%-9ld", 1234567L);
200 assert(n == 9);
201 assert(bsize == 0 || STRNCMP(buf, "1234567 ", bsize_int) == 0);
202 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
203
204 n = vim_snprintf(buf, bsize, "%x", 0xdeadbeef);
205 assert(n == 8);
206 assert(bsize == 0 || STRNCMP(buf, "deadbeef", bsize_int) == 0);
207 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
208
209 n = vim_snprintf(buf, bsize, fmt_06b, (uvarnumber_T)12);
210 assert(n == 6);
211 assert(bsize == 0 || STRNCMP(buf, "001100", bsize_int) == 0);
212 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
213
214 #ifdef FEAT_FLOAT
215 n = vim_snprintf(buf, bsize, "%f", 1.234);
216 assert(n == 8);
217 assert(bsize == 0 || STRNCMP(buf, "1.234000", bsize_int) == 0);
218 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
219
220 n = vim_snprintf(buf, bsize, "%e", 1.234);
221 assert(n == 12);
222 assert(bsize == 0 || STRNCMP(buf, "1.234000e+00", bsize_int) == 0);
223 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
224
225 n = vim_snprintf(buf, bsize, "%f", 0.0/0.0);
226 assert(n == 3);
227 assert(bsize == 0 || STRNCMP(buf, "nan", bsize_int) == 0);
228 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
229
230 n = vim_snprintf(buf, bsize, "%f", 1.0/0.0);
231 assert(n == 3);
232 assert(bsize == 0 || STRNCMP(buf, "inf", bsize_int) == 0);
233 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
234
235 n = vim_snprintf(buf, bsize, "%f", -1.0/0.0);
236 assert(n == 4);
237 assert(bsize == 0 || STRNCMP(buf, "-inf", bsize_int) == 0);
238 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
239
240 n = vim_snprintf(buf, bsize, "%f", -0.0);
241 assert(n == 9);
242 assert(bsize == 0 || STRNCMP(buf, "-0.000000", bsize_int) == 0);
243 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
244 #endif
245
246 n = vim_snprintf(buf, bsize, "%s", "漢語");
247 assert(n == 6);
248 assert(bsize == 0 || STRNCMP(buf, "漢語", bsize_int) == 0);
249 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
250
251 n = vim_snprintf(buf, bsize, "%8s", "漢語");
252 assert(n == 8);
253 assert(bsize == 0 || STRNCMP(buf, " 漢語", bsize_int) == 0);
254 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
255
256 n = vim_snprintf(buf, bsize, "%-8s", "漢語");
257 assert(n == 8);
258 assert(bsize == 0 || STRNCMP(buf, "漢語 ", bsize_int) == 0);
259 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
260
261 n = vim_snprintf(buf, bsize, "%.3s", "漢語");
262 assert(n == 3);
263 assert(bsize == 0 || STRNCMP(buf, "漢", bsize_int) == 0);
264 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
265
266 n = vim_snprintf(buf, bsize, fmt_5S, "foo");
267 assert(n == 5);
268 assert(bsize == 0 || STRNCMP(buf, " foo", bsize_int) == 0);
269 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
270
271 n = vim_snprintf(buf, bsize, "%%%%%%");
272 assert(n == 3);
273 assert(bsize == 0 || STRNCMP(buf, "%%%", bsize_int) == 0);
274 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
275
276 n = vim_snprintf(buf, bsize, "%c%c", 1, 2);
277 assert(n == 2);
278 assert(bsize == 0 || STRNCMP(buf, "\x01\x02", bsize_int) == 0);
279 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
280
281 // %p format is not tested in vim script tests Test_printf*()
282 // as it only makes sense in C code.
283 // NOTE: SunOS libc doesn't use the prefix "0x" on %p.
284 #ifdef SUN_SYSTEM
285 # define PREFIX_LEN 0
286 # define PREFIX_STR1 ""
287 # define PREFIX_STR2 "00"
288 #else
289 # define PREFIX_LEN 2
290 # define PREFIX_STR1 "0x"
291 # define PREFIX_STR2 "0x"
292 #endif
293 n = vim_snprintf(buf, bsize, "%p", ptr);
294 assert(n == 8 + PREFIX_LEN);
295 assert(bsize == 0
296 || STRNCMP(buf, PREFIX_STR1 "87654321", bsize_int) == 0);
297 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
298
299 n = vim_snprintf(buf, bsize, fmt_012p, ptr);
300 assert(n == 12);
301 assert(bsize == 0
302 || STRNCMP(buf, PREFIX_STR2 "0087654321", bsize_int) == 0);
303 assert(bsize == 0 || buf[MIN(n, bsize_int)] == '\0');
304
305 free(buf);
306 }
307 }
308
309 int
main(int argc,char ** argv)310 main(int argc, char **argv)
311 {
312 CLEAR_FIELD(params);
313 params.argc = argc;
314 params.argv = argv;
315 common_init(¶ms);
316
317 set_option_value((char_u *)"encoding", 0, (char_u *)"utf-8", 0);
318 init_chartab();
319 test_trunc_string();
320 test_trunc_string_mbyte();
321 test_vim_snprintf();
322
323 set_option_value((char_u *)"encoding", 0, (char_u *)"latin1", 0);
324 init_chartab();
325 test_trunc_string();
326 test_vim_snprintf();
327
328 return 0;
329 }
330