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