225 lines
5.4 KiB
Python
225 lines
5.4 KiB
Python
#!/usr/bin/env python
|
|
|
|
import breakdancer
|
|
from breakdancer import Condition, Effect, Action, Driver
|
|
|
|
TESTKEY = 'testkey'
|
|
|
|
######################################################################
|
|
# Conditions
|
|
######################################################################
|
|
|
|
class ExistsCondition(Condition):
|
|
|
|
def __call__(self, state):
|
|
return TESTKEY in state
|
|
|
|
class ExistsAsNumber(Condition):
|
|
|
|
def __call__(self, state):
|
|
try:
|
|
int(state[TESTKEY])
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
class MaybeExistsAsNumber(ExistsAsNumber):
|
|
|
|
def __call__(self, state):
|
|
return TESTKEY not in state or ExistsAsNumber.__call__(self, state)
|
|
|
|
class DoesNotExistCondition(Condition):
|
|
|
|
def __call__(self, state):
|
|
return TESTKEY not in state
|
|
|
|
class NothingExistsCondition(Condition):
|
|
|
|
def __call__(self, state):
|
|
return not bool(state)
|
|
|
|
######################################################################
|
|
# Effects
|
|
######################################################################
|
|
|
|
class StoreEffect(Effect):
|
|
|
|
def __init__(self, v='0'):
|
|
self.v = v
|
|
|
|
def __call__(self, state):
|
|
state[TESTKEY] = self.v
|
|
|
|
class DeleteEffect(Effect):
|
|
|
|
def __call__(self, state):
|
|
del state[TESTKEY]
|
|
|
|
class FlushEffect(Effect):
|
|
|
|
def __call__(self, state):
|
|
state.clear()
|
|
|
|
class AppendEffect(Effect):
|
|
|
|
suffix = '-suffix'
|
|
|
|
def __call__(self, state):
|
|
state[TESTKEY] = state[TESTKEY] + self.suffix
|
|
|
|
class PrependEffect(Effect):
|
|
|
|
prefix = 'prefix-'
|
|
|
|
def __call__(self, state):
|
|
state[TESTKEY] = self.prefix + state[TESTKEY]
|
|
|
|
class ArithmeticEffect(Effect):
|
|
|
|
default = '0'
|
|
|
|
def __init__(self, by=1):
|
|
self.by = by
|
|
|
|
def __call__(self, state):
|
|
if TESTKEY in state:
|
|
state[TESTKEY] = str(max(0, int(state[TESTKEY]) + self.by))
|
|
else:
|
|
state[TESTKEY] = self.default
|
|
|
|
######################################################################
|
|
# Actions
|
|
######################################################################
|
|
|
|
class Set(Action):
|
|
|
|
effect = StoreEffect()
|
|
postconditions = [ExistsCondition()]
|
|
|
|
class Add(Action):
|
|
|
|
preconditions = [DoesNotExistCondition()]
|
|
effect = StoreEffect()
|
|
postconditions = [ExistsCondition()]
|
|
|
|
class Delete(Action):
|
|
|
|
preconditions = [ExistsCondition()]
|
|
effect = DeleteEffect()
|
|
postconditions = [DoesNotExistCondition()]
|
|
|
|
class Flush(Action):
|
|
|
|
effect = FlushEffect()
|
|
postconditions = [NothingExistsCondition()]
|
|
|
|
class Delay(Flush):
|
|
pass
|
|
|
|
class Append(Action):
|
|
|
|
preconditions = [ExistsCondition()]
|
|
effect = AppendEffect()
|
|
postconditions = [ExistsCondition()]
|
|
|
|
class Prepend(Action):
|
|
|
|
preconditions = [ExistsCondition()]
|
|
effect = PrependEffect()
|
|
postconditions = [ExistsCondition()]
|
|
|
|
class Incr(Action):
|
|
|
|
preconditions = [ExistsAsNumber()]
|
|
effect = ArithmeticEffect(1)
|
|
postconditions = [ExistsAsNumber()]
|
|
|
|
class Decr(Action):
|
|
|
|
preconditions = [ExistsAsNumber()]
|
|
effect = ArithmeticEffect(-1)
|
|
postconditions = [ExistsAsNumber()]
|
|
|
|
class IncrWithDefault(Action):
|
|
|
|
preconditions = [MaybeExistsAsNumber()]
|
|
effect = ArithmeticEffect(1)
|
|
postconditions = [ExistsAsNumber()]
|
|
|
|
class DecrWithDefault(Action):
|
|
|
|
preconditions = [MaybeExistsAsNumber()]
|
|
effect = ArithmeticEffect(-1)
|
|
postconditions = [ExistsAsNumber()]
|
|
|
|
######################################################################
|
|
# Driver
|
|
######################################################################
|
|
|
|
class EngineTestAppDriver(Driver):
|
|
|
|
def preSuite(self, seq):
|
|
print '#include "suite_stubs.h"'
|
|
print ""
|
|
|
|
def testName(self, seq):
|
|
return 'test_' + '_'.join(a.name for a in seq)
|
|
|
|
def startSequence(self, seq):
|
|
f = "static enum test_result %s" % self.testName(seq)
|
|
print ("%s(ENGINE_HANDLE *h,\n%sENGINE_HANDLE_V1 *h1) {"
|
|
% (f, " " * (len(f) + 1)))
|
|
|
|
def startAction(self, action):
|
|
if isinstance(action, Delay):
|
|
s = " delay(expiry+1);"
|
|
elif isinstance(action, Flush):
|
|
s = " flush(h, h1);"
|
|
elif isinstance(action, Delete):
|
|
s = ' del(h, h1);'
|
|
else:
|
|
s = ' %s(h, h1);' % (action.name)
|
|
print s
|
|
|
|
def postSuite(self, seq):
|
|
print """MEMCACHED_PUBLIC_API
|
|
engine_test_t* get_tests(void) {
|
|
|
|
static engine_test_t tests[] = {
|
|
"""
|
|
for seq in sorted(seq):
|
|
print ' {"%s",\n %s,\n test_setup, teardown, NULL},' % (
|
|
', '.join(a.name for a in seq),
|
|
self.testName(seq))
|
|
|
|
print """ {NULL, NULL, NULL, NULL, NULL}
|
|
};
|
|
return tests;
|
|
}"""
|
|
|
|
def endSequence(self, seq, state):
|
|
val = state.get(TESTKEY)
|
|
if val:
|
|
print ' checkValue(h, h1, "%s");' % val
|
|
else:
|
|
print ' assertNotExists(h, h1);'
|
|
print " return SUCCESS;"
|
|
print "}"
|
|
print ""
|
|
|
|
def endAction(self, action, state, errored):
|
|
value = state.get(TESTKEY)
|
|
if value:
|
|
vs = ' // value is "%s"' % value
|
|
else:
|
|
vs = ' // value is not defined'
|
|
|
|
if errored:
|
|
print " assertHasError();" + vs
|
|
else:
|
|
print " assertHasNoError();" + vs
|
|
|
|
if __name__ == '__main__':
|
|
breakdancer.runTest(breakdancer.findActions(globals().values()),
|
|
EngineTestAppDriver())
|