diff --git a/vasl_templates/webapp/file_server/utils.py b/vasl_templates/webapp/file_server/utils.py index 2b263eb..83871cb 100644 --- a/vasl_templates/webapp/file_server/utils.py +++ b/vasl_templates/webapp/file_server/utils.py @@ -11,16 +11,39 @@ def get_vo_gpids( data_dir ): gpids = set() for vo_type in ("vehicles","ordnance"): dname = os.path.join( data_dir, vo_type ) + + # process each file for root,_,fnames in os.walk(dname): for fname in fnames: if os.path.splitext(fname)[1] != ".json": continue + + # load the GPID's from the next file entries = json.load( open( os.path.join(root,fname), "r" ) ) for entry in entries: if isinstance( entry["gpid"], list): - gpids.update( entry["gpid"] ) + gpids.update( get_effective_gpid(gpid) for gpid in entry["gpid"] ) else: gpids.add( entry["gpid"] ) + gpids.remove( None ) return gpids + +# --------------------------------------------------------------------- + +# VASL 6.4.3 removed several PieceSlot's. There's no comment for the commmit (0a27c24) +# but I suspect it's because they're duplicates. Our data files have the following mappings: +# SdKfz 10/5: 7140, 2775 +# SdKfz 10/4: 7146, 2772 +# but we can't just remove the now-missing GPID's, since any scenarios that use them +# will break. This kind of thing is going to happen again, so we provide a generic mechanism +# for dealing with this kind of thing... +GPID_REMAPPINGS = { + 7140: 2775, # SdKfz 10/5 + 7146: 2772, # SdKfz 10/4 +} + +def get_effective_gpid( gpid ): + """Return the effective GPID.""" + return GPID_REMAPPINGS.get( gpid, gpid ) diff --git a/vasl_templates/webapp/file_server/vasl_mod.py b/vasl_templates/webapp/file_server/vasl_mod.py index c6014c5..85916b0 100644 --- a/vasl_templates/webapp/file_server/vasl_mod.py +++ b/vasl_templates/webapp/file_server/vasl_mod.py @@ -9,9 +9,9 @@ import xml.etree.ElementTree import logging _logger = logging.getLogger( "vasl_mod" ) -from vasl_templates.webapp.file_server.utils import get_vo_gpids +from vasl_templates.webapp.file_server.utils import get_vo_gpids, get_effective_gpid -SUPPORTED_VASL_MOD_VERSIONS = [ "6.3.3", "6.4.0", "6.4.1", "6.4.2" ] +SUPPORTED_VASL_MOD_VERSIONS = [ "6.3.3", "6.4.0", "6.4.1", "6.4.2", "6.4.3" ] # --------------------------------------------------------------------- @@ -30,7 +30,10 @@ class VaslMod: """Get the image for the specified piece.""" # get the image path - entry = self.pieces[ gpid ] + gpid = get_effective_gpid( gpid ) + if gpid not in self.pieces: + return None, None + entry = self.pieces[ get_effective_gpid( gpid ) ] assert side in ("front","back") image_paths = entry[ side+"_images" ] if not image_paths: @@ -93,6 +96,9 @@ class VaslMod: for node in doc.iter( "VASSAL.build.widget.PieceSlot" ): # load the next entry + # FUDGE! 6.4.3 introduced weird GPID's for "Hex Grid" pieces :-/ + if node.attrib["gpid"].startswith( "4d0:" ): + continue gpid = int( node.attrib["gpid"] ) if gpid not in target_gpids: continue diff --git a/vasl_templates/webapp/tests/fixtures/gpid-remapping.json b/vasl_templates/webapp/tests/fixtures/gpid-remapping.json new file mode 100644 index 0000000..e2bdcdd --- /dev/null +++ b/vasl_templates/webapp/tests/fixtures/gpid-remapping.json @@ -0,0 +1,8 @@ +{ + "SCENARIO_NAME": "GPID remapping test", + "PLAYER_1": "german", + "OB_VEHICLES_1": [ + { "id":"ge/v:106", "name":"SdKfz 10/5", "image_id":"7140/0" }, + { "id":"ge/v:105", "name":"SdKfz 10/4" } + ] +} diff --git a/vasl_templates/webapp/tests/fixtures/vasl-pieces.txt b/vasl_templates/webapp/tests/fixtures/vasl-pieces.txt index d53f0ce..17d2afd 100644 --- a/vasl_templates/webapp/tests/fixtures/vasl-pieces.txt +++ b/vasl_templates/webapp/tests/fixtures/vasl-pieces.txt @@ -1198,8 +1198,6 @@ 7128 FT-17 730(f) ge/veh/geFT-17 37.png 7132 38H 735(f) ge/veh/ge38H 735.png 7136 35-S 739(f) ge/veh/ge35S 739.png - 7140 SdKfz 10/5 ge/veh/sdkfz105.gif ge/veh/sdkfz105b.gif - 7146 SdKfz 10/4 ge/veh/sdkfz104.gif ge/veh/sdkfz104b.gif 7220 T-26B (r) fi/veh/fiT-26b2(r).png 7409 76 ItK/28 B(s) fiAA76L.png fiAA76LB.png 7418 T-28 M34(L) T28M34L.gif diff --git a/vasl_templates/webapp/tests/test_counters.py b/vasl_templates/webapp/tests/test_counters.py index 0ae93d4..423561d 100644 --- a/vasl_templates/webapp/tests/test_counters.py +++ b/vasl_templates/webapp/tests/test_counters.py @@ -3,14 +3,18 @@ import os import io import glob +import json +import re import urllib.request import pytest import tabulate from vasl_templates.webapp.file_server.utils import get_vo_gpids -from vasl_templates.webapp.config.constants import DATA_DIR -from vasl_templates.webapp.tests.utils import load_vasl_mod +from vasl_templates.webapp.file_server import utils as webapp_file_server_utils +from vasl_templates.webapp.config.constants import DATA_DIR as REAL_DATA_DIR +from vasl_templates.webapp.tests.utils import init_webapp, load_vasl_mod, select_tab, find_child, find_children +from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario # --------------------------------------------------------------------- @@ -28,7 +32,7 @@ def test_counter_images( webapp, monkeypatch ): # NOTE: This is ridiculously slow on Windows :-/ # figure out which pieces we're interested in - gpids = get_vo_gpids( DATA_DIR ) + gpids = get_vo_gpids( REAL_DATA_DIR ) def check_images( check_front, check_back ): #pylint: disable=unused-argument """Check getting the front and back images for each counter.""" @@ -57,10 +61,10 @@ def test_counter_images( webapp, monkeypatch ): fname = os.path.join( os.path.split(__file__)[0], "fixtures/vasl-pieces.txt" ) expected_vasl_pieces = open( fname, "r" ).read() fspec = os.path.join( pytest.config.option.vasl_mods, "*.vmod" ) #pylint: disable=no-member - for fname in glob.glob(fspec): + for fname in glob.glob( fspec ): # install the VASL module file - vasl_mod = load_vasl_mod( DATA_DIR, monkeypatch ) + vasl_mod = load_vasl_mod( REAL_DATA_DIR, monkeypatch, fname=os.path.split(fname)[1] ) # check the pieces loaded buf = io.StringIO() @@ -85,3 +89,64 @@ def _dump_pieces( vasl_mod, out ): assert piece["gpid"] == gpid results.append( [ gpid, piece["name"], piece["front_images"], piece["back_images"] ] ) print( tabulate.tabulate( results, headers="firstrow" ), file=out ) + +# --------------------------------------------------------------------- + +@pytest.mark.skipif( + not pytest.config.option.vasl_mods, #pylint: disable=no-member + reason = "--vasl-mods-tests not specified" +) +def test_gpid_remapping( webapp, webdriver, monkeypatch ): + """Test GPID remapping.""" + + # initialize + monkeypatch.setitem( webapp.config, "DATA_DIR", REAL_DATA_DIR ) + + def check_gpid_image( gpid ): + """Check if we can get the image for the specified GPID.""" + url = webapp.url_for( "get_counter_image", gpid=gpid, side="front" ) + try: + resp = urllib.request.urlopen( url ) + return resp.code + except urllib.error.HTTPError as ex: + assert ex.code != 200 + return ex.code + + def check_entry( entry, url_stem, valid_image ): + """Check a vehicle entry in the UI.""" + img = find_child( "img.vasl-image", entry ) + assert img.get_attribute( "src" ).endswith( url_stem ) + mo = re.search( r"^/counter/(\d+)/", url_stem ) + gpid = mo.group(1) + if valid_image: + assert check_gpid_image( gpid ) == 200 + assert img.get_attribute( "width" ) == "45" + else: + assert check_gpid_image( gpid ) == 404 + + def do_test( vasl_mod, valid_images ): + """Do the test.""" + # initialize (using the specified VASL vmod) + load_vasl_mod( REAL_DATA_DIR, monkeypatch, vasl_mod ) + init_webapp( webapp, webdriver, scenario_persistence=1 ) + load_scenario( scenario_data ) + # check that the German vehicles loaded correctly + select_tab( "ob1" ) + vehicles_sortable = find_child( "#ob_vehicles-sortable_1" ) + entries = find_children( "li", vehicles_sortable ) + assert len(entries) == 2 + check_entry( entries[0], "/counter/7140/front/0", valid_images ) + check_entry( entries[1], "/counter/7146/front", valid_images ) + + # load the test scenario + fname = os.path.join( os.path.split(__file__)[0], "fixtures/gpid-remapping.json" ) + scenario_data = json.load( open( fname, "r" ) ) + + # run the tests using VASL 6.4.2 and 6.4.3 + do_test( "vasl-6.4.2.vmod", True ) + do_test( "vasl-6.4.3.vmod", True ) + + # disable GPID remapping and try again + monkeypatch.setattr( webapp_file_server_utils, "GPID_REMAPPINGS", {} ) + do_test( "vasl-6.4.2.vmod", True ) + do_test( "vasl-6.4.3.vmod", False ) diff --git a/vasl_templates/webapp/tests/utils.py b/vasl_templates/webapp/tests/utils.py index d68a8b9..8b85677 100644 --- a/vasl_templates/webapp/tests/utils.py +++ b/vasl_templates/webapp/tests/utils.py @@ -7,6 +7,7 @@ import time import re import uuid import glob +import random import pytest from PyQt5.QtWidgets import QApplication @@ -48,14 +49,18 @@ def init_webapp( webapp, webdriver, **options ): # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -def load_vasl_mod( data_dir, monkeypatch ): +def load_vasl_mod( data_dir, monkeypatch, fname=None ): """Load a VASL module.""" if data_dir: - # NOTE: Some tests require a VASL module to be loaded, and since they should all - # should behave in the same way, it doesn't matter which one we load. - fspec = os.path.join( pytest.config.option.vasl_mods, "*.vmod" ) #pylint: disable=no-member - fname = glob.glob( fspec )[0] + vasl_mods_dir = pytest.config.option.vasl_mods #pylint: disable=no-member + if fname: + fname = os.path.join( vasl_mods_dir, fname ) + else: + # NOTE: Some tests require a VASL module to be loaded, and since they should all + # should behave in the same way, it doesn't matter which one we load. + fspec = os.path.join( vasl_mods_dir, "*.vmod" ) + fname = random.choice( glob.glob( fspec ) ) vasl_mod = VaslMod( fname, data_dir ) else: vasl_mod = None