Got VASSAL running inside the Docker container.

master
Pacman Ghost 4 years ago
parent ab1d3251d9
commit b22c714ec5
  1. 1
      .dockerignore
  2. 6
      Dockerfile
  3. 1
      docker/config/debug.cfg
  4. 20
      docker/config/logging.yaml
  5. 1
      docker/config/site.cfg
  6. 3
      docker/run.sh
  7. 106
      run-container.sh
  8. 3
      vasl_templates/webapp/snippets.py
  9. 10
      vasl_templates/webapp/tests/remote.py
  10. 102
      vasl_templates/webapp/tests/test_vassal.py
  11. 15
      vasl_templates/webapp/vassal.py

@ -3,5 +3,6 @@
! setup.py ! setup.py
! requirements*.txt ! requirements*.txt
! vasl_templates/ ! vasl_templates/
! vassal-shim/release/
! docker/ ! docker/
! LICENSE.txt ! LICENSE.txt

@ -5,9 +5,10 @@
FROM centos:8 AS base FROM centos:8 AS base
# update packages and install Python # update packages and install Python and Java
RUN dnf -y upgrade-minimal && \ RUN dnf -y upgrade-minimal && \
dnf install -y python36 && \ dnf install -y python36 && \
curl -s "https://download.java.net/java/GA/jdk15.0.1/51f4f36ad4ef43e39d0dfdbaf6549e32/9/GPL/openjdk-15.0.1_linux-x64_bin.tar.gz" | tar -C /usr/bin/ -xz && \
dnf clean all dnf clean all
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -40,12 +41,13 @@ RUN wget -qO- "https://download.mozilla.org/?product=firefox-latest-ssl&os=linux
echo "exclude=firefox" >> /etc/dnf/dnf.conf echo "exclude=firefox" >> /etc/dnf/dnf.conf
# install geckodriver # install geckodriver
RUN url=$( curl -s https://api.github.com/repos/mozilla/geckodriver/releases/latest | grep -Eoh 'https.*linux64\.tar\.gz' ) && \ RUN url=$( curl -s https://api.github.com/repos/mozilla/geckodriver/releases/latest | grep -Poh 'https.*linux64\.tar\.gz(?!\.)' ) && \
curl -sL "$url" | tar -C /usr/bin/ -xz curl -sL "$url" | tar -C /usr/bin/ -xz
# install the application # install the application
WORKDIR /app WORKDIR /app
COPY vasl_templates vasl_templates COPY vasl_templates vasl_templates
COPY vassal-shim/release/vassal-shim.jar vassal-shim/release/
COPY setup.py requirements.txt requirements-dev.txt LICENSE.txt ./ COPY setup.py requirements.txt requirements-dev.txt LICENSE.txt ./
RUN pip install -e . RUN pip install -e .

@ -1,4 +1,5 @@
[Debug] [Debug]
TEST_VASSAL_ENGINES = /test-data/vassal/
TEST_VASL_MODS = /test-data/vasl-vmods/ TEST_VASL_MODS = /test-data/vasl-vmods/
TEST_VASL_EXTNS_DIR = /test-data/vasl-extensions/ TEST_VASL_EXTNS_DIR = /test-data/vasl-extensions/

@ -19,13 +19,25 @@ handlers:
loggers: loggers:
werkzeug: werkzeug:
level: "WARNING" level: "WARNING"
handlers: [ "console" ] handlers: [ "file" ]
vasl_mod: vasl_mod:
level: "WARNING" level: "WARNING"
handlers: [ "console", "file" ] handlers: [ "file" ]
update_vsav: update_vsav:
level: "WARNING" level: "WARNING"
handlers: [ "console", "file" ] handlers: [ "file" ]
analyze_vsav:
level: "WARNING"
handlers: [ "file" ]
analyze_vlog:
level: "WARNING"
handlers: [ "file" ]
webdriver:
level: "WARNING"
handlers: [ "file" ]
downloads:
level: "WARNING"
handlers: [ "file" ]
control_tests: control_tests:
level: "DEBUG" level: "INFO"
handlers: [ "console", "file" ] handlers: [ "console", "file" ]

@ -3,4 +3,5 @@
FLASK_HOST = 0.0.0.0 FLASK_HOST = 0.0.0.0
IS_CONTAINER = 1 IS_CONTAINER = 1
JAVA_PATH = /usr/bin/jdk-15.0.1/bin/java
WEBDRIVER_PATH = /usr/bin/geckodriver WEBDRIVER_PATH = /usr/bin/geckodriver

@ -1,7 +1,8 @@
#!/bin/sh #!/bin/sh
# set up the display (so we can run a webdriver) # set up the display (so we can run VASSAL and a webdriver)
export ENV=10 export ENV=10
export DISPLAY=:10.0
Xvfb :10 -ac 1>/tmp/xvfb.log 2>/tmp/xvfb.err & Xvfb :10 -ac 1>/tmp/xvfb.log 2>/tmp/xvfb.err &
# run the webapp server # run the webapp server

@ -5,29 +5,32 @@
function print_help { function print_help {
echo "`basename "$0"` {options}" echo "`basename "$0"` {options}"
echo " Build and launch the \"vasl-templates\" container."
echo
echo " -p --port Web server port number."
echo " -v --vasl-vmod Path to the VASL .vmod file."
echo " -e --vasl-extensions Path to the VASL extensions directory."
echo " -h --chapter-h Path to the Chapter H notes directory."
echo " -u --user-files Path to the user files directory."
echo
echo " -t --tag Docker tag."
echo " -d --detach Detach from the container and let it run in the background."
echo " --no-build Launch the container as-is (i.e. without rebuilding it first)."
echo " --build-network Docker network to use when building the container."
echo " --run-network Docker network to use when running the container."
echo
cat <<EOM cat <<EOM
NOTE: If the port the webapp server is listening on *inside* the container is different Build and launch the "vasl-templates" container.
to the port exposed *outside* the container, webdriver image generation (e.g. Shift-Click
on a snippet button, or Chapter H content as images) may not work properly. This is because -p --port Web server port number.
a web browser is launched internally with snippet HTML and a screenshot taken of it, but --vassal VASSAL installation directory.
the HTML will contain links to the webapp server that work from outside the container, -v --vasl-vmod Path to the VASL .vmod file.
but if those links don't resolve from inside the container, you will get broken images. -e --vasl-extensions Path to the VASL extensions directory.
In this case, you will need to make such links resolve from inside the container e.g. by --boards Path to the VASL boards.
port-forwarding, or via DNS. --chapter-h Path to the Chapter H notes directory.
--user-files Path to the user files directory.
-k --template-pack Path to a user-defined template pack.
-t --tag Docker tag.
-d --detach Detach from the container and let it run in the background.
--no-build Launch the container as-is (i.e. without rebuilding it first).
--build-network Docker network to use when building the container.
--run-network Docker network to use when running the container.
NOTE: If the port the webapp server is listening on *inside* the container is different
to the port exposed *outside* the container, webdriver image generation (e.g. Shift-Click
on a snippet button, or Chapter H content as images) may not work properly. This is because
a web browser is launched internally with snippet HTML and a screenshot taken of it, but
the HTML will contain links to the webapp server that work from outside the container,
but if those links don't resolve from inside the container, you will get broken images.
In this case, you will need to make such links resolve from inside the container e.g. by
port-forwarding, or via DNS.
EOM EOM
} }
@ -36,12 +39,18 @@ EOM
# initialize # initialize
cd `dirname "$0"` cd `dirname "$0"`
PORT=5010 PORT=5010
VASSAL_LOCAL=
VASSAL=
VASL_MOD_LOCAL= VASL_MOD_LOCAL=
VASL_MOD= VASL_MOD=
VASL_BOARDS_LOCAL=
VASL_BOARDS=
VASL_EXTNS_LOCAL= VASL_EXTNS_LOCAL=
VASL_EXTNS= VASL_EXTNS=
CHAPTER_H_NOTES_LOCAL= CHAPTER_H_NOTES_LOCAL=
CHAPTER_H_NOTES= CHAPTER_H_NOTES=
TEMPLATE_PACK_LOCAL=
TEMPLATE_PACK=
USER_FILES_LOCAL= USER_FILES_LOCAL=
USER_FILES= USER_FILES=
TAG=latest TAG=latest
@ -55,7 +64,7 @@ if [ $# -eq 0 ]; then
print_help print_help
exit 0 exit 0
fi fi
params="$(getopt -o p:v:e:h:u:t:d -l port:,vasl-vmod:,vasl-extensions:,chapter-h:,user-files:,tag:,detach,no-build,build-network:,run-network:,help --name "$0" -- "$@")" params="$(getopt -o p:v:e:k:t:d -l port:,vassal:,vasl-vmod:,vasl-extensions:,boards:,chapter-h:,user-files:,template-pack:,tag:,detach,no-build,build-network:,run-network:,help --name "$0" -- "$@")"
if [ $? -ne 0 ]; then exit 1; fi if [ $? -ne 0 ]; then exit 1; fi
eval set -- "$params" eval set -- "$params"
while true; do while true; do
@ -63,18 +72,27 @@ while true; do
-p | --port) -p | --port)
PORT=$2 PORT=$2
shift 2 ;; shift 2 ;;
--vassal)
VASSAL_LOCAL=$2
shift 2 ;;
-v | --vasl-vmod) -v | --vasl-vmod)
VASL_MOD_LOCAL=$2 VASL_MOD_LOCAL=$2
shift 2 ;; shift 2 ;;
-e | --vasl-extensions) -e | --vasl-extensions)
VASL_EXTNS_LOCAL=$2 VASL_EXTNS_LOCAL=$2
shift 2 ;; shift 2 ;;
-h | --chapter-h) --boards)
VASL_BOARDS_LOCAL=$2
shift 2 ;;
--chapter-h)
CHAPTER_H_NOTES_LOCAL=$2 CHAPTER_H_NOTES_LOCAL=$2
shift 2 ;; shift 2 ;;
-u | --user-files) --user-files)
USER_FILES_LOCAL=$2 USER_FILES_LOCAL=$2
shift 2 ;; shift 2 ;;
-k | --template-pack)
TEMPLATE_PACK_LOCAL=$2
shift 2 ;;
-t | --tag) -t | --tag)
TAG=$2 TAG=$2
shift 2 ;; shift 2 ;;
@ -102,6 +120,17 @@ while true; do
esac esac
done done
# check if a VASSAL directory has been specified
if [ -n "$VASSAL_LOCAL" ]; then
if [ ! -d "$VASSAL_LOCAL" ]; then
echo "Can't find the VASSAL directory: $VASSAL_LOCAL"
exit 1
fi
VASSAL=/data/vassal/
VASSAL_VOLUME="--volume `readlink -f "$VASSAL_LOCAL"`:$VASSAL"
VASSAL_ENV="--env VASSAL_DIR=$VASSAL"
fi
# check if a VASL .vmod file has been specified # check if a VASL .vmod file has been specified
if [ -n "$VASL_MOD_LOCAL" ]; then if [ -n "$VASL_MOD_LOCAL" ]; then
if [ ! -f "$VASL_MOD_LOCAL" ]; then if [ ! -f "$VASL_MOD_LOCAL" ]; then
@ -113,6 +142,17 @@ if [ -n "$VASL_MOD_LOCAL" ]; then
VASL_MOD_ENV="--env VASL_MOD=$VASL_MOD" VASL_MOD_ENV="--env VASL_MOD=$VASL_MOD"
fi fi
# check if a VASL boards directory has been specified
if [ -n "$VASL_BOARDS_LOCAL" ]; then
if [ ! -d "$VASL_BOARDS_LOCAL" ]; then
echo "Can't find the VASL boards directory: $VASL_BOARDS_LOCAL"
exit 1
fi
VASL_BOARDS=/data/boards/
VASL_BOARDS_VOLUME="--volume `readlink -f "$VASL_BOARDS_LOCAL"`:$VASL_BOARDS"
VASL_BOARDS_ENV="--env VASL_BOARDS_DIR=$VASL_BOARDS"
fi
# check if a VASL extensions directory has been specified # check if a VASL extensions directory has been specified
if [ -n "$VASL_EXTNS_LOCAL" ]; then if [ -n "$VASL_EXTNS_LOCAL" ]; then
if [ ! -d "$VASL_EXTNS_LOCAL" ]; then if [ ! -d "$VASL_EXTNS_LOCAL" ]; then
@ -135,6 +175,18 @@ if [ -n "$CHAPTER_H_NOTES_LOCAL" ]; then
CHAPTER_H_NOTES_ENV="--env CHAPTER_H_NOTES_DIR=$CHAPTER_H_NOTES" CHAPTER_H_NOTES_ENV="--env CHAPTER_H_NOTES_DIR=$CHAPTER_H_NOTES"
fi fi
# check if a template pack has been specified
if [ -n "$TEMPLATE_PACK_LOCAL" ]; then
# NOTE: The template pack can either be a file (ZIP) or a directory.
if ! ls "$TEMPLATE_PACK_LOCAL" >/dev/null 2>&1 ; then
echo "Can't find the template pack: $TEMPLATE_PACK_LOCAL"
exit 1
fi
TEMPLATE_PACK=/data/template-pack
TEMPLATE_PACK_VOLUME="--volume `readlink -f "$TEMPLATE_PACK_LOCAL"`:$TEMPLATE_PACK"
TEMPLATE_PACK_ENV="--env DEFAULT_TEMPLATE_PACK=$TEMPLATE_PACK"
fi
# check if a user files directory has been specified # check if a user files directory has been specified
if [ -n "$USER_FILES_LOCAL" ]; then if [ -n "$USER_FILES_LOCAL" ]; then
if [ ! -d "$USER_FILES_LOCAL" ]; then if [ ! -d "$USER_FILES_LOCAL" ]; then
@ -160,8 +212,8 @@ echo Launching the \"$TAG\" container...
docker run \ docker run \
--publish $PORT:5010 \ --publish $PORT:5010 \
--name vasl-templates \ --name vasl-templates \
$VASL_MOD_VOLUME $VASL_EXTNS_VOLUME $CHAPTER_H_NOTES_VOLUME $USER_FILES_VOLUME \ $VASSAL_VOLUME $VASL_MOD_VOLUME $VASL_BOARDS_VOLUME $VASL_EXTNS_VOLUME $CHAPTER_H_NOTES_VOLUME $USER_FILES_VOLUME $TEMPLATE_PACK_VOLUME \
$VASL_MOD_ENV $VASL_EXTNS_ENV $CHAPTER_H_NOTES_ENV $USER_FILES_ENV \ $VASSAL_ENV $VASL_MOD_ENV $VASL_BOARDS_ENV $VASL_EXTNS_ENV $CHAPTER_H_NOTES_ENV $USER_FILES_ENV $TEMPLATE_PACK_ENV \
$RUN_NETWORK $DETACH \ $RUN_NETWORK $DETACH \
-it --rm \ -it --rm \
vasl-templates:$TAG \ vasl-templates:$TAG \

@ -58,6 +58,9 @@ def load_default_template_pack(): #pylint: disable=too-many-locals
data["templates"]["extras/"+key] = val data["templates"]["extras/"+key] = val
# check if a default template pack has been configured # check if a default template pack has been configured
# NOTE: The Docker container configures this setting via an environment variable.
global default_template_pack
default_template_pack = os.environ.get( "DEFAULT_TEMPLATE_PACK", default_template_pack )
if default_template_pack: if default_template_pack:
dname = default_template_pack dname = default_template_pack
data["_path_"] = dname data["_path_"] = dname

@ -19,7 +19,9 @@ import pytest
from vasl_templates.webapp import app, globvars from vasl_templates.webapp import app, globvars
from vasl_templates.webapp.config.constants import DATA_DIR from vasl_templates.webapp.config.constants import DATA_DIR
from vasl_templates.webapp.vassal import VassalShim
from vasl_templates.webapp.vasl_mod import set_vasl_mod from vasl_templates.webapp.vasl_mod import set_vasl_mod
from vasl_templates.webapp.utils import TempFile
from vasl_templates.webapp import main as webapp_main from vasl_templates.webapp import main as webapp_main
from vasl_templates.webapp import snippets as webapp_snippets from vasl_templates.webapp import snippets as webapp_snippets
from vasl_templates.webapp import scenarios as webapp_scenarios from vasl_templates.webapp import scenarios as webapp_scenarios
@ -260,6 +262,14 @@ class ControlTests:
app.config["VASSAL_DIR"] = vengine app.config["VASSAL_DIR"] = vengine
return self return self
def _get_vsav_dump( self, bin_data=None ): #pylint: disable=no-self-use
"""Dump a VSAV file."""
with TempFile( mode="wb" ) as temp_file:
temp_file.write( bin_data )
temp_file.close( delete=False )
vassal_shim = VassalShim()
return vassal_shim.dump_scenario( temp_file.name )
def _set_vo_notes_dir( self, dtype=None ): def _set_vo_notes_dir( self, dtype=None ):
"""Set the vehicle/ordnance notes directory.""" """Set the vehicle/ordnance notes directory."""
if dtype == "real": if dtype == "real":

@ -9,10 +9,8 @@ import typing.re #pylint: disable=import-error
import pytest import pytest
from vasl_templates.webapp.vassal import VassalShim
from vasl_templates.webapp.vasl_mod import compare_version_strings from vasl_templates.webapp.vasl_mod import compare_version_strings
from vasl_templates.webapp.utils import TempFile, change_extn from vasl_templates.webapp.utils import TempFile, change_extn
from vasl_templates.webapp import globvars
from vasl_templates.webapp.tests.utils import \ from vasl_templates.webapp.tests.utils import \
init_webapp, refresh_webapp, select_menu_option, get_stored_msg, set_stored_msg, set_stored_msg_marker, wait_for, \ init_webapp, refresh_webapp, select_menu_option, get_stored_msg, set_stored_msg, set_stored_msg_marker, wait_for, \
new_scenario, set_player new_scenario, set_player
@ -21,13 +19,6 @@ from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario,
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
class DummyVaslMod:
"""Dummy VaslMod class that lets us run the VASSAL shim locally (to dump scenarios)."""
def __init__( self, fname ):
self.filename = fname
# ---------------------------------------------------------------------
@pytest.mark.skipif( not pytest.config.option.vasl_mods, reason="--vasl-mods not specified" ) #pylint: disable=no-member @pytest.mark.skipif( not pytest.config.option.vasl_mods, reason="--vasl-mods not specified" ) #pylint: disable=no-member
@pytest.mark.skipif( not pytest.config.option.vassal, reason="--vassal not specified" ) #pylint: disable=no-member @pytest.mark.skipif( not pytest.config.option.vassal, reason="--vassal not specified" ) #pylint: disable=no-member
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member @pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
@ -114,7 +105,7 @@ def test_full_update( webapp, webdriver ):
# NOTE: We could arguably only do this once, but updating scenarios is the key functionality of the VASSAL shim, # NOTE: We could arguably only do this once, but updating scenarios is the key functionality of the VASSAL shim,
# and so it's worth checking that every VASSAL+VASL combination understands its input correctly. # and so it's worth checking that every VASSAL+VASL combination understands its input correctly.
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/full.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/full.vsav" )
vsav_dump = _dump_vsav( fname ) vsav_dump = _dump_vsav( control_tests, fname )
_check_vsav_dump( vsav_dump, { _check_vsav_dump( vsav_dump, {
"scenario": "Somewhere", "scenario": "Somewhere",
"players": re.compile( r"American:.*Belgian:" ), "players": re.compile( r"American:.*Belgian:" ),
@ -150,7 +141,7 @@ def test_full_update( webapp, webdriver ):
# check the results # check the results
temp_file.write( updated_vsav_data ) temp_file.write( updated_vsav_data )
temp_file.close( delete=False ) temp_file.close( delete=False )
updated_vsav_dump = _dump_vsav( temp_file.name ) updated_vsav_dump = _dump_vsav( control_tests, temp_file.name )
expected = { expected = {
"scenario": "Modified scenario name (<>{}\"'\\)", "scenario": "Modified scenario name (<>{}\"'\\)",
"players": re.compile( r"American:.*Belgian:" ), "players": re.compile( r"American:.*Belgian:" ),
@ -234,12 +225,14 @@ def test_latw_autocreate( webapp, webdriver ):
# check the VASL scenario # check the VASL scenario
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/empty.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/empty.vsav" )
vsav_dump = _dump_vsav( fname ) vsav_dump = _dump_vsav( control_tests, fname )
_check_vsav_dump( vsav_dump, {}, ignore_labels ) _check_vsav_dump( vsav_dump, {}, ignore_labels )
# update the scenario (German/Russian, no date) # update the scenario (German/Russian, no date)
load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } ) load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
# nb: no LATW labels should have been created # nb: no LATW labels should have been created
}, ignore_labels ) }, ignore_labels )
@ -248,7 +241,9 @@ def test_latw_autocreate( webapp, webdriver ):
load_scenario_params( { load_scenario_params( {
"scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "10/01/1943" } "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "10/01/1943" }
} ) } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 4 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 4 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust", "german/pf": "Panzerfaust",
}, ignore_labels ) }, ignore_labels )
@ -257,14 +252,18 @@ def test_latw_autocreate( webapp, webdriver ):
load_scenario_params( { load_scenario_params( {
"scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "01/01/1944" } "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "01/01/1944" }
} ) } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 5 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 5 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust", "german/atmm": "ATMM check:", "german/pf": "Panzerfaust", "german/atmm": "ATMM check:",
}, ignore_labels ) }, ignore_labels )
# update the scenario (British/American, no date) # update the scenario (British/American, no date)
load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } ) load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
# nb: no LATW labels should have been created # nb: no LATW labels should have been created
}, ignore_labels ) }, ignore_labels )
@ -273,7 +272,9 @@ def test_latw_autocreate( webapp, webdriver ):
load_scenario_params( { load_scenario_params( {
"scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1945" } "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1945" }
} ) } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
# nb: no LATW labels should have been created # nb: no LATW labels should have been created
}, ignore_labels ) }, ignore_labels )
@ -304,7 +305,7 @@ def test_latw_update( webapp, webdriver ):
# check the VASL scenario # check the VASL scenario
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw.vsav" )
vsav_dump = _dump_vsav( fname ) vsav_dump = _dump_vsav( control_tests, fname )
_check_vsav_dump( vsav_dump, { _check_vsav_dump( vsav_dump, {
"german/psk": "Panzerschrek", "german/atmm": "ATMM check:", # nb: the PF label has no snippet ID "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", # nb: the PF label has no snippet ID
"russian/mol-p": "TH#", # nb: the MOL label has no snippet ID "russian/mol-p": "TH#", # nb: the MOL label has no snippet ID
@ -317,7 +318,9 @@ def test_latw_update( webapp, webdriver ):
# NOTE: We changed the MOL-P template (to add custom list bullets), so the snippet is different # NOTE: We changed the MOL-P template (to add custom list bullets), so the snippet is different
# to when this test was originally written, and so #updated changed from 2 to 3. # to when this test was originally written, and so #updated changed from 2 to 3.
# NOTE: Same thing happened when we factored out the common CSS into common.css :-/ Sigh... # NOTE: Same thing happened when we factored out the common CSS into common.css :-/ Sigh...
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3, "updated": 5 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust", # nb: the PF label now has a snippet ID "german/pf": "Panzerfaust", # nb: the PF label now has a snippet ID
"german/psk": "Panzerschrek", "german/atmm": "ATMM check:", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:",
@ -330,7 +333,9 @@ def test_latw_update( webapp, webdriver ):
load_scenario_params( { load_scenario_params( {
"scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1943" } "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1943" }
} ) } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
# NOTE: We used to delete the PSK/ATMM/MOL-P labels, but this no longer happens with player-owned labels. # NOTE: We used to delete the PSK/ATMM/MOL-P labels, but this no longer happens with player-owned labels.
"german/psk": "Panzerschrek", "german/atmm": "ATMM check:", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:",
@ -360,7 +365,7 @@ def test_dump_vsav( webapp, webdriver ):
# dump the VASL scenario # dump the VASL scenario
fname = os.path.join( os.path.split(__file__)[0], "fixtures/dump-vsav/labels.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/dump-vsav/labels.vsav" )
vsav_dump = _dump_vsav( fname ) vsav_dump = _dump_vsav( control_tests, fname )
# check the result # check the result
fname = change_extn( fname, ".txt" ) fname = change_extn( fname, ".txt" )
@ -392,7 +397,7 @@ def test_update_legacy_labels( webapp, webdriver ):
# dump the VASL scenario # dump the VASL scenario
# NOTE: We implemented snippet ID's in v0.5, this scenario is the "Hill 621" example from v0.4. # NOTE: We implemented snippet ID's in v0.5, this scenario is the "Hill 621" example from v0.4.
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/hill621-legacy.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/hill621-legacy.vsav" )
vsav_dump = _dump_vsav( fname ) vsav_dump = _dump_vsav( control_tests, fname )
labels = _get_vsav_labels( vsav_dump ) labels = _get_vsav_labels( vsav_dump )
assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 20 assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 20
assert len( [ lbl for lbl in labels if "vasl-templates:id" in lbl ] ) == 0 #pylint: disable=len-as-condition assert len( [ lbl for lbl in labels if "vasl-templates:id" in lbl ] ) == 0 #pylint: disable=len-as-condition
@ -402,7 +407,9 @@ def test_update_legacy_labels( webapp, webdriver ):
saved_scenario = json.load( open( fname2, "r" ) ) saved_scenario = json.load( open( fname2, "r" ) )
load_scenario( saved_scenario ) load_scenario( saved_scenario )
expected = 5 if enable_vo_notes else 1 expected = 5 if enable_vo_notes else 1
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": expected, "updated": 20 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": expected, "updated": 20 }
)
# check the results # check the results
# nb: the update process should create 1 new label (the "Download from MMP" scenario note) # nb: the update process should create 1 new label (the "Download from MMP" scenario note)
@ -474,7 +481,7 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
# dump the VASL scenario # dump the VASL scenario
# NOTE: This scenario contains LATW labels created using v0.4 i.e. they have no snippet ID's. # NOTE: This scenario contains LATW labels created using v0.4 i.e. they have no snippet ID's.
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw-legacy.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw-legacy.vsav" )
vsav_dump = _dump_vsav( fname ) vsav_dump = _dump_vsav( control_tests, fname )
labels = _get_vsav_labels( vsav_dump ) labels = _get_vsav_labels( vsav_dump )
assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 8 assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 8
assert len( [ lbl for lbl in labels if "vasl-templates:id" in lbl ] ) == 0 #pylint: disable=len-as-condition assert len( [ lbl for lbl in labels if "vasl-templates:id" in lbl ] ) == 0 #pylint: disable=len-as-condition
@ -486,7 +493,9 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
load_scenario_params( { load_scenario_params( {
"scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "12/31/1945" } "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "12/31/1945" }
} ) } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3, "updated": 5 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", "german/pf": "Panzerfaust", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:",
"russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#", "russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#",
@ -499,7 +508,9 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
load_scenario_params( { load_scenario_params( {
"scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1945" } "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1945" }
} ) } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"british/piat": "PIAT", "british/piat": "PIAT",
"american/baz": "Bazooka ('45)", "american/baz": "Bazooka ('45)",
@ -510,7 +521,9 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
# update the VSAV (some LATW are active) # update the VSAV (some LATW are active)
load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } ) load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3, "updated": 5 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", "german/pf": "Panzerfaust", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:",
"russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#", "russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#",
@ -521,7 +534,9 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
# update the VSAV (some LATW are active) # update the VSAV (some LATW are active)
load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } ) load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump, { _check_vsav_dump( updated_vsav_dump, {
"british/piat": "PIAT", "british/piat": "PIAT",
"american/baz": "Bazooka", "american/baz": "Bazooka",
@ -566,7 +581,9 @@ def test_player_owned_labels( webapp, webdriver ):
# - scenario (timestamp) # - scenario (timestamp)
# - players (new American player) # - players (new American player)
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels-legacy.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels-legacy.vsav" )
updated_vsav_dump = _update_vsav_and_dump( fname, { "updated": 4 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "updated": 4 }
)
_check_vsav_dump( updated_vsav_dump , { _check_vsav_dump( updated_vsav_dump , {
"german/ob_setup_1.1": "german setup #1", "german/ob_setup_1.1": "german setup #1",
"american/ob_setup_2.1": "american setup #1", "american/ob_setup_2.1": "american setup #1",
@ -580,7 +597,9 @@ def test_player_owned_labels( webapp, webdriver ):
# - players (new American player) # - players (new American player)
# The existing Russian OB setup label should be ignored and left in-place. # The existing Russian OB setup label should be ignored and left in-place.
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels.vsav" ) fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels.vsav" )
updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 1, "updated": 2 } ) updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 1, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump , { _check_vsav_dump( updated_vsav_dump , {
"german/ob_setup_1.1": "german setup #1", "german/ob_setup_1.1": "german setup #1",
"american/ob_setup_2.1": "american setup #1", "american/ob_setup_2.1": "american setup #1",
@ -819,13 +838,6 @@ def run_vassal_tests( control_tests, func, test_all, min_vasl_version=None ):
else: else:
assert False, "Can't find a valid combination of VASSAL and VASL." assert False, "Can't find a valid combination of VASSAL and VASL."
# FUDGE! If we are running the tests against a remote server, we still need to be able to run
# the VASSAL shim locally (to dump VASSAL save files), so we need to set up things up enough
# for this to work.
if control_tests.server_url:
vasl_mods_local = control_tests._do_get_vasl_mods() #pylint: disable=protected-access
globvars.vasl_mod = DummyVaslMod( random.choice( vasl_mods_local ) )
# run the test for each VASSAL+VASL # run the test for each VASSAL+VASL
for vassal_engine in vassal_engines: for vassal_engine in vassal_engines:
control_tests.set_vassal_engine( vengine=vassal_engine ) control_tests.set_vassal_engine( vengine=vassal_engine )
@ -836,9 +848,9 @@ def run_vassal_tests( control_tests, func, test_all, min_vasl_version=None ):
vasl_version = mo.group() vasl_version = mo.group()
if min_vasl_version and compare_version_strings( vasl_version, min_vasl_version ) < 0: if min_vasl_version and compare_version_strings( vasl_version, min_vasl_version ) < 0:
continue continue
control_tests.set_vasl_mod( vmod=vasl_mod )
if not is_valid_combo( vassal_engine, vasl_mod ): if not is_valid_combo( vassal_engine, vasl_mod ):
continue continue
control_tests.set_vasl_mod( vmod=vasl_mod )
func() func()
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
@ -878,25 +890,25 @@ def _update_vsav( fname, expected ):
return updated_vsav_data return updated_vsav_data
def _update_vsav_and_dump( fname, expected ): def _update_vsav_and_dump( control_tests, fname, expected ):
"""Update a VASL scenario and dump the result.""" """Update a VASL scenario and dump the result."""
# update the VASL # update the VSAV
updated_vsav_data = _update_vsav( fname, expected ) updated_vsav_data = _update_vsav( fname, expected )
# dump the updated VSAV # dump the updated VSAV
with TempFile() as temp_file: with TempFile() as temp_file:
temp_file.write( updated_vsav_data ) temp_file.write( updated_vsav_data )
temp_file.close( delete=False ) temp_file.close( delete=False )
return _dump_vsav( temp_file.name ) return _dump_vsav( control_tests, temp_file.name )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def _dump_vsav( fname ): def _dump_vsav( control_tests, fname ):
"""Dump a VASL scenario file.""" """Dump a VASL scenario file."""
# NOTE: This is run locally, even if we're running the tests against a remote server. with open( fname, "rb" ) as fp:
vassal_shim = VassalShim() vsav_data = fp.read()
return vassal_shim.dump_scenario( fname ) return control_tests.get_vsav_dump( bin_data=vsav_data )
def _check_vsav_dump( vsav_dump, expected, ignore=None ): def _check_vsav_dump( vsav_dump, expected, ignore=None ):
""""Check that a VASL scenario dump contains what we expect.""" """"Check that a VASL scenario dump contains what we expect."""

@ -289,7 +289,7 @@ class VassalShim:
def __init__( self ): #pylint: disable=too-many-branches def __init__( self ): #pylint: disable=too-many-branches
# locate the VASSAL engine # locate the VASSAL engine
vassal_dir = app.config.get( "VASSAL_DIR" ) vassal_dir = self._get_vassal_dir()
if not vassal_dir: if not vassal_dir:
raise SimpleError( "The VASSAL installation directory has not been configured." ) raise SimpleError( "The VASSAL installation directory has not been configured." )
self.vengine_jar = None self.vengine_jar = None
@ -323,7 +323,7 @@ class VassalShim:
@staticmethod @staticmethod
def get_version(): def get_version():
"""Get the VASSAL version.""" """Get the VASSAL version."""
vassal_dir = app.config.get( "VASSAL_DIR" ) vassal_dir = VassalShim._get_vassal_dir()
if not vassal_dir: if not vassal_dir:
return None return None
# FUDGE! We can't capture the output on Windows, get the result in a temp file instead :-/ # FUDGE! We can't capture the output on Windows, get the result in a temp file instead :-/
@ -471,7 +471,8 @@ class VassalShim:
@staticmethod @staticmethod
def get_boards_dir(): def get_boards_dir():
"""Get the configured boards directory.""" """Get the configured boards directory."""
boards_dir = app.config.get( "BOARDS_DIR" ) # NOTE: The Docker container configures this setting via an environment variable.
boards_dir = app.config.get( "BOARDS_DIR", os.environ.get("VASL_BOARDS_DIR") )
if not boards_dir: if not boards_dir:
raise SimpleError( "The VASL boards directory has not been configured." ) raise SimpleError( "The VASL boards directory has not been configured." )
if not os.path.isdir( boards_dir ): if not os.path.isdir( boards_dir ):
@ -513,7 +514,7 @@ class VassalShim:
@staticmethod @staticmethod
def check_vassal_version( msg_store ): def check_vassal_version( msg_store ):
"""Check the version of VASSAL.""" """Check the version of VASSAL."""
if not app.config.get( "VASSAL_DIR" ) or not msg_store: if not VassalShim._get_vassal_dir() or not msg_store:
return return
try: try:
version = VassalShim.get_version() version = VassalShim.get_version()
@ -528,6 +529,12 @@ class VassalShim:
version version
) )
@staticmethod
def _get_vassal_dir():
"""Get the VASSAL installation directory."""
# NOTE: The Docker container configures this setting via an environment variable.
return app.config.get( "VASSAL_DIR", os.environ.get("VASSAL_DIR") )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class VassalShimError( Exception ): class VassalShimError( Exception ):

Loading…
Cancel
Save