Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dcs/unittype.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def load_payloads(cls):
if not payload_dir.exists():
continue
for payload_path in payload_dir.glob("*.lua"):
if FlyingType._payload_cache[payload_path] == cls.id and payload_path.exists():
if FlyingType._payload_cache.get(payload_path) == cls.id and payload_path.exists():
try:
payload_main = lua.loads(payload_path.read_text(), _globals=FlyingType._UnitPayloadGlobals)
except SyntaxError:
Expand Down
70 changes: 70 additions & 0 deletions tests/test_unittype.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import textwrap
from pathlib import Path

import pytest

import dcs.countries
from dcs.liveries.livery import Livery
from dcs.liveries.liverycache import LiveryCache
from dcs.liveries.liveryscanner import LiveryScanner
from dcs.payloads import PayloadDirectories
from dcs.planes import F_16C_50
from dcs.unittype import FlyingType


def test_plane_liveries(tmp_path: Path) -> None:
Expand Down Expand Up @@ -83,3 +87,69 @@ def test_plane_liveries_for_country(tmp_path: Path) -> None:
set(F_16C_50.iter_liveries_for_country(dcs.countries.get_by_short_name("USA")))
== expected
)


def test_load_payloads_skips_globbed_but_uncached_file(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""A payload .lua that scan_payload_dir globs but does not cache must be
skipped, not raise.

scan_payload_dir only records a payload file in _payload_cache when its
content matches the `["unitType"] = "..."` regex, but load_payloads globs
every *.lua and read the cache with a bare subscript. A globbed-but-uncached
file (e.g. the shipping F-100D module, whose payload syntax the regex does
not match) therefore raised KeyError and aborted the entire mission load.
"""
payload_dir = tmp_path / "UnitPayloads"
payload_dir.mkdir(parents=True)

# A payload file the regex cannot cache: it has no `["unitType"] = "..."`
# line, so scan_payload_dir globs it but stores no cache entry for it.
(payload_dir / "Uncacheable.lua").write_text(
textwrap.dedent(
"""\
local unitPayloads = {
name = "F-100D",
payloads = {},
}
return unitPayloads
"""
)
)

# A normal, cacheable payload file for the unit under test, so we also
# confirm the valid file is still loaded while the uncacheable one is
# simply skipped.
(payload_dir / "Viper.lua").write_text(
textwrap.dedent(
f"""\
local unitPayloads = {{
["unitType"] = "{F_16C_50.id}",
["payloads"] = {{
[1] = {{
["name"] = "Test Loadout",
["pylons"] = {{}},
}},
}},
}}
return unitPayloads
"""
)
)

# Point the scanner only at our temp dir (no real DCS install paths), and
# reset the process-global caches so the scan re-runs against it.
monkeypatch.setattr(PayloadDirectories, "preferred", payload_dir)
monkeypatch.setattr(PayloadDirectories, "_dcs", [])
monkeypatch.setattr(PayloadDirectories, "_mod", [])
monkeypatch.setattr(PayloadDirectories, "_user", tmp_path / "does-not-exist")
monkeypatch.setattr(PayloadDirectories, "fallback", None)
monkeypatch.setattr(FlyingType, "_payload_cache", None)
monkeypatch.setattr(F_16C_50, "payloads", None)

# Must not raise KeyError on the globbed-but-uncached file.
payloads = F_16C_50.load_payloads()

# The cacheable file still loads; the uncacheable one was skipped.
assert "Test Loadout" in payloads
Loading