1 | /* GLib testing framework examples and tests |
2 | * Copyright (C) 2019 Руслан Ижбулатов <lrn1986@gmail.com> |
3 | * |
4 | * This work is provided "as is"; redistribution and modification |
5 | * in whole or in part, in any medium, physical or electronic is |
6 | * permitted without restriction. |
7 | * |
8 | * This work is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
11 | * |
12 | * In no event shall the authors or contributors be liable for any |
13 | * direct, indirect, incidental, special, exemplary, or consequential |
14 | * damages (including, but not limited to, procurement of substitute |
15 | * goods or services; loss of use, data, or profits; or business |
16 | * interruption) however caused and on any theory of liability, whether |
17 | * in contract, strict liability, or tort (including negligence or |
18 | * otherwise) arising in any way out of the use of this software, even |
19 | * if advised of the possibility of such damage. |
20 | */ |
21 | |
22 | #include <glib/glib.h> |
23 | #include <gio/gio.h> |
24 | #include <stdlib.h> |
25 | |
26 | #include "../giowin32-private.c" |
27 | |
28 | static int |
29 | g_utf16_cmp0 (const gunichar2 *str1, |
30 | const gunichar2 *str2) |
31 | { |
32 | if (!str1) |
33 | return -(str1 != str2); |
34 | if (!str2) |
35 | return str1 != str2; |
36 | |
37 | while (TRUE) |
38 | { |
39 | if (str1[0] > str2[0]) |
40 | return 1; |
41 | else if (str1[0] < str2[0]) |
42 | return -1; |
43 | else if (str1[0] == 0 && str2[0] == 0) |
44 | return 0; |
45 | |
46 | str1++; |
47 | str2++; |
48 | } |
49 | } |
50 | |
51 | #define g_assert_cmputf16(s1, cmp, s2, s1u8, s2u8) \ |
52 | G_STMT_START { \ |
53 | const gunichar2 *__s1 = (s1), *__s2 = (s2); \ |
54 | if (g_utf16_cmp0 (__s1, __s2) cmp 0) ; else \ |
55 | g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ |
56 | #s1u8 " " #cmp " " #s2u8, s1u8, #cmp, s2u8); \ |
57 | } G_STMT_END |
58 | |
59 | static void |
60 | test_utf16_strfuncs (void) |
61 | { |
62 | gsize i; |
63 | |
64 | struct { |
65 | gsize len; |
66 | const gunichar2 utf16[10]; |
67 | const gchar *utf8; |
68 | const gchar *utf8_folded; |
69 | } string_cases[] = { |
70 | { |
71 | 0, |
72 | { 0x0000 }, |
73 | "" , |
74 | "" , |
75 | }, |
76 | { |
77 | 1, |
78 | { 0x0020, 0x0000 }, |
79 | " " , |
80 | " " , |
81 | }, |
82 | { |
83 | 2, |
84 | { 0x0020, 0xd800, 0x0000 }, |
85 | NULL, |
86 | NULL, |
87 | }, |
88 | }; |
89 | |
90 | for (i = 0; i < G_N_ELEMENTS (string_cases); i++) |
91 | { |
92 | gsize len; |
93 | gunichar2 *str; |
94 | gboolean success; |
95 | gchar *utf8; |
96 | gchar *utf8_folded; |
97 | |
98 | len = g_utf16_len (str: string_cases[i].utf16); |
99 | g_assert_cmpuint (len, ==, string_cases[i].len); |
100 | |
101 | str = (gunichar2 *) g_utf16_find_basename (filename: string_cases[i].utf16, len: -1); |
102 | /* This only works because all testcases lack separators */ |
103 | g_assert_true (string_cases[i].utf16 == str); |
104 | |
105 | str = g_wcsdup (str: string_cases[i].utf16, str_len: string_cases[i].len); |
106 | g_assert_cmpmem (string_cases[i].utf16, len, str, len); |
107 | g_free (mem: str); |
108 | |
109 | str = g_wcsdup (str: string_cases[i].utf16, str_len: -1); |
110 | g_assert_cmpmem (string_cases[i].utf16, len, str, len); |
111 | g_free (mem: str); |
112 | |
113 | success = g_utf16_to_utf8_and_fold (str: string_cases[i].utf16, length: -1, NULL, NULL); |
114 | |
115 | if (string_cases[i].utf8 == NULL) |
116 | g_assert_false (success); |
117 | else |
118 | g_assert_true (success); |
119 | |
120 | utf8 = NULL; |
121 | success = g_utf16_to_utf8_and_fold (str: string_cases[i].utf16, length: -1, str_u8: &utf8, NULL); |
122 | |
123 | if (string_cases[i].utf8 != NULL) |
124 | { |
125 | g_assert_true (success); |
126 | g_assert_cmpstr (string_cases[i].utf8, ==, utf8); |
127 | /* This only works because all testcases lack separators */ |
128 | g_assert_true (utf8 == g_utf8_find_basename (utf8, len)); |
129 | } |
130 | |
131 | g_free (mem: utf8); |
132 | |
133 | utf8 = NULL; |
134 | utf8_folded = NULL; |
135 | success = g_utf16_to_utf8_and_fold (str: string_cases[i].utf16, length: -1, str_u8: &utf8, str_u8_folded: &utf8_folded); |
136 | |
137 | if (string_cases[i].utf8 != NULL) |
138 | { |
139 | g_assert_true (success); |
140 | g_assert_cmpstr (string_cases[i].utf8_folded, ==, utf8_folded); |
141 | } |
142 | |
143 | g_free (mem: utf8); |
144 | g_free (mem: utf8_folded); |
145 | } |
146 | } |
147 | |
148 | struct { |
149 | const char *orig; |
150 | const char *executable; |
151 | const char *executable_basename; |
152 | gboolean is_rundll32; |
153 | const char *fixed; |
154 | } rundll32_commandlines[] = { |
155 | { |
156 | "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1" , |
157 | "%SystemRoot%\\System32\\rundll32.exe" , |
158 | "rundll32.exe" , |
159 | TRUE, |
160 | "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1" , |
161 | }, |
162 | { |
163 | "%SystemRoot%/System32/rundll32.exe \"%ProgramFiles%/Windows Photo Viewer/PhotoViewer.dll\", ImageView_Fullscreen %1" , |
164 | "%SystemRoot%/System32/rundll32.exe" , |
165 | "rundll32.exe" , |
166 | TRUE, |
167 | "%SystemRoot%/System32/rundll32.exe \"%ProgramFiles%/Windows Photo Viewer/PhotoViewer.dll\" ImageView_Fullscreen %1" , |
168 | }, |
169 | { |
170 | "%SystemRoot%\\System32/rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1" , |
171 | "%SystemRoot%\\System32/rundll32.exe" , |
172 | "rundll32.exe" , |
173 | TRUE, |
174 | "%SystemRoot%\\System32/rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1" , |
175 | }, |
176 | { |
177 | "\"some path with spaces\\rundll32.exe\" \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\", ImageView_Fullscreen %1" , |
178 | "some path with spaces\\rundll32.exe" , |
179 | "rundll32.exe" , |
180 | TRUE, |
181 | "\"some path with spaces\\rundll32.exe\" \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1" , |
182 | }, |
183 | { |
184 | " \"some path with spaces\\rundll32.exe\"\"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\",ImageView_Fullscreen %1" , |
185 | "some path with spaces\\rundll32.exe" , |
186 | "rundll32.exe" , |
187 | TRUE, |
188 | " \"some path with spaces\\rundll32.exe\"\"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll\" ImageView_Fullscreen %1" , |
189 | }, |
190 | { |
191 | "rundll32.exe foo.bar,baz" , |
192 | "rundll32.exe" , |
193 | "rundll32.exe" , |
194 | TRUE, |
195 | "rundll32.exe foo.bar baz" , |
196 | }, |
197 | { |
198 | " rundll32.exe foo.bar,baz" , |
199 | "rundll32.exe" , |
200 | "rundll32.exe" , |
201 | TRUE, |
202 | " rundll32.exe foo.bar baz" , |
203 | }, |
204 | { |
205 | "rundll32.exe" , |
206 | "rundll32.exe" , |
207 | "rundll32.exe" , |
208 | FALSE, |
209 | NULL, |
210 | }, |
211 | { |
212 | "rundll32.exe ,foobar" , |
213 | "rundll32.exe" , |
214 | "rundll32.exe" , |
215 | FALSE, |
216 | NULL, |
217 | }, |
218 | { |
219 | "rundll32.exe ,foobar" , |
220 | "rundll32.exe" , |
221 | "rundll32.exe" , |
222 | FALSE, |
223 | NULL, |
224 | }, |
225 | { |
226 | "rundll32.exe foo.dll" , |
227 | "rundll32.exe" , |
228 | "rundll32.exe" , |
229 | FALSE, |
230 | NULL, |
231 | }, |
232 | { |
233 | "rundll32.exe \"foo bar\",baz" , |
234 | "rundll32.exe" , |
235 | "rundll32.exe" , |
236 | TRUE, |
237 | "rundll32.exe \"foo bar\" baz" , |
238 | }, |
239 | { |
240 | "\"rundll32.exe\" \"foo bar\",baz" , |
241 | "rundll32.exe" , |
242 | "rundll32.exe" , |
243 | TRUE, |
244 | "\"rundll32.exe\" \"foo bar\" baz" , |
245 | }, |
246 | { |
247 | "\"rundll32.exe\" \"foo bar\",, , ,,, , ,,baz" , |
248 | "rundll32.exe" , |
249 | "rundll32.exe" , |
250 | TRUE, |
251 | "\"rundll32.exe\" \"foo bar\" , , ,,, , ,,baz" , |
252 | }, |
253 | { |
254 | "\"rundll32.exe\" foo.bar,,,,,,,,,baz" , |
255 | "rundll32.exe" , |
256 | "rundll32.exe" , |
257 | TRUE, |
258 | "\"rundll32.exe\" foo.bar ,,,,,,,,baz" , |
259 | }, |
260 | { |
261 | "\"rundll32.exe\" foo.bar baz" , |
262 | "rundll32.exe" , |
263 | "rundll32.exe" , |
264 | TRUE, |
265 | "\"rundll32.exe\" foo.bar baz" , |
266 | }, |
267 | { |
268 | "\"RuNdlL32.exe\" foo.bar baz" , |
269 | "RuNdlL32.exe" , |
270 | "RuNdlL32.exe" , |
271 | TRUE, |
272 | "\"RuNdlL32.exe\" foo.bar baz" , |
273 | }, |
274 | { |
275 | "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll,\" ImageView_Fullscreen %1" , |
276 | "%SystemRoot%\\System32\\rundll32.exe" , |
277 | "rundll32.exe" , |
278 | TRUE, |
279 | "%SystemRoot%\\System32\\rundll32.exe \"%ProgramFiles%\\Windows Photo Viewer\\PhotoViewer.dll,\" ImageView_Fullscreen %1" , |
280 | }, |
281 | { |
282 | "\"rundll32.exe\" \"foo bar,\"baz" , |
283 | "rundll32.exe" , |
284 | "rundll32.exe" , |
285 | TRUE, |
286 | "\"rundll32.exe\" \"foo bar,\"baz" , |
287 | }, |
288 | { |
289 | "\"rundll32.exe\" some,thing" , |
290 | "rundll32.exe" , |
291 | "rundll32.exe" , |
292 | TRUE, |
293 | "\"rundll32.exe\" some thing" , |
294 | }, |
295 | { |
296 | "\"rundll32.exe\" some," , |
297 | "rundll32.exe" , |
298 | "rundll32.exe" , |
299 | FALSE, |
300 | "\"rundll32.exe\" some," , |
301 | }, |
302 | /* These filenames are not allowed on Windows, but our function doesn't care about that */ |
303 | { |
304 | "run\"dll32.exe foo\".bar,baz" , |
305 | "run\"dll32.exe" , |
306 | "run\"dll32.exe" , |
307 | FALSE, |
308 | NULL, |
309 | }, |
310 | { |
311 | "run,dll32.exe foo.bar,baz" , |
312 | "run,dll32.exe" , |
313 | "run,dll32.exe" , |
314 | FALSE, |
315 | NULL, |
316 | }, |
317 | { |
318 | "\"rundll32.exe\" some, thing" , |
319 | "rundll32.exe" , |
320 | "rundll32.exe" , |
321 | TRUE, |
322 | "\"rundll32.exe\" some thing" , |
323 | }, |
324 | /* Commands with "rundll32" (without the .exe suffix) do exist, |
325 | * but GLib currently does not recognize them, so there's no point |
326 | * in testing these. |
327 | */ |
328 | }; |
329 | |
330 | static void |
331 | test_win32_rundll32_fixup (void) |
332 | { |
333 | gsize i; |
334 | |
335 | for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++) |
336 | { |
337 | gunichar2 *argument; |
338 | gunichar2 *expected; |
339 | |
340 | if (!rundll32_commandlines[i].is_rundll32) |
341 | continue; |
342 | |
343 | argument = g_utf8_to_utf16 (str: rundll32_commandlines[i].orig, len: -1, NULL, NULL, NULL); |
344 | expected = g_utf8_to_utf16 (str: rundll32_commandlines[i].fixed, len: -1, NULL, NULL, NULL); |
345 | |
346 | g_assert_nonnull (argument); |
347 | g_assert_nonnull (expected); |
348 | _g_win32_fixup_broken_microsoft_rundll_commandline (commandline: argument); |
349 | |
350 | g_assert_cmputf16 (argument, ==, expected, rundll32_commandlines[i].orig, rundll32_commandlines[i].fixed); |
351 | |
352 | g_free (mem: argument); |
353 | g_free (mem: expected); |
354 | } |
355 | } |
356 | |
357 | static void |
358 | (void) |
359 | { |
360 | gsize i; |
361 | |
362 | for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++) |
363 | { |
364 | gunichar2 *argument; |
365 | gchar *dll_function; |
366 | gchar *executable; |
367 | gchar *executable_basename; |
368 | gchar *executable_folded; |
369 | gchar *executable_folded_basename; |
370 | |
371 | argument = g_utf8_to_utf16 (str: rundll32_commandlines[i].orig, len: -1, NULL, NULL, NULL); |
372 | |
373 | _g_win32_extract_executable (commandline: argument, NULL, NULL, NULL, NULL, dll_function_out: &dll_function); |
374 | |
375 | if (rundll32_commandlines[i].is_rundll32) |
376 | g_assert_nonnull (dll_function); |
377 | else |
378 | g_assert_null (dll_function); |
379 | |
380 | g_free (mem: dll_function); |
381 | |
382 | executable = NULL; |
383 | executable_basename = NULL; |
384 | executable_folded = NULL; |
385 | executable_folded_basename = NULL; |
386 | _g_win32_extract_executable (commandline: argument, ex_out: &executable, ex_basename_out: &executable_basename, ex_folded_out: &executable_folded, ex_folded_basename_out: &executable_folded_basename, NULL); |
387 | |
388 | g_assert_cmpstr (rundll32_commandlines[i].executable, ==, executable); |
389 | g_assert_cmpstr (rundll32_commandlines[i].executable_basename, ==, executable_basename); |
390 | g_assert_nonnull (executable_folded); |
391 | |
392 | g_free (mem: executable); |
393 | g_free (mem: executable_folded); |
394 | |
395 | /* Check the corner-case where we don't want to know where basename is */ |
396 | executable = NULL; |
397 | executable_folded = NULL; |
398 | _g_win32_extract_executable (commandline: argument, ex_out: &executable, NULL, ex_folded_out: &executable_folded, NULL, NULL); |
399 | |
400 | g_assert_cmpstr (rundll32_commandlines[i].executable, ==, executable); |
401 | g_assert_nonnull (executable_folded); |
402 | |
403 | g_free (mem: executable); |
404 | g_free (mem: executable_folded); |
405 | |
406 | g_free (mem: argument); |
407 | } |
408 | } |
409 | |
410 | static void |
411 | test_win32_parse_filename (void) |
412 | { |
413 | gsize i; |
414 | |
415 | for (i = 0; i < G_N_ELEMENTS (rundll32_commandlines); i++) |
416 | { |
417 | gunichar2 *argument; |
418 | argument = g_utf8_to_utf16 (str: rundll32_commandlines[i].orig, len: -1, NULL, NULL, NULL); |
419 | /* Just checking that it doesn't blow up on various (sometimes incorrect) strings */ |
420 | _g_win32_parse_filename (commandline: argument, FALSE, NULL, NULL, NULL, NULL); |
421 | g_free (mem: argument); |
422 | } |
423 | } |
424 | |
425 | static void |
426 | do_fail_on_broken_utf16_1 (void) |
427 | { |
428 | const gunichar2 utf16[] = { 0xd800, 0x0000 }; |
429 | _g_win32_extract_executable (commandline: utf16, NULL, NULL, NULL, NULL, NULL); |
430 | } |
431 | |
432 | static void |
433 | do_fail_on_broken_utf16_2 (void) |
434 | { |
435 | /* "rundll32.exe <invalid utf16> r" */ |
436 | gchar *dll_function; |
437 | const gunichar2 utf16[] = { 0x0072, 0x0075, 0x006E, 0x0064, 0x006C, 0x006C, 0x0033, 0x0032, |
438 | 0x002E, 0x0065, 0x0078, 0x0065, 0x0020, 0xd800, 0x0020, 0x0072, 0x0000 }; |
439 | _g_win32_extract_executable (commandline: utf16, NULL, NULL, NULL, NULL, dll_function_out: &dll_function); |
440 | } |
441 | |
442 | static void |
443 | test_fail_on_broken_utf16 (void) |
444 | { |
445 | g_test_trap_subprocess (test_path: "/appinfo/subprocess/win32-assert-broken-utf16_1" , usec_timeout: 0, test_flags: 0); |
446 | g_test_trap_assert_failed (); |
447 | g_test_trap_assert_stderr ("*GLib-GIO:ERROR:*giowin32-private.c:*:_g_win32_extract_executable: assertion failed: (folded)*" ); |
448 | g_test_trap_subprocess (test_path: "/appinfo/subprocess/win32-assert-broken-utf16_2" , usec_timeout: 0, test_flags: 0); |
449 | g_test_trap_assert_failed (); |
450 | g_test_trap_assert_stderr ("*GLib-GIO:ERROR:*giowin32-private.c:*:_g_win32_extract_executable: assertion failed: (folded)*" ); |
451 | } |
452 | |
453 | int |
454 | main (int argc, |
455 | char *argv[]) |
456 | { |
457 | g_test_init (argc: &argc, argv: &argv, NULL); |
458 | |
459 | g_test_add_func (testpath: "/appinfo/utf16-strfuncs" , test_func: test_utf16_strfuncs); |
460 | g_test_add_func (testpath: "/appinfo/win32-extract-executable" , test_func: test_win32_extract_executable); |
461 | g_test_add_func (testpath: "/appinfo/win32-rundll32-fixup" , test_func: test_win32_rundll32_fixup); |
462 | g_test_add_func (testpath: "/appinfo/win32-parse-filename" , test_func: test_win32_parse_filename); |
463 | g_test_add_func (testpath: "/appinfo/win32-utf16-conversion-fail" , test_func: test_fail_on_broken_utf16); |
464 | |
465 | g_test_add_func (testpath: "/appinfo/subprocess/win32-assert-broken-utf16_1" , test_func: do_fail_on_broken_utf16_1); |
466 | g_test_add_func (testpath: "/appinfo/subprocess/win32-assert-broken-utf16_2" , test_func: do_fail_on_broken_utf16_2); |
467 | |
468 | return g_test_run (); |
469 | } |
470 | |