comparison tests/test_pipelines_asset.py @ 974:72f17534d58e

tests: First pass on making unit tests work again. - Fix all imports - Add more helper functions to work with mock file-systems - Simplify some code by running chef directly on the mock FS - Fix a couple tests
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 17 Oct 2017 01:07:30 -0700
parents
children 45ad976712ec
comparison
equal deleted inserted replaced
973:8419daaa7a0e 974:72f17534d58e
1 import time
2 import os.path
3 import shutil
4 import inspect
5 import pytest
6 from piecrust.pipelines.asset import get_filtered_processors
7 from piecrust.pipelines.records import MultiRecord
8 from piecrust.processing.base import SimpleFileProcessor
9 from .mockutil import mock_fs, mock_fs_scope
10
11
12 class FooProcessor(SimpleFileProcessor):
13 def __init__(self, exts=None, open_func=None):
14 exts = exts or {'foo', 'foo'}
15 super(FooProcessor, self).__init__({exts[0]: exts[1]})
16 self.PROCESSOR_NAME = exts[0]
17 self.open_func = open_func or open
18
19 def _doProcess(self, in_path, out_path):
20 with self.open_func(in_path, 'r') as f:
21 text = f.read()
22 with self.open_func(out_path, 'w') as f:
23 f.write("%s: %s" % (self.PROCESSOR_NAME.upper(), text))
24 return True
25
26
27 class NoopProcessor(SimpleFileProcessor):
28 def __init__(self, exts):
29 super(NoopProcessor, self).__init__({exts[0]: exts[1]})
30 self.PROCESSOR_NAME = exts[0]
31 self.processed = []
32
33 def _doProcess(self, in_path, out_path):
34 self.processed.append(in_path)
35 shutil.copyfile(in_path, out_path)
36 return True
37
38
39 def _get_test_fs(processors=None):
40 if processors is None:
41 processors = 'copy'
42 return (mock_fs()
43 .withDir('counter')
44 .withConfig({
45 'pipelines': {
46 'asset': {
47 'processors': processors
48 }
49 }
50 }))
51
52
53 def _create_test_plugin(fs, *, foo_exts=None, noop_exts=None):
54 src = [
55 'from piecrust.plugins.base import PieCrustPlugin',
56 'from piecrust.processing.base import SimpleFileProcessor']
57
58 foo_lines = inspect.getsourcelines(FooProcessor)
59 src += ['']
60 src += map(lambda l: l.rstrip('\n'), foo_lines[0])
61
62 noop_lines = inspect.getsourcelines(NoopProcessor)
63 src += ['']
64 src += map(lambda l: l.rstrip('\n'), noop_lines[0])
65
66 src += [
67 '',
68 'class FooNoopPlugin(PieCrustPlugin):',
69 ' def getProcessors(self):',
70 ' yield FooProcessor(%s)' % repr(foo_exts),
71 ' yield NoopProcessor(%s)' % repr(noop_exts),
72 '',
73 '__piecrust_plugin__ = FooNoopPlugin']
74
75 fs.withFile('kitchen/plugins/foonoop.py', src)
76
77
78 def _bake_assets(fs):
79 fs.runChef('bake', '-p', 'asset')
80
81
82 def test_empty():
83 fs = _get_test_fs()
84 with mock_fs_scope(fs):
85 expected = {}
86 assert expected == fs.getStructure('counter')
87 _bake_assets(fs)
88 expected = {}
89 assert expected == fs.getStructure('counter')
90
91
92 def test_one_file():
93 fs = (_get_test_fs()
94 .withFile('kitchen/assets/something.html', 'A test file.'))
95 with mock_fs_scope(fs):
96 expected = {}
97 assert expected == fs.getStructure('counter')
98 _bake_assets(fs)
99 expected = {'something.html': 'A test file.'}
100 assert expected == fs.getStructure('counter')
101
102
103 def test_one_level_dirtyness():
104 fs = (_get_test_fs()
105 .withFile('kitchen/assets/blah.foo', 'A test file.'))
106 with mock_fs_scope(fs):
107 _bake_assets(fs)
108 expected = {'blah.foo': 'A test file.'}
109 assert expected == fs.getStructure('counter')
110 mtime = os.path.getmtime(fs.path('/counter/blah.foo'))
111 assert abs(time.time() - mtime) <= 2
112
113 time.sleep(1)
114 _bake_assets(fs)
115 assert expected == fs.getStructure('counter')
116 assert mtime == os.path.getmtime(fs.path('/counter/blah.foo'))
117
118 time.sleep(1)
119 fs.withFile('kitchen/assets/blah.foo', 'A new test file.')
120 _bake_assets(fs)
121 expected = {'blah.foo': 'A new test file.'}
122 assert expected == fs.getStructure('counter')
123 assert mtime < os.path.getmtime(fs.path('/counter/blah.foo'))
124
125
126 def test_two_levels_dirtyness():
127 fs = (_get_test_fs()
128 .withFile('kitchen/assets/blah.foo', 'A test file.'))
129 _create_test_plugin(fs, foo_exts=('foo', 'bar'))
130 with mock_fs_scope(fs):
131 _bake_assets(fs)
132 expected = {'blah.bar': 'FOO: A test file.'}
133 assert expected == fs.getStructure('counter')
134 mtime = os.path.getmtime(fs.path('/counter/blah.bar'))
135 assert abs(time.time() - mtime) <= 2
136
137 time.sleep(1)
138 _bake_assets(fs)
139 assert expected == fs.getStructure('counter')
140 assert mtime == os.path.getmtime(fs.path('/counter/blah.bar'))
141
142 time.sleep(1)
143 fs.withFile('kitchen/assets/blah.foo', 'A new test file.')
144 _bake_assets(fs)
145 expected = {'blah.bar': 'FOO: A new test file.'}
146 assert expected == fs.getStructure('counter')
147 assert mtime < os.path.getmtime(fs.path('/counter/blah.bar'))
148
149
150 def test_removed():
151 fs = (_get_test_fs()
152 .withFile('kitchen/assets/blah1.foo', 'A test file.')
153 .withFile('kitchen/assets/blah2.foo', 'Ooops'))
154 with mock_fs_scope(fs):
155 expected = {
156 'blah1.foo': 'A test file.',
157 'blah2.foo': 'Ooops'}
158 assert expected == fs.getStructure('kitchen/assets')
159 _bake_assets(fs)
160 assert expected == fs.getStructure('counter')
161
162 time.sleep(1)
163 os.remove(fs.path('/kitchen/assets/blah2.foo'))
164 expected = {
165 'blah1.foo': 'A test file.'}
166 assert expected == fs.getStructure('kitchen/assets')
167 _bake_assets(1)
168 assert expected == fs.getStructure('counter')
169
170
171 def test_record_version_change():
172 fs = (_get_test_fs()
173 .withFile('kitchen/assets/blah.foo', 'A test file.'))
174 _create_test_plugin(fs, foo_exts=('foo', 'foo'))
175 with mock_fs_scope(fs):
176 _bake_assets(fs)
177 assert os.path.exists(fs.path('/counter/blah.foo')) is True
178 mtime = os.path.getmtime(fs.path('/counter/blah.foo'))
179
180 time.sleep(1)
181 _bake_assets(fs)
182 assert mtime == os.path.getmtime(fs.path('/counter/blah.foo'))
183
184 time.sleep(1)
185 MultiRecord.RECORD_VERSION += 1
186 try:
187 _bake_assets(fs)
188 assert mtime < os.path.getmtime(fs.path('/counter/blah.foo'))
189 finally:
190 MultiRecord.RECORD_VERSION -= 1
191
192
193 @pytest.mark.parametrize('patterns, expected', [
194 (['_'],
195 {'something.html': 'A test file.'}),
196 (['html'],
197 {}),
198 (['/^_/'],
199 {'something.html': 'A test file.',
200 'foo': {'_important.html': 'Important!'}})
201 ])
202 def test_ignore_pattern(patterns, expected):
203 fs = (_get_test_fs()
204 .withFile('kitchen/assets/something.html', 'A test file.')
205 .withFile('kitchen/assets/_hidden.html', 'Shhh')
206 .withFile('kitchen/assets/foo/_important.html', 'Important!'))
207 fs.withConfig({'pipelines': {'asset': {'ignore': patterns}}})
208 with mock_fs_scope(fs):
209 assert {} == fs.getStructure('counter')
210 _bake_assets(fs)
211 assert expected == fs.getStructure('counter')
212
213
214 @pytest.mark.parametrize('names, expected', [
215 ('all', ['cleancss', 'compass', 'copy', 'concat', 'less', 'requirejs',
216 'sass', 'sitemap', 'uglifyjs', 'pygments_style']),
217 ('all -sitemap', ['cleancss', 'copy', 'compass', 'concat', 'less',
218 'requirejs', 'sass', 'uglifyjs', 'pygments_style']),
219 ('-sitemap -less -sass all', ['cleancss', 'copy', 'compass', 'concat',
220 'requirejs', 'uglifyjs',
221 'pygments_style']),
222 ('copy', ['copy']),
223 ('less sass', ['less', 'sass'])
224 ])
225 def test_filter_processor(names, expected):
226 fs = mock_fs().withConfig()
227 with mock_fs_scope(fs):
228 app = fs.getApp()
229 processors = app.plugin_loader.getProcessors()
230 procs = get_filtered_processors(processors, names)
231 actual = [p.PROCESSOR_NAME for p in procs]
232 assert sorted(actual) == sorted(expected)
233