From 630ce519a1cc3ff3b73f966e6e1a9e56c5e27278 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Sun, 20 Mar 2022 11:37:30 -0500 Subject: [PATCH 1/9] Added instructions for running tests and building docs. --- CONTRIBUTING.rst | 64 +++++++++++++++++++++++++++++++++++++++++++ docs/contributing.rst | 2 ++ docs/index.rst | 1 + tests/test_dice.py | 1 + 4 files changed, 68 insertions(+) create mode 100644 docs/contributing.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ce85c1d..c26fc42 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -78,6 +78,70 @@ IP violations. How to Contribute ================= +Running Tests +------------- + +Dungeonsheets uses tests to verify the package works as +intended. Tests are found in the ``tests/`` folder. To run the tests +using *pytest*, run the following from a console: + +.. code:: bash + + pip install -r requirements.txt -r requirements-tests.txt + pytest + +Dungeonsheets defines tests using the *unittest* package in the +standard library. **For example**, to test a new function in the +``dungeonsheets/dice.py`` module, modify ``tests/test_dice.py``: + +.. code-block:: python + :caption: dice.py + + def roll(a, b=None): + """roll(20) means roll 1d20, roll(2, 6) means roll 2d6""" + if b is None: + return random.randint(1, a) + else: + return sum([random.randint(1, b) for _ in range(a)]) + +.. code-block:: python + :caption: test_dice.py + + from unittest import TestCase + from dungeonsheets.dice import roll + + class TestDice(TestCase): + def test_simple_rolling(self): + num_tests = 100 + # Do a bunch of rolls and make sure the numbers are within the requsted range + for _ in range(num_tests): + result = roll(6) + self.assertGreaterEqual(result, 1) + self.assertLessEqual(result, 6) + + def test_multi_rolling(self): + num_tests = 100 + for _ in range(num_tests): + result = roll(2, 4) # Roll 2d4 + self.assertGreaterEqual(result, 2) + self.assertLessEqual(result, 8) + + +Building Documentation +---------------------- + +Dungeonsheets uses sphinx to build documentations. All files are in +reStructuredText and are kept in the ``docs/`` folder. To build the +HTML files, run: + +.. code:: bash + + pip install -r requirements.txt -r requirements-tests.txt + cd docs/ + make html + +The results can be found in the ``_build/html/`` foler. + Submitting Bugs --------------- diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..89d0049 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,2 @@ +.. include:: ../CONTRIBUTING.rst + diff --git a/docs/index.rst b/docs/index.rst index 411c144..c622c03 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -14,6 +14,7 @@ Welcome to Dungeonsheets's documentation! gm_notes advanced_features examples + contributing Indices and tables ================== diff --git a/tests/test_dice.py b/tests/test_dice.py index 10910a8..99cbe07 100644 --- a/tests/test_dice.py +++ b/tests/test_dice.py @@ -26,6 +26,7 @@ class TestDice(TestCase): def test_simple_rolling(self): num_tests = 100 + # Do a bunch of rolls and make sure the numbers are within the requsted range for _ in range(num_tests): result = roll(6) self.assertGreaterEqual(result, 1) From 82aa9cdf246431b82e76cd7e355aac153cd35bdf Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Sun, 20 Mar 2022 11:58:06 -0500 Subject: [PATCH 2/9] Added instruction on how to run only a sub-set of the tests. --- CONTRIBUTING.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c26fc42..49f70e6 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -90,9 +90,12 @@ using *pytest*, run the following from a console: pip install -r requirements.txt -r requirements-tests.txt pytest -Dungeonsheets defines tests using the *unittest* package in the -standard library. **For example**, to test a new function in the -``dungeonsheets/dice.py`` module, modify ``tests/test_dice.py``: +You can also run a sub-set of the tests, which can be convenient for +development. For example, to run just the tests related to dice +mechanics, use ``pytest tests/test_dice.py``. Dungeonsheets defines +tests using the *unittest* package in the standard library. **For +example**, to test a new function in the ``dungeonsheets/dice.py`` +module, modify ``tests/test_dice.py``: .. code-block:: python :caption: dice.py From 89154bef3b98d7988099b9044ca647bbbc0b95b3 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Tue, 7 Jun 2022 22:16:09 -0500 Subject: [PATCH 3/9] Foundry JSON file now resolves magic item names. --- dungeonsheets/readers.py | 31 ++++++++++++++++++++++++++++--- tests/test_readers.py | 13 ++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/dungeonsheets/readers.py b/dungeonsheets/readers.py index 0d402e9..b266801 100644 --- a/dungeonsheets/readers.py +++ b/dungeonsheets/readers.py @@ -9,6 +9,8 @@ from typing import Union from pathlib import Path from dungeonsheets import exceptions +from dungeonsheets.magic_items import MagicItem +from dungeonsheets.content_registry import find_content log = logging.getLogger(__file__) @@ -431,6 +433,29 @@ class FoundryCharacterReader(JSONCharacterReader): item_name += f"({quantity})" yield item_name.lower() + def magic_items(self): + """Loads magic items. If not defined, try to figure out its + properties. + + """ + item_types = ["weapon", "armor", "equipment"] + items = [item for item in self.json_data()['items'] + if item['type'] in item_types] + from pprint import pprint + magic_items = [item for item in items if item['data']['rarity'] not in ["Common", ""]] + # Convert magic items into classes + def make_magic_item(data): + try: + item = find_content(data['name'], valid_classes=[MagicItem]) + except exceptions.ContentNotFound: + # Make a generic version based on the JSON attributes + item_name = data['name'].replace(' ', '') + item = type(item_name, (MagicItem,), {}) + return item + + magic_items = [make_magic_item(item) for item in magic_items] + return magic_items + def class_levels(self): for item in self.json_data()["items"]: if item["type"] == "class": @@ -544,6 +569,8 @@ class FoundryCharacterReader(JSONCharacterReader): char_props["equipment"] = ", ".join(self.equipment()) char_props["armor"] = self.armor() char_props["shield"] = self.shield() + # Magic items + char_props["magic_items"] = self.magic_items() # Personality, etc char_props["personality_traits"] = details["trait"].strip() char_props["flaws"] = details["flaw"].strip() @@ -555,12 +582,10 @@ class FoundryCharacterReader(JSONCharacterReader): # Some unused values warn_msg = ( "Importing the following traits from JSON is not yet supported: " - "magic_items, attacks_and_spellcasting, " - "infusions, wild_shapes." + "attacks_and_spellcasting, infusions, wild_shapes." ) warnings.warn(warn_msg) log.warning(warn_msg) - char_props["magic_items"] = () char_props["attacks_and_spellcasting"] = "" char_props["infusions"] = [] char_props["wild_shapes"] = [] diff --git a/tests/test_readers.py b/tests/test_readers.py index c0d2801..21d694d 100644 --- a/tests/test_readers.py +++ b/tests/test_readers.py @@ -226,7 +226,7 @@ class FoundryReaderTests(unittest.TestCase): gp=162, pp=2, weapons=["rapier"], - magic_items=(), + # magic_items=(), armor="padded armor", shield="shield", personality_traits="Loves a good lawyer joke.", @@ -284,3 +284,14 @@ class FoundryReaderTests(unittest.TestCase): this_result = list(this_result) self.assertEqual(this_result, val, key) + def test_load_homebrew_weapon(self): + """Check that the properties of a homebrew magic weapon get read + properly. + + """ + charfile = FOUNDRY_JSON_FILE + with warnings.catch_warnings(record=True): + result = read_sheet_file(charfile) + # Check that some magic items were set + self.assertGreater(len(result['magic_items']), 0, + "No magic items imported") From be661e888e74af8db7ee030af27543a32db46152 Mon Sep 17 00:00:00 2001 From: Christopher Johnstone Date: Fri, 24 Jun 2022 13:00:26 -0400 Subject: [PATCH 4/9] Created initial GH-actions version of test runner. --- .github/workflows/python-ci.yml | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/python-ci.yml diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml new file mode 100644 index 0000000..126aebc --- /dev/null +++ b/.github/workflows/python-ci.yml @@ -0,0 +1,45 @@ +name: Python package + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] + + steps: + - uses: actions/checkout@v2 + - name: Set up required system dependencies + run: apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4.0.0 + with: + python-version: ${{ matrix.python-version }} + cache: pip + - name: Install dependencies and do a local pip install + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-tests.txt + pip install -e . + - name: Install DnD LaTeX styles + run: | + echo "$(kpsewhich -var-value TEXMFHOME)/tex/latex/" + mkdir -p "$(kpsewhich -var-value TEXMFHOME)/tex/latex/" + git clone https://github.com/rpgtex/DND-5e-LaTeX-Template.git "$(kpsewhich -var-value TEXMFHOME)/tex/latex/dnd" + - name: Run tests + run: | + pytest --cov=dungeonsheets tests/ + flake8 dungeonsheets/ --exit-zero + cd examples/ + makesheets --debug + makesheets --debug --fancy + makesheets --debug --output-format=epub + continue-on-error: true From 725fc32ce957ebc024bdd723ca4cff01b0687ddc Mon Sep 17 00:00:00 2001 From: Christopher Johnstone Date: Fri, 24 Jun 2022 13:02:17 -0400 Subject: [PATCH 5/9] Allow apt-get to use sudo --- .github/workflows/python-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 126aebc..ec36668 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up required system dependencies - run: apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra + run: sudo apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4.0.0 with: From 7b492771738a80b7de790acc64255ee82d62f20a Mon Sep 17 00:00:00 2001 From: Christopher Johnstone Date: Fri, 24 Jun 2022 13:22:26 -0400 Subject: [PATCH 6/9] Separate flake from testrunning --- .github/workflows/python-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index ec36668..0e987c2 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -34,10 +34,11 @@ jobs: echo "$(kpsewhich -var-value TEXMFHOME)/tex/latex/" mkdir -p "$(kpsewhich -var-value TEXMFHOME)/tex/latex/" git clone https://github.com/rpgtex/DND-5e-LaTeX-Template.git "$(kpsewhich -var-value TEXMFHOME)/tex/latex/dnd" + - name: Run flake + run: flake8 dungeonsheets/ --exit-zero - name: Run tests run: | pytest --cov=dungeonsheets tests/ - flake8 dungeonsheets/ --exit-zero cd examples/ makesheets --debug makesheets --debug --fancy From 7b6df481c75c640c49287d46f524f9ba700a3265 Mon Sep 17 00:00:00 2001 From: Christopher Johnstone Date: Fri, 24 Jun 2022 20:28:20 -0400 Subject: [PATCH 7/9] Rearrange final test run to fail GHA run if tests fail --- .github/workflows/python-ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 0e987c2..7d3faa7 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -37,10 +37,10 @@ jobs: - name: Run flake run: flake8 dungeonsheets/ --exit-zero - name: Run tests - run: | + run: > + cd examples/; + makesheets --debug; + makesheets --debug --fancy; + makesheets --debug --output-format=epub; + cd ../; pytest --cov=dungeonsheets tests/ - cd examples/ - makesheets --debug - makesheets --debug --fancy - makesheets --debug --output-format=epub - continue-on-error: true From 002d38990dea913c253f1fc7129f4404d3b2660f Mon Sep 17 00:00:00 2001 From: Christopher Johnstone Date: Fri, 24 Jun 2022 20:08:43 -0400 Subject: [PATCH 8/9] Add a test for racial weapon proficencies. Note: should fail due to misspelling. --- tests/test_character.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_character.py b/tests/test_character.py index 77e4a15..f07b8d3 100644 --- a/tests/test_character.py +++ b/tests/test_character.py @@ -9,7 +9,7 @@ from dungeonsheets.character import ( Wizard, Druid, ) -from dungeonsheets.weapons import Weapon, Shortsword +from dungeonsheets.weapons import Weapon, Shortsword, Battleaxe from dungeonsheets.magic_items import MagicItem from dungeonsheets.armor import Armor, LeatherArmor, Shield @@ -149,6 +149,11 @@ class TestCharacter(TestCase): char.weapon_proficiencies = tuple() char.race = race.HighElf() self.assertTrue(char.is_proficient(sword)) + + def test_racial_is_proficient(self): + char = Character(classes=["Wizard"], race="Mountain Dwarf") + battleaxe = Battleaxe() + self.assertTrue(char.is_proficient(battleaxe)) def test_proficiencies_text(self): char = Character() From a506bdd3047c5b53f0110976b37d362d2c6bbd48 Mon Sep 17 00:00:00 2001 From: Christopher Johnstone Date: Fri, 24 Jun 2022 20:19:30 -0400 Subject: [PATCH 9/9] Fixed misspellings of "proficiencies" --- dungeonsheets/race.py | 8 ++++---- dungeonsheets/readers.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dungeonsheets/race.py b/dungeonsheets/race.py index 94a944b..f8799bb 100644 --- a/dungeonsheets/race.py +++ b/dungeonsheets/race.py @@ -15,7 +15,7 @@ class Race: owner = None languages = ("Common",) proficiencies_text = tuple() - weapon_proficiences = tuple() + weapon_proficiencies = tuple() skill_proficiencies = () skill_choices = () num_skill_choices = 0 @@ -61,7 +61,7 @@ class _Dwarf(Race): languages = ("Common", "Dwarvish") constitution_bonus = 2 proficiencies_text = ("battleaxes", "handaxes", "throwing hammers", "warhammers") - weapon_proficiences = ( + weapon_proficiencies = ( weapons.Battleaxe, weapons.Handaxe, weapons.ThrowingHammer, @@ -388,7 +388,7 @@ class Tabaxi(Race): speed = "30 (20 climb)" languages = ("Common", "[Choose One]") weapon_proficiencies = (weapons.Claws,) - proficiences_text = ("Claws",) + proficiencies_text = ("Claws",) skill_proficiencies = ("perception", "stealth") features = ( feats.Darkvision, @@ -427,7 +427,7 @@ class Aarakocra(Race): wisdom_bonus = 1 languages = ("Common", "Aarakocra", "Auran") weapon_proficiencies = (weapons.Talons,) - proficiences_text = ("Talons",) + proficiencies_text = ("Talons",) def __init__(self, owner=None): super().__init__(owner=owner) diff --git a/dungeonsheets/readers.py b/dungeonsheets/readers.py index b266801..bcc2a88 100644 --- a/dungeonsheets/readers.py +++ b/dungeonsheets/readers.py @@ -500,14 +500,14 @@ class FoundryCharacterReader(JSONCharacterReader): "cha": "charisma", } abilities = self.json_data()["data"]["abilities"] - save_proficiences = [] + save_proficiencies = [] for abbr, attr in attribute_names.items(): char_props[attr] = self.as_int(abilities[abbr]["value"]) # Check proficiency is_proficient = bool(abilities[abbr]["proficient"]) if is_proficient: - save_proficiences.append(attr) - char_props["saving_throw_proficiencies"] = save_proficiences + save_proficiencies.append(attr) + char_props["saving_throw_proficiencies"] = save_proficiencies # Skill proficiencies skill_names = [ "acrobatics",