summaryrefslogtreecommitdiff
path: root/pystache/tests/main.py
blob: 184122d0ba92893e0a3b498feba9999bdf41ebf7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# coding: utf-8

"""
Exposes a main() function that runs all tests in the project.

This module is for our test console script.

"""

import os
import sys
import unittest
from unittest import TestCase, TestProgram

import pystache
from pystache.tests.common import PACKAGE_DIR, PROJECT_DIR, SPEC_TEST_DIR, UNITTEST_FILE_PREFIX
from pystache.tests.common import get_module_names
from pystache.tests.doctesting import get_doctests
from pystache.tests.spectesting import get_spec_tests


# If this command option is present, then the spec test and doctest directories
# will be inserted if not provided.
FROM_SOURCE_OPTION = "--from-source"


def make_extra_tests(text_doctest_dir, spec_test_dir):
    tests = []

    if text_doctest_dir is not None:
        doctest_suites = get_doctests(text_doctest_dir)
        tests.extend(doctest_suites)

    if spec_test_dir is not None:
        spec_testcases = get_spec_tests(spec_test_dir)
        tests.extend(spec_testcases)

    return unittest.TestSuite(tests)


def make_test_program_class(extra_tests):
    """
    Return a subclass of unittest.TestProgram.

    """
    # The function unittest.main() is an alias for unittest.TestProgram's
    # constructor.  TestProgram's constructor does the following:
    #
    # 1. calls self.parseArgs(argv),
    # 2. which in turn calls self.createTests().
    # 3. then the constructor calls self.runTests().
    #
    # The createTests() method sets the self.test attribute by calling one
    # of self.testLoader's "loadTests" methods.  Each loadTest method returns
    # a unittest.TestSuite instance.  Thus, self.test is set to a TestSuite
    # instance prior to calling runTests().
    class PystacheTestProgram(TestProgram):

        """
        Instantiating an instance of this class runs all tests.

        """

        def createTests(self):
            """
            Load tests and set self.test to a unittest.TestSuite instance

            Compare--

              http://docs.python.org/library/unittest.html#unittest.TestSuite

            """
            super(PystacheTestProgram, self).createTests()
            self.test.addTests(extra_tests)

    return PystacheTestProgram


# Do not include "test" in this function's name to avoid it getting
# picked up by nosetests.
def main(sys_argv):
    """
    Run all tests in the project.

    Arguments:

      sys_argv: a reference to sys.argv.

    """
    should_source_exist = False
    spec_test_dir = None
    project_dir = None

    if len(sys_argv) > 1 and sys_argv[1] == FROM_SOURCE_OPTION:
        should_source_exist = True
        sys_argv.pop(1)

    # TODO: use logging module
    print "pystache: running tests: expecting source: %s" % should_source_exist

    try:
        # TODO: use optparse command options instead.
        spec_test_dir = sys_argv[1]
        sys_argv.pop(1)
    except IndexError:
        if should_source_exist:
            if not os.path.exists(SPEC_TEST_DIR):
                # Then the user is probably using a downloaded sdist rather
                # than a repository clone (since the sdist does not include
                # the spec test directory).
                print("pystache: skipping spec tests: spec test directory "
                      "not found")
            else:
                spec_test_dir = SPEC_TEST_DIR

    try:
        # TODO: use optparse command options instead.
        project_dir = sys_argv[1]
        sys_argv.pop(1)
    except IndexError:
        if should_source_exist:
            project_dir = PROJECT_DIR

    if len(sys_argv) <= 1 or sys_argv[-1].startswith("-"):
        # Then no explicit module or test names were provided, so
        # auto-detect all unit tests.
        module_names = _discover_test_modules(PACKAGE_DIR)
        sys_argv.extend(module_names)
        if project_dir is not None:
            # Add the current module for unit tests contained here.
            sys_argv.append(__name__)

    SetupTests.project_dir = project_dir

    extra_tests = make_extra_tests(project_dir, spec_test_dir)
    test_program_class = make_test_program_class(extra_tests)

    # We pass None for the module because we do not want the unittest
    # module to resolve module names relative to a given module.
    # (This would require importing all of the unittest modules from
    # this module.)  See the loadTestsFromName() method of the
    # unittest.TestLoader class for more details on this parameter.
    test_program_class(argv=sys_argv, module=None)
    # No need to return since unitttest.main() exits.


def _discover_test_modules(package_dir):
    """
    Discover and return a sorted list of the names of unit-test modules.

    """
    def is_unittest_module(path):
        file_name = os.path.basename(path)
        return file_name.startswith(UNITTEST_FILE_PREFIX)

    names = get_module_names(package_dir=package_dir, should_include=is_unittest_module)

    # This is a sanity check to ensure that the unit-test discovery
    # methods are working.
    if len(names) < 1:
        raise Exception("No unit-test modules found--\n  in %s" % package_dir)

    return names


class SetupTests(TestCase):

    """Tests about setup.py."""

    project_dir = None

    def test_version(self):
        """
        Test that setup.py's version matches the package's version.

        """
        original_path = list(sys.path)

        sys.path.insert(0, self.project_dir)

        try:
            from setup import VERSION
            self.assertEqual(VERSION, pystache.__version__)
        finally:
            sys.path = original_path