Skip to content

Commit a2f9f59

Browse files
committed
Add tests
1 parent 8c6b3eb commit a2f9f59

File tree

10 files changed

+111
-10
lines changed

10 files changed

+111
-10
lines changed

.github/workflows/python-lib.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3+
4+
name: Python application
5+
6+
on: ['push', 'pull_request']
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
build:
13+
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
- name: Fetch test files
19+
uses: actions/checkout@v4
20+
with:
21+
repository: SiegeEngineers/dat-files
22+
ssh-key: ${{ secrets.DEPLOY_KEY }}
23+
path: tests/testdata/
24+
- name: List test files
25+
run: |
26+
find tests/testdata/ -iname '*.dat'
27+
- name: Set up Python 3.10
28+
uses: actions/setup-python@v3
29+
with:
30+
python-version: "3.11"
31+
- name: Install dependencies
32+
run: |
33+
python -m pip install --upgrade pip
34+
pip install -r requirements-dev.txt
35+
pip install -e .
36+
- name: Run test script
37+
run: |
38+
./test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
2+
tests/testdata/*
23
# Byte-compiled / optimized / DLL files
34
__pycache__/
45
*.py[cod]

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ This library can be used to read and write `empires2_x2_p1.dat` files for Age of
77

88
## Supported dat versions
99

10-
Currently, only the latest version used in Age of Empires II Definitive Edition is supported (`GV_LatestDE2`/`GV_C20`).
10+
Currently, only the recent versions used in Age of Empires II Definitive Edition are supported
11+
(`GV_C20` and above, corresponding to FileVersion 7.7 and above).
1112

1213

1314
## Installation
@@ -48,6 +49,19 @@ for civ in data.civs:
4849
data.save('path/to/modded/empires2_x2_p1.dat')
4950
```
5051

52+
## Running tests
53+
54+
**Before running tests, you need to add sample `empires2_x2_p1.dat` files into the `tests/testdata` subfolder.**
55+
56+
1. Create a virtual environment
57+
`python3 -m venv venv`
58+
2. Activate the virtual environment
59+
`source venv/bin/activate`
60+
3. Install the dev dependencies
61+
`pip install -r requirements-dev.txt`
62+
4. Run the test script
63+
`./test`
64+
5165

5266
## Authors
5367

pyproject.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ Issues = "https://github.com/SiegeEngineers/genieutils-py/issues"
2626

2727
[project.scripts]
2828
dat-to-json = "genieutils.scripts:dat_to_json"
29+
30+
[tool.ruff]
31+
line-length = 120
32+
33+
[tool.coverage.run]
34+
omit = ['tests/*']

requirements-dev.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pytest
2+
pytest-cov
3+
ruff
4+
mypy

src/genieutils/unit.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -829,19 +829,19 @@ def to_bytes(self, version: Version) -> bytes:
829829
building = b''
830830
if self.type != UnitType.AoeTrees:
831831
if self.type >= UnitType.Flag:
832-
speed = self.write_float(self.speed)
832+
speed = self.write_float(self.speed) if self.speed is not None else b''
833833
if self.type >= UnitType.DeadFish:
834-
dead_fish = self.write_class(self.dead_fish, version)
834+
dead_fish = self.write_class(self.dead_fish, version) if self.dead_fish is not None else b''
835835
if self.type >= UnitType.Bird:
836-
bird = self.write_class(self.bird, version)
836+
bird = self.write_class(self.bird, version) if self.bird is not None else b''
837837
if self.type >= UnitType.Combatant:
838-
type_50 = self.write_class(self.type_50, version)
838+
type_50 = self.write_class(self.type_50, version) if self.type_50 is not None else b''
839839
if self.type == UnitType.Projectile:
840-
projectile = self.write_class(self.projectile, version)
840+
projectile = self.write_class(self.projectile, version) if self.projectile is not None else b''
841841
if self.type >= UnitType.Creatable:
842-
creatable = self.write_class(self.creatable, version)
842+
creatable = self.write_class(self.creatable, version) if self.creatable is not None else b''
843843
if self.type == UnitType.Building:
844-
building = self.write_class(self.building, version)
844+
building = self.write_class(self.building, version) if self.building is not None else b''
845845
return b''.join([
846846
self.write_int_8(self.type),
847847
self.write_int_16(self.id),

src/genieutils/unitheaders.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ def from_bytes(cls, content: ByteHandler) -> 'UnitHeaders':
2626
def to_bytes(self, version: Version) -> bytes:
2727
return b''.join([
2828
self.write_int_8(self.exists),
29-
self.write_int_16(len(self.task_list)) if self.exists else b'',
30-
self.write_class_array(self.task_list, version) if self.exists else b'',
29+
self.write_int_16(len(self.task_list)) if self.exists and self.task_list is not None else b'',
30+
self.write_class_array(self.task_list, version) if self.exists and self.task_list is not None else b'',
3131
])

test

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#! /bin/bash
2+
3+
python -m pytest --cov=src --cov-report=html --cov-config=pyproject.toml ./
4+
ruff check src
5+
python -m mypy ./src

tests/test_dat_compatibility.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import zlib
2+
from pathlib import Path
3+
4+
import pytest
5+
6+
from genieutils.common import ByteHandler
7+
from genieutils.datfile import DatFile
8+
9+
TESTDATA_DIR = Path(__file__).with_name('testdata')
10+
11+
12+
class TestDatCompatibility:
13+
def test_there_are_actually_dat_files_for_testing(self):
14+
assert len(list(TESTDATA_DIR.rglob('*.dat')))
15+
16+
@pytest.mark.parametrize('datfile', sorted(TESTDATA_DIR.rglob('*.dat'), reverse=True))
17+
def test_compatibility_with_dat_files(self, datfile: Path):
18+
version, data = self.get_version(datfile)
19+
print(datfile)
20+
print(version)
21+
if version in ('VER 7.8', 'VER 7.7'):
22+
byte_handler = ByteHandler(memoryview(data))
23+
content = DatFile.from_bytes(byte_handler)
24+
re_encoded = content.to_bytes()
25+
assert data == re_encoded
26+
else:
27+
pytest.skip(f'version {version} is currently not supported')
28+
29+
def get_version(self, datfile: Path) -> tuple[str, bytes]:
30+
content = datfile.read_bytes()
31+
data = zlib.decompress(content, wbits=-15)
32+
version = data[:8].rstrip(b'\0').decode()
33+
return version, data

tests/testdata/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)