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 '#include "suite_stubs.h"' 163 print "" 164 165 def testName(self, seq): 166 return 'test_' + '_'.join(a.name for a in seq) 167 168 def startSequence(self, seq): 169 f = "static enum test_result %s" % self.testName(seq) 170 print ("%s(ENGINE_HANDLE *h,\n%sENGINE_HANDLE_V1 *h1) {" 171 % (f, " " * (len(f) + 1))) 172 173 def startAction(self, action): 174 if isinstance(action, Delay): 175 s = " delay(expiry+1);" 176 elif isinstance(action, Flush): 177 s = " flush(h, h1);" 178 elif isinstance(action, Delete): 179 s = ' del(h, h1);' 180 else: 181 s = ' %s(h, h1);' % (action.name) 182 print s 183 184 def postSuite(self, seq): 185 print """MEMCACHED_PUBLIC_API 186engine_test_t* get_tests(void) { 187 188 static engine_test_t tests[] = { 189""" 190 for seq in sorted(seq): 191 print ' {"%s",\n %s,\n test_setup, teardown, NULL},' % ( 192 ', '.join(a.name for a in seq), 193 self.testName(seq)) 194 195 print """ {NULL, NULL, NULL, NULL, NULL} 196 }; 197 return tests; 198}""" 199 200 def endSequence(self, seq, state): 201 val = state.get(TESTKEY) 202 if val: 203 print ' checkValue(h, h1, "%s");' % val 204 else: 205 print ' assertNotExists(h, h1);' 206 print " return SUCCESS;" 207 print "}" 208 print "" 209 210 def endAction(self, action, state, errored): 211 value = state.get(TESTKEY) 212 if value: 213 vs = ' // value is "%s"' % value 214 else: 215 vs = ' // value is not defined' 216 217 if errored: 218 print " assertHasError();" + vs 219 else: 220 print " assertHasNoError();" + vs 221 222if __name__ == '__main__': 223 breakdancer.runTest(breakdancer.findActions(globals().values()), 224 EngineTestAppDriver()) 225