Make Pokemon.form an actual relation
[zzz-pokedex.git] / pokedex / tests / test_media.py
1
2 """Test the media accessors.
3
4 If run directly from the command line, also tests the accessors and the names
5 of all the media by getting just about everything in a naive brute-force way.
6 This, of course, takes a lot of time to run.
7 """
8
9 import os
10 import re
11
12 from nose.tools import *
13 from nose.plugins.skip import SkipTest
14 import nose
15 import pkg_resources
16
17 from pokedex.db import tables, connect
18 from pokedex.util import media
19
20 session = connect()
21 basedir = pkg_resources.resource_filename('pokedex', 'data/media')
22
23 path_re = re.compile('^[-a-z0-9./]*$')
24
25 def test_totodile():
26 """Totodile's female sprite -- same as male"""
27 totodile = session.query(tables.Pokemon).filter_by(identifier=u'totodile').one()
28 accessor = media.PokemonMedia(totodile)
29 assert accessor.sprite() == accessor.sprite(female=True)
30
31 def test_chimecho():
32 """Chimecho's Platinum female backsprite -- diffeent from male"""
33 chimecho = session.query(tables.Pokemon).filter_by(identifier=u'chimecho').one()
34 accessor = media.PokemonMedia(chimecho)
35 male = accessor.sprite('platinum', back=True, frame=2)
36 female = accessor.sprite('platinum', back=True, female=True, frame=2)
37 assert male != female
38
39 def test_venonat():
40 """Venonat's shiny Yellow sprite -- same as non-shiny"""
41 venonat = session.query(tables.Pokemon).filter_by(identifier=u'venonat').one()
42 accessor = media.PokemonMedia(venonat)
43 assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True)
44
45 def test_arceus_icon():
46 """Arceus fire-form icon -- same as base icon"""
47 arceus = session.query(tables.Pokemon).filter_by(identifier=u'arceus').one()
48 accessor = media.PokemonMedia(arceus)
49 fire_arceus = [f for f in arceus.forms if f.identifier == 'fire'][0]
50 fire_accessor = media.PokemonFormMedia(fire_arceus)
51 assert accessor.icon() == fire_accessor.icon()
52
53 @raises(ValueError)
54 def test_strict_castform():
55 """Castform rainy form overworld with strict -- unavailable"""
56 castform = session.query(tables.Pokemon).filter_by(identifier=u'castform').first()
57 rainy_castform = [f for f in castform.forms if f.identifier == 'rainy'][0]
58 rainy_castform = media.PokemonFormMedia(rainy_castform)
59 rainy_castform.overworld('up', strict=True)
60
61 @raises(ValueError)
62 def test_strict_exeggcute():
63 """Exeggcutes's female backsprite, with strict -- unavailable"""
64 exeggcute = session.query(tables.Pokemon).filter_by(identifier=u'exeggcute').one()
65 accessor = media.PokemonMedia(exeggcute)
66 accessor.sprite(female=True, strict=True)
67
68
69
70 def get_all_filenames():
71 print 'Reading all filenames...'
72
73 all_filenames = set()
74
75 for dirpath, dirnames, filenames in os.walk(basedir):
76 for filename in filenames:
77 path = os.path.join(dirpath, filename)
78 assert path_re.match(path), path
79 all_filenames.add(path)
80
81 return all_filenames
82
83 def hit(filenames, method, *args, **kwargs):
84 """
85 Run the given accessor method with args & kwargs; if found remove the
86 result path from filenames and return True, else return False.
87 """
88 try:
89 medium = method(*args, **kwargs)
90 #print 'Hit', medium.relative_path
91 assert medium.exists
92 except ValueError, e:
93 #print 'DNF', e
94 return False
95 except:
96 print 'Error while processing', method, args, kwargs
97 raise
98 try:
99 filenames.remove(medium.path)
100 except KeyError:
101 pass
102 return True
103
104 def check_get_everything():
105 """
106 For every the accessor method, loop over the Cartesian products of all
107 possible values for its arguments.
108 Make sure we get every file in the repo, and that we get a file whenever
109 we should.
110
111 Well, there are exceptions of course.
112 """
113
114 versions = list(session.query(tables.Version).all())
115 versions.append('red-green')
116
117 black = session.query(tables.Version).filter_by(identifier=u'black').one()
118
119 filenames = get_all_filenames()
120
121 # Some small stuff first
122
123 for damage_class in session.query(tables.MoveDamageClass).all():
124 assert hit(filenames, media.DamageClassMedia(damage_class).icon)
125
126 for habitat in session.query(tables.PokemonHabitat).all():
127 assert hit(filenames, media.HabitatMedia(habitat).icon)
128
129 for shape in session.query(tables.PokemonShape).all():
130 assert hit(filenames, media.ShapeMedia(shape).icon)
131
132 for item_pocket in session.query(tables.ItemPocket).all():
133 assert hit(filenames, media.ItemPocketMedia(item_pocket).icon)
134 assert hit(filenames, media.ItemPocketMedia(item_pocket).icon, selected=True)
135
136 for contest_type in session.query(tables.ContestType).all():
137 assert hit(filenames, media.ContestTypeMedia(contest_type).icon)
138
139 for elemental_type in session.query(tables.Type).all():
140 assert hit(filenames, media.TypeMedia(elemental_type).icon)
141
142 # Items
143 versions_for_items = [
144 None,
145 session.query(tables.Version).filter_by(identifier='emerald').one(),
146 ]
147
148 for item in session.query(tables.Item).all():
149 accessor = media.ItemMedia(item)
150 assert hit(filenames, accessor.berry_image) or not item.berry
151 for rotation in (0, 90, 180, 270):
152 assert hit(filenames, accessor.underground, rotation=rotation) or (
153 not item.appears_underground or rotation)
154 for version in versions_for_items:
155 success = hit(filenames, accessor.sprite, version=version)
156 if version is None:
157 assert success
158
159 for color in 'red green blue pale prism'.split():
160 for big in (True, False):
161 accessor = media.UndergroundSphereMedia(color=color, big=big)
162 assert hit(filenames, accessor.underground)
163
164 for rock_type in 'i ii o o-big s t z'.split():
165 accessor = media.UndergroundRockMedia(rock_type)
166 for rotation in (0, 90, 180, 270):
167 success = hit(filenames, accessor.underground, rotation=rotation)
168 assert success or rotation
169
170 # Pokemon!
171 accessors = []
172
173 accessors.append(media.UnknownPokemonMedia())
174 accessors.append(media.EggMedia())
175 manaphy = session.query(tables.Pokemon).filter_by(identifier=u'manaphy').one()
176 accessors.append(media.EggMedia(manaphy))
177 accessors.append(media.SubstituteMedia())
178
179 print 'Loading pokemon'
180
181 for form in session.query(tables.PokemonForm).filter(tables.PokemonForm.identifier != '').all():
182 accessors.append(media.PokemonFormMedia(form))
183
184 for pokemon in session.query(tables.Pokemon).all():
185 accessors.append(media.PokemonMedia(pokemon))
186
187 for accessor in accessors:
188 assert hit(filenames, accessor.footprint) or not accessor.form
189 assert hit(filenames, accessor.trozei) or not accessor.form or (
190 accessor.form.pokemon.generation.id > 3)
191 assert hit(filenames, accessor.cry) or not accessor.form
192 assert hit(filenames, accessor.cropped_sprite) or not accessor.form
193 for female in (True, False):
194 assert hit(filenames, accessor.icon, female=female) or not accessor.form
195 assert hit(filenames, accessor.sugimori, female=female) or (
196 not accessor.form or accessor.form.pokemon.id >= 647)
197 for shiny in (True, False):
198 for frame in (1, 2):
199 for direction in 'up down left right'.split():
200 assert hit(filenames, accessor.overworld,
201 direction=direction,
202 shiny=shiny,
203 female=female,
204 frame=frame,
205 ) or not accessor.form or (
206 accessor.form.pokemon.generation.id > 4)
207 for version in versions:
208 for animated in (True, False):
209 for back in (True, False):
210 for color in (None, 'gray', 'gbc'):
211 success = hit(filenames,
212 accessor.sprite,
213 version,
214 animated=animated,
215 back=back,
216 color=color,
217 shiny=shiny,
218 female=female,
219 frame=frame,
220 )
221 if (version == black and not animated
222 and not back and not color and not
223 shiny and not female and
224 frame == 1):
225 # All pokemon are in Black
226 assert success or not accessor.form
227 if (str(accessor.pokemon_id) == '1'
228 and not animated and not color and
229 frame == 1):
230 # Bulbasaur is in all versions
231 assert success
232
233 # Remove exceptions
234 exceptions = [os.path.join(basedir, dirname) for dirname in
235 'chrome fonts ribbons'.split()]
236 exceptions.append(os.path.join(basedir, 'items', 'hm-'))
237 exceptions = tuple(exceptions)
238
239 for filename in tuple(filenames):
240 if filename.startswith(exceptions):
241 filenames.remove(filename)
242
243 if len(filenames):
244 print
245 print '-----------------'
246 print 'Unaccessed stuff:'
247 for filename in sorted(filenames):
248 print filename
249 print len(filenames), 'unaccessed files :('
250
251 return (not filenames)
252
253 if __name__ == '__main__':
254 result = nose.run(defaultTest=__file__)
255 result = result and check_get_everything()
256 exit(not result)