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
28static int
29g_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) \
52G_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
59static void
60test_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
148struct {
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
330static void
331test_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
357static void
358test_win32_extract_executable (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
410static void
411test_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
425static void
426do_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
432static void
433do_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
442static void
443test_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
453int
454main (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

source code of gtk/subprojects/glib/gio/tests/win32-appinfo.c