1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* ibus - The Input Bus
4  * Copyright (C) 2011 Google, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 
22 #include <string.h>
23 #include "ibus.h"
24 
25 static IBusBus *bus;
26 static void
27 call_next_async_function (IBusInputContext *context);
28 
29 static gboolean
fatal_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)30 fatal_handler(const gchar *log_domain,
31               GLogLevelFlags log_level,
32               const gchar *message,
33               gpointer user_data)
34 {
35     if (!g_strcmp0 (message, "org.freedesktop.IBus.InputContext.GetEngine: GDBus.Error:org.freedesktop.DBus.Error.Failed: Input context does not have engine."))
36         return FALSE; /* do not call abort. */
37     return TRUE;
38 }
39 
40 static void
call_basic_ipcs(IBusInputContext * context)41 call_basic_ipcs (IBusInputContext *context)
42 {
43     ibus_input_context_set_cursor_location (context, 0, 0, 1, 1);
44     ibus_input_context_set_capabilities (context, IBUS_CAP_FOCUS);
45     ibus_input_context_property_activate (context, "dummy.prop.name", PROP_STATE_CHECKED);
46     ibus_input_context_reset (context);
47 
48     /* When enable() is called, ibus-daemon may start a global (or preloaded,
49      * or default) engine in an asynchrnous manner and return immediately.
50      * Therefore, it is not guaranteed that ibus_input_context_is_enabled()
51      * returns TRUE. */
52 
53     ibus_input_context_focus_in (context);
54 }
55 
56 static void
test_input_context(void)57 test_input_context (void)
58 {
59     IBusInputContext *context;
60     GLogLevelFlags flags;
61     IBusEngineDesc *engine_desc;
62     gchar *active_engine_name = NULL;
63     gchar *current_ic;
64 
65     context = ibus_bus_create_input_context (bus, "test");
66     call_basic_ipcs (context);
67 
68     /* "No global engine." warning is not critical message. */
69     flags = g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
70     engine_desc = ibus_bus_get_global_engine (bus);
71     g_log_set_always_fatal (flags);
72     if (engine_desc != NULL) {
73         active_engine_name = g_strdup (ibus_engine_desc_get_name(engine_desc));
74         g_object_unref (engine_desc);
75         engine_desc = NULL;
76     } else {
77         active_engine_name = g_strdup ("dummy");
78     }
79     g_assert (active_engine_name);
80     g_debug ("Use '%s' for testing.", active_engine_name);
81 
82     ibus_input_context_set_engine (context, active_engine_name);
83     current_ic = ibus_bus_current_input_context (bus);
84     g_assert (!strcmp (current_ic, g_dbus_proxy_get_object_path ((GDBusProxy *)context)));
85 
86     g_test_log_set_fatal_handler (fatal_handler, NULL);
87     engine_desc = ibus_input_context_get_engine (context);
88     if (engine_desc) {
89         /* FIXME race condition between ibus_input_context_set_engine and _get_engine.
90          * ibus_input_context_get_engine is not guaranteed to return non-NULL
91          * (even if we use synchronous set_engine()) because ibus-daemon sets a context
92          * engine in an asynchronous manner. See _context_request_engine_cb in
93          * ibusimpl.c which handles context_signals[REQUEST_ENGINE] signal. */
94         g_assert (!strcmp (active_engine_name, ibus_engine_desc_get_name(engine_desc)));
95         g_object_unref (engine_desc);
96         engine_desc = NULL;
97     }
98     ibus_input_context_process_key_event (context, 0, 0, 0);
99 
100     /* An engine is set. Try to call basic IPCs again. */
101     call_basic_ipcs (context);
102 
103     g_free (current_ic);
104     g_object_unref (context);
105 
106     g_free (active_engine_name);
107 }
108 
109 static void
finish_get_engine_async(GObject * source_object,GAsyncResult * res,gpointer user_data)110 finish_get_engine_async (GObject *source_object,
111                          GAsyncResult *res,
112                          gpointer user_data)
113 {
114     IBusInputContext *context = IBUS_INPUT_CONTEXT (source_object);
115     GError *error = NULL;
116     IBusEngineDesc *desc = ibus_input_context_get_engine_async_finish (context,
117                                                                        res,
118                                                                        &error);
119     if (desc) {
120         g_object_unref (desc);
121     }
122     g_debug ("ibus_context_get_engine_async_finish: OK");
123     call_next_async_function (context);
124 }
125 
126 static void
start_get_engine_async(IBusInputContext * context)127 start_get_engine_async (IBusInputContext *context)
128 {
129     ibus_input_context_get_engine_async (context,
130                                          -1, /* timeout */
131                                          NULL, /* cancellable */
132                                          finish_get_engine_async,
133                                          NULL); /* user_data */
134 }
135 
136 static void
finish_process_key_event_async(GObject * source_object,GAsyncResult * res,gpointer user_data)137 finish_process_key_event_async (GObject *source_object,
138                          GAsyncResult *res,
139                          gpointer user_data)
140 {
141     IBusInputContext *context = IBUS_INPUT_CONTEXT (source_object);
142     GError *error = NULL;
143     gboolean result = ibus_input_context_process_key_event_async_finish (context,
144                                                                          res,
145                                                                          &error);
146     g_assert (result || error == NULL);
147     g_debug ("ibus_context_process_key_event_async_finish: OK");
148     call_next_async_function (context);
149 }
150 
151 static void
start_process_key_event_async(IBusInputContext * context)152 start_process_key_event_async (IBusInputContext *context)
153 {
154     ibus_input_context_process_key_event_async (context,
155                                                 0, 0, 0,
156                                                 -1, /* timeout */
157                                                 NULL, /* cancellable */
158                                                 finish_process_key_event_async,
159                                                 NULL); /* user_data */
160 }
161 
162 static gboolean
test_async_apis_finish(gpointer user_data)163 test_async_apis_finish (gpointer user_data)
164 {
165     ibus_quit ();
166     return FALSE;
167 }
168 
169 static void
test_async_apis(void)170 test_async_apis (void)
171 {
172     g_debug ("start");
173     IBusInputContext *context;
174     context = ibus_bus_create_input_context (bus, "test");
175     call_basic_ipcs (context);
176 
177     call_next_async_function (context);
178     ibus_main ();
179 }
180 
181 static void
call_next_async_function(IBusInputContext * context)182 call_next_async_function (IBusInputContext *context)
183 {
184     static void (*async_functions[])(IBusInputContext *) = {
185         start_get_engine_async,
186         start_process_key_event_async,
187     };
188     static guint index = 0;
189 
190     // Use g_timeout_add to make sure test_async_apis finishes even if async_functions is empty.
191     if (index >= G_N_ELEMENTS (async_functions))
192         g_timeout_add (1, test_async_apis_finish, NULL);
193     else
194         (*async_functions[index++])(context);
195 }
196 
197 gint
main(gint argc,gchar ** argv)198 main (gint    argc,
199       gchar **argv)
200 {
201     gint result;
202     ibus_init ();
203     g_test_init (&argc, &argv, NULL);
204     bus = ibus_bus_new ();
205 
206     g_test_add_func ("/ibus/input_context", test_input_context);
207     g_test_add_func ("/ibus/input_context_async_with_callback", test_async_apis);
208 
209     result = g_test_run ();
210     g_object_unref (bus);
211 
212     return result;
213 }
214