Paolo Bonzini | 18fa3eb | 2021-07-18 08:49:22 +0200 | [diff] [blame] | 1 | /* |
| 2 | * QAPI Forwarding Visitor unit-tests. |
| 3 | * |
| 4 | * Copyright (C) 2021 Red Hat Inc. |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 7 | * See the COPYING file in the top-level directory. |
| 8 | */ |
| 9 | |
| 10 | #include "qemu/osdep.h" |
| 11 | |
Paolo Bonzini | 18fa3eb | 2021-07-18 08:49:22 +0200 | [diff] [blame] | 12 | #include "qapi/forward-visitor.h" |
| 13 | #include "qapi/qobject-input-visitor.h" |
| 14 | #include "qapi/error.h" |
| 15 | #include "qapi/qmp/qobject.h" |
| 16 | #include "qapi/qmp/qdict.h" |
| 17 | #include "test-qapi-visit.h" |
Marc-André Lureau | 9ca9c89 | 2022-04-20 17:26:06 +0400 | [diff] [blame] | 18 | #include "qemu/keyval.h" |
Paolo Bonzini | 18fa3eb | 2021-07-18 08:49:22 +0200 | [diff] [blame] | 19 | |
| 20 | typedef bool GenericVisitor (Visitor *, const char *, void **, Error **); |
| 21 | #define CAST_VISIT_TYPE(fn) ((GenericVisitor *)(fn)) |
| 22 | |
| 23 | /* |
| 24 | * Parse @srcstr and wrap it with a ForwardFieldVisitor converting "src" to |
| 25 | * "dst". Check that visiting the result with "src" name fails, and return |
| 26 | * the result of visiting "dst". |
| 27 | */ |
| 28 | static void *visit_with_forward(const char *srcstr, GenericVisitor *fn) |
| 29 | { |
| 30 | bool help = false; |
| 31 | QDict *src = keyval_parse(srcstr, NULL, &help, &error_abort); |
| 32 | Visitor *v, *alias_v; |
| 33 | Error *err = NULL; |
| 34 | void *result = NULL; |
| 35 | |
| 36 | v = qobject_input_visitor_new_keyval(QOBJECT(src)); |
| 37 | visit_start_struct(v, NULL, NULL, 0, &error_abort); |
| 38 | |
| 39 | alias_v = visitor_forward_field(v, "dst", "src"); |
| 40 | fn(alias_v, "src", &result, &err); |
| 41 | error_free_or_abort(&err); |
| 42 | assert(!result); |
| 43 | fn(alias_v, "dst", &result, &err); |
| 44 | assert(err == NULL); |
| 45 | visit_free(alias_v); |
| 46 | |
| 47 | visit_end_struct(v, NULL); |
| 48 | visit_free(v); |
| 49 | qobject_unref(QOBJECT(src)); |
| 50 | return result; |
| 51 | } |
| 52 | |
| 53 | static void test_forward_any(void) |
| 54 | { |
| 55 | QObject *src = visit_with_forward("src.integer=42,src.string=Hello,src.enum1=value2", |
| 56 | CAST_VISIT_TYPE(visit_type_any)); |
| 57 | Visitor *v = qobject_input_visitor_new_keyval(src); |
| 58 | Error *err = NULL; |
| 59 | UserDefOne *dst; |
| 60 | |
| 61 | visit_type_UserDefOne(v, NULL, &dst, &err); |
| 62 | assert(err == NULL); |
| 63 | visit_free(v); |
| 64 | |
| 65 | g_assert_cmpint(dst->integer, ==, 42); |
| 66 | g_assert_cmpstr(dst->string, ==, "Hello"); |
| 67 | g_assert_cmpint(dst->has_enum1, ==, true); |
| 68 | g_assert_cmpint(dst->enum1, ==, ENUM_ONE_VALUE2); |
| 69 | qapi_free_UserDefOne(dst); |
| 70 | qobject_unref(QOBJECT(src)); |
| 71 | } |
| 72 | |
| 73 | static void test_forward_size(void) |
| 74 | { |
| 75 | /* |
| 76 | * visit_type_size does not return a pointer, so visit_with_forward |
| 77 | * cannot be used. |
| 78 | */ |
| 79 | bool help = false; |
| 80 | QDict *src = keyval_parse("src=1.5M", NULL, &help, &error_abort); |
| 81 | Visitor *v, *alias_v; |
| 82 | Error *err = NULL; |
| 83 | uint64_t result = 0; |
| 84 | |
| 85 | v = qobject_input_visitor_new_keyval(QOBJECT(src)); |
| 86 | visit_start_struct(v, NULL, NULL, 0, &error_abort); |
| 87 | |
| 88 | alias_v = visitor_forward_field(v, "dst", "src"); |
| 89 | visit_type_size(alias_v, "src", &result, &err); |
| 90 | error_free_or_abort(&err); |
| 91 | visit_type_size(alias_v, "dst", &result, &err); |
| 92 | assert(result == 3 << 19); |
| 93 | assert(err == NULL); |
| 94 | visit_free(alias_v); |
| 95 | |
| 96 | visit_end_struct(v, NULL); |
| 97 | visit_free(v); |
| 98 | qobject_unref(QOBJECT(src)); |
| 99 | } |
| 100 | |
| 101 | static void test_forward_number(void) |
| 102 | { |
| 103 | /* |
| 104 | * visit_type_number does not return a pointer, so visit_with_forward |
| 105 | * cannot be used. |
| 106 | */ |
| 107 | bool help = false; |
| 108 | QDict *src = keyval_parse("src=1.5", NULL, &help, &error_abort); |
| 109 | Visitor *v, *alias_v; |
| 110 | Error *err = NULL; |
| 111 | double result = 0.0; |
| 112 | |
| 113 | v = qobject_input_visitor_new_keyval(QOBJECT(src)); |
| 114 | visit_start_struct(v, NULL, NULL, 0, &error_abort); |
| 115 | |
| 116 | alias_v = visitor_forward_field(v, "dst", "src"); |
| 117 | visit_type_number(alias_v, "src", &result, &err); |
| 118 | error_free_or_abort(&err); |
| 119 | visit_type_number(alias_v, "dst", &result, &err); |
| 120 | assert(result == 1.5); |
| 121 | assert(err == NULL); |
| 122 | visit_free(alias_v); |
| 123 | |
| 124 | visit_end_struct(v, NULL); |
| 125 | visit_free(v); |
| 126 | qobject_unref(QOBJECT(src)); |
| 127 | } |
| 128 | |
| 129 | static void test_forward_string(void) |
| 130 | { |
| 131 | char *dst = visit_with_forward("src=Hello", |
| 132 | CAST_VISIT_TYPE(visit_type_str)); |
| 133 | |
| 134 | g_assert_cmpstr(dst, ==, "Hello"); |
| 135 | g_free(dst); |
| 136 | } |
| 137 | |
| 138 | static void test_forward_struct(void) |
| 139 | { |
| 140 | UserDefOne *dst = visit_with_forward("src.integer=42,src.string=Hello", |
| 141 | CAST_VISIT_TYPE(visit_type_UserDefOne)); |
| 142 | |
| 143 | g_assert_cmpint(dst->integer, ==, 42); |
| 144 | g_assert_cmpstr(dst->string, ==, "Hello"); |
| 145 | g_assert_cmpint(dst->has_enum1, ==, false); |
| 146 | qapi_free_UserDefOne(dst); |
| 147 | } |
| 148 | |
| 149 | static void test_forward_alternate(void) |
| 150 | { |
| 151 | AltStrObj *s_dst = visit_with_forward("src=hello", |
| 152 | CAST_VISIT_TYPE(visit_type_AltStrObj)); |
| 153 | AltStrObj *o_dst = visit_with_forward("src.integer=42,src.boolean=true,src.string=world", |
| 154 | CAST_VISIT_TYPE(visit_type_AltStrObj)); |
| 155 | |
| 156 | g_assert_cmpint(s_dst->type, ==, QTYPE_QSTRING); |
| 157 | g_assert_cmpstr(s_dst->u.s, ==, "hello"); |
| 158 | g_assert_cmpint(o_dst->type, ==, QTYPE_QDICT); |
| 159 | g_assert_cmpint(o_dst->u.o.integer, ==, 42); |
| 160 | g_assert_cmpint(o_dst->u.o.boolean, ==, true); |
| 161 | g_assert_cmpstr(o_dst->u.o.string, ==, "world"); |
| 162 | |
| 163 | qapi_free_AltStrObj(s_dst); |
| 164 | qapi_free_AltStrObj(o_dst); |
| 165 | } |
| 166 | |
| 167 | static void test_forward_list(void) |
| 168 | { |
| 169 | uint8List *dst = visit_with_forward("src.0=1,src.1=2,src.2=3,src.3=4", |
| 170 | CAST_VISIT_TYPE(visit_type_uint8List)); |
| 171 | uint8List *tmp; |
| 172 | int i; |
| 173 | |
| 174 | for (tmp = dst, i = 1; i <= 4; i++) { |
| 175 | g_assert(tmp); |
| 176 | g_assert_cmpint(tmp->value, ==, i); |
| 177 | tmp = tmp->next; |
| 178 | } |
| 179 | g_assert(!tmp); |
| 180 | qapi_free_uint8List(dst); |
| 181 | } |
| 182 | |
| 183 | int main(int argc, char **argv) |
| 184 | { |
| 185 | g_test_init(&argc, &argv, NULL); |
| 186 | |
| 187 | g_test_add_func("/visitor/forward/struct", test_forward_struct); |
| 188 | g_test_add_func("/visitor/forward/alternate", test_forward_alternate); |
| 189 | g_test_add_func("/visitor/forward/string", test_forward_string); |
| 190 | g_test_add_func("/visitor/forward/size", test_forward_size); |
| 191 | g_test_add_func("/visitor/forward/number", test_forward_number); |
| 192 | g_test_add_func("/visitor/forward/any", test_forward_any); |
| 193 | g_test_add_func("/visitor/forward/list", test_forward_list); |
| 194 | |
| 195 | return g_test_run(); |
| 196 | } |