Switch to py.test #604

This commit is contained in:
Petr Viktorin 2011-05-02 10:20:28 +03:00
parent c710457717
commit 332647c362
10 changed files with 392 additions and 359 deletions

21
conftest.py Normal file
View file

@ -0,0 +1,21 @@
# Configuration for the tests.
# Use `py.test` to run the tests.
# (This file needs to be in or above the directory where py.test is called)
import pytest
import os
def pytest_addoption(parser):
parser.addoption("--media-root", action="store",
default=None,
help="Root for the media files (if not specified and pokedex/data/media doesn't exist, tests are skipped)")
parser.addoption("--all", action="store_true", default=False,
help="Run all tests, even those that take a lot of time")
def pytest_generate_tests(metafunc):
for funcargs in getattr(metafunc.function, 'funcarglist', ()):
metafunc.addcall(funcargs=funcargs)
for posargs in getattr(metafunc.function, 'posarglist', ()):
metafunc.addcall(funcargs=dict(zip(metafunc.funcargnames, posargs)))

View file

@ -1,6 +1,45 @@
def setup():
# XXX This needs to recreate the database, someday. :(
pass
def teardown(): import inspect
pass from functools import wraps
# test support code
def params(funcarglist):
"""Basic list-of-dicts test parametrization
From: http://pytest.org/funcargs.html
Example:
@params([dict(a=1, b=2), dict(a=3, b=4)])
def test_lt(a, b):
assert a < b
"""
def decorator(function):
function.funcarglist = funcarglist
return function
return decorator
def positional_params(*paramlist):
"""Magic list-of-lists parametrization
Example:
@params([(1, 2), (3, 4)])
def test_lt(a, b):
assert a < b
"""
def decorator(function):
function.posarglist = paramlist
return function
return decorator
def single_params(*paramlist):
"""Magic list-of-lists parametrization
Example:
@params('1', '2', '3', '4'])
def test_int(k):
assert int(k)
"""
def decorator(function):
function.posarglist = [[param] for param in paramlist]
return function
return decorator

View file

@ -1,5 +1,6 @@
from nose.tools import *
import unittest import pytest
from sqlalchemy.orm import aliased from sqlalchemy.orm import aliased
from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.orm.exc import NoResultFound
@ -21,8 +22,8 @@ def test_encounter_slots():
.join((version_group_b, tables.Version.version_group)) \ .join((version_group_b, tables.Version.version_group)) \
.filter(version_group_a.id != version_group_b.id) .filter(version_group_a.id != version_group_b.id)
assert_equal(sanity_q.count(), 0, # Encounter slots all match the encounters they belong to
"Encounter slots all match the encounters they belong to") assert sanity_q.count() == 0
def test_nonzero_autoincrement_ids(): def test_nonzero_autoincrement_ids():
"""Check that autoincrementing ids don't contain zeroes """Check that autoincrementing ids don't contain zeroes
@ -34,8 +35,8 @@ def test_nonzero_autoincrement_ids():
for cls in tables.mapped_classes: for cls in tables.mapped_classes:
if 'id' in cls.__table__.c: if 'id' in cls.__table__.c:
if cls.__table__.c.id.autoincrement: if cls.__table__.c.id.autoincrement:
@raises(NoResultFound)
def nonzero_id(cls): def nonzero_id(cls):
util.get(session, cls, id=0) with pytest.raises(NoResultFound):
util.get(session, cls, id=0)
nonzero_id.description = "No zero id in %s" % cls.__name__ nonzero_id.description = "No zero id in %s" % cls.__name__
yield nonzero_id, cls yield nonzero_id, cls

View file

@ -1,18 +1,12 @@
# encoding: utf8 # Encoding: UTF-8
from nose.tools import *
import unittest from pokedex.tests import *
from pokedex.lookup import PokedexLookup from pokedex.lookup import PokedexLookup
lookup = None lookup = PokedexLookup()
def setup(): @positional_params(
# Recreate data
global lookup
lookup = PokedexLookup()
def test_exact_lookup():
tests = [
# Simple lookups # Simple lookups
(u'Eevee', 'pokemon_species',133), (u'Eevee', 'pokemon_species',133),
(u'Scratch', 'moves', 10), (u'Scratch', 'moves', 10),
@ -37,68 +31,61 @@ def test_exact_lookup():
(u'이브이', 'pokemon_species', 133), (u'이브이', 'pokemon_species', 133),
(u'伊布', 'pokemon_species', 133), (u'伊布', 'pokemon_species', 133),
(u'Evoli', 'pokemon_species', 133), (u'Evoli', 'pokemon_species', 133),
] )
def test_exact_lookup(input, table, id):
results = lookup.lookup(input)
assert len(results) == 1
assert results[0].exact == True
for input, table, id in tests: row = results[0].object
results = lookup.lookup(input) assert row.__tablename__ == table
assert_equal(len(results), 1, u"'%s' returns one result" % input) assert row.id == id
assert_equal(results[0].exact, True, u"'%s' match exactly" % input)
row = results[0].object
assert_equal(row.__tablename__, table, u"'%s' is in the right table" % input)
assert_equal(row.id, id, u"'%s' returns the right id" % input)
def test_id_lookup(): def test_id_lookup():
results = lookup.lookup(u'1') results = lookup.lookup(u'1')
assert_true(len(results) >= 5, u'At least five things have id 1') assert len(results) >= 5
assert_true(all(_.object.id == 1 for _ in results), assert all(result.object.id == 1 for result in results)
u'All results have id 1')
def test_multi_lookup(): def test_multi_lookup():
results = lookup.lookup(u'Metronome') results = lookup.lookup(u'Metronome')
assert_equal(len(results), 2, u'Two things called "Metronome"') assert len(results) == 2
assert_true(results[0].exact, u'Metronome matches are exact') assert results[0].exact
def test_type_lookup(): def test_type_lookup():
results = lookup.lookup(u'pokemon:1') results = lookup.lookup(u'pokemon:1')
assert_equal(results[0].object.__tablename__, 'pokemon_species', assert results[0].object.__tablename__ == 'pokemon_species'
u'Type restriction works correctly') assert len(results) == 1
assert_equal(len(results), 1, u'Only one id result when type is specified') assert results[0].object.name == u'Bulbasaur'
assert_equal(results[0].object.name, u'Bulbasaur',
u'Type + id returns the right result')
results = lookup.lookup(u'1', valid_types=['pokemon_species']) results = lookup.lookup(u'1', valid_types=['pokemon_species'])
assert_equal(results[0].object.name, u'Bulbasaur', assert results[0].object.name == u'Bulbasaur'
u'valid_types works as well as type: prefix')
def test_language_lookup(): def test_language_lookup():
# There are two objects named "charge": the move Charge, and the move # There are two objects named "charge": the move Charge, and the move
# Tackle, which is called "Charge" in French. # Tackle, which is called "Charge" in French.
results = lookup.lookup(u'charge') results = lookup.lookup(u'charge')
assert_true(len(results) > 1, u'There are multiple "charge"s') assert len(results) > 1
results = lookup.lookup(u'@fr:charge') results = lookup.lookup(u'@fr:charge')
assert_equal(results[0].iso639, u'fr', u'Language restriction works correctly') assert results[0].iso639 == u'fr'
assert_equal(len(results), 1, u'Only one "charge" result when language is specified') assert len(results) == 1
assert_equal(results[0].object.name, u'Tackle', assert results[0].object.name == u'Tackle'
u'Language + vague name returns the right result')
results = lookup.lookup(u'charge', valid_types=['@fr']) results = lookup.lookup(u'charge', valid_types=['@fr'])
assert_equal(results[0].object.name, u'Tackle', assert results[0].object.name == u'Tackle'
u'valid_types works as well as @lang: prefix')
results = lookup.lookup(u'@fr,move:charge') results = lookup.lookup(u'@fr,move:charge')
assert_equal(results[0].object.name, u'Tackle', assert results[0].object.name == u'Tackle'
u'Languages and types both work together')
results = lookup.lookup(u'@fr:charge', valid_types=['move']) results = lookup.lookup(u'@fr:charge', valid_types=['move'])
assert_equal(results[0].object.name, u'Tackle', assert results[0].object.name, u'Tackle'
u'valid_types and language prefixes get along')
def test_fuzzy_lookup():
tests = [ @positional_params(
# Regular English names # Regular English names
(u'chamander', u'Charmander'), (u'chamander', u'Charmander'),
(u'pokeball', u'Poké Ball'), (u'pokeball', u'Poké Ball'),
@ -110,44 +97,51 @@ def test_fuzzy_lookup():
# Sufficiently long foreign names # Sufficiently long foreign names
(u'カクレオ', u'Kecleon'), (u'カクレオ', u'Kecleon'),
(u'Yamikrasu', u'Murkrow'), (u'Yamikrasu', u'Murkrow'),
] )
def test_fuzzy_lookup(misspelling, name):
results = lookup.lookup(misspelling)
first_result = results[0]
assert first_result.object.name == name
for misspelling, name in tests:
results = lookup.lookup(misspelling)
first_result = results[0]
assert_equal(first_result.object.name, name,
u'Simple misspellings are corrected')
def test_nidoran():
results = lookup.lookup(u'Nidoran') results = lookup.lookup(u'Nidoran')
top_names = [_.object.name for _ in results[0:2]] top_names = [result.object.name for result in results[0:2]]
assert_true(u'Nidoran♂' in top_names, u'Nidoran♂ is a top result for "Nidoran"') assert u'Nidoran♂' in top_names
assert_true(u'Nidoran♀' in top_names, u'Nidoran♀ is a top result for "Nidoran"') assert u'Nidoran♀' in top_names
def test_wildcard_lookup():
tests = [ @positional_params(
(u'pokemon:*meleon', u'Charmeleon'), (u'pokemon:*meleon', u'Charmeleon'),
(u'item:master*', u'Master Ball'), (u'item:master*', u'Master Ball'),
(u'ee?ee', u'Eevee'), (u'ee?ee', u'Eevee'),
] )
def test_wildcard_lookup(wildcard, name):
results = lookup.lookup(wildcard)
first_result = results[0]
assert first_result.object.name == name
for wildcard, name in tests:
results = lookup.lookup(wildcard)
first_result = results[0]
assert_equal(first_result.object.name, name,
u'Wildcards work correctly')
def test_random_lookup(): def test_bare_random():
for _ in xrange(5): for i in range(5):
results = lookup.lookup(u'random') results = lookup.lookup(u'random')
assert_equal(len(results), 1, u'Random returns one result') assert len(results) == 1
@positional_params(
[u'pokemon_species'],
[u'moves'],
[u'items'],
[u'abilities'],
[u'types'],
)
def test_qualified_random(table_name):
results = lookup.lookup(u'random', valid_types=[table_name])
assert len(results) == 1
assert results[0].object.__tablename__ == table_name
for table_name in [u'pokemon_species', u'moves', u'items', u'abilities', u'types']:
results = lookup.lookup(u'random', valid_types=[table_name])
assert_equal(len(results), 1, u'Constrained random returns one result')
assert_equal(results[0].object.__tablename__, table_name,
u'Constrained random returns result from the right table')
def test_crash_empty_prefix(): def test_crash_empty_prefix():
"""Searching for ':foo' used to crash, augh!""" """Searching for ':foo' used to crash, augh!"""
results = lookup.lookup(u':Eevee') results = lookup.lookup(u':Eevee')
assert_equal(results[0].object.name, u'Eevee', u'Empty prefix dun crash') assert results[0].object.name == u'Eevee'

View file

@ -1,4 +1,3 @@
"""Test the media accessors. """Test the media accessors.
If run directly from the command line, also tests the accessors and the names If run directly from the command line, also tests the accessors and the names
@ -6,45 +5,33 @@ 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. This, of course, takes a lot of time to run.
""" """
import pytest
import os import os
import re import re
from functools import wraps
from nose.tools import *
from nose.plugins.skip import SkipTest
import nose
import pkg_resources
from pokedex.db import tables, connect from pokedex.db import tables, connect
from pokedex.util import media from pokedex.util import media
def pytest_funcarg__root(request):
root = request.config.option.media_root
if not root:
root = os.path.join(os.path.dirname(__file__), *'../data/media'.split('/'))
if not media.BaseMedia(root).available:
raise pytest.skip("Media unavailable")
return root
session = connect() session = connect()
basedir = pkg_resources.resource_filename('pokedex', 'data/media')
path_re = re.compile('^[-a-z0-9./]*$') path_re = re.compile('^[-a-z0-9./]*$')
root = pkg_resources.resource_filename('pokedex', 'data/media') def test_totodile(root):
media_available = media.BaseMedia(root).available
def if_available(func):
@wraps(func)
def if_available_wrapper(*args, **kwargs):
if not media_available:
raise SkipTest('Media not available at %s' % root)
else:
func(*args, **kwargs)
return if_available_wrapper
@if_available
def test_totodile():
"""Totodile's female sprite -- same as male""" """Totodile's female sprite -- same as male"""
totodile = session.query(tables.PokemonSpecies).filter_by(identifier=u'totodile').one() totodile = session.query(tables.PokemonSpecies).filter_by(identifier=u'totodile').one()
accessor = media.PokemonSpeciesMedia(root, totodile) accessor = media.PokemonSpeciesMedia(root, totodile)
assert accessor.sprite() == accessor.sprite(female=True) assert accessor.sprite() == accessor.sprite(female=True)
@if_available def test_chimecho(root):
def test_chimecho():
"""Chimecho's Platinum female backsprite -- diffeent from male""" """Chimecho's Platinum female backsprite -- diffeent from male"""
chimecho = session.query(tables.PokemonSpecies).filter_by(identifier=u'chimecho').one() chimecho = session.query(tables.PokemonSpecies).filter_by(identifier=u'chimecho').one()
accessor = media.PokemonSpeciesMedia(root, chimecho) accessor = media.PokemonSpeciesMedia(root, chimecho)
@ -52,15 +39,13 @@ def test_chimecho():
female = accessor.sprite('platinum', back=True, female=True, frame=2) female = accessor.sprite('platinum', back=True, female=True, frame=2)
assert male != female assert male != female
@if_available def test_venonat(root):
def test_venonat():
"""Venonat's shiny Yellow sprite -- same as non-shiny""" """Venonat's shiny Yellow sprite -- same as non-shiny"""
venonat = session.query(tables.PokemonSpecies).filter_by(identifier=u'venonat').one() venonat = session.query(tables.PokemonSpecies).filter_by(identifier=u'venonat').one()
accessor = media.PokemonSpeciesMedia(root, venonat) accessor = media.PokemonSpeciesMedia(root, venonat)
assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True) assert accessor.sprite('yellow') == accessor.sprite('yellow', shiny=True)
@if_available def test_arceus_icon(root):
def test_arceus_icon():
"""Arceus fire-form icon -- same as base icon""" """Arceus fire-form icon -- same as base icon"""
arceus = session.query(tables.PokemonSpecies).filter_by(identifier=u'arceus').one() arceus = session.query(tables.PokemonSpecies).filter_by(identifier=u'arceus').one()
accessor = media.PokemonSpeciesMedia(root, arceus) accessor = media.PokemonSpeciesMedia(root, arceus)
@ -68,32 +53,28 @@ def test_arceus_icon():
fire_accessor = media.PokemonFormMedia(root, fire_arceus) fire_accessor = media.PokemonFormMedia(root, fire_arceus)
assert accessor.icon() == fire_accessor.icon() assert accessor.icon() == fire_accessor.icon()
@if_available def test_strict_castform(root):
@raises(ValueError)
def test_strict_castform():
"""Castform rainy form overworld with strict -- unavailable""" """Castform rainy form overworld with strict -- unavailable"""
castform = session.query(tables.PokemonSpecies).filter_by(identifier=u'castform').first() with pytest.raises(ValueError):
rainy_castform = [f for f in castform.forms if f.form_identifier == 'rainy'][0] castform = session.query(tables.PokemonSpecies).filter_by(identifier=u'castform').first()
print rainy_castform rainy_castform = [f for f in castform.forms if f.form_identifier == 'rainy'][0]
rainy_castform = media.PokemonFormMedia(root, rainy_castform) print rainy_castform
rainy_castform.overworld('up', strict=True) rainy_castform = media.PokemonFormMedia(root, rainy_castform)
rainy_castform.overworld('up', strict=True)
@if_available def test_strict_exeggcute(root):
@raises(ValueError)
def test_strict_exeggcute():
"""Exeggcutes's female backsprite, with strict -- unavailable""" """Exeggcutes's female backsprite, with strict -- unavailable"""
exeggcute = session.query(tables.PokemonSpecies).filter_by(identifier=u'exeggcute').one() with pytest.raises(ValueError):
accessor = media.PokemonSpeciesMedia(root, exeggcute) exeggcute = session.query(tables.PokemonSpecies).filter_by(identifier=u'exeggcute').one()
accessor.sprite(female=True, strict=True) accessor = media.PokemonSpeciesMedia(root, exeggcute)
accessor.sprite(female=True, strict=True)
def get_all_filenames(): def get_all_filenames(root):
print 'Reading all filenames...'
all_filenames = set() all_filenames = set()
for dirpath, dirnames, filenames in os.walk(basedir): for dirpath, dirnames, filenames in os.walk(root):
dirnames[:] = [dirname for dirname in dirnames if dirname != '.git'] dirnames[:] = [dirname for dirname in dirnames if dirname != '.git']
for filename in filenames: for filename in filenames:
path = os.path.join(dirpath, filename) path = os.path.join(dirpath, filename)
@ -123,8 +104,8 @@ def hit(filenames, method, *args, **kwargs):
pass pass
return True return True
@if_available @pytest.mark.skipif("not config.getvalue('all')", reason='`--all` not specified')
def check_get_everything(): def test_get_everything(root, pytestconfig):
""" """
For every the accessor method, loop over the Cartesian products of all For every the accessor method, loop over the Cartesian products of all
possible values for its arguments. possible values for its arguments.
@ -133,13 +114,14 @@ def check_get_everything():
Well, there are exceptions of course. Well, there are exceptions of course.
""" """
assert pytestconfig.getvalue('all')
versions = list(session.query(tables.Version).all()) versions = list(session.query(tables.Version).all())
versions.append('red-green') versions.append('red-green')
black = session.query(tables.Version).filter_by(identifier=u'black').one() black = session.query(tables.Version).filter_by(identifier=u'black').one()
filenames = get_all_filenames() filenames = get_all_filenames(root)
# Some small stuff first # Some small stuff first
@ -252,26 +234,21 @@ def check_get_everything():
assert success assert success
# Remove exceptions # Remove exceptions
exceptions = [os.path.join(basedir, dirname) for dirname in exceptions = [os.path.join(root, dirname) for dirname in
'chrome fonts ribbons'.split()] 'chrome fonts ribbons'.split()]
exceptions.append(os.path.join(basedir, 'items', 'hm-')) exceptions.append(os.path.join(root, 'items', 'hm-'))
exceptions = tuple(exceptions) exceptions = tuple(exceptions)
for filename in tuple(filenames): unaccessed_filenames = set(filenames)
for filename in filenames:
if filename.startswith(exceptions): if filename.startswith(exceptions):
filenames.remove(filename) unaccessed_filenames.remove(filename)
if len(filenames): if unaccessed_filenames:
print print 'Unaccessed files:'
print '-----------------' for filename in unaccessed_filenames:
print 'Unaccessed stuff:'
for filename in sorted(filenames):
print filename print filename
print len(filenames), 'unaccessed files :('
assert unaccessed_filenames == set()
return (not filenames) return (not filenames)
if __name__ == '__main__':
result = nose.run(defaultTest=__file__)
result = result and check_get_everything()
exit(not result)

View file

@ -1,12 +1,9 @@
# encoding: utf8 # encoding: utf8
from nose.tools import *
import unittest
import pokedex.roomaji import pokedex.roomaji
from pokedex.tests import positional_params
@positional_params(
def test_roomaji():
tests = [
(u'ヤミカラス', 'yamikarasu'), (u'ヤミカラス', 'yamikarasu'),
# Elongated vowel # Elongated vowel
@ -24,14 +21,13 @@ def test_roomaji():
(u'ラティアス', 'ratiasu'), (u'ラティアス', 'ratiasu'),
(u'ウィー', 'wii'), (u'ウィー', 'wii'),
(u'セレビィ', 'sereby'), (u'セレビィ', 'sereby'),
] )
def test_roomaji(kana, roomaji):
result = pokedex.roomaji.romanize(kana)
assert result == roomaji
for kana, roomaji in tests:
result = pokedex.roomaji.romanize(kana)
assert_equal(result, roomaji, u"'%s' romanizes correctly" % roomaji)
def test_roomaji_cs(): @positional_params(
tests = [
(u'ヤミカラス', u'jamikarasu'), (u'ヤミカラス', u'jamikarasu'),
# Elongated vowel # Elongated vowel
@ -49,8 +45,7 @@ def test_roomaji_cs():
(u'ラティアス', u'ratiasu'), (u'ラティアス', u'ratiasu'),
(u'ウィー', u''), (u'ウィー', u''),
(u'セレビィ', u'serebí'), (u'セレビィ', u'serebí'),
] )
def test_roomaji_cs(kana, roomaji):
for kana, roomaji in tests: result = pokedex.roomaji.romanize(kana, 'cs')
result = pokedex.roomaji.romanize(kana, 'cs') assert result == roomaji
assert_equal(result, roomaji, u"'%s' romanizes correctly for Czech" % roomaji)

View file

@ -1,6 +1,7 @@
# encoding: utf8 # encoding: utf8
from nose.tools import *
import unittest from pokedex.tests import single_params
from sqlalchemy import Column, Integer, String, create_engine from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import class_mapper, joinedload, sessionmaker from sqlalchemy.orm import class_mapper, joinedload, sessionmaker
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
@ -10,22 +11,23 @@ from pokedex.db import tables, markdown
from pokedex.db.multilang import MultilangScopedSession, MultilangSession, \ from pokedex.db.multilang import MultilangScopedSession, MultilangSession, \
create_translation_table create_translation_table
def test_variable_names(): @single_params(*dir(tables))
def test_variable_names(varname):
"""We want pokedex.db.tables to export tables using the class name""" """We want pokedex.db.tables to export tables using the class name"""
for varname in dir(tables): table = getattr(tables, varname)
if not varname[0].isupper(): try:
continue if not issubclass(table, tables.TableBase) or table is tables.TableBase:
table = getattr(tables, varname) return
try: except TypeError:
if not issubclass(table, tables.TableBase) or table is tables.TableBase: return
continue classname = table.__name__
except TypeError: if classname and varname[0].isupper():
continue assert varname == classname, '%s refers to %s' % (varname, classname)
classname = table.__name__
if classname and varname[0].isupper(): @single_params(*tables.mapped_classes)
assert varname == classname, '%s refers to %s' % (varname, classname) def test_variable_names_2(table):
for table in tables.mapped_classes: """We also want all of the tables exported"""
assert getattr(tables, table.__name__) is table assert getattr(tables, table.__name__) is table
def test_class_order(): def test_class_order():
"""The declarative classes should be defined in alphabetical order. """The declarative classes should be defined in alphabetical order.
@ -156,52 +158,52 @@ def test_i18n_table_creation():
assert foo.name_map[lang_en] == 'different english' assert foo.name_map[lang_en] == 'different english'
assert foo.name_map[lang_ru] == 'new russian' assert foo.name_map[lang_ru] == 'new russian'
def test_texts(): classes = []
for cls in tables.mapped_classes:
classes.append(cls)
classes += cls.translation_classes
@single_params(*classes)
def test_texts(cls):
"""Check DB schema for integrity of text columns & translations. """Check DB schema for integrity of text columns & translations.
Mostly protects against copy/paste oversights and rebase hiccups. Mostly protects against copy/paste oversights and rebase hiccups.
If there's a reason to relax the tests, do it If there's a reason to relax the tests, do it
""" """
classes = [] if hasattr(cls, 'local_language') or hasattr(cls, 'language'):
for cls in tables.mapped_classes: good_formats = 'markdown plaintext gametext'.split()
classes.append(cls) assert_text = '%s is language-specific'
classes += cls.translation_classes else:
for cls in classes: good_formats = 'identifier latex'.split()
if hasattr(cls, 'local_language') or hasattr(cls, 'language'): assert_text = '%s is not language-specific'
good_formats = 'markdown plaintext gametext'.split() columns = sorted(cls.__table__.c, key=lambda c: c.name)
assert_text = '%s is language-specific' text_columns = []
for column in columns:
format = column.info.get('format', None)
if format is not None:
if format not in good_formats:
raise AssertionError(assert_text % column)
if (format != 'identifier') and (column.name == 'identifier'):
raise AssertionError('%s: identifier column name/type mismatch' % column)
if column.info.get('official', None) and format not in 'gametext plaintext':
raise AssertionError('%s: official text with bad format' % column)
text_columns.append(column)
else: else:
good_formats = 'identifier latex'.split() if isinstance(column.type, tables.Unicode):
assert_text = '%s is not language-specific' raise AssertionError('%s: text column without format' % column)
columns = sorted(cls.__table__.c, key=lambda c: c.name) if column.name == 'name' and format != 'plaintext':
text_columns = [] raise AssertionError('%s: non-plaintext name' % column)
for column in columns: # No mention of English in the description
format = column.info.get('format', None) assert 'English' not in column.info['description'], column
if format is not None: # If there's more than one text column in a translation table,
if format not in good_formats: # they have to be nullable, to support missing translations
raise AssertionError(assert_text % column) if hasattr(cls, 'local_language') and len(text_columns) > 1:
if (format != 'identifier') and (column.name == 'identifier'): for column in text_columns:
raise AssertionError('%s: identifier column name/type mismatch' % column) assert column.nullable
if column.info.get('official', None) and format not in 'gametext plaintext':
raise AssertionError('%s: official text with bad format' % column)
text_columns.append(column)
else:
if isinstance(column.type, tables.Unicode):
raise AssertionError('%s: text column without format' % column)
if column.name == 'name' and format != 'plaintext':
raise AssertionError('%s: non-plaintext name' % column)
# No mention of English in the description
assert 'English' not in column.info['description'], column
# If there's more than one text column in a translation table,
# they have to be nullable, to support missing translations
if hasattr(cls, 'local_language') and len(text_columns) > 1:
for column in text_columns:
assert column.nullable
def test_identifiers_with_names(): @single_params(*tables.mapped_classes)
def test_identifiers_with_names(table):
"""Test that named tables have identifiers """Test that named tables have identifiers
""" """
for table in sorted(tables.mapped_classes, key=lambda t: t.__name__): for translation_class in table.translation_classes:
for translation_class in table.translation_classes: if hasattr(translation_class, 'name'):
if hasattr(translation_class, 'name'): assert hasattr(table, 'identifier'), table
assert hasattr(table, 'identifier'), table

View file

@ -1,150 +1,147 @@
# Encoding: UTF-8 # Encoding: UTF-8
from nose.tools import * import pytest
from sqlalchemy.orm.exc import NoResultFound
from pokedex.tests import positional_params
from pokedex.db import tables, connect, util, markdown from pokedex.db import tables, connect, util, markdown
class TestStrings(object): connection = connect()
def setup(self):
self.connection = connect()
def teardown(self): def test_filter():
self.connection.rollback() q = connection.query(tables.PokemonSpecies).filter(
tables.PokemonSpecies.name == u"Marowak")
assert q.one().identifier == 'marowak'
def test_filter(self): def test_languages():
q = self.connection.query(tables.PokemonSpecies).filter( q = connection.query(tables.PokemonSpecies).filter(
tables.PokemonSpecies.name == u"Marowak") tables.PokemonSpecies.name == u"Mightyena")
assert q.one().identifier == 'marowak' pkmn = q.one()
for lang, name in (
('en', u'Mightyena'),
('ja', u'グラエナ'),
('roomaji', u'Guraena'),
('fr', u'Grahyèna'),
):
language = connection.query(tables.Language).filter_by(
identifier=lang).one()
assert pkmn.name_map[language] == name
def test_languages(self): def test_bad_lang():
q = self.connection.query(tables.PokemonSpecies).filter( with pytest.raises(KeyError):
tables.PokemonSpecies.name == u"Mightyena") q = connection.query(tables.PokemonSpecies).filter(
pkmn = q.one()
for lang, name in (
('en', u'Mightyena'),
('ja', u'グラエナ'),
('roomaji', u'Guraena'),
('fr', u'Grahyèna'),
):
language = self.connection.query(tables.Language).filter_by(
identifier=lang).one()
assert pkmn.name_map[language] == name
@raises(KeyError)
def test_bad_lang(self):
q = self.connection.query(tables.PokemonSpecies).filter(
tables.PokemonSpecies.name == u"Mightyena") tables.PokemonSpecies.name == u"Mightyena")
pkmn = q.one() pkmn = q.one()
pkmn.names["identifier of a language that doesn't exist"] pkmn.names["identifier of a language that doesn't exist"]
def test_mutating(self): def test_mutating():
item = self.connection.query(tables.Item).filter_by( item = connection.query(tables.Item).filter_by(
identifier=u"jade-orb").one() identifier=u"jade-orb").one()
language = self.connection.query(tables.Language).filter_by( language = connection.query(tables.Language).filter_by(
identifier=u"de").one() identifier=u"de").one()
item.name_map[language] = u"foo" item.name_map[language] = u"foo"
assert item.name_map[language] == "foo" assert item.name_map[language] == "foo"
item.name_map[language] = u"xyzzy" item.name_map[language] = u"xyzzy"
assert item.name_map[language] == "xyzzy" assert item.name_map[language] == "xyzzy"
def test_mutating_default(self): def test_mutating_default():
item = self.connection.query(tables.Item).filter_by( item = connection.query(tables.Item).filter_by(
identifier=u"jade-orb").one() identifier=u"jade-orb").one()
item.name = u"foo" item.name = u"foo"
assert item.name == "foo" assert item.name == "foo"
def test_string_mapping(self): def test_string_mapping():
item = self.connection.query(tables.Item).filter_by( item = connection.query(tables.Item).filter_by(
identifier=u"jade-orb").one() identifier=u"jade-orb").one()
assert len(item.name_map) == len(item.names) assert len(item.name_map) == len(item.names)
for lang in item.names: for lang in item.names:
assert item.name_map[lang] == item.names[lang].name assert item.name_map[lang] == item.names[lang].name
assert lang in item.name_map assert lang in item.name_map
assert "language that doesn't exist" not in item.name_map assert "language that doesn't exist" not in item.name_map
assert tables.Language() not in item.name_map assert tables.Language() not in item.name_map
def test_new_language(self): def test_new_language():
item = self.connection.query(tables.Item).filter_by( item = connection.query(tables.Item).filter_by(
identifier=u"jade-orb").one() identifier=u"jade-orb").one()
language = tables.Language() language = tables.Language()
language.id = -1 language.id = -1
language.identifier = u'test' language.identifier = u'test'
language.iso639 = language.iso3166 = u'--' language.iso639 = language.iso3166 = u'--'
language.official = False language.official = False
self.connection.add(language) connection.add(language)
item.name_map[language] = u"foo" item.name_map[language] = u"foo"
assert item.name_map[language] == "foo" assert item.name_map[language] == "foo"
assert language in item.name_map assert language in item.name_map
item.name_map[language] = u"xyzzy" item.name_map[language] = u"xyzzy"
assert item.name_map[language] == "xyzzy" assert item.name_map[language] == "xyzzy"
def test_markdown(self): def test_markdown():
move = self.connection.query(tables.Move).filter_by( move = connection.query(tables.Move).filter_by(
identifier=u"thunderbolt").one() identifier=u"thunderbolt").one()
language = self.connection.query(tables.Language).filter_by( language = connection.query(tables.Language).filter_by(
identifier=u"en").one() identifier=u"en").one()
assert '10%' in move.effect.as_text() assert '10%' in move.effect.as_text()
assert '10%' in move.effect_map[language].as_text() assert '10%' in move.effect_map[language].as_text()
assert '10%' in move.effect.as_html() assert '10%' in move.effect.as_html()
assert '10%' in move.effect_map[language].as_html() assert '10%' in move.effect_map[language].as_html()
assert '10%' in unicode(move.effect) assert '10%' in unicode(move.effect)
assert '10%' in unicode(move.effect_map[language]) assert '10%' in unicode(move.effect_map[language])
assert '10%' in move.effect.__html__() assert '10%' in move.effect.__html__()
assert '10%' in move.effect_map[language].__html__() assert '10%' in move.effect_map[language].__html__()
def test_markdown_string(self): def test_markdown_string():
en = util.get(self.connection, tables.Language, 'en') en = util.get(connection, tables.Language, 'en')
md = markdown.MarkdownString('[]{move:thunderbolt} [paralyzes]{mechanic:paralysis}', self.connection, en) md = markdown.MarkdownString('[]{move:thunderbolt} [paralyzes]{mechanic:paralysis}', connection, en)
assert unicode(md) == 'Thunderbolt paralyzes' assert unicode(md) == 'Thunderbolt paralyzes'
assert md.as_html() == '<p><span>Thunderbolt</span> <span>paralyzes</span></p>' assert md.as_html() == '<p><span>Thunderbolt</span> <span>paralyzes</span></p>'
assert md.as_html(object_url=lambda category, obj: "%s/%s" % (category, obj.identifier)) == ( assert md.as_html(object_url=lambda category, obj: "%s/%s" % (category, obj.identifier)) == (
'<p><a href="move/thunderbolt">Thunderbolt</a> <span>paralyzes</span></p>') '<p><a href="move/thunderbolt">Thunderbolt</a> <span>paralyzes</span></p>')
print md.as_html(identifier_url=lambda category, ident: "%s/%s" % (category, ident)) print md.as_html(identifier_url=lambda category, ident: "%s/%s" % (category, ident))
assert md.as_html(identifier_url=lambda category, ident: "%s/%s" % (category, ident)) == ( assert md.as_html(identifier_url=lambda category, ident: "%s/%s" % (category, ident)) == (
'<p><a href="move/thunderbolt">Thunderbolt</a> <a href="mechanic/paralysis">paralyzes</a></p>') '<p><a href="move/thunderbolt">Thunderbolt</a> <a href="mechanic/paralysis">paralyzes</a></p>')
def test_markdown_values(self): def markdown_column_params():
"""Check all markdown values """Check all markdown values
Scans the database schema for Markdown columns, runs through every value Scans the database schema for Markdown columns, runs through every value
in each, and ensures that it's valid Markdown. in each, and ensures that it's valid Markdown.
""" """
# Move effects have their own special wrappers. Explicitly test them separately # Move effects have their own special wrappers. Explicitly test them separately
yield self.check_markdown_column, tables.Move, None, 'effect' yield tables.Move, None, 'effect'
yield self.check_markdown_column, tables.Move, None, 'short_effect' yield tables.Move, None, 'short_effect'
for cls in tables.mapped_classes: for cls in tables.mapped_classes:
for translation_cls in cls.translation_classes: for translation_cls in cls.translation_classes:
for column in translation_cls.__table__.c: for column in translation_cls.__table__.c:
if column.info.get('string_getter') == markdown.MarkdownString: if column.info.get('string_getter') == markdown.MarkdownString:
yield self.check_markdown_column, cls, translation_cls, column.name yield cls, translation_cls, column.name
def check_markdown_column(self, parent_class, translation_class, column_name): @positional_params(*markdown_column_params())
"""Implementation for the above""" def test_markdown_values(parent_class, translation_class, column_name):
query = self.connection.query(parent_class) """Implementation for the above"""
if translation_class: query = connection.query(parent_class)
query = query.join(translation_class) if translation_class:
for item in query: query = query.join(translation_class)
for language, markdown in getattr(item, column_name + '_map').items(): for item in query:
for language, markdown in getattr(item, column_name + '_map').items():
if markdown is None: if markdown is None:
continue continue
key = u"Markdown in {0} #{1}'s {2} (lang={3})".format( key = u"Markdown in {0} #{1}'s {2} (lang={3})".format(
parent_class.__name__, item.id, column_name, language.identifier) parent_class.__name__, item.id, column_name, language.identifier)
try: try:
text = markdown.as_text() text = markdown.as_text()
except NoResultFound: except NoResultFound:
assert False, u"{0} references something that doesn't exist:\n{1}".format( assert False, u"{0} references something that doesn't exist:\n{1}".format(
key, markdown.source_text) key, markdown.source_text)
except AttributeError: except AttributeError:
print markdown print markdown
raise raise
error_message = u"{0} leaves syntax cruft:\n{1}" error_message = u"{0} leaves syntax cruft:\n{1}"
error_message = error_message.format(key, text) error_message = error_message.format(key, text)
ok_(not any(char in text for char in '[]{}'), error_message) assert not any(char in text for char in '[]{}'), error_message

View file

@ -2,7 +2,7 @@
import csv import csv
from nose.tools import * import pytest
from pokedex.db import translations, tables from pokedex.db import translations, tables

View file

@ -1,7 +1,8 @@
# encoding: utf8 # Encoding: utf8
from nose.tools import *
import unittest
import pytest
from pokedex.tests import single_params
from pokedex.db import connect, tables, util from pokedex.db import connect, tables, util
session = connect() session = connect()
@ -18,21 +19,21 @@ def test_get_english_by_identifier():
language = util.get(session, tables.Language, 'en') language = util.get(session, tables.Language, 'en')
assert language.name == 'English' assert language.name == 'English'
def test_get_pokemon_identifier(): @single_params(*'burmy shaymin unown cresselia'.split())
for identifier in 'burmy shaymin unown cresselia'.split(): def test_get_pokemon_identifier(identifier):
poke = util.get(session, tables.PokemonSpecies, identifier=identifier) poke = util.get(session, tables.PokemonSpecies, identifier=identifier)
assert poke.identifier == identifier assert poke.identifier == identifier
def test_get_pokemon_name(): @single_params(*'Burmy Shaymin Unown Cresselia'.split())
for name in 'Burmy Shaymin Unown Cresselia'.split(): def test_get_pokemon_name(name):
poke = util.get(session, tables.PokemonSpecies, name=name) poke = util.get(session, tables.PokemonSpecies, name=name)
assert poke.name == name assert poke.name == name
def test_get_pokemon_name_explicit_language(): @single_params(*'Cheniti Shaymin Zarbi Cresselia'.split())
def test_get_pokemon_name_explicit_language(name):
french = util.get(session, tables.Language, 'fr') french = util.get(session, tables.Language, 'fr')
for name in 'Cheniti Shaymin Zarbi Cresselia'.split(): poke = util.get(session, tables.PokemonSpecies, name=name, language=french)
poke = util.get(session, tables.PokemonSpecies, name=name, language=french) assert poke.name_map[french] == name, poke.name_map[french]
assert poke.name_map[french] == name, poke.name_map[french]
def test_types_french_order(): def test_types_french_order():
french = util.get(session, tables.Language, 'fr') french = util.get(session, tables.Language, 'fr')
@ -40,3 +41,9 @@ def test_types_french_order():
types = list(util.order_by_name(types, tables.Type, language=french)) types = list(util.order_by_name(types, tables.Type, language=french))
assert types[0].name_map[french] == 'Acier', types[0].name_map[french] assert types[0].name_map[french] == 'Acier', types[0].name_map[french]
assert types[-1].name_map[french] == 'Vol', types[-1].name_map[french] assert types[-1].name_map[french] == 'Vol', types[-1].name_map[french]
@single_params(*range(1, 10) * 2)
def test_get_pokemon_id(id):
result = util.get(session, tables.Pokemon, id=id)
assert result.id == id
assert result.__tablename__ == 'pokemon'