1 /* 2 * QEMU I/O task tests 3 * 4 * Copyright (c) 2015 Red Hat, 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, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 #include "qemu/osdep.h" 22 23 #include "qom/object.h" 24 #include "io/task.h" 25 #include "qapi/error.h" 26 #include "qemu/module.h" 27 28 #define TYPE_DUMMY "qemu:dummy" 29 30 typedef struct DummyObject DummyObject; 31 typedef struct DummyObjectClass DummyObjectClass; 32 33 struct DummyObject { 34 Object parent; 35 }; 36 37 struct DummyObjectClass { 38 ObjectClass parent; 39 }; 40 41 static const TypeInfo dummy_info = { 42 .parent = TYPE_OBJECT, 43 .name = TYPE_DUMMY, 44 .instance_size = sizeof(DummyObject), 45 .class_size = sizeof(DummyObjectClass), 46 }; 47 48 struct TestTaskData { 49 Object *source; 50 Error *err; 51 bool freed; 52 }; 53 54 55 static void task_callback(QIOTask *task, 56 gpointer opaque) 57 { 58 struct TestTaskData *data = opaque; 59 60 data->source = qio_task_get_source(task); 61 qio_task_propagate_error(task, &data->err); 62 } 63 64 65 static void test_task_complete(void) 66 { 67 QIOTask *task; 68 Object *obj = object_new(TYPE_DUMMY); 69 Object *src; 70 struct TestTaskData data = { NULL, NULL, false }; 71 72 task = qio_task_new(obj, task_callback, &data, NULL); 73 src = qio_task_get_source(task); 74 75 qio_task_complete(task); 76 77 g_assert(obj == src); 78 79 object_unref(obj); 80 81 g_assert(data.source == obj); 82 g_assert(data.err == NULL); 83 g_assert(data.freed == false); 84 } 85 86 87 static void task_data_free(gpointer opaque) 88 { 89 struct TestTaskData *data = opaque; 90 91 data->freed = true; 92 } 93 94 95 static void test_task_data_free(void) 96 { 97 QIOTask *task; 98 Object *obj = object_new(TYPE_DUMMY); 99 struct TestTaskData data = { NULL, NULL, false }; 100 101 task = qio_task_new(obj, task_callback, &data, task_data_free); 102 103 qio_task_complete(task); 104 105 object_unref(obj); 106 107 g_assert(data.source == obj); 108 g_assert(data.err == NULL); 109 g_assert(data.freed == true); 110 } 111 112 113 static void test_task_failure(void) 114 { 115 QIOTask *task; 116 Object *obj = object_new(TYPE_DUMMY); 117 struct TestTaskData data = { NULL, NULL, false }; 118 Error *err = NULL; 119 120 task = qio_task_new(obj, task_callback, &data, NULL); 121 122 error_setg(&err, "Some error"); 123 124 qio_task_set_error(task, err); 125 qio_task_complete(task); 126 127 object_unref(obj); 128 129 g_assert(data.source == obj); 130 g_assert(data.err == err); 131 g_assert(data.freed == false); 132 error_free(data.err); 133 } 134 135 136 struct TestThreadWorkerData { 137 Object *source; 138 Error *err; 139 bool fail; 140 GThread *worker; 141 GThread *complete; 142 GMainLoop *loop; 143 }; 144 145 static void test_task_thread_worker(QIOTask *task, 146 gpointer opaque) 147 { 148 struct TestThreadWorkerData *data = opaque; 149 150 data->worker = g_thread_self(); 151 152 if (data->fail) { 153 Error *err = NULL; 154 error_setg(&err, "Testing fail"); 155 qio_task_set_error(task, err); 156 } 157 } 158 159 160 static void test_task_thread_callback(QIOTask *task, 161 gpointer opaque) 162 { 163 struct TestThreadWorkerData *data = opaque; 164 165 data->source = qio_task_get_source(task); 166 qio_task_propagate_error(task, &data->err); 167 168 data->complete = g_thread_self(); 169 170 g_main_loop_quit(data->loop); 171 } 172 173 174 static void test_task_thread_complete(void) 175 { 176 QIOTask *task; 177 Object *obj = object_new(TYPE_DUMMY); 178 struct TestThreadWorkerData data = { 0 }; 179 GThread *self; 180 181 data.loop = g_main_loop_new(g_main_context_default(), 182 TRUE); 183 184 task = qio_task_new(obj, 185 test_task_thread_callback, 186 &data, 187 NULL); 188 189 qio_task_run_in_thread(task, 190 test_task_thread_worker, 191 &data, 192 NULL, 193 NULL); 194 195 g_main_loop_run(data.loop); 196 197 g_main_loop_unref(data.loop); 198 object_unref(obj); 199 200 g_assert(data.source == obj); 201 g_assert(data.err == NULL); 202 203 self = g_thread_self(); 204 205 /* Make sure the test_task_thread_worker actually got 206 * run in a different thread */ 207 g_assert(data.worker != self); 208 209 /* And that the test_task_thread_callback got rnu in 210 * the main loop thread (ie this one) */ 211 g_assert(data.complete == self); 212 } 213 214 215 static void test_task_thread_failure(void) 216 { 217 QIOTask *task; 218 Object *obj = object_new(TYPE_DUMMY); 219 struct TestThreadWorkerData data = { 0 }; 220 GThread *self; 221 222 data.loop = g_main_loop_new(g_main_context_default(), 223 TRUE); 224 data.fail = true; 225 226 task = qio_task_new(obj, 227 test_task_thread_callback, 228 &data, 229 NULL); 230 231 qio_task_run_in_thread(task, 232 test_task_thread_worker, 233 &data, 234 NULL, 235 NULL); 236 237 g_main_loop_run(data.loop); 238 239 g_main_loop_unref(data.loop); 240 object_unref(obj); 241 242 g_assert(data.source == obj); 243 error_free_or_abort(&data.err); 244 245 self = g_thread_self(); 246 247 /* Make sure the test_task_thread_worker actually got 248 * run in a different thread */ 249 g_assert(data.worker != self); 250 251 /* And that the test_task_thread_callback got rnu in 252 * the main loop thread (ie this one) */ 253 g_assert(data.complete == self); 254 } 255 256 257 int main(int argc, char **argv) 258 { 259 g_test_init(&argc, &argv, NULL); 260 module_call_init(MODULE_INIT_QOM); 261 type_register_static(&dummy_info); 262 g_test_add_func("/crypto/task/complete", test_task_complete); 263 g_test_add_func("/crypto/task/datafree", test_task_data_free); 264 g_test_add_func("/crypto/task/failure", test_task_failure); 265 g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete); 266 g_test_add_func("/crypto/task/thread_failure", test_task_thread_failure); 267 return g_test_run(); 268 } 269