Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add testing with breakdancer
  • Loading branch information
trondn committed Nov 11, 2010
1 parent bf89b92 commit 9a038bc
Show file tree
Hide file tree
Showing 7 changed files with 588 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -6,12 +6,14 @@
*.gcov
*.l[ao]
*.tcov
*.pyc
*~
.deps
/.libs/
/INSTALL
/aclocal.m4
/autom4te.cache
/breakdancer_testsuite.c
/compile
/config.guess
/config.h
Expand Down
27 changes: 25 additions & 2 deletions Makefile.am
Expand Up @@ -168,6 +168,24 @@ basic_engine_testsuite_la_DEPENDENCIES= libmcd_util.la
basic_engine_testsuite_la_LIBADD= libmcd_util.la $(LIBM)
basic_engine_testsuite_la_LDFLAGS= -avoid-version -shared -module -no-undefined

breakdancer_testsuite_la_CPPFLAGS = $(CPPFLAGS) -I$(top_srcdir)/breakdancer
breakdancer_testsuite_la_SOURCES= breakdancer_testsuite.c \
breakdancer/suite_stubs.c \
breakdancer/suite_stubs.h
breakdancer_testsuite_la_LDFLAGS= -avoid-version -shared -module -no-undefined -rpath /nowhere

breakdancer_testsuite.c: breakdancer/breakdancer.py breakdancer/engine_test.py
${top_srcdir}/breakdancer/engine_test.py > breakdancer_testsuite.c || ( rm breakdancer_testsuite.c && /bin/false)

ENGINE_TESTS=test_engine

if ENABLE_BREAKDANCE
noinst_LTLIBRARIES += breakdancer_testsuite.la
BUILT_SOURCES += breakdancer_testsuite.c
CLEANFILES += breakdancer_testsuite.c
ENGINE_TESTS += breakdancer_engine
endif

memcached_dtrace.h: memcached_dtrace.d
${DTRACE} -h -s $(top_srcdir)/memcached_dtrace.d
sed -e 's,void \*,const void \*,g' memcached_dtrace.h | \
Expand All @@ -187,17 +205,22 @@ default_engine_dtrace.lo: $(default_engine_la_OBJECTS)
sed -e s,assoc,default_engine_dtrace,g assoc.lo > default_engine_dtrace.lo

DIST_DIRS = scripts
EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d m4/version.m4
EXTRA_DIST = doc scripts t memcached.spec memcached_dtrace.d m4/version.m4 \
breakdancer/engine_tests.py breakdancer/breakdancer.py

MOSTLYCLEANFILES = *.gcov *.gcno *.gcda *.tcov

TEST_TIMEOUT=30

breakdancer_engine: engine_testapp breakdancer_testsuite.la
./engine_testapp -E .libs/default_engine.so -t $(TEST_TIMEOUT) \
-T .libs/breakdancer_testsuite.so

test_engine: engine_testapp basic_engine_testsuite.la
./engine_testapp -E .libs/default_engine.so -t $(TEST_TIMEOUT) \
-T .libs/basic_engine_testsuite.so

test: test_engine memcached sizes testapp timedrun
test: $(ENGINE_TESTS) memcached sizes testapp timedrun
./sizes
./testapp
prove $(srcdir)/t
Expand Down
117 changes: 117 additions & 0 deletions breakdancer/breakdancer.py
@@ -0,0 +1,117 @@
#!/usr/bin/env python

import itertools

class Condition(object):
"""Something asserted to be true during the test.
A given condition may be used as a precondition or a
postcondition."""

def __call__(self, k, state):
"""Called with a key and a state. True if the condition is met."""
return True

class Effect(object):
"""The affect an action will perform."""

def __call__(self, k, state):
"""Called with a key and a state.
The effect modifies the state as appropriate."""

class Action(object):
"""Actions are the operations that will be permuted into test cases.
Each action has a collection of preconditions and postconditions
that will be evaluated for checking input and output state for the
action.
Action.preconditions is the collection of conditions that must all
be true upon input to the action. If any condition is not true,
the effect is not executed and the action state is considered
"errored."
Action.effect is the callable that is expected to alter the state
to satisfy the postconditions of the action.
Action.postconditions is the collection of conditions that must
all be true after the effect of the action completes.
"""

preconditions = []
effect = None
postconditions = []
enabled = True

@property
def name(self):
"""The name of this action (default derived from class name)"""
n = self.__class__.__name__
return n[0].lower() + n[1:]

class Driver(object):
"""The driver "performs" the test."""

def newState(self):
"""Initialize and return the state for a test."""
return {}

def preSuite(self, seq):
"""Invoked with the sequence of tests before any are run."""

def startSequence(self, seq):
"""Invoked with the sequence of actions in a single test
before it is performed."""

def startAction(self, action):
"""Invoked when before starting an action."""

def endAction(self, action, state, errored):
"""Invoked after the action is performed."""

def endSequence(self, seq, state):
"""Invoked at the end of a sequence of tests."""

def postSuite(self, seq):
"""Invoked with the sequence of tests after all of them are run."""

def runTest(actions, driver, duplicates=3, length=4):
"""Run a test with the given collection of actions and driver.
The optional argument `duplicates' specifies how many times a
given action may be duplicated in a sequence.
The optional argument `length` specifies how long each test
sequence is.
"""

instances = itertools.chain(*itertools.repeat([a() for a in actions],
duplicates))
tests = set(itertools.permutations(instances, length))
driver.preSuite(tests)
for seq in sorted(tests):
state = driver.newState()
driver.startSequence(seq)
for a in seq:
driver.startAction(a)
haserror = not all(p(state) for p in a.preconditions)
if not haserror:
try:
a.effect(state)
haserror = not all(p(state) for p in a.postconditions)
except:
haserror = True
driver.endAction(a, state, haserror)
driver.endSequence(seq, state)
driver.postSuite(tests)

def findActions(classes):
"""Helper function to extract action subclasses from a collection
of classes."""

actions = []
for __t in (t for t in classes if isinstance(type, type(t))):
if Action in __t.__mro__ and __t != Action and __t.enabled:
actions.append(__t)
return actions

0 comments on commit 9a038bc

Please sign in to comment.