Test media accessors, and the media organization itself
[zzz-pokedex.git] / pokedex / tests / test_media.py
diff --git a/pokedex/tests/test_media.py b/pokedex/tests/test_media.py
new file mode 100644 (file)
index 0000000..81b5714
--- /dev/null
@@ -0,0 +1,256 @@
+
+"""Test the media accessors.
+
+If run directly from the command line, also tests the accessors and the names
+of all the media by getting just about everything in a naive brute-force way.
+This, of course, takes a lot of time to run.
+"""
+
+import os
+import re
+
+from nose.tools import *
+from nose.plugins.skip import SkipTest
+import nose
+import pkg_resources
+
+from pokedex.db import tables, connect
+from pokedex.util import media
+
+session = connect()
+basedir = pkg_resources.resource_filename('pokedex', 'data/media')
+
+path_re = re.compile('^[-a-z0-9./]*$')
+
+def test_totodile():
+    """Totodile's female sprite -- same as male"""
+    totodile = session.query(tables.Pokemon).filter_by(identifier=u'totodile').one()
+    accessor = media.PokemonMedia(totodile)
+    assert accessor.sprite() == accessor.sprite(female=True)
+
+def test_chimecho():
+    """Chimecho's Platinum female backsprite -- diffeent from male"""
+    chimecho = session.query(tables.Pokemon).filter_by(identifier=u'chimecho').one()
+    accessor = media.PokemonMedia(chimecho)
+    male = accessor.sprite('platinum', back=True, frame=2)
+    female = accessor.sprite('platinum', back=True, female=True, frame=2)
+    assert male != female
+
+def test_venonat():
+    """Venonat's shiny Yellow sprite -- same as non-shiny"""
+    venonat = session.query(tables.Pokemon).filter_by(identifier=u'venonat').one()
+    accessor = media.PokemonMedia(venonat)
+    assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True)
+
+def test_arceus_icon():
+    """Arceus fire-form icon -- same as base icon"""
+    arceus = session.query(tables.Pokemon).filter_by(identifier=u'arceus').one()
+    accessor = media.PokemonMedia(arceus)
+    fire_arceus = [f for f in arceus.forms if f.identifier == 'fire'][0]
+    fire_accessor = media.PokemonFormMedia(fire_arceus)
+    assert accessor.icon() == fire_accessor.icon()
+
+@raises(ValueError)
+def test_strict_castform():
+    """Castform rainy form overworld with strict -- unavailable"""
+    castform = session.query(tables.Pokemon).filter_by(identifier=u'castform').first()
+    rainy_castform = [f for f in castform.forms if f.identifier == 'rainy'][0]
+    rainy_castform = media.PokemonFormMedia(rainy_castform)
+    rainy_castform.overworld('up', strict=True)
+
+@raises(ValueError)
+def test_strict_exeggcute():
+    """Exeggcutes's female backsprite, with strict -- unavailable"""
+    exeggcute = session.query(tables.Pokemon).filter_by(identifier=u'exeggcute').one()
+    accessor = media.PokemonMedia(exeggcute)
+    accessor.sprite(female=True, strict=True)
+
+
+
+def get_all_filenames():
+    print 'Reading all filenames...'
+
+    all_filenames = set()
+
+    for dirpath, dirnames, filenames in os.walk(basedir):
+        for filename in filenames:
+            path = os.path.join(dirpath, filename)
+            assert path_re.match(path), path
+            all_filenames.add(path)
+
+    return all_filenames
+
+def hit(filenames, method, *args, **kwargs):
+    """
+    Run the given accessor method with args & kwargs; if found remove the
+    result path from filenames and return True, else return False.
+    """
+    try:
+        medium = method(*args, **kwargs)
+        #print 'Hit', medium.relative_path
+        assert medium.exists
+    except ValueError, e:
+        #print 'DNF', e
+        return False
+    except:
+        print 'Error while processing', method, args, kwargs
+        raise
+    try:
+        filenames.remove(medium.path)
+    except KeyError:
+        pass
+    return True
+
+def check_get_everything():
+    """
+    For every the accessor method, loop over the Cartesian products of all
+    possible values for its arguments.
+    Make sure we get every file in the repo, and that we get a file whenever
+    we should.
+
+    Well, there are exceptions of course.
+    """
+
+    versions = list(session.query(tables.Version).all())
+    versions.append('red-green')
+
+    black = session.query(tables.Version).filter_by(identifier=u'black').one()
+
+    filenames = get_all_filenames()
+
+    # Some small stuff first
+
+    for damage_class in session.query(tables.MoveDamageClass).all():
+        assert hit(filenames, media.DamageClassMedia(damage_class).icon)
+
+    for habitat in session.query(tables.PokemonHabitat).all():
+        assert hit(filenames, media.HabitatMedia(habitat).icon)
+
+    for shape in session.query(tables.PokemonShape).all():
+        assert hit(filenames, media.ShapeMedia(shape).icon)
+
+    for item_pocket in session.query(tables.ItemPocket).all():
+        assert hit(filenames, media.ItemPocketMedia(item_pocket).icon)
+        assert hit(filenames, media.ItemPocketMedia(item_pocket).icon, selected=True)
+
+    for contest_type in session.query(tables.ContestType).all():
+        assert hit(filenames, media.ContestTypeMedia(contest_type).icon)
+
+    for elemental_type in session.query(tables.Type).all():
+        assert hit(filenames, media.TypeMedia(elemental_type).icon)
+
+    # Items
+    versions_for_items = [
+            None,
+            session.query(tables.Version).filter_by(identifier='emerald').one(),
+        ]
+
+    for item in session.query(tables.Item).all():
+        accessor = media.ItemMedia(item)
+        assert hit(filenames, accessor.berry_image) or not item.berry
+        for rotation in (0, 90, 180, 270):
+            assert hit(filenames, accessor.underground, rotation=rotation) or (
+                    not item.appears_underground or rotation)
+        for version in versions_for_items:
+            success = hit(filenames, accessor.sprite, version=version)
+            if version is None:
+                assert success
+
+    for color in 'red green blue pale prism'.split():
+        for big in (True, False):
+            accessor = media.UndergroundSphereMedia(color=color, big=big)
+            assert hit(filenames, accessor.underground)
+
+    for rock_type in 'i ii o o-big s t z'.split():
+        accessor = media.UndergroundRockMedia(rock_type)
+        for rotation in (0, 90, 180, 270):
+            success = hit(filenames, accessor.underground, rotation=rotation)
+            assert success or rotation
+
+    # Pokemon!
+    accessors = []
+
+    accessors.append(media.UnknownPokemonMedia())
+    accessors.append(media.EggMedia())
+    manaphy = session.query(tables.Pokemon).filter_by(identifier=u'manaphy').one()
+    accessors.append(media.EggMedia(manaphy))
+    accessors.append(media.SubstituteMedia())
+
+    print 'Loading pokemon'
+
+    for form in session.query(tables.PokemonForm).filter(tables.PokemonForm.identifier != '').all():
+        accessors.append(media.PokemonFormMedia(form))
+
+    for pokemon in session.query(tables.Pokemon).all():
+        accessors.append(media.PokemonMedia(pokemon))
+
+    for accessor in accessors:
+        assert hit(filenames, accessor.footprint) or not accessor.form
+        assert hit(filenames, accessor.trozei) or not accessor.form or (
+                accessor.form.pokemon.generation.id > 3)
+        assert hit(filenames, accessor.cry) or not accessor.form
+        assert hit(filenames, accessor.cropped_sprite) or not accessor.form
+        for female in (True, False):
+            assert hit(filenames, accessor.icon, female=female) or not accessor.form
+            assert hit(filenames, accessor.sugimori, female=female) or (
+                    not accessor.form or accessor.form.pokemon.id >= 647)
+            for shiny in (True, False):
+                for frame in (1, 2):
+                    for direction in 'up down left right'.split():
+                        assert hit(filenames, accessor.overworld,
+                                direction=direction,
+                                shiny=shiny,
+                                female=female,
+                                frame=frame,
+                            ) or not accessor.form or (
+                                    accessor.form.pokemon.generation.id > 4)
+                    for version in versions:
+                        for animated in (True, False):
+                            for back in (True, False):
+                                for color in (None, 'gray', 'gbc'):
+                                    success = hit(filenames,
+                                            accessor.sprite,
+                                            version,
+                                            animated=animated,
+                                            back=back,
+                                            color=color,
+                                            shiny=shiny,
+                                            female=female,
+                                            frame=frame,
+                                        )
+                                    if (version == black and not animated
+                                        and not back and not color and not
+                                        shiny and not female and
+                                        frame == 1):
+                                        # All pokemon are in Black
+                                        assert success or not accessor.form
+                                    if (str(accessor.pokemon_id) == '1'
+                                        and not animated and not color and
+                                        frame == 1):
+                                        # Bulbasaur is in all versions
+                                        assert success
+
+    # Remove exceptions
+    exceptions = [os.path.join(basedir, dirname) for dirname in
+            'chrome fonts ribbons'.split()]
+    exceptions.append(os.path.join(basedir, 'items', 'hm-'))
+    exceptions = tuple(exceptions)
+
+    for filename in tuple(filenames):
+        if filename.startswith(exceptions):
+            filenames.remove(filename)
+
+    if len(filenames):
+        print
+        print '-----------------'
+        print 'Unaccessed stuff:'
+        for filename in sorted(filenames):
+            print filename
+        print len(filenames), 'unaccessed files :('
+
+    return (not filenames)
+
+if __name__ == '__main__':
+    result = nose.run(defaultTest=__file__)
+    result = result and check_get_everything()
+    exit(not result)