1import sys
2import yaml
3import hashlib
4
5DEFINE = 'YAML_GEN_TESTS'
6EVENT_COUNT = 5
7
8def encode_stream(line):
9    for c in line:
10        if c == '\n':
11            yield '\\n'
12        elif c == '"':
13            yield '\\"'
14        elif c == '\t':
15            yield '\\t'
16        elif ord(c) < 0x20:
17            yield '\\x' + hex(ord(c))
18        else:
19            yield c
20
21def encode(line):
22    return ''.join(encode_stream(line))
23
24def doc_start(implicit=False):
25    if implicit:
26        return {'emit': '', 'handle': 'OnDocumentStart(_)'}
27    else:
28        return {'emit': 'BeginDoc', 'handle': 'OnDocumentStart(_)'}
29
30def doc_end(implicit=False):
31    if implicit:
32        return {'emit': '', 'handle': 'OnDocumentEnd()'}
33    else:
34        return {'emit': 'EndDoc', 'handle': 'OnDocumentEnd()'}
35
36def scalar(value, tag='', anchor='', anchor_id=0):
37    emit = []
38    handle = []
39    if tag:
40        emit += ['VerbatimTag("%s")' % encode(tag)]
41    if anchor:
42        emit += ['Anchor("%s")' % encode(anchor)]
43        handle += ['OnAnchor(_, "%s")' % encode(anchor)]
44    if tag:
45        out_tag = encode(tag)
46    else:
47        if value == encode(value):
48            out_tag = '?'
49        else:
50            out_tag = '!'
51    emit += ['"%s"' % encode(value)]
52    handle += ['OnScalar(_, "%s", %s, "%s")' % (out_tag, anchor_id, encode(value))]
53    return {'emit': emit, 'handle': handle}
54
55def comment(value):
56    return {'emit': 'Comment("%s")' % value, 'handle': ''}
57
58def seq_start(tag='', anchor='', anchor_id=0, style='_'):
59    emit = []
60    handle = []
61    if tag:
62        emit += ['VerbatimTag("%s")' % encode(tag)]
63    if anchor:
64        emit += ['Anchor("%s")' % encode(anchor)]
65        handle += ['OnAnchor(_, "%s")' % encode(anchor)]
66    if tag:
67        out_tag = encode(tag)
68    else:
69        out_tag = '?'
70    emit += ['BeginSeq']
71    handle += ['OnSequenceStart(_, "%s", %s, %s)' % (out_tag, anchor_id, style)]
72    return {'emit': emit, 'handle': handle}
73
74def seq_end():
75    return {'emit': 'EndSeq', 'handle': 'OnSequenceEnd()'}
76
77def map_start(tag='', anchor='', anchor_id=0, style='_'):
78    emit = []
79    handle = []
80    if tag:
81        emit += ['VerbatimTag("%s")' % encode(tag)]
82    if anchor:
83        emit += ['Anchor("%s")' % encode(anchor)]
84        handle += ['OnAnchor(_, "%s")' % encode(anchor)]
85    if tag:
86        out_tag = encode(tag)
87    else:
88        out_tag = '?'
89    emit += ['BeginMap']
90    handle += ['OnMapStart(_, "%s", %s, %s)' % (out_tag, anchor_id, style)]
91    return {'emit': emit, 'handle': handle}
92
93def map_end():
94    return {'emit': 'EndMap', 'handle': 'OnMapEnd()'}
95
96def gen_templates():
97    yield [[doc_start(), doc_start(True)],
98           [scalar('foo'), scalar('foo\n'), scalar('foo', 'tag'), scalar('foo', '', 'anchor', 1)],
99           [doc_end(), doc_end(True)]]
100    yield [[doc_start(), doc_start(True)],
101           [seq_start()],
102           [[], [scalar('foo')], [scalar('foo', 'tag')], [scalar('foo', '', 'anchor', 1)], [scalar('foo', 'tag', 'anchor', 1)], [scalar('foo'), scalar('bar')], [scalar('foo', 'tag', 'anchor', 1), scalar('bar', 'tag', 'other', 2)]],
103           [seq_end()],
104           [doc_end(), doc_end(True)]]
105    yield [[doc_start(), doc_start(True)],
106           [map_start()],
107           [[], [scalar('foo'), scalar('bar')], [scalar('foo', 'tag', 'anchor', 1), scalar('bar', 'tag', 'other', 2)]],
108           [map_end()],
109           [doc_end(), doc_end(True)]]
110    yield [[doc_start(True)],
111           [map_start()],
112           [[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
113           [[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
114           [map_end()],
115           [doc_end(True)]]
116    yield [[doc_start(True)],
117           [seq_start()],
118           [[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
119           [[scalar('foo')], [seq_start(), scalar('foo'), seq_end()], [map_start(), scalar('foo'), scalar('bar'), map_end()]],
120           [seq_end()],
121           [doc_end(True)]]
122
123def expand(template):
124    if len(template) == 0:
125        pass
126    elif len(template) == 1:
127        for item in template[0]:
128            if isinstance(item, list):
129                yield item
130            else:
131                yield [item]
132    else:
133        for car in expand(template[:1]):
134            for cdr in expand(template[1:]):
135                yield car + cdr
136
137def gen_events():
138    for template in gen_templates():
139        for events in expand(template):
140            base = list(events)
141            for i in range(0, len(base)+1):
142                cpy = list(base)
143                cpy.insert(i, comment('comment'))
144                yield cpy
145
146def gen_tests():
147    for events in gen_events():
148        name = 'test' + hashlib.sha1(''.join(yaml.dump(event) for event in events)).hexdigest()[:20]
149        yield {'name': name, 'events': events}
150
151class Writer(object):
152    def __init__(self, out):
153        self.out = out
154        self.indent = 0
155
156    def writeln(self, s):
157        self.out.write('%s%s\n' % (' ' * self.indent, s))
158
159class Scope(object):
160    def __init__(self, writer, name, indent):
161        self.writer = writer
162        self.name = name
163        self.indent = indent
164
165    def __enter__(self):
166        self.writer.writeln('%s {' % self.name)
167        self.writer.indent += self.indent
168
169    def __exit__(self, type, value, traceback):
170        self.writer.indent -= self.indent
171        self.writer.writeln('}')
172
173def create_emitter_tests(out):
174    out = Writer(out)
175
176    includes = [
177        'handler_test.h',
178        'yaml-cpp/yaml.h',
179        'gmock/gmock.h',
180        'gtest/gtest.h',
181    ]
182    for include in includes:
183        out.writeln('#include "%s"' % include)
184    out.writeln('')
185
186    usings = [
187        '::testing::_',
188    ]
189    for using in usings:
190        out.writeln('using %s;' % using)
191    out.writeln('')
192
193    with Scope(out, 'namespace YAML', 0) as _:
194        with Scope(out, 'namespace', 0) as _:
195            out.writeln('')
196            out.writeln('typedef HandlerTest GenEmitterTest;')
197            out.writeln('')
198            tests = list(gen_tests())
199
200            for test in tests:
201                with Scope(out, 'TEST_F(%s, %s)' % ('GenEmitterTest', test['name']), 2) as _:
202                    out.writeln('Emitter out;')
203                    for event in test['events']:
204                        emit = event['emit']
205                        if isinstance(emit, list):
206                            for e in emit:
207                                out.writeln('out << %s;' % e)
208                        elif emit:
209                            out.writeln('out << %s;' % emit)
210                    out.writeln('')
211                    for event in test['events']:
212                        handle = event['handle']
213                        if isinstance(handle, list):
214                            for e in handle:
215                                out.writeln('EXPECT_CALL(handler, %s);' % e)
216                        elif handle:
217                            out.writeln('EXPECT_CALL(handler, %s);' % handle)
218                    out.writeln('Parse(out.c_str());')
219                out.writeln('')
220
221if __name__ == '__main__':
222    create_emitter_tests(sys.stdout)
223