1 | /* |
2 | * Copyright (C) 2008 Red Hat, Inc |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General |
15 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Author: Matthias Clasen |
18 | */ |
19 | |
20 | #include <locale.h> |
21 | |
22 | #include <glib/glib.h> |
23 | #include <glib/gstdio.h> |
24 | #include <gio/gio.h> |
25 | #include <gio/gdesktopappinfo.h> |
26 | #include <stdlib.h> |
27 | #include <string.h> |
28 | #include <unistd.h> |
29 | |
30 | static GAppInfo * |
31 | create_app_info (const char *name) |
32 | { |
33 | GError *error; |
34 | GAppInfo *info; |
35 | |
36 | error = NULL; |
37 | info = g_app_info_create_from_commandline (commandline: "true blah" , |
38 | application_name: name, |
39 | flags: G_APP_INFO_CREATE_NONE, |
40 | error: &error); |
41 | g_assert_no_error (error); |
42 | |
43 | /* this is necessary to ensure that the info is saved */ |
44 | g_app_info_set_as_default_for_type (appinfo: info, content_type: "application/x-blah" , error: &error); |
45 | g_assert_no_error (error); |
46 | g_app_info_remove_supports_type (appinfo: info, content_type: "application/x-blah" , error: &error); |
47 | g_assert_no_error (error); |
48 | g_app_info_reset_type_associations (content_type: "application/x-blah" ); |
49 | |
50 | return info; |
51 | } |
52 | |
53 | static void |
54 | test_delete (void) |
55 | { |
56 | GAppInfo *info; |
57 | |
58 | const char *id; |
59 | char *filename; |
60 | gboolean res; |
61 | |
62 | info = create_app_info (name: "Blah" ); |
63 | |
64 | id = g_app_info_get_id (appinfo: info); |
65 | g_assert_nonnull (id); |
66 | |
67 | filename = g_build_filename (first_element: g_get_user_data_dir (), "applications" , id, NULL); |
68 | |
69 | res = g_file_test (filename, test: G_FILE_TEST_EXISTS); |
70 | g_assert_true (res); |
71 | |
72 | res = g_app_info_can_delete (appinfo: info); |
73 | g_assert_true (res); |
74 | |
75 | res = g_app_info_delete (appinfo: info); |
76 | g_assert_true (res); |
77 | |
78 | res = g_file_test (filename, test: G_FILE_TEST_EXISTS); |
79 | g_assert_false (res); |
80 | |
81 | g_object_unref (object: info); |
82 | |
83 | if (g_file_test (filename: "/usr/share/applications/gedit.desktop" , test: G_FILE_TEST_EXISTS)) |
84 | { |
85 | info = (GAppInfo*)g_desktop_app_info_new_from_filename (filename: "/usr/share/applications/gedit.desktop" ); |
86 | g_assert_nonnull (info); |
87 | |
88 | res = g_app_info_can_delete (appinfo: info); |
89 | g_assert_false (res); |
90 | |
91 | res = g_app_info_delete (appinfo: info); |
92 | g_assert_false (res); |
93 | } |
94 | |
95 | g_free (mem: filename); |
96 | } |
97 | |
98 | static void |
99 | test_default (void) |
100 | { |
101 | GAppInfo *info, *info1, *info2, *info3; |
102 | GList *list; |
103 | GError *error = NULL; |
104 | |
105 | info1 = create_app_info (name: "Blah1" ); |
106 | info2 = create_app_info (name: "Blah2" ); |
107 | info3 = create_app_info (name: "Blah3" ); |
108 | |
109 | g_app_info_set_as_default_for_type (appinfo: info1, content_type: "application/x-test" , error: &error); |
110 | g_assert_no_error (error); |
111 | |
112 | g_app_info_set_as_default_for_type (appinfo: info2, content_type: "application/x-test" , error: &error); |
113 | g_assert_no_error (error); |
114 | |
115 | info = g_app_info_get_default_for_type (content_type: "application/x-test" , FALSE); |
116 | g_assert_nonnull (info); |
117 | g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); |
118 | g_object_unref (object: info); |
119 | |
120 | /* now try adding something, but not setting as default */ |
121 | g_app_info_add_supports_type (appinfo: info3, content_type: "application/x-test" , error: &error); |
122 | g_assert_no_error (error); |
123 | |
124 | /* check that info2 is still default */ |
125 | info = g_app_info_get_default_for_type (content_type: "application/x-test" , FALSE); |
126 | g_assert_nonnull (info); |
127 | g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); |
128 | g_object_unref (object: info); |
129 | |
130 | /* now remove info1 again */ |
131 | g_app_info_remove_supports_type (appinfo: info1, content_type: "application/x-test" , error: &error); |
132 | g_assert_no_error (error); |
133 | |
134 | /* and make sure info2 is still default */ |
135 | info = g_app_info_get_default_for_type (content_type: "application/x-test" , FALSE); |
136 | g_assert_nonnull (info); |
137 | g_assert_cmpstr (g_app_info_get_id (info), ==, g_app_info_get_id (info2)); |
138 | g_object_unref (object: info); |
139 | |
140 | /* now clean it all up */ |
141 | g_app_info_reset_type_associations (content_type: "application/x-test" ); |
142 | |
143 | list = g_app_info_get_all_for_type (content_type: "application/x-test" ); |
144 | g_assert_null (list); |
145 | |
146 | g_app_info_delete (appinfo: info1); |
147 | g_app_info_delete (appinfo: info2); |
148 | g_app_info_delete (appinfo: info3); |
149 | |
150 | g_object_unref (object: info1); |
151 | g_object_unref (object: info2); |
152 | g_object_unref (object: info3); |
153 | } |
154 | |
155 | static void |
156 | test_fallback (void) |
157 | { |
158 | GAppInfo *info1, *info2, *app = NULL; |
159 | GList *apps, *recomm, *fallback, *list, *l, *m; |
160 | GError *error = NULL; |
161 | gint old_length; |
162 | |
163 | info1 = create_app_info (name: "Test1" ); |
164 | info2 = create_app_info (name: "Test2" ); |
165 | |
166 | g_assert_true (g_content_type_is_a ("text/x-python" , "text/plain" )); |
167 | |
168 | apps = g_app_info_get_all_for_type (content_type: "text/x-python" ); |
169 | old_length = g_list_length (list: apps); |
170 | g_list_free_full (list: apps, free_func: g_object_unref); |
171 | |
172 | g_app_info_add_supports_type (appinfo: info1, content_type: "text/x-python" , error: &error); |
173 | g_assert_no_error (error); |
174 | |
175 | g_app_info_add_supports_type (appinfo: info2, content_type: "text/plain" , error: &error); |
176 | g_assert_no_error (error); |
177 | |
178 | /* check that both apps are registered */ |
179 | apps = g_app_info_get_all_for_type (content_type: "text/x-python" ); |
180 | g_assert_cmpint (g_list_length (apps), ==, old_length + 2); |
181 | |
182 | /* check that Test1 is among the recommended apps */ |
183 | recomm = g_app_info_get_recommended_for_type (content_type: "text/x-python" ); |
184 | g_assert_nonnull (recomm); |
185 | for (l = recomm; l; l = l->next) |
186 | { |
187 | app = l->data; |
188 | if (g_app_info_equal (appinfo1: info1, appinfo2: app)) |
189 | break; |
190 | } |
191 | g_assert_nonnull (app); |
192 | g_assert_true (g_app_info_equal (info1, app)); |
193 | |
194 | /* and that Test2 is among the fallback apps */ |
195 | fallback = g_app_info_get_fallback_for_type (content_type: "text/x-python" ); |
196 | g_assert_nonnull (fallback); |
197 | for (l = fallback; l; l = l->next) |
198 | { |
199 | app = l->data; |
200 | if (g_app_info_equal (appinfo1: info2, appinfo2: app)) |
201 | break; |
202 | } |
203 | g_assert_cmpstr (g_app_info_get_name (app), ==, "Test2" ); |
204 | |
205 | /* check that recomm + fallback = all applications */ |
206 | list = g_list_concat (list1: g_list_copy (list: recomm), list2: g_list_copy (list: fallback)); |
207 | g_assert_cmpuint (g_list_length (list), ==, g_list_length (apps)); |
208 | |
209 | for (l = list, m = apps; l != NULL && m != NULL; l = l->next, m = m->next) |
210 | { |
211 | g_assert_true (g_app_info_equal (l->data, m->data)); |
212 | } |
213 | |
214 | g_list_free (list); |
215 | |
216 | g_list_free_full (list: apps, free_func: g_object_unref); |
217 | g_list_free_full (list: recomm, free_func: g_object_unref); |
218 | g_list_free_full (list: fallback, free_func: g_object_unref); |
219 | |
220 | g_app_info_reset_type_associations (content_type: "text/x-python" ); |
221 | g_app_info_reset_type_associations (content_type: "text/plain" ); |
222 | |
223 | g_app_info_delete (appinfo: info1); |
224 | g_app_info_delete (appinfo: info2); |
225 | |
226 | g_object_unref (object: info1); |
227 | g_object_unref (object: info2); |
228 | } |
229 | |
230 | static void |
231 | test_last_used (void) |
232 | { |
233 | GList *applications; |
234 | GAppInfo *info1, *info2, *default_app; |
235 | GError *error = NULL; |
236 | |
237 | info1 = create_app_info (name: "Test1" ); |
238 | info2 = create_app_info (name: "Test2" ); |
239 | |
240 | g_app_info_set_as_default_for_type (appinfo: info1, content_type: "application/x-test" , error: &error); |
241 | g_assert_no_error (error); |
242 | |
243 | g_app_info_add_supports_type (appinfo: info2, content_type: "application/x-test" , error: &error); |
244 | g_assert_no_error (error); |
245 | |
246 | applications = g_app_info_get_recommended_for_type (content_type: "application/x-test" ); |
247 | g_assert_cmpuint (g_list_length (applications), ==, 2); |
248 | |
249 | /* the first should be the default app now */ |
250 | g_assert_true (g_app_info_equal (g_list_nth_data (applications, 0), info1)); |
251 | g_assert_true (g_app_info_equal (g_list_nth_data (applications, 1), info2)); |
252 | |
253 | g_list_free_full (list: applications, free_func: g_object_unref); |
254 | |
255 | g_app_info_set_as_last_used_for_type (appinfo: info2, content_type: "application/x-test" , error: &error); |
256 | g_assert_no_error (error); |
257 | |
258 | applications = g_app_info_get_recommended_for_type (content_type: "application/x-test" ); |
259 | g_assert_cmpuint (g_list_length (applications), ==, 2); |
260 | |
261 | default_app = g_app_info_get_default_for_type (content_type: "application/x-test" , FALSE); |
262 | g_assert_true (g_app_info_equal (default_app, info1)); |
263 | |
264 | /* the first should be the other app now */ |
265 | g_assert_true (g_app_info_equal (g_list_nth_data (applications, 0), info2)); |
266 | g_assert_true (g_app_info_equal (g_list_nth_data (applications, 1), info1)); |
267 | |
268 | g_list_free_full (list: applications, free_func: g_object_unref); |
269 | |
270 | g_app_info_reset_type_associations (content_type: "application/x-test" ); |
271 | |
272 | g_app_info_delete (appinfo: info1); |
273 | g_app_info_delete (appinfo: info2); |
274 | |
275 | g_object_unref (object: info1); |
276 | g_object_unref (object: info2); |
277 | g_object_unref (object: default_app); |
278 | } |
279 | |
280 | static void |
281 | (void) |
282 | { |
283 | GDesktopAppInfo *appinfo; |
284 | const gchar *lang; |
285 | gchar *s; |
286 | gboolean b; |
287 | |
288 | lang = setlocale (LC_ALL, NULL); |
289 | g_setenv (variable: "LANGUAGE" , value: "de_DE.UTF8" , TRUE); |
290 | setlocale (LC_ALL, locale: "" ); |
291 | |
292 | appinfo = g_desktop_app_info_new_from_filename (filename: g_test_get_filename (file_type: G_TEST_DIST, first_path: "appinfo-test-static.desktop" , NULL)); |
293 | g_assert_nonnull (appinfo); |
294 | |
295 | g_assert_true (g_desktop_app_info_has_key (appinfo, "Terminal" )); |
296 | g_assert_false (g_desktop_app_info_has_key (appinfo, "Bratwurst" )); |
297 | |
298 | s = g_desktop_app_info_get_string (info: appinfo, key: "StartupWMClass" ); |
299 | g_assert_cmpstr (s, ==, "appinfo-class" ); |
300 | g_free (mem: s); |
301 | |
302 | s = g_desktop_app_info_get_locale_string (info: appinfo, key: "X-JunkFood" ); |
303 | g_assert_cmpstr (s, ==, "Bratwurst" ); |
304 | g_free (mem: s); |
305 | |
306 | g_setenv (variable: "LANGUAGE" , value: "sv_SE.UTF8" , TRUE); |
307 | setlocale (LC_ALL, locale: "" ); |
308 | |
309 | s = g_desktop_app_info_get_locale_string (info: appinfo, key: "X-JunkFood" ); |
310 | g_assert_cmpstr (s, ==, "Burger" ); /* fallback */ |
311 | g_free (mem: s); |
312 | |
313 | b = g_desktop_app_info_get_boolean (info: appinfo, key: "Terminal" ); |
314 | g_assert_true (b); |
315 | |
316 | g_object_unref (object: appinfo); |
317 | |
318 | g_setenv (variable: "LANGUAGE" , value: lang, TRUE); |
319 | setlocale (LC_ALL, locale: "" ); |
320 | } |
321 | |
322 | static void |
323 | wait_for_file (const gchar *want_this, |
324 | const gchar *but_not_this, |
325 | const gchar *or_this) |
326 | { |
327 | guint retries = 600; |
328 | |
329 | /* I hate time-based conditions in tests, but this will wait up to one |
330 | * whole minute for "touch file" to finish running. I think it should |
331 | * be OK. |
332 | * |
333 | * 600 * 100ms = 60 seconds. |
334 | */ |
335 | while (access (name: want_this, F_OK) != 0) |
336 | { |
337 | g_usleep (microseconds: 100000); /* 100ms */ |
338 | g_assert_cmpuint (retries, >, 0); |
339 | retries--; |
340 | } |
341 | |
342 | g_assert_cmpuint (access (but_not_this, F_OK), !=, 0); |
343 | g_assert_cmpuint (access (or_this, F_OK), !=, 0); |
344 | |
345 | unlink (name: want_this); |
346 | unlink (name: but_not_this); |
347 | unlink (name: or_this); |
348 | } |
349 | |
350 | static void |
351 | test_actions (void) |
352 | { |
353 | const char *expected[] = { "frob" , "tweak" , "twiddle" , "broken" , NULL }; |
354 | const gchar * const *actions; |
355 | GDesktopAppInfo *appinfo; |
356 | gchar *name; |
357 | |
358 | appinfo = g_desktop_app_info_new_from_filename (filename: g_test_get_filename (file_type: G_TEST_DIST, first_path: "appinfo-test-actions.desktop" , NULL)); |
359 | g_assert_nonnull (appinfo); |
360 | |
361 | actions = g_desktop_app_info_list_actions (info: appinfo); |
362 | g_assert_cmpstrv (actions, expected); |
363 | |
364 | name = g_desktop_app_info_get_action_name (info: appinfo, action_name: "frob" ); |
365 | g_assert_cmpstr (name, ==, "Frobnicate" ); |
366 | g_free (mem: name); |
367 | |
368 | name = g_desktop_app_info_get_action_name (info: appinfo, action_name: "tweak" ); |
369 | g_assert_cmpstr (name, ==, "Tweak" ); |
370 | g_free (mem: name); |
371 | |
372 | name = g_desktop_app_info_get_action_name (info: appinfo, action_name: "twiddle" ); |
373 | g_assert_cmpstr (name, ==, "Twiddle" ); |
374 | g_free (mem: name); |
375 | |
376 | name = g_desktop_app_info_get_action_name (info: appinfo, action_name: "broken" ); |
377 | g_assert_nonnull (name); |
378 | g_assert_true (g_utf8_validate (name, -1, NULL)); |
379 | g_free (mem: name); |
380 | |
381 | unlink (name: "frob" ); unlink (name: "tweak" ); unlink (name: "twiddle" ); |
382 | |
383 | g_desktop_app_info_launch_action (info: appinfo, action_name: "frob" , NULL); |
384 | wait_for_file (want_this: "frob" , but_not_this: "tweak" , or_this: "twiddle" ); |
385 | |
386 | g_desktop_app_info_launch_action (info: appinfo, action_name: "tweak" , NULL); |
387 | wait_for_file (want_this: "tweak" , but_not_this: "frob" , or_this: "twiddle" ); |
388 | |
389 | g_desktop_app_info_launch_action (info: appinfo, action_name: "twiddle" , NULL); |
390 | wait_for_file (want_this: "twiddle" , but_not_this: "frob" , or_this: "tweak" ); |
391 | |
392 | g_object_unref (object: appinfo); |
393 | } |
394 | |
395 | static gchar * |
396 | run_apps (const gchar *command, |
397 | const gchar *arg, |
398 | gboolean with_usr, |
399 | gboolean with_home, |
400 | const gchar *locale_name, |
401 | const gchar *language, |
402 | const gchar *xdg_current_desktop) |
403 | { |
404 | gboolean success; |
405 | gchar **envp; |
406 | gchar **argv; |
407 | gint status; |
408 | gchar *out; |
409 | gchar *argv_str = NULL; |
410 | |
411 | argv = g_new (gchar *, 4); |
412 | argv[0] = g_test_build_filename (file_type: G_TEST_BUILT, first_path: "apps" , NULL); |
413 | argv[1] = g_strdup (str: command); |
414 | argv[2] = g_strdup (str: arg); |
415 | argv[3] = NULL; |
416 | |
417 | envp = g_get_environ (); |
418 | |
419 | if (with_usr) |
420 | { |
421 | gchar *tmp = g_test_build_filename (file_type: G_TEST_DIST, first_path: "desktop-files" , "usr" , NULL); |
422 | envp = g_environ_setenv (envp, variable: "XDG_DATA_DIRS" , value: tmp, TRUE); |
423 | g_free (mem: tmp); |
424 | } |
425 | else |
426 | envp = g_environ_setenv (envp, variable: "XDG_DATA_DIRS" , value: "/does-not-exist" , TRUE); |
427 | |
428 | if (with_home) |
429 | { |
430 | gchar *tmp = g_test_build_filename (file_type: G_TEST_DIST, first_path: "desktop-files" , "home" , NULL); |
431 | envp = g_environ_setenv (envp, variable: "XDG_DATA_HOME" , value: tmp, TRUE); |
432 | g_free (mem: tmp); |
433 | } |
434 | else |
435 | envp = g_environ_setenv (envp, variable: "XDG_DATA_HOME" , value: "/does-not-exist" , TRUE); |
436 | |
437 | if (locale_name) |
438 | envp = g_environ_setenv (envp, variable: "LC_ALL" , value: locale_name, TRUE); |
439 | else |
440 | envp = g_environ_setenv (envp, variable: "LC_ALL" , value: "C" , TRUE); |
441 | |
442 | if (language) |
443 | envp = g_environ_setenv (envp, variable: "LANGUAGE" , value: language, TRUE); |
444 | else |
445 | envp = g_environ_unsetenv (envp, variable: "LANGUAGE" ); |
446 | |
447 | if (xdg_current_desktop) |
448 | envp = g_environ_setenv (envp, variable: "XDG_CURRENT_DESKTOP" , value: xdg_current_desktop, TRUE); |
449 | else |
450 | envp = g_environ_unsetenv (envp, variable: "XDG_CURRENT_DESKTOP" ); |
451 | |
452 | envp = g_environ_setenv (envp, variable: "G_MESSAGES_DEBUG" , value: "" , TRUE); |
453 | |
454 | success = g_spawn_sync (NULL, argv, envp, flags: 0, NULL, NULL, standard_output: &out, NULL, exit_status: &status, NULL); |
455 | g_assert_true (success); |
456 | g_assert_cmpuint (status, ==, 0); |
457 | |
458 | argv_str = g_strjoinv (separator: " " , str_array: argv); |
459 | g_test_message (format: "%s: `%s` returned: %s" , G_STRFUNC, argv_str, out); |
460 | g_free (mem: argv_str); |
461 | |
462 | g_strfreev (str_array: envp); |
463 | g_strfreev (str_array: argv); |
464 | |
465 | return out; |
466 | } |
467 | |
468 | static void |
469 | assert_strings_equivalent (const gchar *expected, |
470 | const gchar *result) |
471 | { |
472 | gchar **expected_words; |
473 | gchar **result_words; |
474 | gint i, j; |
475 | |
476 | expected_words = g_strsplit (string: expected, delimiter: " " , max_tokens: 0); |
477 | result_words = g_strsplit_set (string: result, delimiters: " \n" , max_tokens: 0); |
478 | |
479 | for (i = 0; expected_words[i]; i++) |
480 | { |
481 | for (j = 0; result_words[j]; j++) |
482 | if (g_str_equal (v1: expected_words[i], v2: result_words[j])) |
483 | goto got_it; |
484 | |
485 | g_test_message (format: "Unable to find expected string '%s' in result '%s'" , expected_words[i], result); |
486 | g_test_fail (); |
487 | |
488 | got_it: |
489 | continue; |
490 | } |
491 | |
492 | g_assert_cmpint (g_strv_length (expected_words), ==, g_strv_length (result_words)); |
493 | g_strfreev (str_array: expected_words); |
494 | g_strfreev (str_array: result_words); |
495 | } |
496 | |
497 | static void |
498 | assert_list (const gchar *expected, |
499 | gboolean with_usr, |
500 | gboolean with_home, |
501 | const gchar *locale_name, |
502 | const gchar *language) |
503 | { |
504 | gchar *result; |
505 | |
506 | result = run_apps (command: "list" , NULL, with_usr, with_home, locale_name, language, NULL); |
507 | g_strchomp (string: result); |
508 | assert_strings_equivalent (expected, result); |
509 | g_free (mem: result); |
510 | } |
511 | |
512 | static void |
513 | assert_info (const gchar *desktop_id, |
514 | const gchar *expected, |
515 | gboolean with_usr, |
516 | gboolean with_home, |
517 | const gchar *locale_name, |
518 | const gchar *language) |
519 | { |
520 | gchar *result; |
521 | |
522 | result = run_apps (command: "show-info" , arg: desktop_id, with_usr, with_home, locale_name, language, NULL); |
523 | g_assert_cmpstr (result, ==, expected); |
524 | g_free (mem: result); |
525 | } |
526 | |
527 | static void |
528 | assert_search (const gchar *search_string, |
529 | const gchar *expected, |
530 | gboolean with_usr, |
531 | gboolean with_home, |
532 | const gchar *locale_name, |
533 | const gchar *language) |
534 | { |
535 | gchar **expected_lines; |
536 | gchar **result_lines; |
537 | gchar *result; |
538 | gint i; |
539 | |
540 | expected_lines = g_strsplit (string: expected, delimiter: "\n" , max_tokens: -1); |
541 | result = run_apps (command: "search" , arg: search_string, with_usr, with_home, locale_name, language, NULL); |
542 | result_lines = g_strsplit (string: result, delimiter: "\n" , max_tokens: -1); |
543 | g_assert_cmpint (g_strv_length (expected_lines), ==, g_strv_length (result_lines)); |
544 | for (i = 0; expected_lines[i]; i++) |
545 | assert_strings_equivalent (expected: expected_lines[i], result: result_lines[i]); |
546 | g_strfreev (str_array: expected_lines); |
547 | g_strfreev (str_array: result_lines); |
548 | g_free (mem: result); |
549 | } |
550 | |
551 | static void |
552 | assert_implementations (const gchar *interface, |
553 | const gchar *expected, |
554 | gboolean with_usr, |
555 | gboolean with_home) |
556 | { |
557 | gchar *result; |
558 | |
559 | result = run_apps (command: "implementations" , arg: interface, with_usr, with_home, NULL, NULL, NULL); |
560 | g_strchomp (string: result); |
561 | assert_strings_equivalent (expected, result); |
562 | g_free (mem: result); |
563 | } |
564 | |
565 | #define ALL_USR_APPS "evince-previewer.desktop nautilus-classic.desktop gnome-font-viewer.desktop " \ |
566 | "baobab.desktop yelp.desktop eog.desktop cheese.desktop org.gnome.clocks.desktop " \ |
567 | "gnome-contacts.desktop kde4-kate.desktop gcr-prompter.desktop totem.desktop " \ |
568 | "gnome-terminal.desktop nautilus-autorun-software.desktop gcr-viewer.desktop " \ |
569 | "nautilus-connect-server.desktop kde4-dolphin.desktop gnome-music.desktop " \ |
570 | "kde4-konqbrowser.desktop gucharmap.desktop kde4-okular.desktop nautilus.desktop " \ |
571 | "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop " \ |
572 | "invalid-desktop.desktop" |
573 | #define HOME_APPS "epiphany-weather-for-toronto-island-9c6a4e022b17686306243dada811d550d25eb1fb.desktop" |
574 | #define ALL_HOME_APPS HOME_APPS " eog.desktop" |
575 | |
576 | static void |
577 | test_search (void) |
578 | { |
579 | assert_list (expected: "" , FALSE, FALSE, NULL, NULL); |
580 | assert_list (ALL_USR_APPS, TRUE, FALSE, NULL, NULL); |
581 | assert_list (ALL_HOME_APPS, FALSE, TRUE, NULL, NULL); |
582 | assert_list (ALL_USR_APPS " " HOME_APPS, TRUE, TRUE, NULL, NULL); |
583 | |
584 | /* The user has "installed" their own version of eog.desktop which |
585 | * calls it "Eye of GNOME". Do some testing based on that. |
586 | * |
587 | * We should always find "Pictures" keyword no matter where we look. |
588 | */ |
589 | assert_search (search_string: "Picture" , expected: "eog.desktop\n" , TRUE, TRUE, NULL, NULL); |
590 | assert_search (search_string: "Picture" , expected: "eog.desktop\n" , TRUE, FALSE, NULL, NULL); |
591 | assert_search (search_string: "Picture" , expected: "eog.desktop\n" , FALSE, TRUE, NULL, NULL); |
592 | assert_search (search_string: "Picture" , expected: "" , FALSE, FALSE, NULL, NULL); |
593 | |
594 | /* We should only find it called "eye of gnome" when using the user's |
595 | * directory. |
596 | */ |
597 | assert_search (search_string: "eye gnome" , expected: "" , TRUE, FALSE, NULL, NULL); |
598 | assert_search (search_string: "eye gnome" , expected: "eog.desktop\n" , FALSE, TRUE, NULL, NULL); |
599 | assert_search (search_string: "eye gnome" , expected: "eog.desktop\n" , TRUE, TRUE, NULL, NULL); |
600 | |
601 | /* We should only find it called "image viewer" when _not_ using the |
602 | * user's directory. |
603 | */ |
604 | assert_search (search_string: "image viewer" , expected: "eog.desktop\n" , TRUE, FALSE, NULL, NULL); |
605 | assert_search (search_string: "image viewer" , expected: "" , FALSE, TRUE, NULL, NULL); |
606 | assert_search (search_string: "image viewer" , expected: "" , TRUE, TRUE, NULL, NULL); |
607 | |
608 | /* There're "flatpak" apps (clocks) installed as well - they should *not* |
609 | * match the prefix command ("/bin/sh") in the Exec= line though. |
610 | */ |
611 | assert_search (search_string: "sh" , expected: "gnome-terminal.desktop\n" , TRUE, FALSE, NULL, NULL); |
612 | |
613 | /* "frobnicator.desktop" is ignored by get_all() because the binary is |
614 | * missing, but search should still find it (to avoid either stale results |
615 | * from the cache or expensive stat() calls for each potential result) |
616 | */ |
617 | assert_search (search_string: "frobni" , expected: "frobnicator.desktop\n" , TRUE, FALSE, NULL, NULL); |
618 | |
619 | /* Obvious multi-word search */ |
620 | assert_search (search_string: "gno hel" , expected: "yelp.desktop\n" , TRUE, TRUE, NULL, NULL); |
621 | |
622 | /* Repeated search terms should do nothing... */ |
623 | assert_search (search_string: "files file fil fi f" , expected: "nautilus.desktop\n" |
624 | "gedit.desktop\n" , TRUE, TRUE, NULL, NULL); |
625 | |
626 | /* "con" will match "connect" and "contacts" on name but dconf only on |
627 | * the "config" keyword |
628 | */ |
629 | assert_search (search_string: "con" , expected: "nautilus-connect-server.desktop gnome-contacts.desktop\n" |
630 | "dconf-editor.desktop\n" , TRUE, TRUE, NULL, NULL); |
631 | |
632 | /* "gnome" will match "eye of gnome" from the user's directory, plus |
633 | * matching "GNOME Clocks" X-GNOME-FullName. It's only a comment on |
634 | * yelp and gnome-contacts, though. |
635 | */ |
636 | assert_search (search_string: "gnome" , expected: "eog.desktop\n" |
637 | "org.gnome.clocks.desktop\n" |
638 | "yelp.desktop gnome-contacts.desktop\n" , TRUE, TRUE, NULL, NULL); |
639 | |
640 | /* eog has exec name 'false' in usr only */ |
641 | assert_search (search_string: "false" , expected: "eog.desktop\n" , TRUE, FALSE, NULL, NULL); |
642 | assert_search (search_string: "false" , expected: "" , FALSE, TRUE, NULL, NULL); |
643 | assert_search (search_string: "false" , expected: "" , TRUE, TRUE, NULL, NULL); |
644 | assert_search (search_string: "false" , expected: "" , FALSE, FALSE, NULL, NULL); |
645 | |
646 | /* make sure we only search the first component */ |
647 | assert_search (search_string: "nonsearchable" , expected: "" , TRUE, FALSE, NULL, NULL); |
648 | |
649 | /* "gnome con" will match only gnome contacts; via the name for |
650 | * "contacts" and the comment for "gnome" |
651 | */ |
652 | assert_search (search_string: "gnome con" , expected: "gnome-contacts.desktop\n" , TRUE, TRUE, NULL, NULL); |
653 | |
654 | /* make sure we get the correct kde4- prefix on the application IDs |
655 | * from subdirectories |
656 | */ |
657 | assert_search (search_string: "konq" , expected: "kde4-konqbrowser.desktop\n" , TRUE, TRUE, NULL, NULL); |
658 | assert_search (search_string: "kate" , expected: "kde4-kate.desktop\n" , TRUE, TRUE, NULL, NULL); |
659 | |
660 | /* make sure we can look up apps by name properly */ |
661 | assert_info (desktop_id: "kde4-kate.desktop" , |
662 | expected: "kde4-kate.desktop\n" |
663 | "Kate\n" |
664 | "Kate\n" |
665 | "nil\n" , TRUE, TRUE, NULL, NULL); |
666 | |
667 | assert_info (desktop_id: "nautilus.desktop" , |
668 | expected: "nautilus.desktop\n" |
669 | "Files\n" |
670 | "Files\n" |
671 | "Access and organize files\n" , TRUE, TRUE, NULL, NULL); |
672 | |
673 | /* make sure localised searching works properly */ |
674 | assert_search (search_string: "foliumi" , expected: "nautilus.desktop\n" |
675 | "kde4-konqbrowser.desktop\n" |
676 | "eog.desktop\n" , TRUE, FALSE, locale_name: "en_US.UTF-8" , language: "eo" ); |
677 | /* the user's eog.desktop has no translations... */ |
678 | assert_search (search_string: "foliumi" , expected: "nautilus.desktop\n" |
679 | "kde4-konqbrowser.desktop\n" , TRUE, TRUE, locale_name: "en_US.UTF-8" , language: "eo" ); |
680 | } |
681 | |
682 | static void |
683 | test_implements (void) |
684 | { |
685 | /* Make sure we can find our search providers... */ |
686 | assert_implementations (interface: "org.gnome.Shell.SearchProvider2" , |
687 | expected: "gnome-music.desktop gnome-contacts.desktop eog.desktop" , |
688 | TRUE, FALSE); |
689 | |
690 | /* And our image acquisition possibilities... */ |
691 | assert_implementations (interface: "org.freedesktop.ImageProvider" , |
692 | expected: "cheese.desktop" , |
693 | TRUE, FALSE); |
694 | |
695 | /* Make sure the user's eog is properly masking the system one */ |
696 | assert_implementations (interface: "org.gnome.Shell.SearchProvider2" , |
697 | expected: "gnome-music.desktop gnome-contacts.desktop" , |
698 | TRUE, TRUE); |
699 | |
700 | /* Make sure we get nothing if we have nothing */ |
701 | assert_implementations (interface: "org.gnome.Shell.SearchProvider2" , expected: "" , FALSE, FALSE); |
702 | } |
703 | |
704 | static void |
705 | assert_shown (const gchar *desktop_id, |
706 | gboolean expected, |
707 | const gchar *xdg_current_desktop) |
708 | { |
709 | gchar *result; |
710 | |
711 | result = run_apps (command: "should-show" , arg: desktop_id, TRUE, TRUE, NULL, NULL, xdg_current_desktop); |
712 | g_assert_cmpstr (result, ==, expected ? "true\n" : "false\n" ); |
713 | g_free (mem: result); |
714 | } |
715 | |
716 | static void |
717 | test_show_in (void) |
718 | { |
719 | assert_shown (desktop_id: "gcr-prompter.desktop" , FALSE, NULL); |
720 | assert_shown (desktop_id: "gcr-prompter.desktop" , FALSE, xdg_current_desktop: "GNOME" ); |
721 | assert_shown (desktop_id: "gcr-prompter.desktop" , FALSE, xdg_current_desktop: "KDE" ); |
722 | assert_shown (desktop_id: "gcr-prompter.desktop" , FALSE, xdg_current_desktop: "GNOME:GNOME-Classic" ); |
723 | assert_shown (desktop_id: "gcr-prompter.desktop" , TRUE, xdg_current_desktop: "GNOME-Classic:GNOME" ); |
724 | assert_shown (desktop_id: "gcr-prompter.desktop" , TRUE, xdg_current_desktop: "GNOME-Classic" ); |
725 | assert_shown (desktop_id: "gcr-prompter.desktop" , TRUE, xdg_current_desktop: "GNOME-Classic:KDE" ); |
726 | assert_shown (desktop_id: "gcr-prompter.desktop" , TRUE, xdg_current_desktop: "KDE:GNOME-Classic" ); |
727 | assert_shown (desktop_id: "invalid-desktop.desktop" , TRUE, xdg_current_desktop: "GNOME" ); |
728 | assert_shown (desktop_id: "invalid-desktop.desktop" , FALSE, xdg_current_desktop: "../invalid/desktop" ); |
729 | assert_shown (desktop_id: "invalid-desktop.desktop" , FALSE, xdg_current_desktop: "../invalid/desktop:../invalid/desktop" ); |
730 | } |
731 | |
732 | /* Test g_desktop_app_info_launch_uris_as_manager() and |
733 | * g_desktop_app_info_launch_uris_as_manager_with_fds() |
734 | */ |
735 | static void |
736 | test_launch_as_manager (void) |
737 | { |
738 | GDesktopAppInfo *appinfo; |
739 | GError *error = NULL; |
740 | gboolean retval; |
741 | const gchar *path; |
742 | |
743 | if (g_getenv (variable: "DISPLAY" ) == NULL || g_getenv (variable: "DISPLAY" )[0] == '\0') |
744 | { |
745 | g_test_skip (msg: "No DISPLAY. Skipping test." ); |
746 | return; |
747 | } |
748 | |
749 | path = g_test_get_filename (file_type: G_TEST_BUILT, first_path: "appinfo-test.desktop" , NULL); |
750 | appinfo = g_desktop_app_info_new_from_filename (filename: path); |
751 | |
752 | if (appinfo == NULL) |
753 | { |
754 | g_test_skip (msg: "appinfo-test binary not installed" ); |
755 | return; |
756 | } |
757 | |
758 | retval = g_desktop_app_info_launch_uris_as_manager (appinfo, NULL, NULL, spawn_flags: 0, |
759 | NULL, NULL, |
760 | NULL, NULL, |
761 | error: &error); |
762 | g_assert_no_error (error); |
763 | g_assert_true (retval); |
764 | |
765 | retval = g_desktop_app_info_launch_uris_as_manager_with_fds (appinfo, |
766 | NULL, NULL, spawn_flags: 0, |
767 | NULL, NULL, |
768 | NULL, NULL, |
769 | stdin_fd: -1, stdout_fd: -1, stderr_fd: -1, |
770 | error: &error); |
771 | g_assert_no_error (error); |
772 | g_assert_true (retval); |
773 | |
774 | g_object_unref (object: appinfo); |
775 | } |
776 | |
777 | int |
778 | main (int argc, |
779 | char *argv[]) |
780 | { |
781 | /* While we use %G_TEST_OPTION_ISOLATE_DIRS to create temporary directories |
782 | * for each of the tests, we want to use the system MIME registry, assuming |
783 | * that it exists and correctly has shared-mime-info installed. */ |
784 | g_content_type_set_mime_dirs (NULL); |
785 | |
786 | g_test_init (argc: &argc, argv: &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL); |
787 | |
788 | g_test_add_func (testpath: "/desktop-app-info/delete" , test_func: test_delete); |
789 | g_test_add_func (testpath: "/desktop-app-info/default" , test_func: test_default); |
790 | g_test_add_func (testpath: "/desktop-app-info/fallback" , test_func: test_fallback); |
791 | g_test_add_func (testpath: "/desktop-app-info/lastused" , test_func: test_last_used); |
792 | g_test_add_func (testpath: "/desktop-app-info/extra-getters" , test_func: test_extra_getters); |
793 | g_test_add_func (testpath: "/desktop-app-info/actions" , test_func: test_actions); |
794 | g_test_add_func (testpath: "/desktop-app-info/search" , test_func: test_search); |
795 | g_test_add_func (testpath: "/desktop-app-info/implements" , test_func: test_implements); |
796 | g_test_add_func (testpath: "/desktop-app-info/show-in" , test_func: test_show_in); |
797 | g_test_add_func (testpath: "/desktop-app-info/launch-as-manager" , test_func: test_launch_as_manager); |
798 | |
799 | return g_test_run (); |
800 | } |
801 | |