1#!/usr/bin/env python
2
3import breakdancer
4from breakdancer import Condition, Effect, Action, Driver
5
6TESTKEY = 'testkey'
7
8######################################################################
9# Conditions
10######################################################################
11
12class ExistsCondition(Condition):
13
14    def __call__(self, state):
15        return TESTKEY in state
16
17class ExistsAsNumber(Condition):
18
19    def __call__(self, state):
20        try:
21            int(state[TESTKEY])
22            return True
23        except:
24            return False
25
26class MaybeExistsAsNumber(ExistsAsNumber):
27
28    def __call__(self, state):
29        return TESTKEY not in state or ExistsAsNumber.__call__(self, state)
30
31class DoesNotExistCondition(Condition):
32
33    def __call__(self, state):
34        return TESTKEY not in state
35
36class NothingExistsCondition(Condition):
37
38    def __call__(self, state):
39        return not bool(state)
40
41######################################################################
42# Effects
43######################################################################
44
45class StoreEffect(Effect):
46
47    def __init__(self, v='0'):
48        self.v = v
49
50    def __call__(self, state):
51        state[TESTKEY] = self.v
52
53class DeleteEffect(Effect):
54
55    def __call__(self, state):
56        del state[TESTKEY]
57
58class FlushEffect(Effect):
59
60    def __call__(self, state):
61        state.clear()
62
63class AppendEffect(Effect):
64
65    suffix = '-suffix'
66
67    def __call__(self, state):
68        state[TESTKEY] = state[TESTKEY] + self.suffix
69
70class PrependEffect(Effect):
71
72    prefix = 'prefix-'
73
74    def __call__(self, state):
75        state[TESTKEY] = self.prefix + state[TESTKEY]
76
77class ArithmeticEffect(Effect):
78
79    default = '0'
80
81    def __init__(self, by=1):
82        self.by = by
83
84    def __call__(self, state):
85        if TESTKEY in state:
86            state[TESTKEY] = str(max(0, int(state[TESTKEY]) + self.by))
87        else:
88            state[TESTKEY] = self.default
89
90######################################################################
91# Actions
92######################################################################
93
94class Set(Action):
95
96    effect = StoreEffect()
97    postconditions = [ExistsCondition()]
98
99class Add(Action):
100
101    preconditions = [DoesNotExistCondition()]
102    effect = StoreEffect()
103    postconditions = [ExistsCondition()]
104
105class Delete(Action):
106
107    preconditions = [ExistsCondition()]
108    effect = DeleteEffect()
109    postconditions = [DoesNotExistCondition()]
110
111class Flush(Action):
112
113    effect = FlushEffect()
114    postconditions = [NothingExistsCondition()]
115
116class Delay(Flush):
117    pass
118
119class Append(Action):
120
121    preconditions = [ExistsCondition()]
122    effect = AppendEffect()
123    postconditions = [ExistsCondition()]
124
125class Prepend(Action):
126
127    preconditions = [ExistsCondition()]
128    effect = PrependEffect()
129    postconditions = [ExistsCondition()]
130
131class Incr(Action):
132
133    preconditions = [ExistsAsNumber()]
134    effect = ArithmeticEffect(1)
135    postconditions = [ExistsAsNumber()]
136
137class Decr(Action):
138
139    preconditions = [ExistsAsNumber()]
140    effect = ArithmeticEffect(-1)
141    postconditions = [ExistsAsNumber()]
142
143class IncrWithDefault(Action):
144
145    preconditions = [MaybeExistsAsNumber()]
146    effect = ArithmeticEffect(1)
147    postconditions = [ExistsAsNumber()]
148
149class DecrWithDefault(Action):
150
151    preconditions = [MaybeExistsAsNumber()]
152    effect = ArithmeticEffect(-1)
153    postconditions = [ExistsAsNumber()]
154
155######################################################################
156# Driver
157######################################################################
158
159class EngineTestAppDriver(Driver):
160
161    def preSuite(self, seq):
162        print '/* DO NOT EDIT.. GENERATED SOURCE */'
163        print ""
164        print '#include "testsuite/breakdancer/disable_optimize.h"'
165        print '#include "testsuite/breakdancer/suite_stubs.h"'
166
167    def testName(self, seq):
168        return 'test_' + '_'.join(a.name for a in seq)
169
170    def startSequence(self, seq):
171        f = "static enum test_result %s" % self.testName(seq)
172        print ("%s(ENGINE_HANDLE *h,\n%sENGINE_HANDLE_V1 *h1) {"
173               % (f, " " * (len(f) + 1)))
174
175    def startAction(self, action):
176        if isinstance(action, Delay):
177            s = "    delay(expiry+1);"
178        elif isinstance(action, Flush):
179            s = "    flush(h, h1);"
180        elif isinstance(action, Delete):
181            s = '    del(h, h1);'
182        else:
183            s = '    %s(h, h1);' % (action.name)
184        print s
185
186    def postSuite(self, seq):
187        print """MEMCACHED_PUBLIC_API
188engine_test_t* get_tests(void) {
189
190    static engine_test_t tests[]  = {
191"""
192        for seq in sorted(seq):
193            print '        {"%s",\n         %s,\n         test_setup, teardown, NULL},' % (
194                ', '.join(a.name for a in seq),
195                self.testName(seq))
196
197        print """        {NULL, NULL, NULL, NULL, NULL}
198    };
199    return tests;
200}"""
201
202    def endSequence(self, seq, state):
203        val = state.get(TESTKEY)
204        if val:
205            print '    checkValue(h, h1, "%s");' % val
206        else:
207            print '    assertNotExists(h, h1);'
208        print "    return SUCCESS;"
209        print "}"
210        print ""
211
212    def endAction(self, action, state, errored):
213        value = state.get(TESTKEY)
214        if value:
215            vs = ' /* value is "%s"' % value + " */"
216        else:
217            vs = ' /* value is not defined */'
218
219        if errored:
220            print "    assertHasError();" + vs
221        else:
222            print "    assertHasNoError();" + vs
223
224if __name__ == '__main__':
225    breakdancer.runTest(breakdancer.findActions(globals().values()),
226                        EngineTestAppDriver())
227