mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-05-18 20:23:27 +02:00
Ran flake8 and black against tests.
This commit is contained in:
+80
-57
@@ -1,27 +1,32 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from unittest import TestCase
|
||||
from pathlib import Path
|
||||
import warnings
|
||||
|
||||
from dungeonsheets import race, monsters, exceptions, spells, infusions
|
||||
from dungeonsheets.character import Character, Wizard, Druid, read_character_file, _resolve_mechanic
|
||||
from dungeonsheets.character import (
|
||||
Character,
|
||||
Wizard,
|
||||
Druid,
|
||||
_resolve_mechanic,
|
||||
)
|
||||
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")
|
||||
|
||||
self.assertIsInstance(char, Character)
|
||||
|
||||
def test_hit_dice(self):
|
||||
# Test the getter
|
||||
char = Character()
|
||||
char.level = 2
|
||||
char.hit_dice_faces = 10
|
||||
self.assertEqual(char.hit_dice, '2d10')
|
||||
self.assertEqual(char.hit_dice, "2d10")
|
||||
|
||||
def test_max_hp(self):
|
||||
char = Wizard(level=3, constitution=12)
|
||||
@@ -29,37 +34,41 @@ class TestCharacter(TestCase):
|
||||
|
||||
def test_set_attrs(self):
|
||||
char = Character()
|
||||
char.set_attrs(name='Inara')
|
||||
self.assertEqual(char.name, 'Inara')
|
||||
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'])
|
||||
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')
|
||||
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')
|
||||
char.set_attrs(race="high elf")
|
||||
self.assertIsInstance(char.race, race.HighElf)
|
||||
# Check inspiration works
|
||||
char.set_attrs(inspiration=True)
|
||||
self.assertTrue(char.inspiration)
|
||||
char.set_attrs(inspiration=False)
|
||||
self.assertFalse(char.inspiration)
|
||||
|
||||
|
||||
def test_homebrew_spells(self):
|
||||
char = Character()
|
||||
|
||||
class MySpell(spells.Spell):
|
||||
name="my spell!"
|
||||
name = "my spell!"
|
||||
|
||||
char.set_attrs(spells=(MySpell,))
|
||||
self.assertIsInstance(char.spells[0], spells.Spell)
|
||||
self.assertEqual(char.spells[0].name, "my spell!")
|
||||
|
||||
def test_homebrew_infusions(self):
|
||||
char = Character(classes="artificer")
|
||||
|
||||
class MyInfusion(infusions.Infusion):
|
||||
name="my infusion!"
|
||||
name = "my infusion!"
|
||||
|
||||
# Pass an already created infusion class
|
||||
char.set_attrs(infusions=(MyInfusion,))
|
||||
self.assertIsInstance(char.infusions[0], infusions.Infusion)
|
||||
@@ -69,54 +78,58 @@ class TestCharacter(TestCase):
|
||||
char.set_attrs(infusions=("spam_infusion",))
|
||||
self.assertIsInstance(char.infusions[0], infusions.Infusion)
|
||||
self.assertEqual(char.infusions[0].name, "Spam Infusion")
|
||||
|
||||
|
||||
def test_resolve_mechanic(self):
|
||||
# Test a well defined mechanic
|
||||
NewSpell = _resolve_mechanic("mage_hand", spells, None)
|
||||
self.assertTrue(issubclass(NewSpell, spells.Spell))
|
||||
|
||||
# Test an unknown mechanic
|
||||
def new_spell(**params):
|
||||
return spells.Spell
|
||||
|
||||
NewSpell = _resolve_mechanic("hocus_pocus", spells, spells.Spell)
|
||||
self.assertTrue(issubclass(NewSpell, spells.Spell))
|
||||
|
||||
# Test direct resolution of a proper subclass
|
||||
class MySpell(spells.Spell):
|
||||
pass
|
||||
|
||||
NewSpell = _resolve_mechanic(MySpell, spells, spells.Spell)
|
||||
|
||||
|
||||
def test_wield_weapon(self):
|
||||
char = Character()
|
||||
char.strength = 14
|
||||
char.weapon_proficiencies = [Shortsword]
|
||||
# Add a weapon
|
||||
char.wield_weapon('shortsword')
|
||||
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
|
||||
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')
|
||||
char.wield_weapon("shortsword")
|
||||
sword = char.weapons[0]
|
||||
self.assertEqual(sword.attack_modifier, 5) # dex + prof
|
||||
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')
|
||||
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>')
|
||||
|
||||
self.assertEqual(str(char), "Inara")
|
||||
self.assertEqual(repr(char), "<Wizard: Inara>")
|
||||
|
||||
def test_is_proficient(self):
|
||||
char = Character(classes=['Wizard'])
|
||||
char = Character(classes=["Wizard"])
|
||||
char.weapon_proficiencies
|
||||
sword = Shortsword()
|
||||
# Check for not-proficient weapon
|
||||
@@ -128,24 +141,32 @@ class TestCharacter(TestCase):
|
||||
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())
|
||||
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())
|
||||
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")
|
||||
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
|
||||
@@ -168,7 +189,7 @@ class TestCharacter(TestCase):
|
||||
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
|
||||
@@ -181,10 +202,10 @@ class TestCharacter(TestCase):
|
||||
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')
|
||||
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)
|
||||
@@ -194,25 +215,25 @@ class TestCharacter(TestCase):
|
||||
# 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')
|
||||
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')
|
||||
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')
|
||||
self.assertEqual(char.speed, "30")
|
||||
|
||||
|
||||
class DruidTestCase(TestCase):
|
||||
@@ -221,44 +242,46 @@ class DruidTestCase(TestCase):
|
||||
be ignored."""
|
||||
char = Druid()
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', message="Druids cannot learn spells")
|
||||
char.set_attrs(spells=['invisibility'],
|
||||
spells_prepared=['druidcraft'])
|
||||
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']
|
||||
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']
|
||||
char.wild_shapes = ["Wolf", "Hyperion Loader"]
|
||||
# Check what happens if a valid monster class is directly added
|
||||
char.wild_shapes = [monsters.Wolf(), ]
|
||||
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']
|
||||
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')
|
||||
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')
|
||||
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'
|
||||
description = "beast"
|
||||
|
||||
new_druid = Druid(level=1)
|
||||
low_druid = Druid(level=2)
|
||||
mid_druid = Druid(level=4)
|
||||
@@ -270,7 +293,7 @@ class DruidTestCase(TestCase):
|
||||
self.assertTrue(low_druid.can_assume_shape(beast))
|
||||
# Check that challenge rating is checked
|
||||
hard_beast = Beast()
|
||||
hard_beast.challenge_rating = 1/2
|
||||
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))
|
||||
|
||||
+4
-4
@@ -3,16 +3,16 @@ from unittest import TestCase
|
||||
from dungeonsheets.exceptions import DiceError
|
||||
from dungeonsheets import dice
|
||||
|
||||
class TestDice(TestCase):
|
||||
|
||||
class TestDice(TestCase):
|
||||
def test_read_dice_str(self):
|
||||
out = dice.read_dice_str('1d6')
|
||||
out = dice.read_dice_str("1d6")
|
||||
self.assertEqual(out.faces, 6)
|
||||
self.assertEqual(out.num, 1)
|
||||
# Multiple digits
|
||||
out = dice.read_dice_str('15d10')
|
||||
out = dice.read_dice_str("15d10")
|
||||
self.assertEqual(out.faces, 10)
|
||||
self.assertEqual(out.num, 15)
|
||||
# Check a bad value
|
||||
with self.assertRaises(DiceError):
|
||||
dice.read_dice_str('Ed15')
|
||||
dice.read_dice_str("Ed15")
|
||||
|
||||
+11
-6
@@ -8,21 +8,26 @@ from dungeonsheets.features import create_feature, Feature, all_features
|
||||
|
||||
class TestFeatures(TestCase):
|
||||
"""Tests for features and feature-related activities."""
|
||||
|
||||
def test_all_features(self):
|
||||
# Make sure only features are returned
|
||||
for ThisFeature in all_features():
|
||||
self.assertTrue(isinstance(ThisFeature, type),
|
||||
f"``all_features`` returned {ThisFeature} (not a class)")
|
||||
self.assertTrue(issubclass(ThisFeature, Feature),
|
||||
f"``all_features`` returned {ThisFeature} (not a feature)")
|
||||
self.assertTrue(
|
||||
isinstance(ThisFeature, type),
|
||||
f"``all_features`` returned {ThisFeature} (not a class)",
|
||||
)
|
||||
self.assertTrue(
|
||||
issubclass(ThisFeature, Feature),
|
||||
f"``all_features`` returned {ThisFeature} (not a feature)",
|
||||
)
|
||||
# Pick a couple of known features to spot-check for
|
||||
all_the_features = list(all_features())
|
||||
self.assertIn(features.FalseIdentity, all_the_features)
|
||||
self.assertIn(features.DivineSmite, all_the_features)
|
||||
|
||||
|
||||
def test_create_feature(self):
|
||||
NewFeature = create_feature(name="Hello world")
|
||||
self.assertTrue(issubclass(NewFeature, Feature))
|
||||
self.assertEqual(NewFeature.name, 'Hello world')
|
||||
self.assertEqual(NewFeature.name, "Hello world")
|
||||
feature = NewFeature()
|
||||
print(feature, feature.__class__, type(feature))
|
||||
|
||||
+43
-39
@@ -1,53 +1,56 @@
|
||||
import unittest
|
||||
|
||||
from dungeonsheets import make_sheets, character, spells, features, latex
|
||||
from dungeonsheets import spells, features, latex
|
||||
|
||||
|
||||
class MarkdownTestCase(unittest.TestCase):
|
||||
"""Check that conversion of markdown formats to LaTeX code works
|
||||
correctly."""
|
||||
|
||||
|
||||
def test_rst_bold(self):
|
||||
text = latex.rst_to_latex('**hello**')
|
||||
self.assertEqual(text, '\n\\textbf{hello}\n')
|
||||
|
||||
text = latex.rst_to_latex("**hello**")
|
||||
self.assertEqual(text, "\n\\textbf{hello}\n")
|
||||
|
||||
def test_hit_dice(self):
|
||||
text = latex.rst_to_latex('1d6+3')
|
||||
self.assertEqual(text.strip("\n"), '\\texttt{1d6+3}')
|
||||
|
||||
text = latex.rst_to_latex("1d6+3")
|
||||
self.assertEqual(text.strip("\n"), "\\texttt{1d6+3}")
|
||||
|
||||
def test_no_text(self):
|
||||
text = latex.rst_to_latex(None)
|
||||
self.assertEqual(text, '')
|
||||
|
||||
self.assertEqual(text, "")
|
||||
|
||||
def test_verbatim(self):
|
||||
text = latex.rst_to_latex('``hello, world``')
|
||||
self.assertIn(r'\texttt{hello, world}', text)
|
||||
text = latex.rst_to_latex("``hello, world``")
|
||||
self.assertIn(r"\texttt{hello, world}", text)
|
||||
|
||||
def test_literal_backslash(self):
|
||||
text = latex.rst_to_latex(r'\\')
|
||||
self.assertEqual(r'\textbackslash{}', text.strip("\n"))
|
||||
text = latex.rst_to_latex(r"\\")
|
||||
self.assertEqual(r"\textbackslash{}", text.strip("\n"))
|
||||
|
||||
@unittest.skip("Headings are all screwed up because it treats them as the document title")
|
||||
@unittest.skip(
|
||||
"Headings are all screwed up because it treats them as the document title"
|
||||
)
|
||||
def test_headings(self):
|
||||
# Simple heading by itself
|
||||
text = latex.rst_to_latex('Hello, world\n------------\n\nGoodbye, world')
|
||||
self.assertEqual('\\section*{Hello, world}\n', text)
|
||||
text = latex.rst_to_latex("Hello, world\n------------\n\nGoodbye, world")
|
||||
self.assertEqual("\\section*{Hello, world}\n", text)
|
||||
# Simple heading with leading whitespace
|
||||
text = latex.rst_to_latex(' Hello, world\n ============\n')
|
||||
self.assertEqual('\\section*{Hello, world}\n', text)
|
||||
text = latex.rst_to_latex(" Hello, world\n ============\n")
|
||||
self.assertEqual("\\section*{Hello, world}\n", text)
|
||||
# Heading with text after it
|
||||
text = latex.rst_to_latex('Hello, world\n============\n\nThis is some text')
|
||||
self.assertEqual('\\section*{Hello, world}\n\nThis is some text', text)
|
||||
text = latex.rst_to_latex("Hello, world\n============\n\nThis is some text")
|
||||
self.assertEqual("\\section*{Hello, world}\n\nThis is some text", text)
|
||||
# Heading with text before it
|
||||
text = latex.rst_to_latex('This is a paragraph\n\nHello, world\n============\n')
|
||||
self.assertEqual('This is a paragraph\n\n\\section*{Hello, world}\n', text)
|
||||
text = latex.rst_to_latex("This is a paragraph\n\nHello, world\n============\n")
|
||||
self.assertEqual("This is a paragraph\n\n\\section*{Hello, world}\n", text)
|
||||
# Check that levels of headings are parsed appropriately
|
||||
text = latex.rst_to_latex('Hello, world\n^^^^^^^^^^^^\n')
|
||||
self.assertEqual('\\subsubsection*{Hello, world}\n', text)
|
||||
text = latex.rst_to_latex('Hello, world\n^^^^^^^^^^^^\n', top_heading_level=3)
|
||||
self.assertEqual('\\subparagraph*{Hello, world}\n', text)
|
||||
text = latex.rst_to_latex("Hello, world\n^^^^^^^^^^^^\n")
|
||||
self.assertEqual("\\subsubsection*{Hello, world}\n", text)
|
||||
text = latex.rst_to_latex("Hello, world\n^^^^^^^^^^^^\n", top_heading_level=3)
|
||||
self.assertEqual("\\subparagraph*{Hello, world}\n", text)
|
||||
# This is a bad heading missing with all the underline on one line
|
||||
text = latex.rst_to_latex('Hello, world^^^^^^^^^^^^\n')
|
||||
self.assertEqual('Hello, world\\^\\^\\^\\^\\^\\^\\^\\^\\^\\^\\^\\^\n', text)
|
||||
text = latex.rst_to_latex("Hello, world^^^^^^^^^^^^\n")
|
||||
self.assertEqual("Hello, world\\^\\^\\^\\^\\^\\^\\^\\^\\^\\^\\^\\^\n", text)
|
||||
|
||||
def test_bullet_list(self):
|
||||
tex = latex.rst_to_latex("\n- Hello\n- World\n\n")
|
||||
@@ -67,14 +70,14 @@ class MarkdownTestCase(unittest.TestCase):
|
||||
"""
|
||||
tex = latex.rst_to_latex(real_list)
|
||||
self.assertIn("\\begin{itemize}", tex)
|
||||
|
||||
|
||||
def test_multiline_bullet_list(self):
|
||||
md_list = """
|
||||
- Secondhand (you have heard
|
||||
of the target) - +5
|
||||
- Firsthand (you have met
|
||||
- Firsthand (you have met
|
||||
the target) - +0
|
||||
- Familiar (you know the target
|
||||
- Familiar (you know the target
|
||||
well) - -5
|
||||
|
||||
"""
|
||||
@@ -100,16 +103,17 @@ class MarkdownTestCase(unittest.TestCase):
|
||||
self.assertNotIn("endfoot", tex)
|
||||
self.assertNotIn("endhead", tex)
|
||||
self.assertNotIn("endfirsthead", tex)
|
||||
|
||||
|
||||
def test_rst_all_spells(self):
|
||||
for spell in spells.all_spells():
|
||||
tex = latex.rst_to_latex(spell.__doc__)
|
||||
self.assertNotIn("DUadmonition", tex,
|
||||
f"spell {spell} is not valid reStructured text")
|
||||
|
||||
self.assertNotIn(
|
||||
"DUadmonition", tex, f"spell {spell} is not valid reStructured text"
|
||||
)
|
||||
|
||||
def test_rst_all_features(self):
|
||||
for feature in features.all_features():
|
||||
tex = latex.rst_to_latex(feature.__doc__)
|
||||
self.assertNotIn("DUadmonition", tex,
|
||||
f"feature {feature} is not valid reStructured text")
|
||||
|
||||
self.assertNotIn(
|
||||
"DUadmonition", tex, f"feature {feature} is not valid reStructured text"
|
||||
)
|
||||
|
||||
+13
-15
@@ -1,38 +1,36 @@
|
||||
import unittest
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
from dungeonsheets import make_sheets, character
|
||||
|
||||
EG_DIR = os.path.abspath(os.path.join(os.path.split(__file__)[0], '../examples/'))
|
||||
CHARFILE = os.path.join(EG_DIR, 'rogue1.py')
|
||||
EG_DIR = os.path.abspath(os.path.join(os.path.split(__file__)[0], "../examples/"))
|
||||
CHARFILE = os.path.join(EG_DIR, "rogue1.py")
|
||||
|
||||
|
||||
class CharacterFileTestCase(unittest.TestCase):
|
||||
example_dir = Path(__file__).parent.parent / "examples"
|
||||
# def test_(self):
|
||||
# print(self.example_dir)
|
||||
# subprocess.run(['makesheets', self.example_dir])
|
||||
|
||||
def test_load_character_file(self):
|
||||
charfile = CHARFILE
|
||||
result = make_sheets.load_character_file(charfile)
|
||||
self.assertEqual(result['strength'], 10)
|
||||
self.assertEqual(result["strength"], 10)
|
||||
|
||||
|
||||
class PdfOutputTeestCase(unittest.TestCase):
|
||||
basename = 'clara'
|
||||
|
||||
basename = "clara"
|
||||
|
||||
def tearDown(self):
|
||||
temp_files = [f'{self.basename}.pdf']
|
||||
temp_files = [f"{self.basename}.pdf"]
|
||||
for f in temp_files:
|
||||
if os.path.exists(f):
|
||||
os.remove(f)
|
||||
|
||||
|
||||
def test_file_created(self):
|
||||
# Check that a file is created once the function is run
|
||||
pdf_name = f'{self.basename}.pdf'
|
||||
pdf_name = f"{self.basename}.pdf"
|
||||
# self.assertFalse(os.path.exists(pdf_name), f'{pdf_name} already exists.')
|
||||
char = character.Character(name='Clara')
|
||||
char.saving_throw_proficiencies = ['strength']
|
||||
char = character.Character(name="Clara")
|
||||
char.saving_throw_proficiencies = ["strength"]
|
||||
make_sheets.create_character_pdf(character=char, basename=self.basename)
|
||||
self.assertTrue(os.path.exists(pdf_name), f'{pdf_name} not created.')
|
||||
self.assertTrue(os.path.exists(pdf_name), f"{pdf_name} not created.")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from dungeonsheets import monsters, exceptions
|
||||
from dungeonsheets import monsters
|
||||
|
||||
|
||||
class MonsterTestCase(TestCase):
|
||||
|
||||
+24
-28
@@ -1,38 +1,39 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from unittest import TestCase
|
||||
import warnings
|
||||
|
||||
from dungeonsheets import race, monsters, exceptions, spells
|
||||
from dungeonsheets.character import Character
|
||||
from dungeonsheets.weapons import Weapon, Shortsword
|
||||
from dungeonsheets.armor import Armor, LeatherArmor, Shield
|
||||
from dungeonsheets.weapons import Shortsword
|
||||
|
||||
|
||||
class TestMulticlass(TestCase):
|
||||
"""
|
||||
Tests for Multiclass character.
|
||||
"""
|
||||
|
||||
def test_constructor(self):
|
||||
char = Character(name='Multiclass',
|
||||
classes=['wizard', 'fighter'],
|
||||
levels=[5, 4])
|
||||
char = Character(
|
||||
name="Multiclass", classes=["wizard", "fighter"], levels=[5, 4]
|
||||
)
|
||||
self.assertIsInstance(char, Character)
|
||||
|
||||
def test_level(self):
|
||||
char = Character(name='Multiclass',
|
||||
classes=['wizard', 'fighter'],
|
||||
levels=[5, 4])
|
||||
char = Character(
|
||||
name="Multiclass", classes=["wizard", "fighter"], levels=[5, 4]
|
||||
)
|
||||
self.assertEqual(char.level, 9)
|
||||
|
||||
def test_spellcasting(self):
|
||||
char = Character(name='Multiclass',
|
||||
classes=['wizard', 'fighter'],
|
||||
levels=[5, 4])
|
||||
char = Character(
|
||||
name="Multiclass", classes=["wizard", "fighter"], levels=[5, 4]
|
||||
)
|
||||
self.assertEqual(len(char.spellcasting_classes), 1)
|
||||
char = Character(name='Multiclass',
|
||||
classes=['wizard', 'fighter'],
|
||||
subclasses=[None, 'Eldritch Knight'],
|
||||
levels=[5, 4])
|
||||
char = Character(
|
||||
name="Multiclass",
|
||||
classes=["wizard", "fighter"],
|
||||
subclasses=[None, "Eldritch Knight"],
|
||||
levels=[5, 4],
|
||||
)
|
||||
self.assertEqual(len(char.spellcasting_classes), 2)
|
||||
# equivalent spellcasting level: 6
|
||||
self.assertEqual(char.spell_slots(spell_level=1), 4)
|
||||
@@ -41,20 +42,15 @@ class TestMulticlass(TestCase):
|
||||
self.assertEqual(char.spell_slots(spell_level=4), 0)
|
||||
|
||||
def test_proficiencies(self):
|
||||
char1 = Character(name='Multiclass',
|
||||
classes=['wizard', 'fighter'],
|
||||
levels=[5, 4])
|
||||
for svt in ('intelligence', 'wisdom'):
|
||||
char1 = Character(
|
||||
name="Multiclass", classes=["wizard", "fighter"], levels=[5, 4]
|
||||
)
|
||||
for svt in ("intelligence", "wisdom"):
|
||||
self.assertIn(svt, char1.saving_throw_proficiencies)
|
||||
char2 = Character(name='Multiclass',
|
||||
classes=['wizard', 'rogue'],
|
||||
levels=[5, 4])
|
||||
char3 = Character(name='Multiclass',
|
||||
classes=['rogue', 'wizard'],
|
||||
levels=[4, 5])
|
||||
char2 = Character(name="Multiclass", classes=["wizard", "rogue"], levels=[5, 4])
|
||||
char3 = Character(name="Multiclass", classes=["rogue", "wizard"], levels=[4, 5])
|
||||
sword = Shortsword()
|
||||
self.assertTrue(char1.is_proficient(sword))
|
||||
# multiclassing into Rogue doesn't give simple weapon proficiency
|
||||
self.assertFalse(char2.is_proficient(sword))
|
||||
self.assertTrue(char3.is_proficient(sword))
|
||||
|
||||
|
||||
+57
-22
@@ -6,21 +6,22 @@ import types
|
||||
from dungeonsheets.readers import read_character_file
|
||||
|
||||
EG_DIR = (Path(__file__).parent.parent / "examples").resolve()
|
||||
CHAR_PYTHON_FILE = EG_DIR / 'rogue1.py'
|
||||
CHAR_JSON_FILE = EG_DIR / 'barbarian3.json'
|
||||
SPELLCASTER_JSON_FILE = EG_DIR / 'artificer2.json'
|
||||
CHAR_PYTHON_FILE = EG_DIR / "rogue1.py"
|
||||
CHAR_JSON_FILE = EG_DIR / "barbarian3.json"
|
||||
SPELLCASTER_JSON_FILE = EG_DIR / "artificer2.json"
|
||||
|
||||
|
||||
class PythonReaderTests(unittest.TestCase):
|
||||
def test_load_python_file(self):
|
||||
charfile = CHAR_PYTHON_FILE
|
||||
result = read_character_file(charfile)
|
||||
self.assertEqual(result['strength'], 10)
|
||||
self.assertEqual(result["strength"], 10)
|
||||
|
||||
|
||||
class JSONReaderTests(unittest.TestCase):
|
||||
class JSONReaderTests(unittest.TestCase):
|
||||
def test_load_json_file(self):
|
||||
charfile = CHAR_JSON_FILE
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with warnings.catch_warnings(record=True):
|
||||
result = read_character_file(charfile)
|
||||
expected_data = dict(
|
||||
name="Ulthar Jenkins",
|
||||
@@ -35,9 +36,22 @@ class JSONReaderTests(unittest.TestCase):
|
||||
constitution=19,
|
||||
intelligence=8,
|
||||
hp_max=32,
|
||||
skill_proficiencies=["athletics", "survival",],
|
||||
weapon_proficiencies=["simple weapons", "martial weapons", "battleaxe", "handaxe", "light hammer", "warhammer", "unarmed strike",],
|
||||
_proficiencies_text=["Brewer's Supplies",],
|
||||
skill_proficiencies=[
|
||||
"athletics",
|
||||
"survival",
|
||||
],
|
||||
weapon_proficiencies=[
|
||||
"simple weapons",
|
||||
"martial weapons",
|
||||
"battleaxe",
|
||||
"handaxe",
|
||||
"light hammer",
|
||||
"warhammer",
|
||||
"unarmed strike",
|
||||
],
|
||||
_proficiencies_text=[
|
||||
"Brewer's Supplies",
|
||||
],
|
||||
languages="common, dwarvish",
|
||||
cp=26,
|
||||
sp=55,
|
||||
@@ -48,13 +62,17 @@ class JSONReaderTests(unittest.TestCase):
|
||||
magic_items=(),
|
||||
armor="",
|
||||
shield="",
|
||||
personality_traits="Can easily dismember a body\n\nKnow fight battle tactics",
|
||||
personality_traits=(
|
||||
"Can easily dismember a body\n\nKnow fight battle tactics"
|
||||
),
|
||||
ideals="Vengence",
|
||||
bonds="friends and adventurers.",
|
||||
flaws="Bloodthirsty and wants to solve every problem by murder",
|
||||
equipment=("warhammer, handaxe, explorer's pack, javelin (4), backpack, "
|
||||
"bedroll, mess kit, tinderbox, torch (10), rations (10), "
|
||||
"waterskin, hempen rope"),
|
||||
equipment=(
|
||||
"warhammer, handaxe, explorer's pack, javelin (4), backpack, "
|
||||
"bedroll, mess kit, tinderbox, torch (10), rations (10), "
|
||||
"waterskin, hempen rope"
|
||||
),
|
||||
attacks_and_spellcasting="",
|
||||
spells_prepared=[],
|
||||
spells=[],
|
||||
@@ -65,19 +83,36 @@ class JSONReaderTests(unittest.TestCase):
|
||||
if isinstance(this_result, types.GeneratorType):
|
||||
this_result = list(this_result)
|
||||
self.assertEqual(this_result, val, key)
|
||||
|
||||
|
||||
def test_load_json_spells(self):
|
||||
charfile = SPELLCASTER_JSON_FILE
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
with warnings.catch_warnings(record=True):
|
||||
result = read_character_file(charfile)
|
||||
expected_data = dict(
|
||||
spells_prepared=["cure wounds",],
|
||||
spells=["spare the dying", "fire bolt", "absorb elements",
|
||||
"alarm", "catapult", "cure wounds", "detect magic",
|
||||
"disguise self", "expeditious retreat", "faerie fire",
|
||||
"false life", "feather fall", "grease", "identify",
|
||||
"jump", "longstrider", "purify food and drink",
|
||||
"sanctuary", "snare",],
|
||||
spells_prepared=[
|
||||
"cure wounds",
|
||||
],
|
||||
spells=[
|
||||
"spare the dying",
|
||||
"fire bolt",
|
||||
"absorb elements",
|
||||
"alarm",
|
||||
"catapult",
|
||||
"cure wounds",
|
||||
"detect magic",
|
||||
"disguise self",
|
||||
"expeditious retreat",
|
||||
"faerie fire",
|
||||
"false life",
|
||||
"feather fall",
|
||||
"grease",
|
||||
"identify",
|
||||
"jump",
|
||||
"longstrider",
|
||||
"purify food and drink",
|
||||
"sanctuary",
|
||||
"snare",
|
||||
],
|
||||
)
|
||||
for key, val in expected_data.items():
|
||||
this_result = result[key]
|
||||
|
||||
+15
-10
@@ -8,32 +8,37 @@ from dungeonsheets.spells import create_spell, Spell, all_spells
|
||||
|
||||
class TestSpells(TestCase):
|
||||
"""Tests for spells and spell-related activities."""
|
||||
|
||||
def test_all_spells(self):
|
||||
# Make sure only spells are returned
|
||||
for ThisSpell in all_spells():
|
||||
self.assertTrue(isinstance(ThisSpell, type),
|
||||
f"``all_spells`` returned {ThisSpell} (not a class)")
|
||||
self.assertTrue(issubclass(ThisSpell, Spell),
|
||||
f"``all_spells`` returned {ThisSpell} (not a spell)")
|
||||
self.assertTrue(
|
||||
isinstance(ThisSpell, type),
|
||||
f"``all_spells`` returned {ThisSpell} (not a class)",
|
||||
)
|
||||
self.assertTrue(
|
||||
issubclass(ThisSpell, Spell),
|
||||
f"``all_spells`` returned {ThisSpell} (not a spell)",
|
||||
)
|
||||
# Pick a couple of known spells to spot-check for
|
||||
all_the_spells = list(all_spells())
|
||||
self.assertIn(spells.MagicMissile, all_the_spells)
|
||||
self.assertIn(spells.Thunderwave, all_the_spells)
|
||||
|
||||
|
||||
def test_create_spell(self):
|
||||
NewSpell = create_spell(name="Hello world")
|
||||
self.assertTrue(issubclass(NewSpell, Spell))
|
||||
self.assertEqual(NewSpell.name, 'Hello world')
|
||||
self.assertEqual(NewSpell.name, "Hello world")
|
||||
spell = NewSpell()
|
||||
print(spell, spell.__class__, type(spell))
|
||||
|
||||
|
||||
def test_spell_str(self):
|
||||
spell = Spell()
|
||||
spell.name = "My spell"
|
||||
self.assertEqual(str(spell), 'My spell')
|
||||
self.assertEqual(str(spell), "My spell")
|
||||
# Try with a ritual
|
||||
spell.ritual = True
|
||||
self.assertEqual(str(spell), 'My spell (R)')
|
||||
self.assertEqual(str(spell), "My spell (R)")
|
||||
# Try with a ritual and a concentration
|
||||
spell.concentration = True
|
||||
self.assertEqual(str(spell), 'My spell (R, C)')
|
||||
self.assertEqual(str(spell), "My spell (R, C)")
|
||||
|
||||
+27
-22
@@ -2,27 +2,29 @@ from unittest import TestCase
|
||||
|
||||
from dungeonsheets import stats, character
|
||||
|
||||
class TestStats(TestCase):
|
||||
|
||||
class TestStats(TestCase):
|
||||
def test_mod_str(self):
|
||||
self.assertEqual(stats.mod_str(-3), '-3')
|
||||
self.assertEqual(stats.mod_str(0), '+0')
|
||||
self.assertEqual(stats.mod_str(2), '+2')
|
||||
|
||||
self.assertEqual(stats.mod_str(-3), "-3")
|
||||
self.assertEqual(stats.mod_str(0), "+0")
|
||||
self.assertEqual(stats.mod_str(2), "+2")
|
||||
|
||||
def test_saving_throw(self):
|
||||
# Try it with an ST proficiency
|
||||
class MyClass(character.Character):
|
||||
saving_throw_proficiencies = ['strength']
|
||||
saving_throw_proficiencies = ["strength"]
|
||||
proficiency_bonus = 2
|
||||
strength = stats.Ability(14)
|
||||
|
||||
my_class = MyClass()
|
||||
self.assertEqual(my_class.strength.saving_throw, 4)
|
||||
|
||||
|
||||
def test_modifier(self):
|
||||
class MyCharacter(character.Character):
|
||||
saving_throw_proficiencies = ['strength']
|
||||
saving_throw_proficiencies = ["strength"]
|
||||
proficiency_bonus = 2
|
||||
strength = stats.Ability(14)
|
||||
|
||||
my_char = MyCharacter()
|
||||
ranges = [
|
||||
((1,), -5),
|
||||
@@ -47,46 +49,49 @@ class TestStats(TestCase):
|
||||
for value in range_:
|
||||
my_char.strength = value
|
||||
stat = my_char.strength
|
||||
msg = f"Stat {value} doesn't produce modifier {target} ({stat.modifier})"
|
||||
msg = (
|
||||
f"Stat {value} doesn't produce modifier {target} ({stat.modifier})"
|
||||
)
|
||||
self.assertEqual(stat.modifier, target, msg)
|
||||
|
||||
|
||||
def test_setter(self):
|
||||
"""Verify that this class works as a data descriptor."""
|
||||
# Set up a dummy class
|
||||
class MyCharacter(character.Character):
|
||||
stat = stats.Ability()
|
||||
|
||||
char = MyCharacter()
|
||||
# Check that the stat works as expected once set
|
||||
char.stat = 15
|
||||
self.assertEqual(char.stat.value, 15)
|
||||
self.assertEqual(char.stat.modifier, 2)
|
||||
|
||||
|
||||
def test_skill(self):
|
||||
"""Test for a skill, that depends on another ability."""
|
||||
|
||||
class MyClass(character.Character):
|
||||
dexterity = stats.Ability(14)
|
||||
acrobatics = stats.Skill(ability='dexterity')
|
||||
acrobatics = stats.Skill(ability="dexterity")
|
||||
skill_proficiencies = []
|
||||
proficiency_bonus = 2
|
||||
|
||||
my_class = MyClass()
|
||||
self.assertEqual(my_class.acrobatics, 2)
|
||||
# Check for a proficiency
|
||||
my_class.skill_proficiencies = ['acrobatics']
|
||||
my_class.skill_proficiencies = ["acrobatics"]
|
||||
self.assertEqual(my_class.acrobatics, 4)
|
||||
|
||||
def test_findattr(self):
|
||||
"""Check if the function can find attributes."""
|
||||
class TestClass():
|
||||
|
||||
class TestClass:
|
||||
my_attr = 47
|
||||
YourAttr = 53
|
||||
|
||||
test_class = TestClass()
|
||||
# Direct access
|
||||
self.assertEqual(stats.findattr(test_class, 'my_attr'),
|
||||
test_class.my_attr)
|
||||
self.assertEqual(stats.findattr(test_class, 'YourAttr'),
|
||||
test_class.YourAttr)
|
||||
self.assertEqual(stats.findattr(test_class, "my_attr"), test_class.my_attr)
|
||||
self.assertEqual(stats.findattr(test_class, "YourAttr"), test_class.YourAttr)
|
||||
# Swapping spaces for capitalization
|
||||
self.assertEqual(stats.findattr(test_class, 'my attr'),
|
||||
test_class.my_attr)
|
||||
self.assertEqual(stats.findattr(test_class, 'your attr'),
|
||||
test_class.YourAttr)
|
||||
self.assertEqual(stats.findattr(test_class, "my attr"), test_class.my_attr)
|
||||
self.assertEqual(stats.findattr(test_class, "your attr"), test_class.YourAttr)
|
||||
|
||||
@@ -6,8 +6,8 @@ from dungeonsheets.weapons import Weapon
|
||||
class WeaponTestCase(unittest.TestCase):
|
||||
def test_weapon_damage(self):
|
||||
weapon = Weapon()
|
||||
weapon.base_damage = '1d6'
|
||||
self.assertEqual(weapon.damage, '1d6')
|
||||
weapon.base_damage = "1d6"
|
||||
self.assertEqual(weapon.damage, "1d6")
|
||||
# Now add some bonus damage
|
||||
weapon.damage_bonus = 2
|
||||
self.assertEqual(weapon.damage, '1d6+2')
|
||||
self.assertEqual(weapon.damage, "1d6+2")
|
||||
|
||||
Reference in New Issue
Block a user