13
13
from matplotlib import animation
14
14
15
15
16
+ @pytest .fixture ()
17
+ def anim (request ):
18
+ """Create a simple animation (with options)."""
19
+ fig , ax = plt .subplots ()
20
+ line , = ax .plot ([], [])
21
+
22
+ ax .set_xlim (0 , 10 )
23
+ ax .set_ylim (- 1 , 1 )
24
+
25
+ def init ():
26
+ line .set_data ([], [])
27
+ return line ,
28
+
29
+ def animate (i ):
30
+ x = np .linspace (0 , 10 , 100 )
31
+ y = np .sin (x + i )
32
+ line .set_data (x , y )
33
+ return line ,
34
+
35
+ # "klass" can be passed to determine the class returned by the fixture
36
+ kwargs = dict (getattr (request , 'param' , {})) # make a copy
37
+ klass = kwargs .pop ('klass' , animation .FuncAnimation )
38
+ if 'frames' not in kwargs :
39
+ kwargs ['frames' ] = 5
40
+ return klass (fig = fig , func = animate , init_func = init , ** kwargs )
41
+
42
+
16
43
class NullMovieWriter (animation .AbstractMovieWriter ):
17
44
"""
18
45
A minimal MovieWriter. It doesn't actually write anything.
@@ -40,26 +67,8 @@ def finish(self):
40
67
pass
41
68
42
69
43
- def make_animation (** kwargs ):
44
- fig , ax = plt .subplots ()
45
- line , = ax .plot ([])
46
-
47
- def init ():
48
- pass
49
-
50
- def animate (i ):
51
- line .set_data ([0 , 1 ], [0 , i ])
52
- return line ,
53
-
54
- return animation .FuncAnimation (fig , animate , ** kwargs )
55
-
56
-
57
- def test_null_movie_writer ():
70
+ def test_null_movie_writer (anim ):
58
71
# Test running an animation with NullMovieWriter.
59
-
60
- num_frames = 5
61
- anim = make_animation (frames = num_frames )
62
-
63
72
filename = "unused.null"
64
73
dpi = 50
65
74
savefig_kwargs = dict (foo = 0 )
@@ -68,17 +77,17 @@ def test_null_movie_writer():
68
77
anim .save (filename , dpi = dpi , writer = writer ,
69
78
savefig_kwargs = savefig_kwargs )
70
79
71
- assert writer .fig == plt .figure (1 ) # The figure used by make_animation.
80
+ assert writer .fig == plt .figure (1 ) # The figure used by anim fixture
72
81
assert writer .outfile == filename
73
82
assert writer .dpi == dpi
74
83
assert writer .args == ()
75
84
assert writer .savefig_kwargs == savefig_kwargs
76
- assert writer ._count == num_frames
77
-
85
+ assert writer ._count == anim .save_count
78
86
79
- def test_animation_delete ():
80
- anim = make_animation (frames = 5 )
81
87
88
+ @pytest .mark .parametrize ('anim' , [dict (klass = dict )], indirect = ['anim' ])
89
+ def test_animation_delete (anim ):
90
+ anim = animation .FuncAnimation (** anim )
82
91
with pytest .warns (Warning , match = 'Animation was deleted' ):
83
92
del anim
84
93
gc .collect ()
@@ -140,44 +149,62 @@ def isAvailable(cls):
140
149
# design more sophisticated tests which compare resulting frames a-la
141
150
# matplotlib.testing.image_comparison
142
151
@pytest .mark .parametrize ('writer, output' , WRITER_OUTPUT )
143
- def test_save_animation_smoketest (tmpdir , writer , output ):
152
+ @pytest .mark .parametrize ('anim' , [dict (klass = dict )], indirect = ['anim' ])
153
+ def test_save_animation_smoketest (tmpdir , writer , output , anim ):
144
154
if not animation .writers .is_available (writer ):
145
155
pytest .skip ("writer '%s' not available on this system" % writer )
146
- fig , ax = plt .subplots ()
147
- line , = ax .plot ([], [])
148
-
149
- ax .set_xlim (0 , 10 )
150
- ax .set_ylim (- 1 , 1 )
151
156
157
+ anim = animation .FuncAnimation (** anim )
152
158
dpi = None
153
159
codec = None
154
160
if writer == 'ffmpeg' :
155
161
# Issue #8253
156
- fig .set_size_inches ((10.85 , 9.21 ))
162
+ anim . _fig .set_size_inches ((10.85 , 9.21 ))
157
163
dpi = 100.
158
164
codec = 'h264'
159
165
160
- def init ():
161
- line .set_data ([], [])
162
- return line ,
163
-
164
- def animate (i ):
165
- x = np .linspace (0 , 10 , 100 )
166
- y = np .sin (x + i )
167
- line .set_data (x , y )
168
- return line ,
169
-
170
166
# Use temporary directory for the file-based writers, which produce a file
171
167
# per frame with known names.
172
168
with tmpdir .as_cwd ():
173
- anim = animation .FuncAnimation (fig , animate , init_func = init , frames = 5 )
174
169
anim .save (output , fps = 30 , writer = writer , bitrate = 500 , dpi = dpi ,
175
170
codec = codec )
171
+ with pytest .warns (None ):
172
+ del anim
176
173
177
174
178
- def test_no_length_frames ():
179
- (make_animation (frames = iter (range (5 )))
180
- .save ('unused.null' , writer = NullMovieWriter ()))
175
+ @pytest .mark .parametrize ('writer' , [
176
+ pytest .param (
177
+ 'ffmpeg' , marks = pytest .mark .skipif (
178
+ not animation .FFMpegWriter .isAvailable (),
179
+ reason = 'Requires FFMpeg' )),
180
+ pytest .param (
181
+ 'imagemagick' , marks = pytest .mark .skipif (
182
+ not animation .ImageMagickWriter .isAvailable (),
183
+ reason = 'Requires ImageMagick' )),
184
+ ])
185
+ @pytest .mark .parametrize ('html, want' , [
186
+ ('none' , None ),
187
+ ('html5' , '<video width' ),
188
+ ('jshtml' , '<script ' )
189
+ ])
190
+ @pytest .mark .parametrize ('anim' , [dict (klass = dict )], indirect = ['anim' ])
191
+ def test_animation_repr_html (writer , html , want , anim ):
192
+ # create here rather than in the fixture otherwise we get __del__ warnings
193
+ # about producing no output
194
+ anim = animation .FuncAnimation (** anim )
195
+ with plt .rc_context ({'animation.writer' : writer ,
196
+ 'animation.html' : html }):
197
+ html = anim ._repr_html_ ()
198
+ if want is None :
199
+ assert html is None
200
+ else :
201
+ assert want in html
202
+
203
+
204
+ @pytest .mark .parametrize ('anim' , [dict (frames = iter (range (5 )))],
205
+ indirect = ['anim' ])
206
+ def test_no_length_frames (anim ):
207
+ anim .save ('unused.null' , writer = NullMovieWriter ())
181
208
182
209
183
210
def test_movie_writer_registry ():
@@ -196,11 +223,12 @@ def test_movie_writer_registry():
196
223
not animation .writers .is_available (mpl .rcParams ["animation.writer" ]),
197
224
reason = "animation writer not installed" )),
198
225
"to_jshtml" ])
199
- def test_embed_limit (method_name , caplog , tmpdir ):
226
+ @pytest .mark .parametrize ('anim' , [dict (frames = 1 )], indirect = ['anim' ])
227
+ def test_embed_limit (method_name , caplog , tmpdir , anim ):
200
228
caplog .set_level ("WARNING" )
201
229
with tmpdir .as_cwd ():
202
230
with mpl .rc_context ({"animation.embed_limit" : 1e-6 }): # ~1 byte.
203
- getattr (make_animation ( frames = 1 ) , method_name )()
231
+ getattr (anim , method_name )()
204
232
assert len (caplog .records ) == 1
205
233
record , = caplog .records
206
234
assert (record .name == "matplotlib.animation"
@@ -213,14 +241,15 @@ def test_embed_limit(method_name, caplog, tmpdir):
213
241
not animation .writers .is_available (mpl .rcParams ["animation.writer" ]),
214
242
reason = "animation writer not installed" )),
215
243
"to_jshtml" ])
216
- def test_cleanup_temporaries (method_name , tmpdir ):
244
+ @pytest .mark .parametrize ('anim' , [dict (frames = 1 )], indirect = ['anim' ])
245
+ def test_cleanup_temporaries (method_name , tmpdir , anim ):
217
246
with tmpdir .as_cwd ():
218
- getattr (make_animation ( frames = 1 ) , method_name )()
247
+ getattr (anim , method_name )()
219
248
assert list (Path (str (tmpdir )).iterdir ()) == []
220
249
221
250
222
251
@pytest .mark .skipif (os .name != "posix" , reason = "requires a POSIX OS" )
223
- def test_failing_ffmpeg (tmpdir , monkeypatch ):
252
+ def test_failing_ffmpeg (tmpdir , monkeypatch , anim ):
224
253
"""
225
254
Test that we correctly raise a CalledProcessError when ffmpeg fails.
226
255
@@ -235,7 +264,7 @@ def test_failing_ffmpeg(tmpdir, monkeypatch):
235
264
"[[ $@ -eq 0 ]]\n " )
236
265
os .chmod (str (exe_path ), 0o755 )
237
266
with pytest .raises (subprocess .CalledProcessError ):
238
- make_animation () .save ("test.mpeg" )
267
+ anim .save ("test.mpeg" )
239
268
240
269
241
270
@pytest .mark .parametrize ("cache_frame_data" , [False , True ])
0 commit comments