Completed
Pull Request — master (#694)
by Eric
02:14
created

AbstractAcceptanceTest.teardown_method()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""
5
support
6
-------
7
8
Add test support utilities which are globally available throughout the suite.
9
"""
10
import os
11
import tempfile
12
13
from abc import ABCMeta, abstractmethod
14
from cookiecutter.main import cookiecutter
15
from cookiecutter.utils import rmtree
16
17
18
def read_file(file):
19
    """
20
    helper method to get the content of a given file
21
    :param file: the file to read
22
    """
23
    fd = open(file, 'r')
24
    content = fd.read()
25
    fd.close()
26
27
    return content
28
29
30
class Runner(object):
31
    """
32
    helper class to run cookierunner main command
33
    """
34
35
    def __init__(self, settings):
36
        """
37
        :param settings: SettingObject instance
38
        """
39
        self.settings = settings
40
41
    def run(self):
42
        """
43
        run cookiecutter
44
        """
45
        cookiecutter(
46
            self.settings.template,
47
            self.settings.checkout,
48
            self.settings.no_input,
49
            self.settings.extra_context,
50
            self.settings.replay,
51
            self.settings.overwrite_if_exists,
52
            self.settings.output_dir,
53
            self.settings.config_file
54
        )
55
56
57
class SettingObject(object):
58
    """
59
    config object to pass to the runner instance to configure the cookiecutter
60
    runtime environment
61
    """
62
63
    def __init__(
64
        self, template_dir, config_file,
65
        extra_context=None, output_dir=u'.'
66
    ):
67
        """
68
        :param template_dir: the template directory
69
        :param config_file: path to the user cookiecutterrc file
70
        :param extra_context: extra context dictionary
71
        :output_dir: output directory
72
        """
73
        self.template = template_dir
74
        self.checkout = None
75
        self.no_input = True
76
        self.extra_context = extra_context
77
        self.replay = False
78
        self.overwrite_if_exists = False
79
        self.output_dir = output_dir
80
        self.config_file = config_file
81
82
83
class AbstractAcceptanceTest(object):
84
    """
85
    abstract base class to create acceptance tests
86
87
    to use this class, after having imported it :
88
    1. create a test class that inherits from this abstract class
89
    2. implement the abstract method _repo_id
90
    3. write your test methods as usual and consume the public api of
91
    this class
92
93
    eq.
94
    from .support import AbstractAcceptanceTest
95
96
    class TestFeatureAcceptance(AbstractAcceptanceTest):
97
        def _repo_id(self):
98
            # this means fixtures are set under ./tests/test-feature-acceptance
99
            return 'test-feature-acceptance'
100
101
        def test_some_behaviour(self):
102
            self.run('template')
103
            assert my_expected_behaviour_of_my_template
104
    """
105
    __metaclass__ = ABCMeta
106
107
    @abstractmethod
108
    def _repo_id(self):
109
        """
110
        abstract property to be implemented by acceptance tests
111
        it should return the directory basename in which are set the templates
112
        used to build the tests
113
        ie. return 'test-feature-acceptance'
114
        """
115
116
    def setup_method(self, method):
117
        """
118
        setup method
119
        take care of calling the super().setup_method() in case of overwriting
120
        """
121
        self.__setup()
122
        self.__reset_runtime()
123
        self.__prepare_test_environment()
124
125
    def teardown_method(self, method):
126
        """
127
        teardown method
128
        take care of calling the super().teardown_method() in case of
129
        overwriting
130
        """
131
        self.__reset_runtime()
132
        self.__cleanup_test_environment()
133
134
    @property
135
    def repo_path(self):
136
        """
137
        path of the test repository in which are set test fixtures
138
        read-only
139
        """
140
        return os.path.join(self.__tests_dir, self._repo_id())
141
142
    @property
143
    def user_config(self):
144
        """
145
        user configuration file
146
        taken from repo_path if it exists or from the main fixtures directory
147
        by default
148
        it has to be named: cookiecutterrc
149
        read-only
150
        """
151
        name = 'cookiecutterrc'
152
        rcfile = os.path.join(self.repo_path, name)
153
154
        return rcfile if os.path.exists(rcfile) else os.path.join(
155
            self.__tests_dir, 'fixtures', name)
156
157
    @property
158
    def templates_path(self):
159
        """
160
        path of the directory where lie templates fixtures
161
        read-only
162
        """
163
        return os.path.join(self.repo_path, 'templates')
164
165
    @property
166
    def project(self):
167
        """
168
        project name
169
        """
170
        return self.__project
171
172
    @project.setter
173
    def project(self, name):
174
        """
175
        project name setter
176
        :param name: name of the project
177
        """
178
        self.__project = name
179
180
    @property
181
    def project_dir(self):
182
        """
183
        output directory of the current project
184
        its name is automatically generated from the project name and is
185
        created under the output_dir
186
        read-only
187
        """
188
        return os.path.join(
189
            self.output_dir,
190
            self.project.strip().lower().replace(' ', '-')
191
        )
192
193
    @property
194
    def output_dir(self):
195
        """
196
        output diretory where the project under test is created
197
        read-only
198
        """
199
        if not self.__output_dir:
200
            self.__output_dir = tempfile.mkdtemp()
201
202
        return self.__output_dir
203
204
    @property
205
    def runner(self):
206
        """
207
        current cookiecutter runner instance
208
        read-only
209
        """
210
        return self.__runner
211
212
    @property
213
    def settings(self):
214
        """
215
        current cookiecutter runner configuration object
216
        read-only
217
        """
218
        return self.__settings
219
220
    def configure(self, template, extra_context=None, config_file=None):
221
        """
222
        configure the runtime environment
223
        :param template: name of the template
224
        :param extra_context: extra context dictionary
225
        :param config_file: path to the user cookiecutterrc file
226
        """
227
        template_dir = os.path.join(self.templates_path, template)
228
        assert os.path.exists(template_dir)
229
        context = {"project_name": self.project} if not extra_context \
230
            else extra_context
231
        config = self.user_config if not config_file else config_file
232
233
        self.__settings = SettingObject(
234
            template_dir, config, context, self.output_dir)
235
        self.__runner = Runner(self.settings)
236
237
    def run(self, template=None):
238
        """
239
        run cookiecutter
240
        :param template: template name if a default configuration is sufficient
241
        """
242
        if template:
243
            self.configure(template)
244
        assert self.runner
245
        self.runner.run()
246
247
    ###############
248
    # PRIVATE API #
249
    ###############
250
251
    def __setup(self):
252
        """
253
        helper method to initialize the test class internals
254
        """
255
        self.__tests_dir = os.path.abspath('tests')
256
        self.__output_dir = None
257
        self.project = 'Dummy Project'
258
259
    def __reset_runtime(self):
260
        """
261
        helper method to reset the runtime environment
262
        """
263
        self.__runner = None
264
        self.__settings = None
265
266
    def __prepare_test_environment(self):
267
        """
268
        helper method to prepare the test environment
269
        """
270
        assert os.path.exists(self.output_dir)
271
272
    def __cleanup_test_environment(self):
273
        """
274
        helper method to clean up the test environment
275
        """
276
        rmtree(self.output_dir)
277
        assert not os.path.exists(self.output_dir)
278
        self.__output_dir = None
279