1#!/usr/bin/python
2
3from __future__ import print_function
4
5import os
6import sys
7import re
8import random
9
10import idl
11
12def randomize_char(c):
13    if random.random() < 0.5:
14        return str.lower(c)
15    else:
16        return str.upper(c)
17
18def randomize_case(s):
19    r = [randomize_char(c) for c in s]
20    return "".join(r)
21
22def randomize_enum(e):
23    return random.choice([v.name for v in e.values])
24
25handcoded = ["libxl_bitmap", "libxl_key_value_list",
26             "libxl_cpuid_policy_list", "libxl_string_list"]
27
28def gen_rand_init(ty, v, indent = "    ", parent = None):
29    s = ""
30    if isinstance(ty, idl.Enumeration):
31        s += "%s = %s;\n" % (ty.pass_arg(v, parent is None), randomize_enum(ty))
32    elif isinstance(ty, idl.Array):
33        if parent is None:
34            raise Exception("Array type must have a parent")
35        s += "%s = test_rand(8);\n" % (parent + ty.lenvar.name)
36        s += "%s = calloc(%s, sizeof(*%s));\n" % \
37            (v, parent + ty.lenvar.name, v)
38        s += "assert(%s);\n" % (v, )
39        s += "{\n"
40        s += "    int i;\n"
41        s += "    for (i=0; i<%s; i++)\n" % (parent + ty.lenvar.name)
42        s += gen_rand_init(ty.elem_type, v+"[i]",
43                           indent + "        ", parent)
44        s += "}\n"
45    elif isinstance(ty, idl.KeyedUnion):
46        if parent is None:
47            raise Exception("KeyedUnion type must have a parent")
48        s += gen_rand_init(ty.keyvar.type, parent + ty.keyvar.name, indent, parent)
49        s += "switch (%s) {\n" % (parent + ty.keyvar.name)
50        for f in ty.fields:
51            (nparent,fexpr) = ty.member(v, f, parent is None)
52            s += "case %s:\n" % f.enumname
53            if f.type is not None:
54                s += gen_rand_init(f.type, fexpr, indent + "    ", nparent)
55            s += "    break;\n"
56        s += "}\n"
57    elif isinstance(ty, idl.Struct) \
58     and (parent is None or ty.json_gen_fn is None):
59        for f in [f for f in ty.fields if not f.const]:
60            (nparent,fexpr) = ty.member(v, f, parent is None)
61            s += gen_rand_init(f.type, fexpr, "", nparent)
62    elif hasattr(ty, "rand_init") and ty.rand_init is not None:
63        s += "%s(%s);\n" % (ty.rand_init,
64                            ty.pass_arg(v, isref=parent is None,
65                                        passby=idl.PASS_BY_REFERENCE))
66    elif ty.typename in ["libxl_uuid", "libxl_mac", "libxl_hwcap", "libxl_ms_vm_genid"]:
67        s += "rand_bytes((uint8_t *)%s, sizeof(*%s));\n" % (v,v)
68    elif ty.typename in ["libxl_domid", "libxl_devid"] or isinstance(ty, idl.Number):
69        s += "%s = test_rand(sizeof(%s) * 8);\n" % \
70             (ty.pass_arg(v, parent is None),
71              ty.pass_arg(v, parent is None))
72    elif ty.typename in ["bool"]:
73        s += "%s = test_rand(2);\n" % v
74    elif ty.typename in ["libxl_defbool"]:
75        s += "libxl_defbool_set(%s, test_rand(2));\n" % v
76    elif ty.typename in ["char *"]:
77        s += "%s = rand_str();\n" % v
78    elif ty.private:
79        pass
80    elif ty.typename in handcoded:
81        raise Exception("Gen for handcoded %s" % ty.typename)
82    else:
83        raise Exception("Cannot randomly init %s" % ty.typename)
84
85    if s != "":
86        s = indent + s
87    return s.replace("\n", "\n%s" % indent).rstrip(indent)
88
89if __name__ == '__main__':
90    if len(sys.argv) < 3:
91        print("Usage: gentest.py <idl> <implementation>", file=sys.stderr)
92        sys.exit(1)
93
94    random.seed(os.getenv('LIBXL_TESTIDL_SEED'))
95
96    (builtins,types) = idl.parse(sys.argv[1])
97
98    impl = sys.argv[2]
99    f = open(impl, "w")
100    f.write("""
101#include <stdio.h>
102#include <stdlib.h>
103#include <string.h>
104#include <assert.h>
105
106#include "libxl.h"
107#include "libxl_utils.h"
108
109static int test_rand(unsigned max)
110{
111    /* We are not using rand() for its cryptographic properies. */
112    return rand() % max;
113}
114
115static char *rand_str(void)
116{
117    int i, sz = test_rand(32);
118    char *s = malloc(sz+1);
119    assert(s);
120    for (i=0; i<sz; i++)
121        s[i] = 'a' + test_rand(26);
122    s[i] = '\\0';
123    return s;
124}
125
126static void rand_bytes(uint8_t *p, size_t sz)
127{
128    int i;
129    for (i=0; i<sz; i++)
130        p[i] = test_rand(256);
131}
132
133static void libxl_bitmap_rand_init(libxl_bitmap *bitmap)
134{
135    int i;
136    bitmap->size = test_rand(16);
137    bitmap->map = calloc(bitmap->size, sizeof(*bitmap->map));
138    assert(bitmap->map);
139    libxl_for_each_bit(i, *bitmap) {
140        if (test_rand(2))
141            libxl_bitmap_set(bitmap, i);
142        else
143            libxl_bitmap_reset(bitmap, i);
144    }
145}
146
147static void libxl_key_value_list_rand_init(libxl_key_value_list *pkvl)
148{
149    int i, nr_kvp = test_rand(16);
150    libxl_key_value_list kvl = calloc(nr_kvp+1, 2*sizeof(char *));
151    assert(kvl);
152
153    for (i = 0; i<2*nr_kvp; i += 2) {
154        kvl[i] = rand_str();
155        if (test_rand(8))
156            kvl[i+1] = rand_str();
157        else
158            kvl[i+1] = NULL;
159    }
160    kvl[i] = NULL;
161    kvl[i+1] = NULL;
162    *pkvl = kvl;
163}
164
165static void libxl_cpuid_policy_list_rand_init(libxl_cpuid_policy_list *pp)
166{
167    int i, nr_policies = test_rand(16);
168    struct {
169        const char *n;
170        int w;
171    } options[] = {
172      /* A random selection from libxl_cpuid_parse_config */
173        {"maxleaf",     32},
174        {"family",       8},
175        {"model",        8},
176        {"stepping",     4},
177        {"localapicid",  8},
178        {"proccount",    8},
179        {"clflush",      8},
180        {"brandid",      8},
181        {"f16c",         1},
182        {"avx",          1},
183        {"osxsave",      1},
184        {"xsave",        1},
185        {"aes",          1},
186        {"popcnt",       1},
187        {"movbe",        1},
188        {"x2apic",       1},
189        {"sse4.2",       1},
190        {"sse4.1",       1},
191        {"dca",          1},
192        {"pdcm",         1},
193        {"procpkg",      6},
194    };
195    const int nr_options = sizeof(options)/sizeof(options[0]);
196    char buf[64];
197    libxl_cpuid_policy_list p = NULL;
198
199    for (i = 0; i < nr_policies; i++) {
200        int opt = test_rand(nr_options);
201        int val = test_rand(1<<options[opt].w);
202        snprintf(buf, 64, \"%s=%#x\", options[opt].n, val);
203        libxl_cpuid_parse_config(&p, buf);
204    }
205    *pp = p;
206}
207
208static void libxl_string_list_rand_init(libxl_string_list *p)
209{
210    int i, nr = test_rand(16);
211    libxl_string_list l = calloc(nr+1, sizeof(char *));
212    assert(l);
213
214    for (i = 0; i<nr; i++) {
215        l[i] = rand_str();
216    }
217    l[i] = NULL;
218    *p = l;
219}
220""")
221    for ty in builtins + types:
222        if isinstance(ty, idl.Number): continue
223        if ty.typename not in handcoded:
224            f.write("static void %s_rand_init(%s);\n" % \
225                    (ty.typename,
226                     ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
227            f.write("static void %s_rand_init(%s)\n" % \
228                    (ty.typename,
229                     ty.make_arg("p", passby=idl.PASS_BY_REFERENCE)))
230            f.write("{\n")
231            f.write(gen_rand_init(ty, "p"))
232            f.write("}\n")
233            f.write("\n")
234        ty.rand_init = "%s_rand_init" % ty.typename
235
236    f.write("""
237int main(int argc, char **argv)
238{
239""")
240
241    for ty in types:
242        f.write("    %s %s_val, %s_val_new;\n" % \
243                (ty.typename, ty.typename, ty.typename))
244    f.write("""
245    int rc;
246    char *s, *new_s, *json_string;
247    xentoollog_logger_stdiostream *logger;
248    libxl_ctx *ctx;
249
250    logger = xtl_createlogger_stdiostream(stderr, XTL_DETAIL, 0);
251    if (!logger) exit(1);
252
253    if (libxl_ctx_alloc(&ctx, LIBXL_VERSION, 0, (xentoollog_logger*)logger)) {
254        fprintf(stderr, "cannot init xl context\\n");
255        exit(1);
256    }
257""")
258    f.write("    printf(\"Testing TYPE_to/from_json()\\n\");\n")
259    f.write("    printf(\"----------------------\\n\");\n")
260    f.write("    printf(\"\\n\");\n")
261    for ty in [t for t in types if t.json_gen_fn is not None]:
262        arg = ty.typename + "_val"
263        f.write("    %s_rand_init(%s);\n" % (ty.typename, \
264            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
265        if not isinstance(ty, idl.Enumeration):
266            iters = random.randrange(1,10)
267            while iters > 0:
268                f.write("    %s_init(%s_new);\n" % (ty.typename, \
269                    ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
270                iters -= 1
271        f.write("    s = %s_to_json(ctx, %s);\n" % \
272                (ty.typename, ty.pass_arg(arg, isref=False)))
273        f.write("    printf(\"%%s: %%s\\n\", \"%s\", s);\n" % ty.typename)
274        f.write("    if (s == NULL) abort();\n")
275        f.write("    rc = %s_from_json(ctx, &%s_val_new, s);\n" % \
276                (ty.typename, ty.typename))
277        f.write("    if (rc) abort();\n")
278        f.write("    new_s = %s_to_json(ctx, %s_new);\n" % \
279                (ty.typename, ty.pass_arg(arg, isref=False)))
280        f.write("    if (new_s == NULL) abort();\n")
281        f.write("    if (strcmp(s, new_s)) {\n")
282        f.write("        printf(\"Huh? Regenerated string different from original string.\\n\");\n")
283        f.write("        printf(\"Regenerated string: %s\\n\", new_s);\n")
284        f.write("        abort();\n")
285        f.write("    }\n")
286        f.write("    free(s);\n")
287        f.write("    free(new_s);\n")
288        if ty.dispose_fn is not None:
289            iters = random.randrange(1,10)
290            f.write("    %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
291            while iters > 0:
292                f.write("    %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
293                iters -= 1
294        f.write("\n")
295
296    f.write("    printf(\"Testing TYPE_copy()\\n\");\n")
297    f.write("    printf(\"----------------------\\n\");\n")
298    f.write("    printf(\"\\n\");\n")
299    for ty in [t for t in types if t.copy_fn is not None]:
300        f.write("    printf(\"Testing %s_copy, \");\n" % ty.typename)
301        arg = ty.typename + "_val"
302        f.write("    %s_init(%s);\n" % (ty.typename, \
303            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
304        f.write("    %s_rand_init(%s);\n" % (ty.typename, \
305            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
306        f.write("    %s_init(%s_new);\n" % (ty.typename, \
307            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
308        f.write("    %s_copy(ctx, %s_new, %s);\n" % (ty.typename, \
309            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE), \
310            ty.pass_arg(arg, isref=False, passby=idl.PASS_BY_REFERENCE)))
311        f.write("    s = %s_to_json(ctx, %s);\n" % \
312                (ty.typename, ty.pass_arg(arg, isref=False)))
313        f.write("    if (s == NULL) abort();\n")
314        f.write("    new_s = %s_to_json(ctx, %s_new);\n" % \
315                (ty.typename, ty.pass_arg(arg, isref=False)))
316        f.write("    if (new_s == NULL) abort();\n")
317        f.write("    if (strcmp(s, new_s)) {\n")
318        f.write("        printf(\"Huh? Deep copy for %s failed. Regenerated string different from original string.\\n\");\n" \
319                % ty.typename)
320        f.write("        printf(\"Original string: %s\\n\", s);\n")
321        f.write("        printf(\"Regenerated string: %s\\n\", new_s);\n")
322        f.write("        abort();\n")
323        f.write("    }\n")
324        f.write("    free(s);\n")
325        f.write("    free(new_s);\n")
326        if ty.dispose_fn is not None:
327            f.write("    %s(&%s_val);\n" % (ty.dispose_fn, ty.typename))
328            f.write("    %s(&%s_val_new);\n" % (ty.dispose_fn, ty.typename))
329        f.write("    printf(\"done\\n\");\n")
330        f.write("\n")
331
332    f.write("    printf(\"\\n\");\n")
333    f.write("    printf(\"Testing Enumerations\\n\");\n")
334    f.write("    printf(\"--------------------\\n\");\n")
335    f.write("    printf(\"\\n\");\n")
336    for ty in [t for t in types if isinstance(t,idl.Enumeration)]:
337        f.write("    printf(\"%s -- to string:\\n\");\n" % (ty.typename))
338        for v in ty.values:
339            f.write("    printf(\"\\t%s = %%d = \\\"%%s\\\"\\n\", " \
340                    "%s, %s_to_string(%s));\n" % \
341                    (v.valuename, v.name, ty.typename, v.name))
342        f.write("\n")
343
344        f.write("    printf(\"%s -- to JSON:\\n\");\n" % (ty.typename))
345        for v in ty.values:
346            f.write("    json_string = %s_to_json(ctx, %s);\n" % \
347                    (ty.typename, v.name))
348            f.write("    printf(\"\\t%s = %%d = %%s\", " \
349                    "%s, json_string);\n" %\
350                    (v.valuename, v.name))
351            f.write("    free(json_string);\n");
352            f.write("    json_string = NULL;\n");
353        f.write("\n")
354
355        f.write("    printf(\"%s -- from string:\\n\");\n" % (ty.typename))
356        for v in [v.valuename for v in ty.values] + ["AN INVALID VALUE"]:
357            n = randomize_case(v)
358            f.write("    %s_val = -1;\n" % (ty.typename))
359            f.write("    rc = %s_from_string(\"%s\", &%s_val);\n" %\
360                    (ty.typename, n, ty.typename))
361
362            f.write("    printf(\"\\t%s = \\\"%%s\\\" = %%d (rc %%d)\\n\", " \
363                    "\"%s\", %s_val, rc);\n" %\
364                    (v, n, ty.typename))
365        f.write("\n")
366
367    f.write("""
368
369    libxl_ctx_free(ctx);
370    xtl_logger_destroy((xentoollog_logger*)logger);
371
372    return 0;
373}
374""")
375