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