Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for unit test discovery #37

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions cricket/unittest/discoverer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,37 +25,41 @@ def consume(iterable):
except:
yield item

class PyTestDiscoverer:

def __init__(self):
class PyTestDiscoverer:

def __init__(self, start='.', pattern='test*.py', top=None):
self.start = start
self.pattern = pattern
self.top = top
self.collected_tests = []


def __str__(self):
'''
Builds the dotted namespace expected by cricket
'''

resultstr = '\n'.join(self.collected_tests)

return resultstr.strip()


def collect_tests(self):
'''
Collect a list of potentially runnable tests
'''

loader = unittest.TestLoader()
suite = loader.discover('.')
suite = loader.discover(self.start, self.pattern, self.top)
flatresults = list(consume(suite))
named = [r.id() for r in flatresults]
self.collected_tests = named


if __name__ == '__main__':
from argparse import ArgumentParser
from cricket.unittest.options import add_arguments

parser = ArgumentParser()
add_arguments(parser)
options = parser.parse_args()

PTD = PyTestDiscoverer()
PTD = PyTestDiscoverer(options.start, options.pattern, options.top)
PTD.collect_tests()
print str(PTD)
print str(PTD)
62 changes: 36 additions & 26 deletions cricket/unittest/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Its main API is the command line, but it's just as sensible to
call into it. See __main__ for usage
'''

import argparse
import unittest

Expand All @@ -24,16 +25,25 @@ class PyTestExecutor(object):
initiated by the top-level Executor class
'''

def __init__(self):

# Allows the executor to run a specified list of tests
self.specified_list = None
def __init__(self, start='.', pattern='test*.py', top=None, test_names=None):
self.start = start
self.pattern = pattern
self.top = top
self.test_names = test_names

def run_only(self, specified_list):
self.specified_list = specified_list
def load_suite(self):
# Use explicit test_names if provided, other wise fall back to
# discovery equivalent to the discoverer.
loader = unittest.TestLoader()
if self.test_names:
suite = unittest.TestSuite()
for name in self.test_names:
suite.addTest(loader.loadTestsFromName(name))
else:
suite = loader.discover(self.start, self.pattern, self.top)
return suite

def stream_suite(self, suite):

pipes.PipedTestRunner().run(suite)

def stream_results(self):
Expand All @@ -42,16 +52,8 @@ def stream_results(self):
2.) Otherwise fetch specific tests
3.) Execute-and-stream
'''

loader = unittest.TestLoader()

if not self.specified_list:
suite = loader.discover('.')
self.stream_suite(suite)
else:
for module in self.specified_list:
suite = loader.loadTestsFromName(module)
self.stream_suite(suite)
suite = self.load_suite()
self.stream_suite(suite)


class PyTestCoverageExecutor(PyTestExecutor):
Expand All @@ -70,18 +72,26 @@ def stream_suite(self, suite):
parser = argparse.ArgumentParser()

parser.add_argument("--coverage", help="Generate coverage data for the test run", action="store_true")
parser.add_argument(
'labels', nargs=argparse.REMAINDER,
help='Test labels to run.'
)
parser.add_argument('-t', '--test-names', action='store_true',
help='Interpret arguments as test names.')
parser.add_argument('labels', nargs=argparse.REMAINDER,
help='Test labels to run.')

options = parser.parse_args()
options.start = '.'
options.pattern = 'test*.py'
options.top = None

if options.coverage:
PTE = PyTestCoverageExecutor()
cls = PyTestCoverageExecutor
else:
cls = PyTestExecutor

if options.test_names:
executor = cls(test_names=options.labels)
else:
PTE = PyTestExecutor()
for name, value in zip(('start', 'pattern', 'top'), options.labels):
setattr(options, name, value)
executor = cls(options.start, options.pattern, options.top)

if options.labels:
PTE.run_only(options.labels)
PTE.stream_results()
executor.stream_results()
23 changes: 21 additions & 2 deletions cricket/unittest/model.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
import sys

from cricket.model import Project
import cricket.unittest.options


class UnittestProject(Project):

def __init__(self, options=None):
self.start = getattr(options, 'start', '.')
self.pattern = getattr(options, 'pattern', 'test*py')
self.top = getattr(options, 'top', None)
super(UnittestProject, self).__init__()

@classmethod
def add_arguments(cls, parser):
cricket.unittest.options.add_arguments(parser)

def discover_commandline(self):
"Command line: Discover all available tests in a project."
return [sys.executable, '-m', 'cricket.unittest.discoverer']
args = [sys.executable, '-m', 'cricket.unittest.discoverer',
self.start, self.pattern]
if self.top is not None:
args.append(self.top)
return args

def execute_commandline(self, labels):
"Return the command line to execute the specified test labels"
args = [sys.executable, '-m', 'cricket.unittest.executor']
if self.coverage:
args.append('--coverage')
return args + labels
if labels:
args.extend(['-t'] + labels)
else:
args.extend([self.start, self.pattern])
if self.top is not None:
args.append(self.top)
return args
15 changes: 15 additions & 0 deletions cricket/unittest/options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
argparse options common to discoverer, executor and model.
"""


def add_arguments(parser):
"""
Add unittest discovery settings to the argument parser.
"""
parser.add_argument('start', nargs='?', default='.',
help="Directory to start discovery ('.' default)")
parser.add_argument('pattern', nargs='?', default='test*.py',
help="Pattern to match tests ('test*.py' default)")
parser.add_argument('top', nargs='?', default=None,
help='Top level directory of project (defaults to start directory)')
12 changes: 12 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ In a unittest project, install cricket, and then run it::
This will pop up a GUI window. Hit "Run all", and watch your test suite
execute.

``cricket-unittest`` also accepts the 'discover' arguments for ``unittest``::

usage: cricket-unittest [-h] [--version] [start] [pattern] [top]

(See also ``cricket-unittest -h``.)

For example this will find tests inside packages. For example, if you have
``simplejson`` installed, you can run the tests present in
``simplejson.tests``::

$ cricket-unittest simplejson

Problems under Ubuntu
~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion tests/test_unit_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_labels(self):
'''

labels = ['tests.test_unit_integration.TestCollection']
cmdline = ['python', '-m', 'cricket.unittest.executor'] + labels
cmdline = ['python', '-m', 'cricket.unittest.executor', '-t'] + labels

runner = subprocess.Popen(
cmdline,
Expand Down