Files
dungeon-sheets/tests/test_character.py
T
2019-01-01 17:11:39 -05:00

252 lines
10 KiB
Python

#!/usr/bin/env python
from unittest import TestCase
import warnings
from dungeonsheets import race, monsters, exceptions, spells
from dungeonsheets.character import Character, Wizard, Druid
from dungeonsheets.weapons import Weapon, Shortsword
from dungeonsheets.armor import Armor, LeatherArmor, Shield
class TestCharacter(TestCase):
"""Tests for the generic character base class."""
def test_constructor(self):
char = Character(name="Inara")
def test_hit_dice(self):
# Test the getter
char = Character()
char.level = 2
char.hit_dice_faces = 10
self.assertEqual(char.hit_dice, '2d10')
def test_set_attrs(self):
char = Character()
char.set_attrs(name='Inara')
self.assertEqual(char.name, 'Inara')
# Check that the weapons get loaded as objects not string
char.set_attrs(weapons=['shortsword'])
self.assertEqual(len(char.weapons), 1)
self.assertTrue(isinstance(char.weapons[0], Shortsword))
# Check that armor and shield gets set_attrs
char.set_attrs(armor='leather armor', shield='shield')
self.assertFalse(isinstance(char.armor, str))
self.assertFalse(isinstance(char.shield, str))
# Check that race gets set to an object
char.set_attrs(race='high elf')
self.assertIsInstance(char.race, race.HighElf)
def test_wield_weapon(self):
char = Character()
char.strength = 14
char.weapon_proficiencies = [Shortsword]
# Add a weapon
char.wield_weapon('shortsword')
self.assertEqual(len(char.weapons), 1)
sword = char.weapons[0]
self.assertTrue(isinstance(sword, Weapon))
self.assertTrue(isinstance(sword, Shortsword))
self.assertEqual(sword.attack_modifier, 4) # str + prof
self.assertEqual(sword.damage, '1d6+2') # str
# Check if dexterity is used if it's higher (Finesse weapon)
char.weapons = []
char.dexterity = 16
char.wield_weapon('shortsword')
sword = char.weapons[0]
self.assertEqual(sword.attack_modifier, 5) # dex + prof
# Check if race weapon proficiencies are considered
char.weapons = []
char.weapon_proficiencies = []
char.race = race.HighElf()
char.wield_weapon('shortsword')
sword = char.weapons[0]
self.assertEqual(sword.attack_modifier, 5)
def test_str(self):
char = Wizard(name="Inara")
self.assertEqual(str(char), 'Inara')
self.assertEqual(repr(char), '<Wizard: Inara>')
def test_is_proficient(self):
char = Character(classes=['Wizard'])
char.weapon_proficiencies
sword = Shortsword()
# Check for not-proficient weapon
self.assertFalse(char.is_proficient(sword))
# Check if we're proficient in the weapon
char.weapon_proficiencies = [Shortsword]
self.assertTrue(char.is_proficient(sword))
# Now try it with a racial proficiency
char.weapon_proficiencies = tuple()
char.race = race.HighElf()
self.assertTrue(char.is_proficient(sword))
def test_proficiencies_text(self):
char = Character()
char._proficiencies_text = ('hello', 'world')
self.assertIn('hello', char.proficiencies_text.lower())
self.assertIn('world', char.proficiencies_text.lower())
# Check for extra proficiencies
char._proficiencies_text += ("it's", "me")
self.assertIn("it's", char.proficiencies_text.lower())
self.assertIn('me', char.proficiencies_text.lower())
# Check that race proficienceis are included
elf = race.HighElf()
char.race = elf
expected = ("hello", "world", "longswords", "shortswords", "shortbows",
"longbows", "it's", "me")
for e in expected:
self.assertIn(e, char.proficiencies_text.lower())
def test_proficiency_bonus(self):
char = Character()
char.level = 1
self.assertEqual(char.proficiency_bonus, 2)
char.level = 4
self.assertEqual(char.proficiency_bonus, 2)
char.level = 5
self.assertEqual(char.proficiency_bonus, 3)
char.level = 8
self.assertEqual(char.proficiency_bonus, 3)
char.level = 9
self.assertEqual(char.proficiency_bonus, 4)
char.level = 12
self.assertEqual(char.proficiency_bonus, 4)
char.level = 13
self.assertEqual(char.proficiency_bonus, 5)
char.level = 16
self.assertEqual(char.proficiency_bonus, 5)
char.level = 17
self.assertEqual(char.proficiency_bonus, 6)
char.level = 20
self.assertEqual(char.proficiency_bonus, 6)
def test_spell_slots(self):
char = Wizard()
# Wizard level 1
char.level = 1
self.assertEqual(char.spell_slots(spell_level=0), 3)
self.assertEqual(char.spell_slots(spell_level=1), 2)
self.assertEqual(char.spell_slots(spell_level=2), 0)
# Wizard level 2
char.level = 2
self.assertEqual(char.spell_slots(spell_level=0), 3)
self.assertEqual(char.spell_slots(spell_level=1), 3)
self.assertEqual(char.spell_slots(spell_level=2), 0)
def test_equip_armor(self):
char = Character(dexterity=16)
char.wear_armor('leather armor')
self.assertTrue(isinstance(char.armor, Armor))
# Now make sure the armor class is correct
self.assertEqual(char.armor_class, 14)
# Try passing an Armor object directly
char.wear_armor(LeatherArmor())
self.assertEqual(char.armor_class, 14)
# Test equipped armor with max dexterity mod_str
char.armor.dexterity_mod_max = 1
self.assertEqual(char.armor_class, 12)
def test_wield_shield(self):
char = Character(dexterity=16)
char.wield_shield('shield')
self.assertTrue(isinstance(char.shield, Shield), msg=char.shield)
# Now make sure the armor class is correct
self.assertEqual(char.armor_class, 15)
# Try passing an Armor object directly
char.wield_shield(Shield)
self.assertEqual(char.armor_class, 15)
def test_speed(self):
# Check that the speed pulls from the character's race
char = Character(race='lightfoot halfling')
self.assertEqual(char.speed, '25')
# Check that a character with no race defaults to 30 feet
char = Character()
char.race = None
self.assertEqual(char.speed, '30')
class DruidTestCase(TestCase):
def test_learned_spells(self):
"""For a druid, learning spells is not necessary and this field should
be ignored."""
char = Druid()
with warnings.catch_warnings():
warnings.filterwarnings('ignore', message="Druids cannot learn spells")
char.set_attrs(spells=['invisibility'],
spells_prepared=['druidcraft'])
# self.assertEqual(len(char.spells), 1)
self.assertEqual(len(char.spells), 2)
self.assertIn(spells.Druidcraft(), char.spells)
def test_wild_shapes(self):
char = Druid()
# Druid level 2
char.level = 2
# Set reasonable wild shapes
char.wild_shapes = ['Wolf']
self.assertIsInstance(char.wild_shapes[0], monsters.Wolf)
# Check what happens if a non-existent wild_shape is added
with self.assertRaises(exceptions.MonsterError):
char.wild_shapes = ['Wolf', 'Hyperion Loader']
# Check what happens if a valid monster class is directly added
char.wild_shapes = [monsters.Wolf(), ]
self.assertIsInstance(char.wild_shapes[0], monsters.Wolf)
# Check that invalid monsters aren't accepted
char.wild_shapes = ['Wolf', 'giant eagle']
self.assertEqual(len(char.wild_shapes), 1)
self.assertIsInstance(char.wild_shapes[0], monsters.Wolf)
def test_moon_druid_wild_shapes(self):
# Moon druid level 2 gets beasts up to CR 1
char = Druid(level=2, wild_shapes=['Ape'], circle='moon')
self.assertEqual(len(char.wild_shapes), 1)
self.assertIsInstance(char.wild_shapes[0], monsters.Ape)
# Moon druid above level 6 gets beasts up to CR level / 3
char = Druid(level=9, wild_shapes=['ankylosaurus'], circle='moon')
self.assertEqual(len(char.wild_shapes), 1)
self.assertIsInstance(char.wild_shapes[0], monsters.Ankylosaurus)
def test_can_assume_shape(self):
class Beast(monsters.Monster):
description = 'beast'
new_druid = Druid(level=1)
low_druid = Druid(level=2)
mid_druid = Druid(level=4)
high_druid = Druid(level=8)
beast = Beast()
# Check that level 1 druid automatically fails
self.assertFalse(new_druid.can_assume_shape(beast))
# Check if a basic beast can be transformed
self.assertTrue(low_druid.can_assume_shape(beast))
# Check that challenge rating is checked
hard_beast = Beast()
hard_beast.challenge_rating = 1/2
really_hard_beast = Beast()
really_hard_beast.challenge_rating = 1
self.assertFalse(low_druid.can_assume_shape(hard_beast))
self.assertFalse(low_druid.can_assume_shape(really_hard_beast))
self.assertTrue(mid_druid.can_assume_shape(hard_beast))
self.assertFalse(mid_druid.can_assume_shape(really_hard_beast))
self.assertTrue(high_druid.can_assume_shape(hard_beast))
self.assertTrue(high_druid.can_assume_shape(really_hard_beast))
# Check that swim speed is enforced
swim_beast = Beast()
swim_beast.swim_speed = 15
self.assertFalse(low_druid.can_assume_shape(swim_beast))
self.assertTrue(mid_druid.can_assume_shape(swim_beast))
self.assertTrue(high_druid.can_assume_shape(swim_beast))
# Check that fly speed is enforced
fly_beast = Beast()
fly_beast.fly_speed = 15
self.assertFalse(low_druid.can_assume_shape(fly_beast))
self.assertFalse(mid_druid.can_assume_shape(fly_beast))
self.assertTrue(high_druid.can_assume_shape(fly_beast))
# Check that non-beasts are not allowed
not_beast = monsters.Monster()
not_beast.description = "monster"
self.assertFalse(low_druid.can_assume_shape(not_beast))