Some love for the interface & tests

This commit is contained in:
Petr Viktorin 2011-04-27 16:00:50 +03:00
parent b27187e99a
commit 11d803fd63
2 changed files with 121 additions and 80 deletions

View file

@ -1,62 +1,82 @@
import sys
from pokedex.db import connect from pokedex.db import connect
from pokedex.util.movesets import main from pokedex.util.movesets import main
testcase_args = u"""
NO muk
NO beedrill rage pursuit agility endeavor toxic
NO ditto psystrike aeroblast mist-ball judgment
OK metapod tackle harden
OK lugia aeroblast punishment dive snore
OK yanmega bug-bite bug-buzz tackle whirlwind
OK crobat brave-bird quick-attack gust zen-headbutt
OK bagon defense-curl fire-fang hydro-pump shadow-claw
OK volcarona endure toxic fly fire-blast
OK hippopotas curse revenge sleep-talk swallow
OK hippopotas curse revenge sleep-talk snore
OK smeargle bug-bite bug-buzz splash fire-blast
NO smeargle bug-bite chatter splash fire-blast
NO azurill muddy-water iron-tail scald mimic
OK salamence dragon-dance dragon-claw fire-blast earthquake -v platinum
OK crawdaunt brick-break rock-slide facade toxic -v platinum
NO cleffa tickle wish amnesia splash
OK tyrogue pursuit
NO happiny softboiled
NO mamoswine bite body-slam curse double-edge
OK shedinja swords-dance
NO shedinja swords-dance screech
OK shedinja screech double-team fury-cutter x-scissor
OK raichu volt-tackle
OK raichu surf -v gold
OK pikachu volt-tackle thunderbolt bide
OK gyarados flail thrash iron-head outrage
OK drifblim memento gust thunderbolt pain-split
OK crobat nasty-plot brave-bird
NO crobat nasty-plot hypnosis
OK garchomp double-edge thrash outrage
OK nidoking counter disable amnesia head-smash
OK aggron stomp smellingsalt screech fire-punch
OK tyranitar dragon-dance outrage thunder-wave surf
NO butterfree morning-sun harden
OK pikachu reversal bide nasty-plot discharge
NO pikachu surf charge
NO blissey wish counter
NO clefairy copycat dynamicpunch
"""
result_map = {'OK': True, 'NO': False} result_map = {'OK': True, 'NO': False}
def test_cases(): def test_cases():
session = connect() session = connect()
for argstring in u""" for argstring in testcase_args.strip().splitlines():
NO muk
NO beedrill rage pursuit agility endeavor toxic
NO ditto psystrike aeroblast mist-ball judgment
OK lugia aeroblast punishment dive snore
OK yanmega bug-bite bug-buzz tackle whirlwind
OK crobat brave-bird quick-attack gust zen-headbutt
OK bagon defense-curl fire-fang hydro-pump shadow-claw
OK volcarona endure toxic fly fire-blast
OK hippopotas curse revenge sleep-talk swallow
OK hippopotas curse revenge sleep-talk snore
OK smeargle bug-bite bug-buzz splash fire-blast
NO smeargle bug-bite chatter splash fire-blast
NO azurill muddy-water iron-tail scald mimic
OK salamence dragon-dance dragon-claw fire-blast earthquake -v platinum
OK crawdaunt brick-break rock-slide facade toxic -v platinum
NO cleffa tickle wish amnesia splash
OK tyrogue pursuit
NO happiny softboiled
NO mamoswine bite body-slam curse double-edge
OK raichu volt-tackle
OK raichu surf -v gold
OK pikachu volt-tackle thunderbolt bide
OK gyarados flail thrash iron-head outrage
OK drifblim memento gust thunderbolt pain-split
OK crobat nasty-plot brave-bird
NO crobat nasty-plot hypnosis
OK garchomp double-edge thrash outrage
OK nidoking counter disable amnesia head-smash
OK aggron stomp smellingsalt screech fire-punch
OK tyranitar dragon-dance outrage thunder-wave surf
NO butterfree morning-sun harden
OK pikachu reversal bide nasty-plot discharge
NO pikachu surf charge
NO blissey wish counter
NO clefairy copycat dynamicpunch
""".strip().splitlines():
def run_test(argstring): def run_test(argstring):
args = argstring.split() args = argstring.split() + ['-q']
assert main(args[1:], session=session) == result_map[args[0]] assert bool(main(args[1:], session=session)) == result_map[args[0]]
run_test.description = 'Moveset checker test: ' + argstring.strip() run_test.description = 'Moveset checker test: ' + argstring.strip()
yield run_test, argstring.strip() yield run_test, argstring.strip()
if __name__ == '__main__': if __name__ == '__main__':
# Nose's default profiler, the unmaintained hotshot, sucks.
# Use cProfile instead.
filename = 'movesets.profile' filename = 'movesets.profile'
print 'Profiling the moveset checker' print 'Profiling the moveset checker'
import cProfile import cProfile
def header(str): ok_fail = [0, 0]
print def run_case(f, argv):
print str print argv, '...',
cProfile.runctx("[(header(argv), f(argv)) for f, argv in test_cases()]", sys.stdout.flush()
try:
f(argv)
ok_fail[0] += 1
print 'ok'
except AssertionError:
ok_fail[1] += 1
print 'FAIL'
cases = list(test_cases())
cProfile.runctx("[(run_case(f, argv)) for f, argv in cases]",
globals(), locals(), filename=filename) globals(), locals(), filename=filename)
print "{0} tests: {1[0]} OK, {1[1]} failed".format(sum(ok_fail), ok_fail)
print 'Profile stats saved to', filename print 'Profile stats saved to', filename

View file

@ -45,7 +45,7 @@ class MovesetSearch(object):
_cache = WeakKeyDictionary() _cache = WeakKeyDictionary()
def __init__(self, session, pokemon, version, moves, level=100, costs=None, def __init__(self, session, pokemon, version, moves, level=100, costs=None,
exclude_versions=(), exclude_pokemon=(), debug_level=False): exclude_versions=(), exclude_pokemon=(), debug_level=0):
self.session = session self.session = session
@ -1263,9 +1263,23 @@ class GoalNode(PokemonNode):
return True return True
### ###
### CLI interface ### Interface
### ###
def verify_moveset(session, pokemon, version, moves, level=100, **kwargs):
"""Verify the given moveset.
Returns a result with a hint on how to obtain the moveset, if it is valid.
Otherwise, returns a false value.
"""
try:
search = MovesetSearch(session, pokemon, version, moves, level, **kwargs)
except IllegalMoveCombination, e:
return False
else:
for result in search:
return result
def print_result(result, moves=()): def print_result(result, moves=()):
template = u"{cost:4} {est:4} {action:45.45}{long:1} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}" template = u"{cost:4} {est:4} {action:45.45}{long:1} {pokemon:10}{level:>3}{nl:1}{versions:2} {moves}"
print template.format(cost='Cost', est='Est.', action='Action', pokemon='Pokemon', print template.format(cost='Cost', est='Est.', action='Action', pokemon='Pokemon',
@ -1304,6 +1318,10 @@ def main(argv, session=None):
default='black', default='black',
help='Version to search in.') help='Version to search in.')
parser.add_argument('-q', '--quiet', action='store_true', default=False,
help="Don't print out the result, only indicate it by the return "
"value.")
parser.add_argument('-V', '--exclude-version', metavar='VER', type=unicode, parser.add_argument('-V', '--exclude-version', metavar='VER', type=unicode,
action='append', default=[], action='append', default=[],
help='Versions to exclude (along with their ' help='Versions to exclude (along with their '
@ -1316,8 +1334,8 @@ def main(argv, session=None):
parser.add_argument('-d', '--debug', action='append_const', const=1, parser.add_argument('-d', '--debug', action='append_const', const=1,
default=[], default=[],
help='Output timing and debugging information (can be specified more ' help='Output timing and debugging information. Can be specified more '
'than once).') 'than once for even more verbosity.')
args = parser.parse_args(argv) args = parser.parse_args(argv)
args.debug = len(args.debug) args.debug = len(args.debug)
@ -1331,54 +1349,57 @@ def main(argv, session=None):
if args.debug: if args.debug:
print 'Parsing arguments' print 'Parsing arguments'
class BadArgs(ValueError): pass
def _get_list(table, idents, name): def _get_list(table, idents, name):
if not idents:
return []
result = [] result = []
query = session.query(table).filter(table.identifier.in_(idents))
query = query.order_by(table.id.desc()) # overwrite pokemon alt. forms
ident_map = dict((thing.identifier, thing) for thing in query)
for ident in idents: for ident in idents:
try: try:
result.append(util.get(session, table, identifier=ident)) result.append(ident_map[ident])
except NoResultFound: except KeyError:
print>>sys.stderr, ('%s %s not found. Please use ' print>>sys.stderr, ('%s %s not found. Please use '
'the identifier.' % (name, ident)) 'the identifier.' % (name, ident))
return False raise BadArgs
return result return result
pokemon = _get_list(tables.Pokemon, [args.pokemon], 'Pokemon')[0] try:
moves = _get_list(tables.Move, args.move, 'Move') all_pokemon = _get_list(tables.Pokemon,
version = _get_list(tables.Version, [args.version], 'Version')[0] [args.pokemon] + args.exclude_pokemon, 'Pokemon')
excl_versions = _get_list(tables.Version, args.exclude_version, 'Version') all_versions = _get_list(tables.Version,
excl_pokemon = _get_list(tables.Pokemon, args.exclude_pokemon, 'Pokemon') [args.version] + args.exclude_version, 'Version')
pokemon = all_pokemon[0]
moves = _get_list(tables.Move, args.move, 'Move')
version = all_versions[0]
excl_versions = all_versions[1:]
excl_pokemon = all_pokemon[1:]
except BadArgs:
return False
if args.debug: if args.debug:
print 'Starting search' print 'Starting search'
no_results = True result = verify_moveset(session, pokemon, version, moves, args.level,
try: exclude_versions=excl_versions, exclude_pokemon=excl_pokemon,
search = MovesetSearch(session, pokemon, version, moves, args.level, debug_level=args.debug)
exclude_versions=excl_versions, exclude_pokemon=excl_pokemon, # XXX: Support more than one result
debug_level=args.debug) if result:
except IllegalMoveCombination, e: if args.debug:
print 'Error:', e print '-' * 79
if not args.quiet:
print_result(result, moves=moves)
else: else:
if args.debug: if args.debug:
print 'Setup done' print ' ' * 79
if not args.quiet:
print 'Illegal move combination.'
for result in search: return result
if args.debug and search.output_objects:
print '**warning: search looked up output objects**'
no_results = False
print '-' * 79
print_result(result, moves=moves)
# XXX: Support more than one result
break
if args.debug:
print
print 'Done'
if no_results:
print 'Illegal move combination.'
return (not no_results)
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(not main(sys.argv[1:])) sys.exit(not main(sys.argv[1:]))