1"""
2figleafsections plugin for nose.
3
4Automatically records coverage info for Python tests and connects it with
5with test was being run at the time.  Can be used to produce a "barcode"
6of code execution.
7"""
8
9DEFAULT_COVERAGE_FILE='.figleaf_sections'
10import pkg_resources
11
12try:
13    pkg_resources.require('figleaf>=0.6.1')
14    import figleaf
15except (ImportError, pkg_resources.DistributionNotFound):
16    figleaf = None
17
18import sys
19err = sys.stderr
20
21import nose.case
22from nose.plugins.base import Plugin
23
24import logging
25import os
26
27log = logging.getLogger(__name__)
28
29def calc_testname(test):
30    """
31    Build a reasonably human-readable testname from each test.
32    """
33    name = str(test)
34    if ' ' in name:
35        name = name.split(' ')[1]
36
37    return name
38
39class FigleafSections(Plugin):
40    def __init__(self):
41        Plugin.__init__(self)
42        self.testname = None
43
44    def add_options(self, parser, env=os.environ):
45        env_opt = 'NOSE_WITH_%s' % self.name.upper()
46        env_opt.replace('-', '_')
47        parser.add_option("--with-%s" % self.name,
48                          action="store_true",
49                          dest=self.enableOpt,
50                          default=env.get(env_opt),
51                          help="Enable plugin %s: %s [%s]" %
52                          (self.__class__.__name__, self.help(), env_opt))
53
54        parser.add_option("--figleaf-file",
55                          action="store",
56                          dest="figleaf_file",
57                          default=None,
58                          help="Store figleaf section coverage in this file")
59
60    def configure(self, options, config):
61        """
62        Configure: enable plugin?  And if so, where should the coverage
63        info be placed?
64        """
65        self.conf = config
66
67        # enable?
68        if hasattr(options, self.enableOpt):
69            self.enabled = getattr(options, self.enableOpt)
70
71        ### save coverage file name, if given.
72        if options.figleaf_file:
73            self.figleaf_file = options.figleaf_file
74        else:
75            self.figleaf_file = DEFAULT_COVERAGE_FILE
76
77        if self.enabled and figleaf is None:
78                raise Exception("You must install figleaf 0.6.1 before you can use the figleafsections plugin! See http://darcs.idyll.org/~t/projects/figleaf/doc/")
79
80    def begin(self):
81        """
82        Initialize: start recording coverage info.
83        """
84        figleaf.start()
85
86    def finalize(self, result):
87        """
88        Finalize: stop recording coverage info, save & exit.
89        """
90        figleaf.stop()
91
92        fp = open(self.figleaf_file, 'w')
93        figleaf.dump_pickled_coverage(fp)
94        fp.close()
95
96    def startTest(self, test):
97        """
98        Run at the beginning of each test, before per-test fixtures.
99
100        One weakness is that this is only run for specific kinds of
101        nose testcases.
102        """
103        if isinstance(test, nose.case.FunctionTestCase) or \
104           isinstance(test, nose.case.MethodTestCase):
105            self.testname = calc_testname(test)
106            assert self.testname
107
108            figleaf.start_section(self.testname)
109
110    def stopTest(self, test):
111        """
112        Run at the end of each test, after per-test fixtures.
113        """
114        if self.testname:
115            figleaf.stop_section()
116            self.testname = None
117
118