Re-architected the test suite.

Changed how environment variables are set in the Docker container.
master
Pacman Ghost 3 years ago
parent 92ccfdccf9
commit 02d1aeb408
  1. 4
      .pylintrc
  2. 14
      Dockerfile
  3. 95
      conftest.py
  4. 4
      docker/config/debug.cfg
  5. 1
      docker/config/site.cfg
  6. 4
      docker/run.sh
  7. 1
      requirements-dev.txt
  8. 193
      run-container.sh
  9. 5
      vasl_templates/main.py
  10. 20
      vasl_templates/tools/webdriver_stress_test.py
  11. 133
      vasl_templates/webapp/__init__.py
  12. 10
      vasl_templates/webapp/config/debug.cfg.example
  13. 2
      vasl_templates/webapp/config/site.cfg.example
  14. 3
      vasl_templates/webapp/downloads.py
  15. 3
      vasl_templates/webapp/files.py
  16. 78
      vasl_templates/webapp/main.py
  17. 95
      vasl_templates/webapp/run_server.py
  18. 45
      vasl_templates/webapp/testing.py
  19. 17
      vasl_templates/webapp/tests/compile-proto.sh
  20. 227
      vasl_templates/webapp/tests/control_tests.py
  21. 536
      vasl_templates/webapp/tests/control_tests_servicer.py
  22. BIN
      vasl_templates/webapp/tests/fixtures/vasl-extensions/real/BFP_v403.vmdx
  23. BIN
      vasl_templates/webapp/tests/fixtures/vasl-extensions/real/kgs-v1.1.mdx
  24. 32
      vasl_templates/webapp/tests/proto/__init__.py
  25. 164
      vasl_templates/webapp/tests/proto/control_tests.proto
  26. 7
      vasl_templates/webapp/tests/proto/delete_app_config_val_request.py
  27. 7
      vasl_templates/webapp/tests/proto/dump_vsav_request.py
  28. 1554
      vasl_templates/webapp/tests/proto/generated/control_tests_pb2.py
  29. 898
      vasl_templates/webapp/tests/proto/generated/control_tests_pb2_grpc.py
  30. 7
      vasl_templates/webapp/tests/proto/get_vasl_pieces_request.py
  31. 7
      vasl_templates/webapp/tests/proto/save_temp_file_request.py
  32. 14
      vasl_templates/webapp/tests/proto/set_app_config_val_request.py
  33. 7
      vasl_templates/webapp/tests/proto/set_asa_scenario_index_request.py
  34. 10
      vasl_templates/webapp/tests/proto/set_data_dir_request.py
  35. 7
      vasl_templates/webapp/tests/proto/set_default_scenario_request.py
  36. 21
      vasl_templates/webapp/tests/proto/set_default_template_pack_request.py
  37. 7
      vasl_templates/webapp/tests/proto/set_roar_scenario_index_request.py
  38. 7
      vasl_templates/webapp/tests/proto/set_user_files_dir_request.py
  39. 7
      vasl_templates/webapp/tests/proto/set_vasl_extn_info_dir_request.py
  40. 13
      vasl_templates/webapp/tests/proto/set_vasl_version_request.py
  41. 7
      vasl_templates/webapp/tests/proto/set_vassal_version_request.py
  42. 10
      vasl_templates/webapp/tests/proto/set_veh_ord_notes_dir_request.py
  43. 44
      vasl_templates/webapp/tests/proto/utils.py
  44. 373
      vasl_templates/webapp/tests/remote.py
  45. 74
      vasl_templates/webapp/tests/test_capabilities.py
  46. 22
      vasl_templates/webapp/tests/test_comments.py
  47. 125
      vasl_templates/webapp/tests/test_counters.py
  48. 5
      vasl_templates/webapp/tests/test_default_scenario.py
  49. 4
      vasl_templates/webapp/tests/test_dirty_scenario_checks.py
  50. 5
      vasl_templates/webapp/tests/test_extras_templates.py
  51. 21
      vasl_templates/webapp/tests/test_files.py
  52. 6
      vasl_templates/webapp/tests/test_jshint.py
  53. 82
      vasl_templates/webapp/tests/test_lfa.py
  54. 14
      vasl_templates/webapp/tests/test_national_capabilities.py
  55. 3
      vasl_templates/webapp/tests/test_ob.py
  56. 47
      vasl_templates/webapp/tests/test_online_images.py
  57. 4
      vasl_templates/webapp/tests/test_scenario_persistence.py
  58. 31
      vasl_templates/webapp/tests/test_scenario_search.py
  59. 23
      vasl_templates/webapp/tests/test_snippets.py
  60. 31
      vasl_templates/webapp/tests/test_template_packs.py
  61. 102
      vasl_templates/webapp/tests/test_user_settings.py
  62. 169
      vasl_templates/webapp/tests/test_vasl_extensions.py
  63. 321
      vasl_templates/webapp/tests/test_vassal.py
  64. 60
      vasl_templates/webapp/tests/test_vehicles_ordnance.py
  65. 88
      vasl_templates/webapp/tests/test_vo_notes.py
  66. 20
      vasl_templates/webapp/tests/test_vo_reports.py
  67. 37
      vasl_templates/webapp/tests/utils.py
  68. 14
      vasl_templates/webapp/utils.py
  69. 24
      vasl_templates/webapp/vasl_mod.py
  70. 10
      vasl_templates/webapp/vassal.py
  71. 2
      vasl_templates/webapp/vo.py
  72. 3
      vasl_templates/webapp/vo_notes.py

@ -7,7 +7,7 @@ extension-pkg-whitelist=PyQt5
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
ignore=generated
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
@ -18,7 +18,7 @@ ignore-patterns=
#init-hook=
# Use multiple processes to speed up Pylint.
jobs=1
jobs=4
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.

@ -24,18 +24,19 @@ RUN url=$( curl -s https://api.github.com/repos/mozilla/geckodriver/releases/lat
curl -sL "$url" \
| tar -C /usr/bin/ -xz
# clean up
RUN dnf clean all
# install the application requirements
WORKDIR /app
COPY requirements.txt requirements-dev.txt ./
RUN pip3 install -r requirements.txt
ARG ENABLE_TESTS
RUN if [ "$ENABLE_TESTS" ]; then \
ARG CONTROL_TESTS_PORT
RUN if [ -n "$CONTROL_TESTS_PORT" ]; then \
dnf install -y gcc-c++ python3-devel && \
pip3 install -r requirements-dev.txt \
; fi
# clean up
RUN dnf clean all
# install the application
COPY vasl_templates/webapp/ ./vasl_templates/webapp/
COPY vassal-shim/release/vassal-shim.jar ./vassal-shim/release/
@ -44,9 +45,6 @@ RUN pip3 install --editable .
# install the config files
COPY docker/config/ ./vasl_templates/webapp/config/
RUN if [ "$ENABLE_TESTS" ]; then \
echo "ENABLE_REMOTE_TEST_CONTROL = 1" >>vasl_templates/webapp/config/debug.cfg \
; fi
# create a new user
RUN useradd --create-home app

@ -2,6 +2,8 @@
import os
import threading
import json
import re
import logging
import tempfile
import urllib.request
@ -11,8 +13,8 @@ import pytest
from flask import url_for
from vasl_templates.webapp import app
app.testing = True
from vasl_templates.webapp.tests import utils
from vasl_templates.webapp.tests.control_tests import ControlTests
FLASK_WEBAPP_PORT = 5011
@ -25,7 +27,7 @@ def pytest_addoption( parser ):
# add test options
parser.addoption(
"--server-url", action="store", dest="server_url", default=None,
"--webapp", action="store", dest="webapp_url", default=None,
help="Webapp server to test against."
)
# NOTE: Chrome seems to be ~15% faster than Firefox, headless ~5% faster than headful.
@ -48,31 +50,6 @@ def pytest_addoption( parser ):
help="Run a shorter version of the test suite."
)
# NOTE: Some tests require the VASL module file(s). We don't want to put these into source control,
# so we provide this option to allow the caller to specify where they live.
parser.addoption(
"--vasl-mods", action="store", dest="vasl_mods", default=None,
help="Directory containing the VASL .vmod file(s)."
)
parser.addoption(
"--vasl-extensions", action="store", dest="vasl_extensions", default=None,
help="Directory containing the VASL extensions."
)
# NOTE: Some tests require VASSAL to be installed. This option allows the caller to specify
# where it is (multiple installations can be placed in sub-directories).
parser.addoption(
"--vassal", action="store", dest="vassal", default=None,
help="Directory containing VASSAL installation(s)."
)
# NOTE: Some tests require Chapter H vehicle/ordnance notes. This is copyrighted material,
# so it is kept in a private repo.
parser.addoption(
"--vo-notes", action="store", dest="vo_notes", default=None,
help="Directory containing Chapter H vehicle/ordnance notes and test results."
)
# NOTE: It's not good to have the code run differently to how it will normally,
# but using the clipboard to retrieve snippets causes more trouble than it's worth :-/
# since any kind of clipboard activity while the tests are running could cause them to fail
@ -84,13 +61,34 @@ def pytest_addoption( parser ):
# ---------------------------------------------------------------------
@pytest.fixture( scope="session" )
_webapp = None
@pytest.fixture( scope="function" )
def webapp():
"""Launch the webapp."""
# get the global webapp fixture
global _webapp
if _webapp is None:
_webapp = _make_webapp()
# reset the remote webapp server
_webapp.control_tests.start_tests()
# return the webapp to the caller
yield _webapp
# reset the remote webapp server
_webapp.control_tests.end_tests()
def _make_webapp():
"""Create the global webapp fixture."""
# initialize
server_url = pytest.config.option.server_url #pylint: disable=no-member
app.base_url = server_url if server_url else "http://localhost:{}".format(FLASK_WEBAPP_PORT)
webapp_url = pytest.config.option.webapp_url #pylint: disable=no-member
if webapp_url and not webapp_url.startswith( "http://" ):
webapp_url = "http://" + webapp_url
app.base_url = webapp_url if webapp_url else "http://localhost:{}".format( FLASK_WEBAPP_PORT )
logging.disable( logging.CRITICAL )
# initialize
@ -119,10 +117,19 @@ def webapp():
app.url_for = make_webapp_url
# check if we need to start a local webapp server
if not server_url:
if not webapp_url:
# yup - make it so
# NOTE: We run the server thread as a daemon so that it won't prevent the tests from finishing
# when they're done. We used to call $/shutdown after yielding the webapp fixture, but when
# we changed it from being per-session to per-function, we can no longer do that.
# This means that the webapp doesn't get a chance to shutdown properly (in particular,
# clean up the gRPC service), but since we send an EndTests message at the of each test,
# the remote server gets a chance to clean up then. It's not perfect (e.g. if the tests fail
# or otherwise finish eearly before they get a chance to send the EndTests message), but
# we can live with it.
thread = threading.Thread(
target = lambda: app.run( host="0.0.0.0", port=FLASK_WEBAPP_PORT, use_reloader=False )
target = lambda: app.run( host="0.0.0.0", port=FLASK_WEBAPP_PORT, use_reloader=False ),
daemon = True
)
thread.start()
# wait for the server to start up
@ -138,13 +145,23 @@ def webapp():
assert False, "Unexpected exception: {}".format(ex)
utils.wait_for( 5, is_ready )
# return the server to the caller
yield app
# shutdown the local webapp server
if not server_url:
urllib.request.urlopen( app.url_for("shutdown") ).read()
thread.join()
# set up control of the remote webapp server
try:
resp = json.load(
urllib.request.urlopen( app.url_for( "get_control_tests" ) )
)
except urllib.error.HTTPError as ex:
if ex.code == 404:
raise RuntimeError( "Can't get the test control port - has remote test control been enabled?" )
raise
port_no = resp.get( "port" )
if not port_no:
raise RuntimeError( "The webapp server is not running the test control service." )
mo = re.search( r"^http://(.+):\d+$", app.base_url )
addr = "{}:{}".format( mo.group(1), port_no )
app.control_tests = ControlTests( addr )
return app
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

@ -1,5 +1,5 @@
[Debug]
; NOTE: These need to be mapped in if you want to run the test suite against a container.
TEST_VASSAL_ENGINES = /test-data/vassal/
TEST_VASL_MODS = /test-data/vasl-vmods/
TEST_VASL_EXTNS_DIR = /test-data/vasl-extensions/
TEST_VASL_MODS = /test-data/vasl-mods/

@ -1,6 +1,5 @@
[Site Config]
FLASK_HOST = 0.0.0.0
IS_CONTAINER = 1
JAVA_PATH = /usr/bin/jdk-15.0.1/bin/java

@ -6,4 +6,6 @@ export DISPLAY=:10.0
Xvfb :10 -ac 1>/tmp/xvfb.log 2>/tmp/xvfb.err &
# run the webapp server
python3 /app/vasl_templates/webapp/run_server.py
python3 /app/vasl_templates/webapp/run_server.py \
--addr 0.0.0.0 \
--force-init-delay 30

@ -1,4 +1,5 @@
pytest==3.6.0
grpcio-tools==1.33.2
tabulate==0.8.2
lxml==4.2.4
pylint==1.9.2

@ -10,19 +10,25 @@ function print_help {
-p --port Web server port number.
--vassal VASSAL installation directory.
-v --vasl-vmod Path to the VASL .vmod file.
-v --vasl Path to the VASL module file (.vmod ).
-e --vasl-extensions Path to the VASL extensions directory.
--boards Path to the VASL boards.
--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.
-t --tag Docker image tag.
--name Docker container name.
-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.
--no-build Launch the container as-is (i.e. without rebuilding the image first).
--build-network Docker network to use when building the image.
--run-network Docker network to use when running the container.
Options for the test suite:
--control-tests-port Remote test control port number.
--test-data-vassal Directory containing VASSAL releases.
--test-data-vasl-mods Directory containing VASL modules.
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
@ -39,32 +45,29 @@ EOM
# initialize
cd `dirname "$0"`
PORT=5010
VASSAL_LOCAL=
VASSAL=
VASL_MOD_LOCAL=
VASL_MOD=
VASL_BOARDS_LOCAL=
VASL_BOARDS=
VASL_EXTNS_LOCAL=
VASL_EXTNS=
CHAPTER_H_NOTES_LOCAL=
VASL_BOARDS=
CHAPTER_H_NOTES=
TEMPLATE_PACK_LOCAL=
TEMPLATE_PACK=
USER_FILES_LOCAL=
USER_FILES=
TAG=latest
TEMPLATE_PACK=
IMAGE_TAG=latest
CONTAINER_NAME=vasl-templates
DETACH=
NO_BUILD=
BUILD_NETWORK=
RUN_NETWORK=
CONTROL_TESTS_PORT=
TEST_DATA_VASSAL=
TEST_DATA_VASL_MODS=
# parse the command-line arguments
if [ $# -eq 0 ]; then
print_help
exit 0
fi
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" -- "$@")"
params="$(getopt -o p:v:e:k:t:d -l port:,control-tests-port:,vassal:,vasl:,vasl-extensions:,boards:,chapter-h:,template-pack:,user-files:,tag:,name:,detach,no-build,build-network:,run-network:,test-data-vassal:,test-data-vasl-mods:,help --name "$0" -- "$@")"
if [ $? -ne 0 ]; then exit 1; fi
eval set -- "$params"
while true; do
@ -73,28 +76,31 @@ while true; do
PORT=$2
shift 2 ;;
--vassal)
VASSAL_LOCAL=$2
VASSAL=$2
shift 2 ;;
-v | --vasl-vmod)
VASL_MOD_LOCAL=$2
-v | --vasl)
VASL_MOD=$2
shift 2 ;;
-e | --vasl-extensions)
VASL_EXTNS_LOCAL=$2
VASL_EXTNS=$2
shift 2 ;;
--boards)
VASL_BOARDS_LOCAL=$2
VASL_BOARDS=$2
shift 2 ;;
--chapter-h)
CHAPTER_H_NOTES_LOCAL=$2
CHAPTER_H_NOTES=$2
shift 2 ;;
--user-files)
USER_FILES_LOCAL=$2
USER_FILES=$2
shift 2 ;;
-k | --template-pack)
TEMPLATE_PACK_LOCAL=$2
TEMPLATE_PACK=$2
shift 2 ;;
-t | --tag)
TAG=$2
IMAGE_TAG=$2
shift 2 ;;
--name)
CONTAINER_NAME=$2
shift 2 ;;
-d | --detach )
DETACH=--detach
@ -110,6 +116,17 @@ while true; do
--run-network )
RUN_NETWORK="--network $2"
shift 2 ;;
--control-tests-port)
CONTROL_TESTS_PORT=$2
shift 2 ;;
--test-data-vassal )
target=`readlink -f "$2"`
TEST_DATA_VASSAL="--volume $target:/test-data/vassal/"
shift 2 ;;
--test-data-vasl-mods )
target=`readlink -f "$2"`
TEST_DATA_VASL_MODS="--volume $target:/test-data/vasl-mods/"
shift 2 ;;
--help )
print_help
exit 0 ;;
@ -121,102 +138,114 @@ while true; do
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"
if [ -n "$VASSAL" ]; then
if [ ! -d "$VASSAL" ]; then
echo "Can't find the VASSAL directory: $VASSAL"
exit 1
fi
VASSAL=/data/vassal/
VASSAL_VOLUME="--volume `readlink -f "$VASSAL_LOCAL"`:$VASSAL"
VASSAL_ENV="--env VASSAL_DIR=$VASSAL"
target=/data/vassal/
VASSAL_VOLUME="--volume `readlink -f "$VASSAL"`:$target"
VASSAL_ENV="--env VASSAL_DIR=$target"
fi
# check if a VASL .vmod file has been specified
if [ -n "$VASL_MOD_LOCAL" ]; then
if [ ! -f "$VASL_MOD_LOCAL" ]; then
echo "Can't find the VASL .vmod file: $VASL_MOD_LOCAL"
# check if a VASL module file has been specified
if [ -n "$VASL_MOD" ]; then
if [ ! -f "$VASL_MOD" ]; then
echo "Can't find the VASL .vmod file: $VASL_MOD"
exit 1
fi
VASL_MOD=/data/vasl.vmod
VASL_MOD_VOLUME="--volume `readlink -f "$VASL_MOD_LOCAL"`:$VASL_MOD"
VASL_MOD_ENV="--env VASL_MOD=$VASL_MOD"
target=/data/vasl.vmod
VASL_MOD_VOLUME="--volume `readlink -f "$VASL_MOD"`:$target"
VASL_MOD_ENV="--env VASL_MOD=$target"
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"
# check if a VASL extensions directory has been specified
if [ -n "$VASL_EXTNS" ]; then
if [ ! -d "$VASL_EXTNS" ]; then
echo "Can't find the VASL extensions directory: $VASL_EXTNS"
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"
target=/data/vasl-extensions/
VASL_EXTNS_VOLUME="--volume `readlink -f "$VASL_EXTNS"`:$target"
VASL_EXTNS_ENV="--env VASL_EXTNS_DIR=$target"
fi
# check if a VASL extensions directory has been specified
if [ -n "$VASL_EXTNS_LOCAL" ]; then
if [ ! -d "$VASL_EXTNS_LOCAL" ]; then
echo "Can't find the VASL extensions directory: $_EXTNS_DIR_LOCAL"
# check if a VASL boards directory has been specified
if [ -n "$VASL_BOARDS" ]; then
if [ ! -d "$VASL_BOARDS" ]; then
echo "Can't find the VASL boards directory: $VASL_BOARDS"
exit 1
fi
VASL_EXTNS=/data/vasl-extensions/
VASL_EXTNS_VOLUME="--volume `readlink -f "$VASL_EXTNS_LOCAL"`:$VASL_EXTNS"
VASL_EXTNS_ENV="--env VASL_EXTNS_DIR=$VASL_EXTNS"
target=/data/boards/
VASL_BOARDS_VOLUME="--volume `readlink -f "$VASL_BOARDS"`:$target"
VASL_BOARDS_ENV="--env BOARDS_DIR=$target"
fi
# check if a Chapter H notes directory has been specified
if [ -n "$CHAPTER_H_NOTES_LOCAL" ]; then
if [ ! -d "$CHAPTER_H_NOTES_LOCAL" ]; then
echo "Can't find the Chapter H notes directory: $CHAPTER_H_NOTES_LOCAL"
if [ -n "$CHAPTER_H_NOTES" ]; then
if [ ! -d "$CHAPTER_H_NOTES" ]; then
echo "Can't find the Chapter H notes directory: $CHAPTER_H_NOTES"
exit 1
fi
CHAPTER_H_NOTES=/data/chapter-h-notes/
CHAPTER_H_NOTES_VOLUME="--volume `readlink -f "$CHAPTER_H_NOTES_LOCAL"`:$CHAPTER_H_NOTES"
CHAPTER_H_NOTES_ENV="--env CHAPTER_H_NOTES_DIR=$CHAPTER_H_NOTES"
target=/data/chapter-h-notes/
CHAPTER_H_NOTES_VOLUME="--volume `readlink -f "$CHAPTER_H_NOTES"`:$target"
CHAPTER_H_NOTES_ENV="--env CHAPTER_H_NOTES_DIR=$target"
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"
# check if a user files directory has been specified
if [ -n "$USER_FILES" ]; then
if [ ! -d "$USER_FILES" ]; then
echo "Can't find the user files directory: $USER_FILES"
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"
target=/data/user-files/
USER_FILES_VOLUME="--volume `readlink -f "$USER_FILES"`:$target"
USER_FILES_ENV="--env USER_FILES_DIR=$target"
fi
# check if a user files directory has been specified
if [ -n "$USER_FILES_LOCAL" ]; then
if [ ! -d "$USER_FILES_LOCAL" ]; then
echo "Can't find the user files directory: $USER_FILES_LOCAL"
# check if a template pack has been specified
if [ -n "$TEMPLATE_PACK" ]; then
# NOTE: The template pack can either be a file (ZIP) or a directory.
if ! ls "$TEMPLATE_PACK" >/dev/null 2>&1 ; then
echo "Can't find the template pack: $TEMPLATE_PACK"
exit 1
fi
USER_FILES=/data/user-files/
USER_FILES_VOLUME="--volume `readlink -f "$USER_FILES_LOCAL"`:$USER_FILES"
USER_FILES_ENV="--env USER_FILES_DIR=$USER_FILES"
target=/data/template-pack
TEMPLATE_PACK_VOLUME="--volume `readlink -f "$TEMPLATE_PACK"`:$target"
TEMPLATE_PACK_ENV="--env DEFAULT_TEMPLATE_PACK=$target"
fi
# check if testing has been enabled
if [ -n "$CONTROL_TESTS_PORT" ]; then
CONTROL_TESTS_PORT_BUILD="--build-arg CONTROL_TESTS_PORT=$CONTROL_TESTS_PORT"
CONTROL_TESTS_PORT_RUN="--env CONTROL_TESTS_PORT=$CONTROL_TESTS_PORT --publish $CONTROL_TESTS_PORT:$CONTROL_TESTS_PORT"
fi
# build the container
# build the image
if [ -z "$NO_BUILD" ]; then
echo Building the \"$TAG\" container...
docker build $BUILD_NETWORK --tag vasl-templates:$TAG . 2>&1 \
| sed -e 's/^/ /'
echo Building the \"$IMAGE_TAG\" image...
docker build \
--tag vasl-templates:$IMAGE_TAG \
$CONTROL_TESTS_PORT_BUILD \
$BUILD_NETWORK \
. 2>&1 \
| sed -e 's/^/ /'
if [ ${PIPESTATUS[0]} -ne 0 ]; then exit 10 ; fi
echo
fi
# launch the container
echo Launching the \"$TAG\" container...
echo Launching the \"$IMAGE_TAG\" image as \"$CONTAINER_NAME\"...
docker run \
--name $CONTAINER_NAME \
--publish $PORT:5010 \
--name vasl-templates \
$VASSAL_VOLUME $VASL_MOD_VOLUME $VASL_BOARDS_VOLUME $VASL_EXTNS_VOLUME $CHAPTER_H_NOTES_VOLUME $USER_FILES_VOLUME $TEMPLATE_PACK_VOLUME \
$VASSAL_ENV $VASL_MOD_ENV $VASL_BOARDS_ENV $VASL_EXTNS_ENV $CHAPTER_H_NOTES_ENV $USER_FILES_ENV $TEMPLATE_PACK_ENV \
$CONTROL_TESTS_PORT_RUN \
$VASSAL_VOLUME $VASL_MOD_VOLUME $VASL_EXTNS_VOLUME $VASL_BOARDS_VOLUME $CHAPTER_H_NOTES_VOLUME $TEMPLATE_PACK_VOLUME $USER_FILES_VOLUME \
$VASSAL_ENV $VASL_MOD_ENV $VASL_EXTNS_ENV $VASL_BOARDS_ENV $CHAPTER_H_NOTES_ENV $TEMPLATE_PACK_ENV $USER_FILES_ENV \
$RUN_NETWORK $DETACH \
$TEST_DATA_VASSAL $TEST_DATA_VASL_MODS \
-it --rm \
vasl-templates:$TAG \
vasl-templates:$IMAGE_TAG \
2>&1 \
| sed -e 's/^/ /'
| sed -e 's/^/ /'
exit ${PIPESTATUS[0]}

@ -205,11 +205,6 @@ def _do_main( template_pack, default_scenario, remote_debugging, debug ): #pylin
main_window.show()
ret_code = qt_app.exec_()
# shutdown the webapp server
url = "http://localhost:{}/shutdown".format( port )
urllib.request.urlopen( url ).read()
thread.join()
return ret_code
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

@ -31,11 +31,11 @@ stats = defaultdict( lambda: [0,0] ) # nb: [ #runs, total elapsed time ]
# ---------------------------------------------------------------------
@click.command()
@click.option( "--server-url", default="http://localhost:5010", help="Webapp server URL." )
@click.option( "--webapp-url", default="http://localhost:5010", help="Webapp server URL." )
@click.option( "--snippet-images", default=1, help="Number of 'snippet image' threads to run." )
@click.option( "--update-vsav", default=1, help="Number of 'update VSAV' threads to run." )
@click.option( "--vsav","vsav_fname", help="VASL scenario file (.vsav) to be updated." )
def main( server_url, snippet_images, update_vsav, vsav_fname ):
def main( webapp_url, snippet_images, update_vsav, vsav_fname ):
"""Stress-test the shared WebDriver."""
# initialize
@ -52,13 +52,13 @@ def main( server_url, snippet_images, update_vsav, vsav_fname ):
threads.append( threading.Thread(
target = snippet_images_thread,
name = "snippet-images/{:02d}".format( 1+i ),
args = ( server_url, )
args = ( webapp_url, )
) )
for i in range(0,update_vsav):
threads.append( threading.Thread(
target = update_vsav_thread,
name = "update-vsav/{:02d}".format( 1+i ),
args = ( server_url, vsav_fname, vsav_data )
args = ( webapp_url, vsav_fname, vsav_data )
) )
# launch the test threads
@ -96,14 +96,14 @@ def main( server_url, snippet_images, update_vsav, vsav_fname ):
# ---------------------------------------------------------------------
def snippet_images_thread( server_url ):
def snippet_images_thread( webapp_url ):
"""Test generating snippet images."""
with WebDriver() as webdriver:
# initialize
webdriver = webdriver.driver
init_webapp( webdriver, server_url,
init_webapp( webdriver, webapp_url,
[ "snippet_image_persistence", "scenario_persistence" ]
)
@ -169,7 +169,7 @@ def snippet_images_thread( server_url ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def update_vsav_thread( server_url, vsav_fname, vsav_data ):
def update_vsav_thread( webapp_url, vsav_fname, vsav_data ):
"""Test updating VASL scenario files."""
# initialize
@ -179,7 +179,7 @@ def update_vsav_thread( server_url, vsav_fname, vsav_data ):
# initialize
webdriver = webdriver.driver
init_webapp( webdriver, server_url,
init_webapp( webdriver, webapp_url,
[ "vsav_persistence", "scenario_persistence" ]
)
@ -242,10 +242,10 @@ def log( fmt, *args, **kwargs ):
# ---------------------------------------------------------------------
def init_webapp( webdriver, server_url, options ):
def init_webapp( webdriver, webapp_url, options ):
"""Initialize the webapp."""
log( "Initializing the webapp." )
url = server_url + "?" + "&".join( "{}=1".format(opt) for opt in options )
url = webapp_url + "?" + "&".join( "{}=1".format(opt) for opt in options )
url += "&store_msgs=1" # nb: stop notification balloons from building up
webdriver.get( url )
wait_for( 5, lambda: find_child("#_page-loaded_",webdriver) is not None )

@ -4,46 +4,70 @@ import sys
import os
import signal
import threading
import time
import tempfile
import configparser
import logging
import logging.config
from flask import Flask
from flask import Flask, request
import yaml
from vasl_templates.webapp.config.constants import BASE_DIR
shutdown_event = threading.Event()
_LOCK_FNAME = os.path.join( tempfile.gettempdir(), "vasl-templates.lock" )
# ---------------------------------------------------------------------
def _on_startup():
_init_done = False
_init_lock = threading.Lock()
def _on_request():
"""Called before each request."""
# initialize the webapp on the first request, except for $/control-tests.
# NOTE: The test suite calls $/control-tests to find out which port the gRPC test control service
# is running on, which is nice since we don't need to configure both ends with a predefined port.
# However, we don't want this call to trigger initialization, since the tests will often want to
# configure the remote webapp before loading the main page.
if request.path == "/control-tests":
return
with _init_lock:
global _init_done
if not _init_done or (request.path == "/" and request.args.get("force-reinit")):
_init_webapp()
_init_done = True
def _init_webapp():
"""Do startup initialization."""
# NOTE: While this is generally called only once (before the first request), the test suite
# can force it be done again, since it wants to reconfigure the server to test different cases.
# start downloading files
# NOTE: We used to do this in the mainline code of __init__, so that we didn't have to wait
# for the first request before starting the download (useful if we are running as a standalone server).
# However, this means that the downloads start whenever we import this module e.g. for a stand-alone
# command-line tool :-/ Instead, we send a dummy request in run_server.py to trigger a call
# to this function.
from vasl_templates.webapp.downloads import DownloadedFile
threading.Thread( daemon=True,
target = DownloadedFile.download_files
).start()
if not _init_done:
from vasl_templates.webapp.downloads import DownloadedFile
threading.Thread( daemon=True,
target = DownloadedFile.download_files
).start()
# load the default template_pack
from vasl_templates.webapp.snippets import load_default_template_pack
load_default_template_pack()
# configure the VASL module
# NOTE: The Docker container configures this setting via an environment variable.
fname = app.config.get( "VASL_MOD", os.environ.get("VASL_MOD") )
if fname:
from vasl_templates.webapp.vasl_mod import set_vasl_mod #pylint: disable=cyclic-import
from vasl_templates.webapp.main import startup_msg_store #pylint: disable=cyclic-import
set_vasl_mod( fname, startup_msg_store )
fname = app.config.get( "VASL_MOD" )
from vasl_templates.webapp.vasl_mod import set_vasl_mod #pylint: disable=cyclic-import
from vasl_templates.webapp.main import startup_msg_store #pylint: disable=cyclic-import
set_vasl_mod( fname, startup_msg_store )
# load the vehicle/ordnance listings
from vasl_templates.webapp.vo import load_vo_listings #pylint: disable=cyclic-import
from vasl_templates.webapp.main import startup_msg_store #pylint: disable=cyclic-import
load_vo_listings( startup_msg_store )
# load the vehicle/ordnance notes
@ -65,19 +89,80 @@ def load_debug_config( fname ):
"""Configure the application."""
_load_config( fname, "Debug" )
def _set_config_from_env( key ):
"""Set an app config setting from an environment variable."""
val = os.environ.get( key )
if val:
app.config[ key ] = val
def _is_flask_child_process():
"""Check if we are the Flask child process."""
# NOTE: There are actually 3 possible cases:
# (*) Flask reloading is enabled:
# - we are the parent process (returns False)
# - we are the child process (returns True)
# (*) Flask reloading is disabled:
# - returns False
return os.environ.get( "WERKZEUG_RUN_MAIN" ) is not None
# ---------------------------------------------------------------------
def _on_sigint( signum, stack ): #pylint: disable=unused-argument
"""Clean up after a SIGINT."""
# FUDGE! Since we added gRPC test control, we want to shutdown properly and clean things up (e.g. temp files
# created by the gRPC service), but the Flask reloader complicates what we have to do here horribly :-(
# Since automatic reloading is a really nice feature to have, we try to handle things.
# If the Flask app is started with reloading enabled, it launches a child process to actually do the work,
# that is restarted when any of the monitored files change. It's easy for each process to figure out
# if it's the parent or child, but they need to synchronize their shutdown. Both processes get the SIGINT,
# but the parent can't just exit, since that will cause the child process to terminate, even if it hasn't
# finished shutting down i.e. the parent process needs to wait for the child process to finish shutting down
# before it can exit itself.
# Unfortunately, the way the child process is launched (see werkzeug._reloader.restart_with_reloader())
# means that there is no way for us to know what the child process is (and hence be able to wait for it),
# so the way the child process tells its parent that it has finished shutting down is via a lock file.
# NOTE: We always go through the shutdown process, regardless of whether we are the Flask parent or child process,
# because if Flask reloading is disabled, there will be only one process (that will look like it's the parent),
# and there doesn't seem to be any way to check if reloading is enabled or not. Note that if reloading is enabled,
# then doing shutdown in the parent process will be harmless, since it won't have done any real work (it's all done
# by the child process), and so there won't be anything to clean up.
# notify everyone that we're shutting down
shutdown_event.set()
# call any registered cleanup handlers
from vasl_templates.webapp import globvars #pylint: disable=cyclic-import
for handler in globvars.cleanup_handlers:
handler()
raise SystemExit()
if _is_flask_child_process():
# notify the parent process that we're done
os.unlink( _LOCK_FNAME )
else:
# we are the Flask parent process (so we wait for the child process to finish) or Flask reloading
# is disabled (and the wait below will end immediately, because the lock file was never created).
# NOTE: If, for whatever reason, the lock file doesn't get deleted, we give up waiting and exit anyway.
# This means that the child process might not get to finish cleaning up properly, but if it hasn't
# deleted the lock file, it was probably in trouble anyway.
for _ in range(0, 20):
# NOTE: os.path.isfile() and .exists() both return True even after the log file has gone!?!?
# Is somebody caching something somewhere? :-/
try:
open( _LOCK_FNAME, "r" )
except FileNotFoundError:
break
time.sleep( 0.1 )
raise SystemExit()
# ---------------------------------------------------------------------
# initialize Flask
app = Flask( __name__ )
if _is_flask_child_process():
# we are the Flask child process - create a lock file
open( _LOCK_FNAME, "w" ).close()
# set config defaults
# NOTE: These are defined here since they are used by both the back- and front-ends.
@ -102,6 +187,21 @@ _fname = os.path.join( config_dir, "debug.cfg" )
if os.path.isfile( _fname ) :
load_debug_config( _fname )
# load any config from environment variables (e.g. set in the Docker container)
# NOTE: We could add these settings to the container's site.cfg, so that they are always defined, and things
# would work (or not) depending on whether anything had been mapped to the endpoints. For example, if nothing
# had been mapped to /data/vassal/, we would not find a Vengine.jar and it would look like no VASSAL engine
# had been configured). However, requiring things to be explicitly turned on via an environment variable
# lets us issue better error message, such as "VASSAL has not been configured".
_set_config_from_env( "VASSAL_DIR" )
_set_config_from_env( "VASL_MOD" )
_set_config_from_env( "VASL_EXTNS_DIR" )
_set_config_from_env( "BOARDS_DIR" )
_set_config_from_env( "CHAPTER_H_NOTES_DIR" )
_set_config_from_env( "USER_FILES_DIR" )
# NOTE: The Docker container also sets DEFAULT_TEMPLATE_PACK, but we read it directly from
# the environment variable, since it is not something that is stored in app.config.
# initialize logging
_fname = os.path.join( config_dir, "logging.yaml" )
if os.path.isfile( _fname ):
@ -125,12 +225,9 @@ import vasl_templates.webapp.nat_caps #pylint: disable=cyclic-import
import vasl_templates.webapp.scenarios #pylint: disable=cyclic-import
import vasl_templates.webapp.downloads #pylint: disable=cyclic-import
import vasl_templates.webapp.lfa #pylint: disable=cyclic-import
if app.config.get( "ENABLE_REMOTE_TEST_CONTROL" ):
print( "*** WARNING: Remote test control enabled! ***" )
import vasl_templates.webapp.testing #pylint: disable=cyclic-import
# install our signal handler (must be done in the main thread)
signal.signal( signal.SIGINT, _on_sigint )
# register startup initialization
app.before_first_request( _on_startup )
app.before_request( _on_request )

@ -0,0 +1,10 @@
[Debug]
; Set this if you want to run the test suite (allows the webapp server to be controlled using gRPC).
; CONTROL_TESTS_PORT = -1
; Set this to a directory containing the VASSAL releases to run the test suite with.
; TEST_VASSAL_ENGINES = ...
; Set this to a directory containing the VASL modules (.vmod files) to run the test suite with.
; TEST_VASL_MODS = ...

@ -6,7 +6,7 @@ VASL_MOD = ...configure the VASL module (e.g. vasl-6.5.0.vmod)...
VASL_EXTNS_DIR = ...configured the VASL extensions directory...
BOARDS_DIR = ...configure the VASL boards directory...
; configure support prorams
; configure support params
; JAVA_PATH = ...configure the Java executable here (optional, must be in the PATH otherwise)...
WEBDRIVER_PATH = ...configure either geckodriver or chromedriver here...

@ -130,6 +130,9 @@ class DownloadedFile:
continue
# download the file
if app.config.get( "DISABLE_DOWNLOADED_FILES" ):
_logger.info( "Download disabled (%s): %s", df.key, url )
continue
_logger.info( "Downloading the %s file: %s", df.key, url )
try:
headers = { "Accept-Encoding": "gzip, deflate" }

@ -60,8 +60,7 @@ class FileServer:
@app.route( "/user/<path:path>" )
def get_user_file( path ):
"""Get a static file."""
# NOTE: The Docker container configures this setting via an environment variable.
dname = app.config.get( "USER_FILES_DIR", os.environ.get("USER_FILES_DIR") )
dname = app.config.get( "USER_FILES_DIR" )
if not dname:
abort( 404 )
if not os.path.isdir( dname ):

@ -1,14 +1,16 @@
""" Main webapp handlers. """
import os
import threading
import concurrent
import json
import uuid
import logging
from flask import request, render_template, jsonify, send_file, redirect, url_for, abort
from vasl_templates.webapp import app
from vasl_templates.webapp.utils import MsgStore
from vasl_templates.webapp import app, shutdown_event
from vasl_templates.webapp.utils import MsgStore, parse_int
import vasl_templates.webapp.config.constants
from vasl_templates.webapp.config.constants import BASE_DIR, DATA_DIR
from vasl_templates.webapp import globvars
@ -212,15 +214,71 @@ def get_default_scenario():
# ---------------------------------------------------------------------
_control_tests_port_no = None
@app.route( "/control-tests" )
def get_control_tests():
"""Return information about the remote test control service."""
def get_port():
"""Get the configured gRPC service port."""
# NOTE: The Docker container configures this setting via an environment variable.
return app.config.get( "CONTROL_TESTS_PORT", os.environ.get("CONTROL_TESTS_PORT") )
# check if the test control service should be made available
port_no = get_port()
if not port_no:
abort( 404 )
# check if we've already started the service
if not _control_tests_port_no:
# nope - make it so
print( "*** WARNING: Remote test control enabled! ***" )
started_event = threading.Event()
def run_service(): #pylint: disable=missing-docstring
import grpc
server = grpc.server( concurrent.futures.ThreadPoolExecutor( max_workers=1 ) )
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2_grpc \
import add_ControlTestsServicer_to_server
from vasl_templates.webapp.tests.control_tests_servicer import ControlTestsServicer #pylint: disable=cyclic-import
servicer = ControlTestsServicer( app )
add_ControlTestsServicer_to_server( servicer, server )
port_no = parse_int( get_port(), -1 ) # nb: have to get this again?!
if port_no <= 0:
# NOTE: Requesting port 0 tells grpc to use any free port, which is usually OK, unless
# we're running inside a Docker container, in which case it needs to be pre-defined,
# so that the port can be mapped to an external port when the container is started.
port_no = 0
port_no = server.add_insecure_port( "[::]:{}".format( port_no ) )
logging.getLogger( "control_tests" ).debug(
"Started the gRPC test control service: port=%s", str(port_no)
)
server.start()
global _control_tests_port_no
_control_tests_port_no = port_no
started_event.set()
shutdown_event.wait()
server.stop( None )
server.wait_for_termination()
thread = threading.Thread( target=run_service, daemon=True )
thread.start()
# wait for the service to start (since the caller will probably try to connect
# to it as soon as we return a response).
started_event.wait( timeout=10 )
# wait for the gRPC server to end cleanly when we shutdown
def cleanup(): #pylint: disable=missing-docstring
thread.join()
globvars.cleanup_handlers.append( cleanup )
# return the service info to the caller
return jsonify( { "port": _control_tests_port_no } )
# ---------------------------------------------------------------------
@app.route( "/ping" )
def ping():
"""Let the caller know we're alive."""
return "pong: {}".format( INSTANCE_ID )
# ---------------------------------------------------------------------
@app.route( "/shutdown" )
def shutdown():
"""Shutdown the webapp (for testing porpoises)."""
request.environ.get( "werkzeug.server.shutdown" )()
return ""

@ -7,37 +7,70 @@ import urllib.request
import time
import glob
import click
# ---------------------------------------------------------------------
# monitor extra files for changes
extra_files = []
for fspec in ["config","static","templates"] :
fspec = os.path.abspath( os.path.join( os.path.dirname(__file__), fspec ) )
if os.path.isdir( fspec ):
files = [ os.path.join(fspec,f) for f in os.listdir(fspec) ]
files = [ f for f in files if os.path.isfile(f) and os.path.splitext(f)[1] not in [".swp"] ]
@click.command()
@click.option( "--addr","-a","bind_addr", help="Webapp server address (host:port)." )
@click.option( "--force-init-delay", default=0, help="Force the webapp to initialize (#seconds delay)." )
@click.option( "--debug","flask_debug", is_flag=True, default=False, help="Run Flask in debug mode." )
def main( bind_addr, force_init_delay, flask_debug ):
"""Run the vasl-templates webapp server."""
# initialize
from vasl_templates.webapp import app
port = None
if bind_addr:
words = bind_addr.split( ":" )
host = words[0]
if len(words) > 1:
port = words[1]
else:
files = glob.glob( fspec )
extra_files.extend( files )
# initialize
from vasl_templates.webapp import app
host = app.config.get( "FLASK_HOST", "localhost" )
port = app.config["FLASK_PORT_NO"]
debug = app.config.get( "FLASK_DEBUG", False )
def start_server():
"""Force the server to do "first request" initialization."""
# NOTE: This is not needed when running the desktop app (since it will request the home page),
# but if we're running just the server (i.e. from the console, or a Docker container), then
# sending a request, any request, will trigger the "first request" initialization (in particular,
# the download thread).
time.sleep( 5 )
url = "http://{}:{}/ping".format( host, port )
_ = urllib.request.urlopen( url )
threading.Thread( target=start_server, daemon=True ).start()
# run the server
app.run( host=host, port=port, debug=debug,
extra_files = extra_files
)
host = app.config.get( "FLASK_HOST", "localhost" )
if not port:
port = app.config.get( "FLASK_PORT_NO" )
if not flask_debug:
flask_debug = app.config.get( "FLASK_DEBUG", False )
# validate the configuration
if not host:
raise RuntimeError( "The server host was not set." )
if not port:
raise RuntimeError( "The server port was not set." )
# monitor extra files for changes
extra_files = []
fspecs = [ "static/", "templates/", "config/" ]
fspecs.extend( [ "tests/control_tests_servicer.py", "tests/proto/generated/" ] )
for fspec in fspecs:
fspec = os.path.abspath( os.path.join( os.path.dirname(__file__), fspec ) )
if os.path.isdir( fspec ):
files = [ os.path.join(fspec,f) for f in os.listdir(fspec) ]
files = [ f for f in files if os.path.isfile(f) and os.path.splitext(f)[1] not in [".swp"] ]
else:
files = glob.glob( fspec )
extra_files.extend( files )
# check if we should force webapp initialization
if force_init_delay > 0:
def _start_server():
"""Force the server to do "first request" initialization."""
# NOTE: This is not needed when running the desktop app (since it will request the home page),
# but if we're running just the server (i.e. from the console, or a Docker container), then
# it's useful to send a request (any request), since this will trigger "first request" initialization
# (in particular, starting the download thread).
time.sleep( force_init_delay )
url = "http://{}:{}/ping".format( host, port )
_ = urllib.request.urlopen( url )
threading.Thread( target=_start_server, daemon=True ).start()
# run the server
app.run( host=host, port=port, debug=flask_debug,
extra_files = extra_files
)
# ---------------------------------------------------------------------
if __name__ == "__main__":
main() #pylint: disable=no-value-for-parameter

@ -1,45 +0,0 @@
"""Webapp handlers for testing porpoises."""
import inspect
import base64
from flask import request, jsonify, abort
from vasl_templates.webapp import app
from vasl_templates.webapp.tests.remote import ControlTests
_control_tests = ControlTests( app )
# ---------------------------------------------------------------------
@app.route( "/control-tests/<action>" )
def control_tests( action ):
"""Accept commands from a remote test suite."""
# check if this functionality has been enabled
if not app.config.get( "ENABLE_REMOTE_TEST_CONTROL" ):
abort( 404 )
# figure out what we're being asked to do
func = getattr( _control_tests, action )
if not func:
abort( 404 )
# get any parameters
sig = inspect.signature( func )
kwargs = {}
for param in sig.parameters.values():
if param.name in ("vengine","vmod","gpids","key","val","dtype","fname","dname","extns_dtype","bin_data"):
kwargs[ param.name ] = request.args.get( param.name, param.default )
if param.name == "bin_data" and kwargs["bin_data"]:
kwargs["bin_data"] = base64.b64decode( kwargs["bin_data"] )
# execute the command
resp = func( **kwargs )
# return any response
if isinstance( resp, (str,list,dict) ):
return jsonify( resp )
else:
assert resp == _control_tests, "Methods should return self if there is no response data."
return "ok"

@ -0,0 +1,17 @@
cd `dirname "$0"`/proto
# initialize
rm -rf generated 2>/dev/null
mkdir generated
# compile the protobuf definitions
python -m grpc_tools.protoc \
--proto_path . \
--python_out=generated/ \
--grpc_python_out=generated/ \
control_tests.proto
# FUDGE! Fix a bogus import :-/
sed --in-place \
's/^import control_tests_pb2 as control__tests__pb2$/import vasl_templates.webapp.tests.proto.generated.control_tests_pb2 as control__tests__pb2/' \
generated/control_tests_pb2_grpc.py

@ -0,0 +1,227 @@
""" Allow the test suite to control a remote webapp server. """
import json
import base64
import grpc
from google.protobuf.empty_pb2 import Empty
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2_grpc import ControlTestsStub
from vasl_templates.webapp.tests.proto.utils import enum_from_string
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
SetVassalVersionRequest, SetVaslVersionRequest, SetVaslExtnInfoDirRequest, SetGpidRemappingsRequest, \
SetDataDirRequest, SetDefaultScenarioRequest, SetDefaultTemplatePackRequest, \
SetVehOrdNotesDirRequest, SetUserFilesDirRequest, \
SetAsaScenarioIndexRequest, SetRoarScenarioIndexRequest, \
DumpVsavRequest, GetVaslPiecesRequest, \
SetAppConfigValRequest, DeleteAppConfigValRequest, \
SaveTempFileRequest
# ---------------------------------------------------------------------
# NOTE: The API for this class should be kept in sync with ControlTestsServicer.
class ControlTests: #pylint: disable=too-many-public-methods
"""Control a remote webapp server."""
def __init__( self, addr ):
# initialize
channel = grpc.insecure_channel( addr )
self._stub = ControlTestsStub( channel )
self._caps = None
def has_capability( self, cap ) :
"""Check if the remote webapp has the specified capability."""
return cap in self._caps
def start_tests( self ):
"""Start a new test run."""
resp = self._stub.startTests( Empty() )
self._caps = set( resp.capabilities )
return self
def end_tests( self ):
"""End a test run."""
self._stub.endTests( Empty() )
self._caps = None
def get_vassal_versions( self ):
"""Get the available VASSAL versions."""
resp = self._stub.getVassalVersions( Empty() )
return resp.vassalVersions
def set_vassal_version( self, vassal_version ):
"""Set the VASSAL version."""
self._stub.setVassalVersion(
SetVassalVersionRequest( vassalVersion = vassal_version )
)
return self
def get_vasl_versions( self ):
"""Get the available VASL versions."""
resp = self._stub.getVaslVersions( Empty() )
return resp.vaslVersions
def set_vasl_version( self, vasl_mod, vasl_extns_type ):
"""Set the VASL version."""
vasl_extns_type = enum_from_string(
SetVaslVersionRequest.VaslExtnsType, #pylint: disable=no-member
vasl_extns_type or "{NONE}"
)
self._stub.setVaslVersion(
SetVaslVersionRequest( vaslVersion=vasl_mod, vaslExtnsType=vasl_extns_type )
)
return self
def get_vasl_extns( self ):
"""Get the VASL extensions."""
resp = self._stub.getVaslExtns( Empty() )
return json.loads( resp.vaslExtnsJson )
def set_vasl_extn_info_dir( self, dname ):
"""Set the VASL extensions info directory."""
self._stub.setVaslExtnInfoDir(
SetVaslExtnInfoDirRequest( dirName = dname )
)
return self
def set_gpid_remappings( self, gpid_remappings ):
"""Set the GPID remappings."""
self._stub.setGpidRemappings(
SetGpidRemappingsRequest( gpidRemappingsJson = json.dumps( gpid_remappings ) )
)
return self
def get_vasl_mod_warnings( self ):
"""Get the vasl_mod warnings."""
resp = self._stub.getVaslModWarnings( Empty() )
return resp.warnings
def set_data_dir( self, dtype ):
"""Set the data directory."""
dtype = enum_from_string( SetDataDirRequest.DirType, dtype ) #pylint: disable=no-member
self._stub.setDataDir(
SetDataDirRequest( dirType = dtype )
)
return self
def set_default_scenario( self, fname ):
"""Set the default scenario."""
self._stub.setDefaultScenario(
SetDefaultScenarioRequest( fileName = fname )
)
return self
def set_default_template_pack( self, template_pack ):
"""Set the default template pack."""
if isinstance( template_pack, str ) and template_pack.startswith( "{" ) and template_pack.endswith( "}" ):
val = enum_from_string(
SetDefaultTemplatePackRequest.TemplatePackType, #pylint: disable=no-member
template_pack
)
req = SetDefaultTemplatePackRequest( templatePackType = val )
elif isinstance( template_pack, str ):
req = SetDefaultTemplatePackRequest( dirName = template_pack )
elif isinstance( template_pack, bytes ):
req = SetDefaultTemplatePackRequest( zipData = template_pack )
else:
raise ValueError( "Can't identify template pack type: {}".format( type(template_pack).__name__ ) )
self._stub.setDefaultTemplatePack( req )
return self
def set_vo_notes_dir( self, dtype ):
"""Set the vehicle/ordnance notes directory."""
dtype = enum_from_string( SetVehOrdNotesDirRequest.DirType, dtype or "{NONE}" ) #pylint: disable=no-member
self._stub.setVehOrdNotesDir(
SetVehOrdNotesDirRequest( dirType = dtype )
)
return self
def set_user_files_dir( self, dname_or_url ):
"""Set the user files directory."""
self._stub.setUserFilesDir(
SetUserFilesDirRequest( dirOrUrl = dname_or_url )
)
return self
def set_asa_scenario_index( self, fname ):
"""Set the ASL Scenario Archive scenario index."""
self._stub.setAsaScenarioIndex(
SetAsaScenarioIndexRequest( fileName = fname )
)
return self
def set_roar_scenario_index( self, fname ):
"""Set the ROAR scenario index."""
self._stub.setRoarScenarioIndex(
SetRoarScenarioIndexRequest( fileName = fname )
)
return self
def get_last_snippet_image( self ):
"""Get the last snippet image."""
resp = self._stub.getLastSnippetImage( Empty() )
return resp.imageData
def reset_last_asa_upload( self ):
"""Reset the last ASL Scenario Archive upload."""
self._stub.resetLastAsaUpload( Empty() )
return self
def get_last_asa_upload( self ):
"""Get the last ASL Scenario Archive upload."""
resp = self._stub.getLastAsaUpload( Empty() )
last_asa_upload = json.loads( resp.lastUploadJson )
if last_asa_upload:
for key in ("vasl_setup","screenshot"):
if last_asa_upload.get( key ):
last_asa_upload[key] = base64.b64decode( last_asa_upload[key].encode( "ascii" ) )
return last_asa_upload
def dump_vsav( self, vsav ):
"""Dump a VASL save file."""
if isinstance( vsav, str ):
with open( vsav, "rb" ) as fp:
vsav = fp.read()
resp = self._stub.dumpVsav(
DumpVsavRequest( vsavData = vsav )
)
return resp.vsavDump
def get_vasl_pieces( self, vasl_version ):
"""Get the pieces for the specified VASL module."""
resp = self._stub.getVaslPieces( GetVaslPiecesRequest( vaslVersion=vasl_version ) )
return resp.pieceDump, resp.gpids
def get_app_config( self ):
"""Get the app config."""
resp = self._stub.getAppConfig( Empty() )
return json.loads( resp.appConfigJson )
def set_app_config_val( self, key, val ):
"""Set an app config value."""
if isinstance( val, str ):
req = SetAppConfigValRequest( key=key, strVal=val )
elif isinstance( val, int ):
req = SetAppConfigValRequest( key=key, intVal=val )
elif isinstance( val, bool ):
req = SetAppConfigValRequest( key=key, boolVal=val )
else:
raise ValueError( "Invalid value type: {}".format( type(val).__name__ ) )
self._stub.setAppConfigVal( req )
return self
def delete_app_config_key( self, key ):
"""Delete an app config value."""
self._stub.deleteAppConfigVal(
DeleteAppConfigValRequest( key = key )
)
return self
def save_temp_file( self, fname, data ):
"""Save a temp file."""
self._stub.saveTempFile(
SaveTempFileRequest( fileName=fname, data=data )
)
return self

@ -0,0 +1,536 @@
"""gRPC servicer that allows the webapp server to be controlled."""
import os
import json
import tempfile
import glob
import re
import logging
import io
import copy
import base64
import inspect
import random
import tabulate
from google.protobuf.empty_pb2 import Empty
from vasl_templates.webapp.config.constants import DATA_DIR
from vasl_templates.webapp.vassal import VassalShim
from vasl_templates.webapp.utils import TempFile
from vasl_templates.webapp import \
main as webapp_main, \
vasl_mod as webapp_vasl_mod, \
scenarios as webapp_scenarios, \
snippets as webapp_snippets, \
globvars as webapp_globvars
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2_grpc \
import ControlTestsServicer as BaseControlTestsServicer
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
SetVassalVersionRequest, SetVaslVersionRequest, SetVaslExtnInfoDirRequest, SetGpidRemappingsRequest, \
SetDataDirRequest, SetDefaultScenarioRequest, SetDefaultTemplatePackRequest, \
SetVehOrdNotesDirRequest, SetUserFilesDirRequest, \
SetAsaScenarioIndexRequest, SetRoarScenarioIndexRequest, \
SetAppConfigValRequest, DeleteAppConfigValRequest
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
StartTestsResponse, \
GetVassalVersionsResponse, GetVaslVersionsResponse, GetVaslExtnsResponse, GetVaslModWarningsResponse, \
GetLastSnippetImageResponse, GetLastAsaUploadResponse, \
DumpVsavResponse, GetVaslPiecesResponse, GetAppConfigResponse
# nb: these are defined as a convenience
_VaslExtnsTypes_NONE = SetVaslVersionRequest.VaslExtnsType.NONE #pylint: disable=no-member
_VaslExtnsTypes_REAL = SetVaslVersionRequest.VaslExtnsType.REAL #pylint: disable=no-member
_VaslExtnsTypes_TEMP_DIR = SetVaslVersionRequest.VaslExtnsType.TEMP_DIR #pylint: disable=no-member
_TemplatePackTypes_DEFAULT = SetDefaultTemplatePackRequest.TemplatePackType.DEFAULT #pylint: disable=no-member
_TemplatePackTypes_REAL = SetDefaultTemplatePackRequest.TemplatePackType.REAL #pylint: disable=no-member
_logger = logging.getLogger( "control_tests" )
_FIXTURES_DIR = os.path.join( os.path.dirname(__file__), "fixtures" )
_ORIG_GPID_REMAPPINGS = copy.deepcopy( webapp_vasl_mod.GPID_REMAPPINGS )
_ORIG_CHAPTER_H_NOTES_DIR = None
# ---------------------------------------------------------------------
# NOTE: The API for this class should be kept in sync with ControlTests.
class ControlTestsServicer( BaseControlTestsServicer ): #pylint: disable=too-many-public-methods
"""Allows a webapp server to be controlled by a remote client."""
def __init__( self, webapp ):
# initialize
self._webapp = webapp
global _ORIG_CHAPTER_H_NOTES_DIR
if not _ORIG_CHAPTER_H_NOTES_DIR:
_ORIG_CHAPTER_H_NOTES_DIR = webapp.config.get( "CHAPTER_H_NOTES_DIR" )
self._temp_dir = None
# look for VASSAL engines
_logger.debug( "Locating VASSAL engines:" )
self._vassal_engines = {}
dname = self._webapp.config.get( "TEST_VASSAL_ENGINES" )
if dname:
for root,_,fnames in os.walk( dname ):
if os.sep + "_disabled_" + os.sep in root:
continue
for fname in fnames:
if fname == "Vengine.jar":
if root.endswith( "/lib" ):
root = root[:-4]
# FUDGE! We assume that the version number is part of the path (we can do this
# since we are only used for running tests i.e. in a controlled environment).
mo = re.search( r"\d+\.\d+\.\d+", root )
self._vassal_engines[ mo.group() ] = root
break
for key,val in self._vassal_engines.items():
_logger.debug( "- %s -> %s", key, val )
# look for VASL modules
_logger.debug( "Locating VASL modules:" )
self._vasl_mods = {}
dname = self._webapp.config.get( "TEST_VASL_MODS" )
if dname:
fspec = os.path.join( dname, "*.vmod" )
for fname in glob.glob( fspec ):
# FUDGE! We assume that the version number is part of the filename (we can do this
# since we are only used for running tests i.e. in a controlled environment).
mo = re.search( r"\d+\.\d+\.\d+", os.path.basename(fname) )
self._vasl_mods[ mo.group() ] = fname
for key,val in self._vasl_mods.items():
_logger.debug( "- %s -> %s", key, val )
def __del__( self ):
# clean up
self.cleanup()
def cleanup( self ):
"""Clean up."""
if self._temp_dir:
self._temp_dir.cleanup()
self._temp_dir = None
def startTests( self, request, context ):
"""Start a new test run."""
_logger.info( "=== START TESTS ===" )
# check that everything has been configured properly
# NOTE: We do this here instead of __init__() so that we can return an error message to the client,
# rather than having the servicer fail to start up, giving the client a "can't connect" error.
if not self._vassal_engines:
raise RuntimeError( "No VASSAL releases were configured (see debug.cfg.example)." )
if not self._vasl_mods:
raise RuntimeError( "No VASL modules were configured (see debug.cfg.example)." )
# set up a directory for our temp files
if self._temp_dir:
self._temp_dir.cleanup()
self._temp_dir = tempfile.TemporaryDirectory()
# reset the webapp server
ctx = None
self.setDataDir(
SetDataDirRequest( dirType = SetDataDirRequest.DirType.TEST ), ctx #pylint: disable=no-member
)
self.setDefaultScenario( SetDefaultScenarioRequest( fileName=None ), ctx )
self.setDefaultTemplatePack(
SetDefaultTemplatePackRequest( templatePackType = _TemplatePackTypes_DEFAULT ),
ctx
)
self.setVehOrdNotesDir(
SetVehOrdNotesDirRequest( dirType = SetVehOrdNotesDirRequest.DirType.NONE ), ctx #pylint: disable=no-member
)
self.setUserFilesDir( SetUserFilesDirRequest( dirOrUrl=None ), ctx )
self.setVassalVersion( SetVassalVersionRequest( vassalVersion=None ), ctx )
self.setVaslVersion( SetVaslVersionRequest( vaslVersion=None ), ctx )
self.setGpidRemappings(
SetGpidRemappingsRequest( gpidRemappingsJson = json.dumps(_ORIG_GPID_REMAPPINGS) ), ctx
)
self.setVaslExtnInfoDir( SetVaslExtnInfoDirRequest( dirName=None ), ctx )
self.setAsaScenarioIndex( SetAsaScenarioIndexRequest( fileName="asl-scenario-archive.json" ), ctx )
self.setRoarScenarioIndex( SetRoarScenarioIndexRequest( fileName="roar-scenario-index.json" ), ctx )
self.setAppConfigVal( SetAppConfigValRequest( key="MAP_URL", strVal="MAP:[{LAT},{LONG}]" ), ctx )
self.setAppConfigVal( SetAppConfigValRequest( key="DISABLE_DOWNLOADED_FILES", boolVal=True ), ctx )
self.setAppConfigVal( SetAppConfigValRequest( key="DISABLE_LOCAL_ASA_INDEX_UPDATES", boolVal=True ), ctx )
self.setAppConfigVal( SetAppConfigValRequest( key="DISABLE_LFA_HOTNESS_FADEIN", boolVal=True ), ctx )
self.deleteAppConfigVal( DeleteAppConfigValRequest( key="ALTERNATE_WEBAPP_BASE_URL" ), ctx )
# NOTE: The webapp has been reconfigured, but the client must reloaed the home page
# with "?force-reinit=1", to force it to re-initialize with the new settings.
# return our capabilities to the caller
caps = []
if _ORIG_CHAPTER_H_NOTES_DIR:
# NOTE: Some tests require real Chapter H vehicle/ordnance notes. This is copyrighted material,
# so it is kept in a private repo. For the purpose of running tests, it is considered optional
# and tests that need it can check this capability and not run if it's not available.
caps.append( "chapter-h" )
return StartTestsResponse( capabilities=caps )
def endTests( self, request, context ):
"""End a test run."""
self._log_request( request, context )
# end the test run
self.cleanup()
return Empty()
def getVassalVersions( self, request, context ):
"""Get the available VASSAL versions."""
self._log_request( request, context )
# get the available VASSAL versions
vassal_versions = list( self._vassal_engines.keys() )
_logger.debug( "- Returning VASSAL versions: %s", " ; ".join( vassal_versions ) )
return GetVassalVersionsResponse( vassalVersions=vassal_versions )
def setVassalVersion( self, request, context ):
"""Set the VASSAL version."""
self._log_request( request, context )
vassal_version = request.vassalVersion
# set the VASSAL engine
if vassal_version:
dname = self._vassal_engines.get( vassal_version )
if not dname:
raise RuntimeError( "Unknown VASSAL version: {}".format( vassal_version ) )
else:
dname = None
_logger.debug( "- Setting VASSAL engine: %s", dname )
self._webapp.config[ "VASSAL_DIR" ] = dname
return Empty()
def getVaslVersions( self, request, context ):
"""Get the available VASL versions."""
self._log_request( request, context )
# get the available VASL versions
vasl_versions = list( self._vasl_mods.keys() )
_logger.debug( "- Returning VASL versions: %s", " ; ".join( vasl_versions ) )
return GetVaslVersionsResponse( vaslVersions=vasl_versions )
def setVaslVersion( self, request, context ):
"""Set the VASL version."""
self._log_request( request, context )
vasl_version, vasl_extns_type = request.vaslVersion, request.vaslExtnsType
# set the VASL module
if vasl_version == "random":
# 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 use.
fname = random.choice( list( self._vasl_mods.values() ) )
elif vasl_version:
fname = self._vasl_mods.get( vasl_version )
if not fname:
raise RuntimeError( "Unknown VASL version: {}".format( vasl_version ) )
else:
fname = None
_logger.debug( "- Setting VASL module: %s", fname )
self._webapp.config[ "VASL_MOD" ] = fname
# configure the VASL extensions
if vasl_extns_type == _VaslExtnsTypes_NONE:
dname = None
elif vasl_extns_type == _VaslExtnsTypes_REAL:
dname = os.path.join( _FIXTURES_DIR, "vasl-extensions/real/" )
elif vasl_extns_type == _VaslExtnsTypes_TEMP_DIR:
dname = self._temp_dir.name
else:
raise RuntimeError( "Unknown VASL extensions type: {}".format( vasl_extns_type ) )
_logger.debug( "- Setting VASL extensions: %s", dname )
self._webapp.config[ "VASL_EXTNS_DIR" ] = dname
return Empty()
def getVaslExtns( self, request, context ):
"""Get the VASL extensions."""
self._log_request( request, context )
# get the VASL extensions
vasl_extns = webapp_globvars.vasl_mod.get_extns()
_logger.debug( "- %s", vasl_extns )
return GetVaslExtnsResponse(
vaslExtnsJson = json.dumps( vasl_extns )
)
def setVaslExtnInfoDir( self, request, context ):
"""Set the VASL extensions info directory."""
self._log_request( request, context )
dname = request.dirName
# set the VASL extensions info directory
if dname:
dname = os.path.join( _FIXTURES_DIR, "vasl-extensions/"+dname )
else:
dname = None
_logger.debug( "- Setting the default VASL extension info directory: %s", dname )
self._webapp.config[ "_VASL_EXTN_INFO_DIR_" ] = dname
return Empty()
def setGpidRemappings( self, request, context ):
"""Set the GPID remappings."""
self._log_request( request, context )
gpid_remappings = json.loads( request.gpidRemappingsJson )
# set the GPID remappings
if gpid_remappings == _ORIG_GPID_REMAPPINGS:
_logger.debug( "- Setting GPID remappings: (original)" )
else:
_logger.debug( "- Setting GPID remappings:" )
for vassal_version, mappings in gpid_remappings.items():
_logger.debug( " - %s: %s", vassal_version, mappings )
webapp_vasl_mod.GPID_REMAPPINGS = gpid_remappings
return Empty()
def getVaslModWarnings( self, request, context ):
"""Get the vasl_mod warnings."""
self._log_request( request, context )
# get the vasl_mod warnings
warnings = webapp_vasl_mod._warnings #pylint: disable=protected-access
_logger.debug( "- %s", warnings )
return GetVaslModWarningsResponse( warnings=warnings )
def setDataDir( self, request, context ):
"""Set the data directory."""
self._log_request( request, context )
dtype = request.dirType
# set the data directory
if dtype == SetDataDirRequest.DirType.TEST: #pylint: disable=no-member
dname = os.path.join( _FIXTURES_DIR, "data" )
elif dtype == SetDataDirRequest.DirType.REAL: #pylint: disable=no-member
dname = DATA_DIR
else:
raise RuntimeError( "Unknown data dir type: {}".format( dtype ) )
_logger.debug( "- Setting data directory: %s", dname )
self._webapp.config[ "DATA_DIR" ] = dname
return Empty()
def setDefaultScenario( self, request, context ):
"""Set the default scenario."""
self._log_request( request, context )
fname = request.fileName
# set the default scenario
if fname:
fname = os.path.join( _FIXTURES_DIR, fname )
else:
fname = None
_logger.debug( "- Setting default scenario: %s", fname )
webapp_main.default_scenario = fname
return Empty()
def setDefaultTemplatePack( self, request, context ):
"""Set the default template pack."""
self._log_request( request, context )
# set the default template pack
if request.HasField( "templatePackType" ):
if request.templatePackType == _TemplatePackTypes_DEFAULT:
target = None
elif request.templatePackType == _TemplatePackTypes_REAL:
target = os.path.join( os.path.dirname(__file__), "../data/default-template-pack/" )
else:
raise RuntimeError( "Invalid TemplatePackType: {}".format( request.templatePackType ) )
elif request.HasField( "dirName" ):
target = os.path.join( _FIXTURES_DIR, "template-packs/"+request.dirName )
elif request.HasField( "zipData" ):
fname = os.path.join( self._temp_dir.name, "default-template-pack.zip" )
with open( fname, "wb" ) as fp:
fp.write( request.zipData )
target = fname
else:
raise RuntimeError( "Can't find the default template pack specification." )
_logger.debug( "- Setting default template pack: %s", target )
webapp_snippets.default_template_pack = target
webapp_globvars.template_pack = None # nb: force the default template pack to be reloaded
return Empty()
def setVehOrdNotesDir( self, request, context ):
"""Set the vehicle/ordnance notes directory."""
self._log_request( request, context )
dtype = request.dirType
# set the vehicle/ordnance notes directory
if dtype == SetVehOrdNotesDirRequest.DirType.NONE: #pylint: disable=no-member
dname = None
elif dtype == SetVehOrdNotesDirRequest.DirType.REAL: #pylint: disable=no-member
dname = _ORIG_CHAPTER_H_NOTES_DIR
elif dtype == SetVehOrdNotesDirRequest.DirType.TEST: #pylint: disable=no-member
dname = os.path.join( _FIXTURES_DIR, "vo-notes" )
else:
raise RuntimeError( "Invalid vehicle/ordnance notes dir.type: {}".format( dtype ) )
_logger.debug( "- Setting vehicle/ordnance notes: %s", dname )
self._webapp.config[ "CHAPTER_H_NOTES_DIR" ] = dname
return Empty()
def setUserFilesDir( self, request, context ):
"""Set the user files directory."""
self._log_request( request, context )
# set the user files directory
dname = request.dirOrUrl
if dname:
if not dname.startswith( ( "http://", "https://" ) ):
dname = os.path.join( _FIXTURES_DIR, dname )
else:
dname = None
_logger.debug( "- Setting user files directory: %s", dname )
self._webapp.config[ "USER_FILES_DIR" ] = dname
return Empty()
def setAsaScenarioIndex( self, request, context ):
"""Set the ASL Scenario Archive scenario index."""
self._log_request( request, context )
fname = request.fileName
# set the ASL Scenario Archive scenario index
if fname:
fname = os.path.join( _FIXTURES_DIR, fname )
else:
fname = None
_logger.debug( "- Setting ASA scenario index: %s", fname )
webapp_scenarios._asa_scenarios._set_data( fname ) #pylint: disable=protected-access
return Empty()
def setRoarScenarioIndex( self, request, context ):
"""Set the ROAR scenario index."""
self._log_request( request, context )
fname = request.fileName
# set the ROAR scenario index
if fname:
fname = os.path.join( _FIXTURES_DIR, fname )
else:
fname = None
_logger.debug( "- Setting ROAR scenario index: %s", fname )
webapp_scenarios._roar_scenarios._set_data( fname ) #pylint: disable=protected-access
return Empty()
def getLastSnippetImage( self, request, context ):
"""Get the last snippet image."""
self._log_request( request, context )
# get the last snippet image
last_snippet_image = webapp_snippets.last_snippet_image
_logger.debug( "- Returning the last snippet image: %s",
"#bytes={}".format( len(last_snippet_image) ) if last_snippet_image else None
)
return GetLastSnippetImageResponse( imageData=last_snippet_image )
def resetLastAsaUpload( self, request, context ):
"""Reset the last ASL Scenario Archive upload."""
self._log_request( request, context )
# reset the last ASL Scenario Archive upload
webapp_scenarios._last_asa_upload = None #pylint: disable=protected-access
return Empty()
def getLastAsaUpload( self, request, context ):
"""Get the last ASL Scenario Archive upload."""
last_asa_upload = webapp_scenarios._last_asa_upload #pylint: disable=protected-access
# return the last ASL Scenario Archive upload
_logger.debug( "- Returning the last ASA upload: %s", last_asa_upload )
if last_asa_upload:
for key in ("vasl_setup","screenshot"):
if last_asa_upload.get( key ):
last_asa_upload[key] = base64.b64encode( last_asa_upload[key] ).decode( "ascii" )
return GetLastAsaUploadResponse( lastUploadJson = json.dumps( last_asa_upload ) )
def dumpVsav( self, request, context ):
"""Dump a VASL save file."""
self._log_request( request, context )
# dump the VSAV
with TempFile( mode="wb" ) as temp_file:
temp_file.write( request.vsavData )
temp_file.close( delete=False )
vassal_shim = VassalShim()
vsav_dump = vassal_shim.dump_scenario( temp_file.name )
_logger.debug( "- VSAV dump: #bytes=%s", len(vsav_dump) )
return DumpVsavResponse( vsavDump=vsav_dump )
def getVaslPieces( self, request, context ):
"""Get the pieces for the specified VASL module."""
self._log_request( request, context )
vasl_version = request.vaslVersion
# dump the VASL pieces
fname = self._vasl_mods[ vasl_version ]
vasl_mod = webapp_vasl_mod.VaslMod( fname, self._webapp.config["DATA_DIR"], None )
buf = io.StringIO()
results = [ [ "GPID", "Name", "Front images", "Back images"] ]
pieces = vasl_mod._pieces #pylint: disable=protected-access
# GPID's were originally int's but then changed to str's. We then started seeing non-numeric GPID's :-/
# For back-compat, we try to maintain sort order for numeric values.
def sort_key( val ): #pylint: disable=missing-docstring
if val.isdigit():
return ( "0"*10 + val )[-10:]
else:
# make sure that alphanumeric values appear after numeric values, even if they start with a number
return "_" + val
gpids = sorted( pieces.keys(), key=sort_key ) # nb: because GPID's changed from int to str :-/
for gpid in gpids:
piece = pieces[ gpid ]
assert piece["gpid"] == gpid
results.append( [ gpid, piece["name"], piece["front_images"], piece["back_images"] ] )
print( tabulate.tabulate( results, headers="firstrow", numalign="left" ), file=buf )
# get the piece GPID's
gpids = webapp_vasl_mod.get_vo_gpids( vasl_mod )
return GetVaslPiecesResponse(
pieceDump=buf.getvalue(), gpids=gpids
)
def getAppConfig( self, request, context ):
"""Get the app config."""
self._log_request( request, context )
# get the app config
app_config = self._webapp.config
_logger.debug( "- %s", app_config )
return GetAppConfigResponse(
appConfigJson = json.dumps( app_config, default=str )
)
def setAppConfigVal( self, request, context ):
"""Set an app config value."""
self._log_request( request, context )
# get the app config setting
for val_type in ( "strVal", "intVal", "boolVal" ):
if request.HasField( val_type ):
key, val = request.key, getattr(request,val_type)
_logger.debug( "- Setting app config: %s = %s (%s)", key, str(val), type(val).__name__ )
self._webapp.config[ key ] = val
return Empty()
raise RuntimeError( "Can't find app config key." )
def deleteAppConfigVal( self, request, context ):
"""Delete an app config value."""
self._log_request( request, context )
key = request.key
# delete the app config setting
_logger.debug( "- Deleting app config: %s", key )
if key in self._webapp.config:
del self._webapp.config[ key ]
return Empty()
def saveTempFile( self, request, context ):
"""Save a temp file."""
self._log_request( request, context )
fname, data = request.fileName, request.data
# save the temp file
fname = os.path.join( self._temp_dir.name, fname )
_logger.debug( "- Saving temp file (#bytes=%d): %s", len(data), fname )
with open( fname, "wb" ) as fp:
fp.write( data )
return Empty()
@staticmethod
def _log_request( req, ctx ):
"""Log a request."""
if ctx is None:
return # nb: we don't log internal calls
# get the entry-point name
msg = "{}()".format( inspect.currentframe().f_back.f_code.co_name )
# add the brief request info
func = getattr( req, "brief", None )
if func:
brief = func()
if brief:
msg += ": {}".format( brief )
# add the request dump
func = getattr( req, "dump", None )
if func:
buf = io.StringIO()
func( out=buf )
buf = buf.getvalue().strip()
if buf:
msg += "\n{}".format( buf )
# log the message
_logger.info( "TEST CONTROL: %s", msg )

@ -0,0 +1,32 @@
"""gRPC protobuf definitions (for controlling tests)."""
import sys
import importlib
# ---------------------------------------------------------------------
def _init_classes():
"""Initialize the gRPC classes."""
# process each request/response class
from .utils import get_classes, split_words
for cls in get_classes():
# check if the class has a corresponding module
words = split_words( cls.__name__ )
mod_name = "_".join( words )
try:
mod2 = importlib.import_module( "vasl_templates.webapp.tests.proto." + mod_name )
except ModuleNotFoundError:
continue
# yup - inject the functions into the class
for elem2 in dir(mod2):
obj = getattr( mod2, elem2 )
if not callable( obj ):
continue
setattr( cls, elem2, obj )
# ---------------------------------------------------------------------
_init_classes()

@ -0,0 +1,164 @@
syntax = "proto3" ;
import "google/protobuf/empty.proto" ;
// --------------------------------------------------------------------
message StartTestsResponse {
repeated string capabilities = 1 ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
message GetVassalVersionsResponse {
repeated string vassalVersions = 1 ;
}
message SetVassalVersionRequest {
string vassalVersion = 1 ;
}
message GetVaslVersionsResponse {
repeated string vaslVersions = 1 ;
}
message SetVaslVersionRequest {
enum VaslExtnsType { NONE=0 ; REAL=1 ; TEMP_DIR=2 ; }
string vaslVersion = 1 ;
VaslExtnsType vaslExtnsType = 2 ;
}
message GetVaslExtnsResponse {
string vaslExtnsJson = 1 ;
}
message SetVaslExtnInfoDirRequest {
string dirName = 1 ; // nb: relative to the fixtures directory
}
message SetGpidRemappingsRequest {
string gpidRemappingsJson = 1 ;
}
message GetVaslModWarningsResponse {
repeated string warnings = 1 ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
message SetDataDirRequest {
enum DirType { TEST=0 ; REAL=1 ; }
DirType dirType = 1 ;
}
message SetDefaultScenarioRequest {
string fileName = 1 ; // nb: relative to the fixtures directory
}
message SetDefaultTemplatePackRequest {
enum TemplatePackType { DEFAULT=0 ; REAL=1 ; }
oneof tp_oneof {
TemplatePackType templatePackType = 1 ;
string dirName = 2 ; // nb: relative to the fixtures directory
bytes zipData = 3 ;
}
}
message SetVehOrdNotesDirRequest {
enum DirType { NONE=0 ; TEST=1 ; REAL=2 ; }
DirType dirType = 1 ;
}
message SetUserFilesDirRequest {
string dirOrUrl = 1 ; // nb: relative to the fixtures directory
}
message SetAsaScenarioIndexRequest {
string fileName = 1 ; // nb: relative to the fixtures directory
}
message SetRoarScenarioIndexRequest {
string fileName = 1 ; // nb: relative to the fixtures directory
}
message GetLastSnippetImageResponse {
bytes imageData = 1 ;
}
message GetLastAsaUploadResponse {
string lastUploadJson = 1 ;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
message DumpVsavRequest {
bytes vsavData = 1 ;
}
message DumpVsavResponse {
string vsavDump = 1 ;
}
message GetVaslPiecesRequest {
string vaslVersion = 1 ;
}
message GetVaslPiecesResponse {
string pieceDump = 1 ;
repeated string gpids = 2 ;
}
message GetAppConfigResponse {
string appConfigJson = 1 ;
}
message SetAppConfigValRequest {
string key = 1 ;
oneof ac_oneof {
string strVal = 2 ;
int32 intVal = 3 ;
bool boolVal = 4 ;
}
}
message DeleteAppConfigValRequest {
string key = 1 ;
}
message SaveTempFileRequest {
string fileName = 1 ; // nb: relative to the servicer's temp directory
bytes data = 2 ;
}
// --------------------------------------------------------------------
service ControlTests
{
rpc startTests( google.protobuf.Empty ) returns ( StartTestsResponse ) ;
rpc endTests( google.protobuf.Empty ) returns ( google.protobuf.Empty ) ;
rpc getVassalVersions( google.protobuf.Empty ) returns ( GetVassalVersionsResponse ) ;
rpc setVassalVersion( SetVassalVersionRequest ) returns ( google.protobuf.Empty ) ;
rpc getVaslVersions( google.protobuf.Empty ) returns ( GetVaslVersionsResponse ) ;
rpc setVaslVersion( SetVaslVersionRequest ) returns ( google.protobuf.Empty ) ;
rpc getVaslExtns( google.protobuf.Empty ) returns ( GetVaslExtnsResponse ) ;
rpc setVaslExtnInfoDir( SetVaslExtnInfoDirRequest ) returns ( google.protobuf.Empty ) ;
rpc setGpidRemappings( SetGpidRemappingsRequest ) returns ( google.protobuf.Empty ) ;
rpc getVaslModWarnings( google.protobuf.Empty ) returns ( GetVaslModWarningsResponse ) ;
rpc setDataDir( SetDataDirRequest ) returns ( google.protobuf.Empty ) ;
rpc setDefaultScenario( SetDefaultScenarioRequest ) returns ( google.protobuf.Empty ) ;
rpc setDefaultTemplatePack( SetDefaultTemplatePackRequest ) returns ( google.protobuf.Empty ) ;
rpc setVehOrdNotesDir( SetVehOrdNotesDirRequest ) returns ( google.protobuf.Empty ) ;
rpc setUserFilesDir( SetUserFilesDirRequest ) returns ( google.protobuf.Empty ) ;
rpc setAsaScenarioIndex( SetAsaScenarioIndexRequest ) returns ( google.protobuf.Empty ) ;
rpc setRoarScenarioIndex( SetRoarScenarioIndexRequest ) returns ( google.protobuf.Empty ) ;
rpc getLastSnippetImage( google.protobuf.Empty ) returns ( GetLastSnippetImageResponse ) ;
rpc resetLastAsaUpload( google.protobuf.Empty ) returns ( google.protobuf.Empty ) ;
rpc getLastAsaUpload( google.protobuf.Empty ) returns ( GetLastAsaUploadResponse ) ;
rpc dumpVsav( DumpVsavRequest ) returns ( DumpVsavResponse ) ;
rpc getVaslPieces( GetVaslPiecesRequest ) returns ( GetVaslPiecesResponse ) ;
rpc getAppConfig( google.protobuf.Empty ) returns ( GetAppConfigResponse ) ;
rpc setAppConfigVal( SetAppConfigValRequest ) returns ( google.protobuf.Empty ) ;
rpc deleteAppConfigVal( DeleteAppConfigValRequest ) returns ( google.protobuf.Empty ) ;
rpc saveTempFile( SaveTempFileRequest ) returns ( google.protobuf.Empty ) ;
}

@ -0,0 +1,7 @@
""" Injected functions for DeleteAppConfigValRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a DeleteAppConfigValRequest as a brief string."""
return self.key

@ -0,0 +1,7 @@
""" Injected functions for DumpVsavRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a DumpVsavRequest as a brief string."""
return "#bytes={}".format( len(self.vsavData) )

File diff suppressed because one or more lines are too long

@ -0,0 +1,898 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import vasl_templates.webapp.tests.proto.generated.control_tests_pb2 as control__tests__pb2
from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2
class ControlTestsStub(object):
"""--------------------------------------------------------------------
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.startTests = channel.unary_unary(
'/ControlTests/startTests',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.StartTestsResponse.FromString,
)
self.endTests = channel.unary_unary(
'/ControlTests/endTests',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.getVassalVersions = channel.unary_unary(
'/ControlTests/getVassalVersions',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetVassalVersionsResponse.FromString,
)
self.setVassalVersion = channel.unary_unary(
'/ControlTests/setVassalVersion',
request_serializer=control__tests__pb2.SetVassalVersionRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.getVaslVersions = channel.unary_unary(
'/ControlTests/getVaslVersions',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetVaslVersionsResponse.FromString,
)
self.setVaslVersion = channel.unary_unary(
'/ControlTests/setVaslVersion',
request_serializer=control__tests__pb2.SetVaslVersionRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.getVaslExtns = channel.unary_unary(
'/ControlTests/getVaslExtns',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetVaslExtnsResponse.FromString,
)
self.setVaslExtnInfoDir = channel.unary_unary(
'/ControlTests/setVaslExtnInfoDir',
request_serializer=control__tests__pb2.SetVaslExtnInfoDirRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setGpidRemappings = channel.unary_unary(
'/ControlTests/setGpidRemappings',
request_serializer=control__tests__pb2.SetGpidRemappingsRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.getVaslModWarnings = channel.unary_unary(
'/ControlTests/getVaslModWarnings',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetVaslModWarningsResponse.FromString,
)
self.setDataDir = channel.unary_unary(
'/ControlTests/setDataDir',
request_serializer=control__tests__pb2.SetDataDirRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setDefaultScenario = channel.unary_unary(
'/ControlTests/setDefaultScenario',
request_serializer=control__tests__pb2.SetDefaultScenarioRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setDefaultTemplatePack = channel.unary_unary(
'/ControlTests/setDefaultTemplatePack',
request_serializer=control__tests__pb2.SetDefaultTemplatePackRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setVehOrdNotesDir = channel.unary_unary(
'/ControlTests/setVehOrdNotesDir',
request_serializer=control__tests__pb2.SetVehOrdNotesDirRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setUserFilesDir = channel.unary_unary(
'/ControlTests/setUserFilesDir',
request_serializer=control__tests__pb2.SetUserFilesDirRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setAsaScenarioIndex = channel.unary_unary(
'/ControlTests/setAsaScenarioIndex',
request_serializer=control__tests__pb2.SetAsaScenarioIndexRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.setRoarScenarioIndex = channel.unary_unary(
'/ControlTests/setRoarScenarioIndex',
request_serializer=control__tests__pb2.SetRoarScenarioIndexRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.getLastSnippetImage = channel.unary_unary(
'/ControlTests/getLastSnippetImage',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetLastSnippetImageResponse.FromString,
)
self.resetLastAsaUpload = channel.unary_unary(
'/ControlTests/resetLastAsaUpload',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.getLastAsaUpload = channel.unary_unary(
'/ControlTests/getLastAsaUpload',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetLastAsaUploadResponse.FromString,
)
self.dumpVsav = channel.unary_unary(
'/ControlTests/dumpVsav',
request_serializer=control__tests__pb2.DumpVsavRequest.SerializeToString,
response_deserializer=control__tests__pb2.DumpVsavResponse.FromString,
)
self.getVaslPieces = channel.unary_unary(
'/ControlTests/getVaslPieces',
request_serializer=control__tests__pb2.GetVaslPiecesRequest.SerializeToString,
response_deserializer=control__tests__pb2.GetVaslPiecesResponse.FromString,
)
self.getAppConfig = channel.unary_unary(
'/ControlTests/getAppConfig',
request_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
response_deserializer=control__tests__pb2.GetAppConfigResponse.FromString,
)
self.setAppConfigVal = channel.unary_unary(
'/ControlTests/setAppConfigVal',
request_serializer=control__tests__pb2.SetAppConfigValRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.deleteAppConfigVal = channel.unary_unary(
'/ControlTests/deleteAppConfigVal',
request_serializer=control__tests__pb2.DeleteAppConfigValRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
self.saveTempFile = channel.unary_unary(
'/ControlTests/saveTempFile',
request_serializer=control__tests__pb2.SaveTempFileRequest.SerializeToString,
response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
)
class ControlTestsServicer(object):
"""--------------------------------------------------------------------
"""
def startTests(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def endTests(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getVassalVersions(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setVassalVersion(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getVaslVersions(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setVaslVersion(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getVaslExtns(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setVaslExtnInfoDir(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setGpidRemappings(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getVaslModWarnings(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setDataDir(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setDefaultScenario(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setDefaultTemplatePack(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setVehOrdNotesDir(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setUserFilesDir(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setAsaScenarioIndex(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setRoarScenarioIndex(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getLastSnippetImage(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def resetLastAsaUpload(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getLastAsaUpload(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def dumpVsav(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getVaslPieces(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def getAppConfig(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def setAppConfigVal(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def deleteAppConfigVal(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def saveTempFile(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_ControlTestsServicer_to_server(servicer, server):
rpc_method_handlers = {
'startTests': grpc.unary_unary_rpc_method_handler(
servicer.startTests,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.StartTestsResponse.SerializeToString,
),
'endTests': grpc.unary_unary_rpc_method_handler(
servicer.endTests,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'getVassalVersions': grpc.unary_unary_rpc_method_handler(
servicer.getVassalVersions,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetVassalVersionsResponse.SerializeToString,
),
'setVassalVersion': grpc.unary_unary_rpc_method_handler(
servicer.setVassalVersion,
request_deserializer=control__tests__pb2.SetVassalVersionRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'getVaslVersions': grpc.unary_unary_rpc_method_handler(
servicer.getVaslVersions,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetVaslVersionsResponse.SerializeToString,
),
'setVaslVersion': grpc.unary_unary_rpc_method_handler(
servicer.setVaslVersion,
request_deserializer=control__tests__pb2.SetVaslVersionRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'getVaslExtns': grpc.unary_unary_rpc_method_handler(
servicer.getVaslExtns,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetVaslExtnsResponse.SerializeToString,
),
'setVaslExtnInfoDir': grpc.unary_unary_rpc_method_handler(
servicer.setVaslExtnInfoDir,
request_deserializer=control__tests__pb2.SetVaslExtnInfoDirRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setGpidRemappings': grpc.unary_unary_rpc_method_handler(
servicer.setGpidRemappings,
request_deserializer=control__tests__pb2.SetGpidRemappingsRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'getVaslModWarnings': grpc.unary_unary_rpc_method_handler(
servicer.getVaslModWarnings,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetVaslModWarningsResponse.SerializeToString,
),
'setDataDir': grpc.unary_unary_rpc_method_handler(
servicer.setDataDir,
request_deserializer=control__tests__pb2.SetDataDirRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setDefaultScenario': grpc.unary_unary_rpc_method_handler(
servicer.setDefaultScenario,
request_deserializer=control__tests__pb2.SetDefaultScenarioRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setDefaultTemplatePack': grpc.unary_unary_rpc_method_handler(
servicer.setDefaultTemplatePack,
request_deserializer=control__tests__pb2.SetDefaultTemplatePackRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setVehOrdNotesDir': grpc.unary_unary_rpc_method_handler(
servicer.setVehOrdNotesDir,
request_deserializer=control__tests__pb2.SetVehOrdNotesDirRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setUserFilesDir': grpc.unary_unary_rpc_method_handler(
servicer.setUserFilesDir,
request_deserializer=control__tests__pb2.SetUserFilesDirRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setAsaScenarioIndex': grpc.unary_unary_rpc_method_handler(
servicer.setAsaScenarioIndex,
request_deserializer=control__tests__pb2.SetAsaScenarioIndexRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'setRoarScenarioIndex': grpc.unary_unary_rpc_method_handler(
servicer.setRoarScenarioIndex,
request_deserializer=control__tests__pb2.SetRoarScenarioIndexRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'getLastSnippetImage': grpc.unary_unary_rpc_method_handler(
servicer.getLastSnippetImage,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetLastSnippetImageResponse.SerializeToString,
),
'resetLastAsaUpload': grpc.unary_unary_rpc_method_handler(
servicer.resetLastAsaUpload,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'getLastAsaUpload': grpc.unary_unary_rpc_method_handler(
servicer.getLastAsaUpload,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetLastAsaUploadResponse.SerializeToString,
),
'dumpVsav': grpc.unary_unary_rpc_method_handler(
servicer.dumpVsav,
request_deserializer=control__tests__pb2.DumpVsavRequest.FromString,
response_serializer=control__tests__pb2.DumpVsavResponse.SerializeToString,
),
'getVaslPieces': grpc.unary_unary_rpc_method_handler(
servicer.getVaslPieces,
request_deserializer=control__tests__pb2.GetVaslPiecesRequest.FromString,
response_serializer=control__tests__pb2.GetVaslPiecesResponse.SerializeToString,
),
'getAppConfig': grpc.unary_unary_rpc_method_handler(
servicer.getAppConfig,
request_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString,
response_serializer=control__tests__pb2.GetAppConfigResponse.SerializeToString,
),
'setAppConfigVal': grpc.unary_unary_rpc_method_handler(
servicer.setAppConfigVal,
request_deserializer=control__tests__pb2.SetAppConfigValRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'deleteAppConfigVal': grpc.unary_unary_rpc_method_handler(
servicer.deleteAppConfigVal,
request_deserializer=control__tests__pb2.DeleteAppConfigValRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
'saveTempFile': grpc.unary_unary_rpc_method_handler(
servicer.saveTempFile,
request_deserializer=control__tests__pb2.SaveTempFileRequest.FromString,
response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'ControlTests', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class ControlTests(object):
"""--------------------------------------------------------------------
"""
@staticmethod
def startTests(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/startTests',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.StartTestsResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def endTests(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/endTests',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getVassalVersions(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getVassalVersions',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetVassalVersionsResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setVassalVersion(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setVassalVersion',
control__tests__pb2.SetVassalVersionRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getVaslVersions(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getVaslVersions',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetVaslVersionsResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setVaslVersion(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setVaslVersion',
control__tests__pb2.SetVaslVersionRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getVaslExtns(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getVaslExtns',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetVaslExtnsResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setVaslExtnInfoDir(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setVaslExtnInfoDir',
control__tests__pb2.SetVaslExtnInfoDirRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setGpidRemappings(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setGpidRemappings',
control__tests__pb2.SetGpidRemappingsRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getVaslModWarnings(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getVaslModWarnings',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetVaslModWarningsResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setDataDir(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setDataDir',
control__tests__pb2.SetDataDirRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setDefaultScenario(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setDefaultScenario',
control__tests__pb2.SetDefaultScenarioRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setDefaultTemplatePack(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setDefaultTemplatePack',
control__tests__pb2.SetDefaultTemplatePackRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setVehOrdNotesDir(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setVehOrdNotesDir',
control__tests__pb2.SetVehOrdNotesDirRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setUserFilesDir(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setUserFilesDir',
control__tests__pb2.SetUserFilesDirRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setAsaScenarioIndex(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setAsaScenarioIndex',
control__tests__pb2.SetAsaScenarioIndexRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setRoarScenarioIndex(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setRoarScenarioIndex',
control__tests__pb2.SetRoarScenarioIndexRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getLastSnippetImage(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getLastSnippetImage',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetLastSnippetImageResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def resetLastAsaUpload(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/resetLastAsaUpload',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getLastAsaUpload(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getLastAsaUpload',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetLastAsaUploadResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def dumpVsav(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/dumpVsav',
control__tests__pb2.DumpVsavRequest.SerializeToString,
control__tests__pb2.DumpVsavResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getVaslPieces(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getVaslPieces',
control__tests__pb2.GetVaslPiecesRequest.SerializeToString,
control__tests__pb2.GetVaslPiecesResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def getAppConfig(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/getAppConfig',
google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString,
control__tests__pb2.GetAppConfigResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def setAppConfigVal(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/setAppConfigVal',
control__tests__pb2.SetAppConfigValRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def deleteAppConfigVal(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/deleteAppConfigVal',
control__tests__pb2.DeleteAppConfigValRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def saveTempFile(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/ControlTests/saveTempFile',
control__tests__pb2.SaveTempFileRequest.SerializeToString,
google_dot_protobuf_dot_empty__pb2.Empty.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

@ -0,0 +1,7 @@
""" Injected functions for GetVaslPiecesRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a GetVaslPiecesRequest as a brief string."""
return self.vaslVersion

@ -0,0 +1,7 @@
""" Injected functions for SaveTempFileRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SaveTempFileRequest as a brief string."""
return "{} (#bytes={})".format( self.fileName, len(self.data) )

@ -0,0 +1,14 @@
""" Injected functions for SetAppConfigValRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetAppConfigValRequest as a brief string."""
for val_type in ( "strVal", "intVal", "boolVal" ):
if self.HasField( val_type ):
val = getattr( self, val_type )
return "{} = {} ({})".format(
self.key, val, type(val).__name__
)
assert False
return "{} = ???".format( self.key )

@ -0,0 +1,7 @@
""" Injected functions for SetAsaScenarioIndexRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetAsaScenarioIndexRequest as a brief string."""
return self.fileName

@ -0,0 +1,10 @@
""" Injected functions for SetDataDirRequest. """
from .generated.control_tests_pb2 import SetDataDirRequest
from .utils import enum_to_string
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetDataDirRequest as a brief string."""
return enum_to_string( SetDataDirRequest.DirType, self.dirType ) #pylint: disable=no-member

@ -0,0 +1,7 @@
""" Injected functions for SetDefaultScenarioRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetDefaultScenarioRequest as a brief string."""
return self.fileName

@ -0,0 +1,21 @@
""" Injected functions for SetDefaultTemplatePackRequest. """
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetDefaultTemplatePackRequest
from .utils import enum_to_string
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetDefaultTemplatePackRequest as a brief string."""
if self.HasField( "templatePackType" ):
return enum_to_string(
SetDefaultTemplatePackRequest.TemplatePackType, #pylint: disable=no-member
self.templatePackType
)
elif self.HasField( "dirName" ):
return self.dirName
elif self.HasField( "zipData" ):
return "zip: #bytes={}".format( len(self.zipData) )
else:
assert False
return str( self ).strip()

@ -0,0 +1,7 @@
""" Injected functions for SetRoarScenarioIndexRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetRoarScenarioIndexRequest as a brief string."""
return self.fileName

@ -0,0 +1,7 @@
""" Injected functions for SetUserFilesDirRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetUserFilesDirRequest as a brief string."""
return self.dirOrUrl

@ -0,0 +1,7 @@
""" Injected functions for SetVaslExtnInfoDirRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetVaslExtnInfoDirRequest as a brief string."""
return self.dirName

@ -0,0 +1,13 @@
""" Injected functions for SetVaslVersionRequest. """
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetVaslVersionRequest
from .utils import enum_to_string
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetVaslVersionRequest as a brief string."""
return "{} (extns={})".format(
self.vaslVersion,
enum_to_string( SetVaslVersionRequest.VaslExtnsType, self.vaslExtnsType ) #pylint: disable=no-member
)

@ -0,0 +1,7 @@
""" Injected functions for SetVassalVersionRequest. """
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetVassalVersionRequest as a brief string."""
return self.vassalVersion

@ -0,0 +1,10 @@
""" Injected functions for SetVehOrdNotesDirRequest. """
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetVehOrdNotesDirRequest
from .utils import enum_to_string
# ---------------------------------------------------------------------
def brief( self ):
"""Return a SetVehOrdNotesDirRequest as a brief string."""
return enum_to_string( SetVehOrdNotesDirRequest.DirType, self.dirType ) #pylint: disable=no-member

@ -0,0 +1,44 @@
"""Utility functions."""
import inspect
# ---------------------------------------------------------------------
def get_classes():
"""Get the request/response classes."""
from .generated import control_tests_pb2
for elem in dir( control_tests_pb2 ):
if not inspect.isclass( type(elem) ):
continue
if not elem.endswith( ( "Request", "Response" ) ):
continue
cls = getattr( control_tests_pb2, elem )
yield cls
# ---------------------------------------------------------------------
def split_words( val ):
"""Extract words from a camel-cased string."""
words, curr_word = [], []
for ch in val:
if ch.isupper():
if curr_word:
words.append( "".join( curr_word ) )
curr_word = []
curr_word.append( ch.lower() )
if curr_word:
words.append( "".join( curr_word ) )
return words
# ---------------------------------------------------------------------
def enum_to_string( enum, val ):
"""Convert an enum value to a string."""
val = enum.Name( val )
return "{%s}" % val
def enum_from_string( enum, val ):
"""Convert a string to an enum value."""
if not val.startswith( "{" ) or not val.endswith( "}" ):
raise ValueError( "Invalid enumerated value for {}: {}".format( enum.DESCRIPTOR.full_name, val ) )
return enum.Value( val[1:-1] )

@ -1,373 +0,0 @@
"""Allow a remote server to be controlled during tests.
We sometimes make changes to the webapp server during tests, and while we used to do that using pytest's monkeypatch,
that will not work if we are talking to a remote (i.e. in another process) server. This module defines the things
that can be changed during the course of tests, and a simple RPC mechanism that lets them be executed remotely.
It needs to be enabled in the server via the ENABLE_REMOTE_TEST_CONTROL debug switch.
"""
import os
import urllib.request
import json
import glob
import base64
import tempfile
import logging
import random
import pytest
from vasl_templates.webapp import app, globvars
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.utils import TempFile
from vasl_templates.webapp import main as webapp_main
from vasl_templates.webapp import snippets as webapp_snippets
from vasl_templates.webapp import scenarios as webapp_scenarios
from vasl_templates.webapp import vasl_mod as vasl_mod_module
from vasl_templates.webapp import vo_utils as vo_utils_module
_logger = logging.getLogger( "control_tests" )
_ORIG_CHAPTER_H_NOTES_DIR = app.config.get( "CHAPTER_H_NOTES_DIR", os.environ.get("CHAPTER_H_NOTES_DIR") )
# ---------------------------------------------------------------------
class ControlTests:
"""Control a remote server during tests."""
def __init__( self, webapp ):
self.webapp = webapp
try:
self.server_url = pytest.config.option.server_url #pylint: disable=no-member
except AttributeError:
self.server_url = None
# set up a directory for our temp files
self._temp_dir = tempfile.TemporaryDirectory()
def __del__( self ):
self._temp_dir.cleanup()
def __getattr__( self, name ):
"""Generic entry point for handling control requests."""
if name.startswith( ("get_","set_","reset_") ):
# check if we are talking to a local or remote server
if self.server_url:
# remote: return a function that will invoke the handler function on the remote server
def call_remote( **kwargs ): #pylint: disable=missing-docstring
return self._remote_test_control( name, **kwargs )
return call_remote
else:
# local: return the actual handler function
return getattr( self, "_"+name )
raise AttributeError( name )
def _remote_test_control( self, action, **kwargs ):
"""Invoke a handler function on the remote server."""
if "bin_data" in kwargs:
kwargs["bin_data"] = base64.b64encode( kwargs["bin_data"] )
if "gpids" in kwargs:
kwargs["gpids"] = json.dumps( kwargs["gpids"] )
resp = urllib.request.urlopen(
self.webapp.url_for( "control_tests", action=action, **kwargs )
).read()
if resp == b"ok":
return self
else:
return json.loads( resp.decode( "utf-8" ) )
def _get_app_config( self ): #pylint: disable=no-self-use
"""Get the webapp config."""
return {
k: v for k,v in app.config.items()
if isinstance( v, (str,int,bool,list,dict) )
}
def _set_app_config( self, key=None, val=None ):
"""Set the webapp config."""
if val is None:
del app.config[ key ]
else:
app.config[ key ] = val
return self
def _set_data_dir( self, dtype=None ):
"""Set the webapp's data directory."""
if dtype == "real":
dname = DATA_DIR
elif dtype == "test":
dname = os.path.join( os.path.split(__file__)[0], "fixtures/data" )
else:
raise RuntimeError( "Unknown data dir type: {}".format( dtype ) )
_logger.info( "Setting data dir: %s", dname )
self.webapp.config[ "DATA_DIR" ] = dname
vo_utils_module._vo_comments = None #pylint: disable=protected-access
from vasl_templates.webapp.vo import load_vo_listings
load_vo_listings( None )
return self
def _set_default_scenario( self, fname=None ):
"""Set the default scenario."""
if fname:
dname = os.path.join( os.path.split(__file__)[0], "fixtures" )
fname = os.path.join( dname, fname )
_logger.info( "Setting default scenario: %s", fname )
webapp_main.default_scenario = fname
return self
def _set_default_template_pack( self, dname=None, bin_data=None ):
"""Set the default template pack."""
if bin_data:
fname = os.path.join( self._temp_dir.name, "default-template-pack.zip" )
with open( fname, "wb" ) as fp:
fp.write( bin_data )
dname = fname
elif dname == "real":
dname = os.path.join( os.path.split(__file__)[0], "../data/default-template-pack" )
elif dname:
dname2 = os.path.join( os.path.split(__file__)[0], "fixtures" )
dname = os.path.join( dname2, dname )
_logger.info( "Setting default template pack: %s", dname )
webapp_snippets.default_template_pack = dname
return self
def _set_gpid_remappings( self, gpids=None ): #pylint: disable=no-self-use
"""Configure the GPID remappings."""
if isinstance( gpids, str ):
gpids = json.loads( gpids.replace( "'", '"' ) )
for row in gpids:
row[1] = { str(k): v for k,v in row[1].items() }
_logger.info( "Setting GPID remappings: %s", gpids )
prev_gpid_mappings = vasl_mod_module.GPID_REMAPPINGS
vasl_mod_module.GPID_REMAPPINGS = gpids
return prev_gpid_mappings
def _get_vasl_mods( self ):
"""Return the available VASL modules."""
fnames = self._do_get_vasl_mods()
_logger.debug( "Returning VASL modules:\n%s",
"\n".join( "- {}".format( f ) for f in fnames )
)
return fnames
def _do_get_vasl_mods( self ): #pylint: disable=no-self-use
"""Return the available VASL modules."""
try:
dname = pytest.config.option.vasl_mods #pylint: disable=no-member
assert dname, "--vasl-mods was not specified."
except AttributeError:
dname = app.config[ "TEST_VASL_MODS" ]
fspec = os.path.join( dname, "*.vmod" )
return glob.glob( fspec )
def _set_vasl_mod( self, vmod=None, extns_dtype=None ):
"""Install a VASL module."""
# configure the VASL extensions
if extns_dtype:
if extns_dtype == "real":
try:
dname = pytest.config.option.vasl_extensions #pylint: disable=no-member
assert dname, "--vasl-extensions was not specified."
except AttributeError:
dname = app.config[ "TEST_VASL_EXTNS_DIR" ]
elif extns_dtype == "test":
dname = self._temp_dir.name
else:
assert False, "Unknown extensions directory type: "+extns_dtype
_logger.info( "Enabling VASL extensions: %s", dname )
app.config[ "VASL_EXTNS_DIR" ] = dname
else:
_logger.info( "Disabling VASL extensions." )
app.config[ "VASL_EXTNS_DIR" ] = None
# configure the VASL module
if vmod:
vmod_fnames = self._do_get_vasl_mods()
if vmod == "random":
# 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.
vmod = random.choice( vmod_fnames )
else:
assert vmod in vmod_fnames
app.config[ "VASL_MOD" ] = vmod
else:
app.config[ "VASL_MOD" ] = None
_logger.info( "Installing VASL module: %s", vmod )
# install the new VASL module
from vasl_templates.webapp.main import startup_msg_store
startup_msg_store.reset()
vasl_mod_module.warnings = []
set_vasl_mod( vmod, startup_msg_store )
from vasl_templates.webapp.vo import load_vo_listings
load_vo_listings( None )
return self
def _get_vasl_extns( self ): #pylint: disable=no-self-use
"""Return the loaded VASL extensions."""
extns = globvars.vasl_mod.get_extns()
_logger.debug( "Returning VASL extensions:\n%s",
"\n".join( "- {}".format( e ) for e in extns )
)
return extns
def _set_test_vasl_extn( self, fname=None, bin_data=None ):
"""Set the test VASL extension."""
fname = os.path.join( self._temp_dir.name, fname )
with open( fname, "wb" ) as fp:
fp.write( bin_data )
return self
def _set_vasl_extn_info_dir( self, dtype=None ):
"""Set the directory containing the VASL extension info files."""
if dtype:
dname = os.path.join( os.path.split(__file__)[0], "fixtures/vasl-extensions" )
dname = os.path.join( dname, dtype )
_logger.info( "Setting the default VASL extension info directory: %s", dname )
app.config[ "_VASL_EXTN_INFO_DIR_" ] = dname
else:
_logger.info( "Using the default VASL extension info directory." )
app.config[ "_VASL_EXTN_INFO_DIR_" ] = None
return self
def _get_vassal_engines( self ):
"""Get the available VASSAL engines."""
vassal_engines = self._do_get_vassal_engines()
_logger.debug( "Returning VASSAL engines:\n%s",
"\n".join( "- {}".format( ve ) for ve in vassal_engines )
)
return vassal_engines
def _do_get_vassal_engines( self ): #pylint: disable=no-self-use
"""Get the available VASSAL engines."""
try:
dname = pytest.config.option.vassal #pylint: disable=no-member
assert dname, "--vassal was not specified."
except AttributeError:
dname = app.config[ "TEST_VASSAL_ENGINES" ]
vassal_engines = []
for root,_,fnames in os.walk( dname ):
if os.sep+"_disabled_"+os.sep in root:
continue
for fname in fnames:
if fname == "Vengine.jar":
if root.endswith( "/lib" ):
root = root[:-4]
vassal_engines.append( root )
return vassal_engines
def _set_vassal_engine( self, vengine=None ):
"""Install a VASSAL engine."""
if vengine:
assert vengine in self._do_get_vassal_engines()
_logger.info( "Installing VASSAL engine: %s", vengine )
app.config["VASSAL_DIR"] = vengine
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 ):
"""Set the vehicle/ordnance notes directory."""
if dtype == "real":
try:
dname = pytest.config.option.vo_notes #pylint: disable=no-member
assert dname, "--vo-notes was not specified."
except AttributeError:
dname = _ORIG_CHAPTER_H_NOTES_DIR
elif dtype == "test":
dname = os.path.join( os.path.split(__file__)[0], "fixtures/vo-notes" )
else:
assert dtype is None
dname = None
_logger.info( "Setting vehicle/ordnance notes: %s", dname )
app.config["CHAPTER_H_NOTES_DIR"] = dname
from vasl_templates.webapp.vo_notes import load_vo_notes
load_vo_notes( None )
return self
def _set_user_files_dir( self, dtype=None ):
"""Set the user files directory."""
if dtype == "test":
dname = os.path.join( os.path.split(__file__)[0], "fixtures/user-files" )
elif dtype and dtype.startswith( ("http://","https://") ):
dname = dtype
else:
assert dtype is None
dname = None
_logger.info( "Setting user files: %s", dname )
app.config["USER_FILES_DIR"] = dname
return self
def _get_last_snippet_image( self ): #pylint: disable=no-self-use
"""Get the last snippet image generated."""
from vasl_templates.webapp.snippets import last_snippet_image
assert last_snippet_image
_logger.info( "Returning the last snippet image: #bytes=%d", len(last_snippet_image) )
return base64.b64encode( last_snippet_image ).decode( "utf-8" )
def _get_vasl_mod_warnings( self ): #pylint: disable=no-self-use
"""Get the vasl_mod startup warnings."""
_logger.info( "Returning the vasl_mod startup warnings: %s", vasl_mod_module.warnings )
return vasl_mod_module.warnings
def _reset_template_pack( self ):
"""Force the default template pack to be reloaded."""
_logger.info( "Reseting the default template pack." )
globvars.template_pack = None
return self
def _set_roar_scenario_index( self, fname=None ):
"""Set the ROAR scenario index file."""
if fname:
dname = os.path.join( os.path.split(__file__)[0], "fixtures" )
fname = os.path.join( dname, fname )
_logger.info( "Setting the ROAR scenario index file: %s", fname )
webapp_scenarios._roar_scenarios._set_data( fname ) #pylint: disable=protected-access
else:
assert False
return self
def _set_scenario_index( self, fname=None ):
"""Set the scenario index file."""
if fname:
dname = os.path.join( os.path.split(__file__)[0], "fixtures" )
fname = os.path.join( dname, fname )
_logger.info( "Setting the scenario index file: %s", fname )
webapp_scenarios._asa_scenarios._set_data( fname ) #pylint: disable=protected-access
else:
assert False
return self
def _reset_last_asa_upload( self ):
"""Reset the saved last upload to the ASL Scenario Archive."""
_logger.info( "Reseting the last ASA upload." )
webapp_scenarios._last_asa_upload = None #pylint: disable=protected-access
return self
def _get_last_asa_upload( self ): #pylint: disable=no-self-use
"""Get the last set of files uploaded to the ASL Scenario Archive."""
last_asa_upload = webapp_scenarios._last_asa_upload #pylint: disable=protected-access
if not last_asa_upload:
return {} # FUDGE! This is for the remote testing framework :-/
last_asa_upload = last_asa_upload.copy()
# FUDGE! We can't send binary data over the remote testing interface, but since the tests just check
# for the presence of a VASL save file and screenshot, we just send an indicator of that, and not
# the data itself. This will be reworked when we switch to using gRPC.
if "vasl_setup" in last_asa_upload:
assert last_asa_upload["vasl_setup"][:2] == b"PK"
last_asa_upload[ "vasl_setup" ] = "PK:{}".format( len(last_asa_upload["vasl_setup"]) )
if "screenshot" in last_asa_upload:
assert last_asa_upload["screenshot"][:2] == b"\xff\xd8" \
and last_asa_upload["screenshot"][-2:] == b"\xff\xd9" # nb: these are the magic numbers for JPEG's
last_asa_upload[ "screenshot" ] = "JPEG:{}".format( len(last_asa_upload["screenshot"]) )
_logger.debug( "Returning the last ASA upload: %s", last_asa_upload )
return last_asa_upload

@ -14,15 +14,12 @@ from vasl_templates.webapp.tests.test_vo_reports import get_vo_report
from vasl_templates.webapp.tests.test_vehicles_ordnance import add_vo
from vasl_templates.webapp.tests.test_scenario_persistence import save_scenario, load_scenario
IGNORE_CAPABILITIES = [ "T", "NT", "ST" ]
_IGNORE_CAPABILITIES = [ "T", "NT", "ST" ]
# ---------------------------------------------------------------------
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
) #pylint: disable=too-many-statements
def test_month_capabilities( webapp, webdriver ):
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_month_capabilities( webapp, webdriver ): #pylint: disable=too-many-statements
"""Test date-based capabilities that change in the middle of a year."""
# Sherman III(a): WP6(J4+)† s8
@ -305,10 +302,7 @@ def test_month_capabilities( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
) #pylint: disable=too-many-statements
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_kfw( webapp, webdriver ):
"""Test date-based capabilities for K:FW vehicles/ordnance."""
@ -328,10 +322,7 @@ def test_kfw( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
)
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_theater_capabilities( webapp, webdriver ):
"""Test theater-specific capabilities."""
@ -407,27 +398,15 @@ def test_theater_capabilities( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
)
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_theater_capabilities_bfp( webapp, webdriver ):
"""Test theater-specific capabilities (BFP extension)."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" )
init_webapp( webapp, webdriver )
# LVT(A)1(L): C10(P)†2
vehicle = [ "american", "vehicles", "LVT(A)1(L)" ]
@ -446,10 +425,7 @@ def test_theater_capabilities_bfp( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
)
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_american_ordnance_note_c( webapp, webdriver ):
"""Test handling of American Ordnance Note C."""
@ -474,10 +450,7 @@ def test_american_ordnance_note_c( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
)
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_nationality_capabilities( webapp, webdriver ):
"""Test nationality-specific capabilities."""
@ -759,19 +732,12 @@ def test_custom_comments( webapp, webdriver ): #pylint: disable=too-many-stateme
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
) #pylint: disable=too-many-statements
def test_capability_updates_in_ui( webapp, webdriver ):
"""Ensure that capabilities are updated in the UI correctly."""
"""Check that capabilities are updated in the UI correctly."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the scenario
scenario_data = {
@ -805,7 +771,7 @@ def test_capability_updates_in_ui( webapp, webdriver ):
results[-1].append( [ c.get_attribute("innerHTML") for c in capabilities ] )
for row in expected:
for i,entries in enumerate(row):
row[i] = [ e for e in entries if e not in IGNORE_CAPABILITIES ]
row[i] = [ e for e in entries if e not in _IGNORE_CAPABILITIES ]
assert results == expected
# no scenario date => we should be showing the raw capabilities
@ -896,10 +862,8 @@ def test_elite( webapp, webdriver ): #pylint: disable=too-many-statements
"""Test elite vehicles/ordnance."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
def get_sortable_elem():
"""Find the sortable element for the test vehicle."""
@ -1046,7 +1010,7 @@ def _check_capabilities( webdriver, webapp,
merge_common = False,
row = row
)
for cap in IGNORE_CAPABILITIES:
for cap in _IGNORE_CAPABILITIES:
expected = re.sub( r"(^|\s+){}($|\s+)".format(cap), "", expected )
assert capabilities == expected

@ -12,9 +12,8 @@ def test_time_based_comments( webapp, webdriver ):
"""Test time-based comments."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# test a "START-" time-range
_test_comments( "german", "vehicles", "SPW 251/10", [
@ -48,9 +47,8 @@ def test_french_veh_f( webapp, webdriver ):
"""Test French Vehicle Note F."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# test an "(a)" vehicle
_test_comments( "french", "vehicles", "Ac de 40 CA(a)", [
@ -82,9 +80,8 @@ def test_axis_minor_veh_e( webapp, webdriver ):
"""Test Axis Minor Vehicle Note E."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# test an "(f)" vehicle
_test_comments( "romanian", "vehicles", "R-35(f)", [
@ -130,9 +127,8 @@ def test_axis_minor_ord_e( webapp, webdriver ):
"""Test Axis Minor Ordnance Note E."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# test a "(g)" gun
_test_comments( "romanian", "ordnance", "leFH 18(g)", [
@ -153,7 +149,7 @@ def test_axis_minor_ord_e( webapp, webdriver ):
# ---------------------------------------------------------------------
def _test_comments( nat, vo_type, vo_name, vals ):
""" Generate and check comments for a series of dates. """
"""Generate and check comments for a series of dates."""
# load the specified vehicle/ordnance
new_scenario()

@ -1,37 +1,26 @@
""" Test serving counter images. """
import os
import io
import json
import re
import shutil
import urllib.request
import pytest
import tabulate
from vasl_templates.webapp.vassal import SUPPORTED_VASSAL_VERSIONS
from vasl_templates.webapp.vasl_mod import VaslMod, get_vo_gpids, compare_version_strings, SUPPORTED_VASL_MOD_VERSIONS
from vasl_templates.webapp.config.constants import DATA_DIR
from vasl_templates.webapp.vasl_mod import get_vo_gpids, SUPPORTED_VASL_MOD_VERSIONS
from vasl_templates.webapp.vo import _kfw_listings #pylint: disable=protected-access
from vasl_templates.webapp.utils import change_extn
from vasl_templates.webapp.utils import compare_version_strings
from vasl_templates.webapp.tests.utils import init_webapp, select_tab, find_child, find_children
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario
from vasl_templates.webapp.tests.remote import ControlTests
_EXPECTED_MISSING_GPIDS_EXCEPTIONS = [ "6.5.0", "6.5.1", "6.6.0", "6.6.1" ]
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
) #pylint: disable=too-many-statements,too-many-locals
def test_counter_images( webapp ):
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_counter_images( webapp, webdriver ): #pylint: disable=too-many-locals
"""Test that counter images are served correctly."""
# NOTE: This is ridiculously slow on Windows :-/
@ -52,8 +41,8 @@ def test_counter_images( webapp ):
assert locals()["check_"+side]( gpid, resp_code, resp_data )
# test counter images when no VASL module has been configured
control_tests = ControlTests( webapp )
control_tests.set_vasl_mod( vmod=None )
webapp.control_tests.set_vasl_version( None, None )
init_webapp( webapp, webdriver )
# NOTE: It doesn't really matter which set of GPID's we use, since we're expecting
# a missing image for everything anyway. We just use the most recent supported version.
gpids = get_vo_gpids( None )
@ -100,6 +89,7 @@ def test_counter_images( webapp ):
expected_missing_gpids.remove( "1002" ) # FUDGE! this is a remapped GPID (11340)
expected_missing_gpids.remove( "1527" ) # FUDGE! this is a remapped GPID (12730)
vasl_version = None
def _do_check_front( gpid, code, data ):
if vasl_version not in _EXPECTED_MISSING_GPIDS_EXCEPTIONS and gpid in expected_missing_gpids:
return code == 404 and not data
@ -117,36 +107,29 @@ def test_counter_images( webapp ):
shutil.rmtree( save_dir )
os.makedirs( save_dir )
# test each VASL module file in the specified directory
# test each VASL version
failed = False
vmod_fnames = control_tests.get_vasl_mods()
for vmod_fname in vmod_fnames:
vasl_versions = webapp.control_tests.get_vasl_versions()
for vasl_version in vasl_versions:
# install the VASL module file
control_tests.set_vasl_mod( vmod=vmod_fname )
# NOTE: We assume we have access to the same VASL modules as the server, but the path on the webserver
# might be different to what it is locally, so we translate it here.
fname = os.path.split( vmod_fname )[1]
vasl_mods_dir = pytest.config.option.vasl_mods #pylint: disable=no-member
fname = os.path.join( vasl_mods_dir, fname )
# initialize
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( vasl_version, None )
init_webapp( webapp, webdriver )
# figure out what we're expecting to see
# NOTE: The results were the same across 6.4.0-6.4.4, but 6.5.0 introduced some changes.
vasl_mod = VaslMod( fname, DATA_DIR, None )
vasl_version = vasl_mod.vasl_version
fname = os.path.join( check_dir, "vasl-pieces-{}.txt".format( vasl_version ) )
if not os.path.isfile( fname ):
fname = os.path.join( check_dir, "vasl-pieces-legacy.txt" )
expected_vasl_pieces = open( fname, "r" ).read()
# generate a report for the pieces loaded
buf = io.StringIO()
_dump_pieces( vasl_mod, buf )
report = buf.getvalue()
report, gpids = webapp.control_tests.get_vasl_pieces( vasl_version )
if save_dir:
fname2 = change_extn( os.path.split(vmod_fname)[1], ".txt" )
with open( os.path.join(save_dir,fname2), "w" ) as fp:
fname2 = os.path.join( save_dir, vasl_version+".txt" )
with open( fname2, "w" ) as fp:
fp.write( report )
# check the report
@ -158,45 +141,17 @@ def test_counter_images( webapp ):
assert False, "Report mismatch: {}".format( vasl_version )
# check each counter
gpids = get_vo_gpids( vasl_mod )
check_images( gpids, check_front=_do_check_front, check_back=_do_check_back )
assert not failed
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def _dump_pieces( vasl_mod, out ):
"""Dump the VaslMod pieces."""
# dump the VASL pieces
results = [ [ "GPID", "Name", "Front images", "Back images"] ]
pieces = vasl_mod._pieces #pylint: disable=protected-access
# GPID's were originally int's but then changed to str's. We then started seeing non-numeric GPID's :-/
# For back-compat, we try to maintain sort order for numeric values.
def sort_key( val ): #pylint: disable=missing-docstring
if val.isdigit():
return ( "0"*10 + val )[-10:]
else:
# nb: we make sure that alphanumeric values appear after numeric values, even if they start with a number
return "_" + val
gpids = sorted( pieces.keys(), key=sort_key ) # nb: because GPID's changed from int to str :-/
for gpid in gpids:
piece = pieces[ gpid ]
assert piece["gpid"] == gpid
results.append( [ gpid, piece["name"], piece["front_images"], piece["back_images"] ] )
print( tabulate.tabulate( results, headers="firstrow", numalign="left" ), file=out )
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_gpid_remapping( webapp, webdriver ):
"""Test GPID remapping."""
# initialize
control_tests = init_webapp( webapp, webdriver )
init_webapp( webapp, webdriver )
def check_gpid_image( gpid ):
"""Check if we can get the image for the specified GPID."""
@ -220,14 +175,13 @@ def test_gpid_remapping( webapp, webdriver ):
else:
assert check_gpid_image( gpid ) == 404
def do_test( vmod_fname, valid_images ):
def do_test( vasl_version, valid_images ):
"""Do the test."""
# initialize (using the specified VASL vmod)
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod=vmod_fname )
)
# initialize (using the specified version of VASL)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( vasl_version, None )
init_webapp( webapp, webdriver, scenario_persistence=1 )
load_scenario( scenario_data )
# check that the German vehicles loaded correctly
select_tab( "ob1" )
@ -247,38 +201,26 @@ def test_gpid_remapping( webapp, webdriver ):
fname = os.path.join( os.path.split(__file__)[0], "fixtures/gpid-remapping.json" )
scenario_data = json.load( open( fname, "r" ) )
# locate the VASL modules
vmod_fnames = control_tests.get_vasl_mods()
def find_vasl_mod( version ):
"""Find the VASL module for the specified version."""
matches = [ fname for fname in vmod_fnames if "vasl-{}.vmod".format(version) in fname ]
assert len(matches) == 1
return matches[0]
# run the tests using VASL 6.4.4 and 6.5.0
# NOTE: Versions of VASL prior to 6.6.0 are no longer officially supported (since they use Java 8),
# but we would still like to run these tests. See VassalShim._run_vassal_shim(), where we figure out
# which version of Java to use, and run_vassal_tests() in test_vassal.py, where we check for invalid
# combinations of VASSAL and VASL. Sigh...
do_test( find_vasl_mod("6.4.4"), True )
do_test( find_vasl_mod("6.5.0"), True )
do_test( find_vasl_mod("6.5.1"), True )
do_test( "6.4.4", True )
do_test( "6.5.0", True )
do_test( "6.5.1", True )
# disable GPID remapping and try again
prev_gpid_mappings = control_tests.set_gpid_remappings( gpids=[] )
try:
do_test( find_vasl_mod("6.4.4"), True )
do_test( find_vasl_mod("6.5.0"), False )
do_test( find_vasl_mod("6.5.1"), False )
finally:
# NOTE: This won't get done if Python exits unexpectedly in the try block,
# which will leave the server in the wrong state if it's remote.
control_tests.set_gpid_remappings( gpids=prev_gpid_mappings )
webapp.control_tests.set_gpid_remappings( {} )
do_test( "6.4.4", True )
do_test( "6.5.0", False )
do_test( "6.5.1", False )
# ---------------------------------------------------------------------
def test_compare_version_strings():
"""Test comparing version strings."""
# test comparing VASSAL version strings
for i,vassal_version in enumerate( SUPPORTED_VASSAL_VERSIONS):
if i > 0:
@ -286,6 +228,7 @@ def test_compare_version_strings():
assert compare_version_strings( SUPPORTED_VASSAL_VERSIONS[i], vassal_version ) == 0
if i < len(SUPPORTED_VASSAL_VERSIONS)-1:
assert compare_version_strings( vassal_version, SUPPORTED_VASSAL_VERSIONS[i+1] ) < 0
# test comparing VASL version strings
for i,vasl_version in enumerate(SUPPORTED_VASL_MOD_VERSIONS):
if i > 0:

@ -12,9 +12,8 @@ def test_default_scenario( webapp, webdriver ):
"""Test loading the default scenario."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_default_scenario( fname="new-default-scenario.json" )
)
webapp.control_tests.set_default_scenario( "new-default-scenario.json" )
init_webapp( webapp, webdriver )
# wait for the scenario to load
elem = find_child( "input[name='SCENARIO_NAME']" )

@ -18,9 +18,7 @@ def test_dirty_scenario_checks( webapp, webdriver ):
"""Test checking for a dirty scenario."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
init_webapp( webapp, webdriver )
# initialize
SIMPLE_NOTES = {

@ -179,9 +179,8 @@ def test_count_remaining_hilites( webapp, webdriver ):
"""Test highlighting in the "count remaining" extras template."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
def do_test( month, year, expected ): #pylint: disable=missing-docstring

@ -81,10 +81,8 @@ def test_local_user_files( webapp, webdriver ):
def do_test( enable_user_files ): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_user_files_dir( dtype = "test" if enable_user_files else None )
)
webapp.control_tests.set_user_files_dir( "user-files/" if enable_user_files else None )
init_webapp( webapp, webdriver )
# try getting a user file
try:
@ -146,8 +144,8 @@ def test_remote_user_files( webapp, webdriver ):
"""Test serving user files from a remote server."""
# initialize
control_tests = init_webapp( webapp, webdriver )
remote_app_config = control_tests.get_app_config()
init_webapp( webapp, webdriver )
remote_app_config = webapp.control_tests.get_app_config()
def do_test( enable_user_files ): #pylint: disable=missing-docstring
@ -160,10 +158,8 @@ def test_remote_user_files( webapp, webdriver ):
# so we need to adjust the user files base URL to reflect that.
remote_base_url = "http://localhost:{}".format( remote_app_config["FLASK_PORT_NO"] )
base_url = re.sub( r"http://.+?:\d+", remote_base_url, base_url )
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_user_files_dir( dtype = base_url if enable_user_files else None )
)
webapp.control_tests.set_user_files_dir( base_url if enable_user_files else None )
init_webapp( webapp, webdriver )
# try getting a user file
try:
@ -189,9 +185,8 @@ def test_user_file_snippets( webapp, webdriver ):
def do_test( enable_user_files ): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_user_files_dir( dtype = "test" if enable_user_files else None )
)
webapp.control_tests.set_user_files_dir( "user-files/" if enable_user_files else None )
init_webapp( webapp, webdriver )
# set the victory conditions
elem = find_child( "textarea[name='VICTORY_CONDITIONS']" )

@ -27,9 +27,11 @@ def test_jshint():
# check each Javascript file
dname = os.path.join( os.path.split(__file__)[0], "../static/" )
for fname in os.listdir(dname):
if os.path.splitext(fname)[1] != ".js":
for fname in os.listdir( dname ):
if os.path.splitext( fname )[1] != ".js":
continue
# run JSHint for the next file
proc = subprocess.run(
[ jshint, os.path.join(dname,fname) ],

@ -4,7 +4,6 @@ import os
import base64
import csv
import pytest
from selenium.webdriver.support.ui import Select
from vasl_templates.webapp.tests.utils import init_webapp, select_menu_option, \
@ -14,16 +13,14 @@ from vasl_templates.webapp.tests.test_vassal import run_vassal_tests
# ---------------------------------------------------------------------
@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
def test_full( webapp, webdriver ):
"""Test a full log file analysis."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
# analyze the log file
# === RPh === === PFPh === === MPh === === DFPh ===
# A1: Other 5 4 A6: IFT 3 1 B5: IFT 4 2
@ -127,21 +124,18 @@ def test_full( webapp, webdriver ):
find_child( "#lfa button.ui-dialog-titlebar-close" ).click()
# run the tests
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------
@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
def test_4players( webapp, webdriver ):
"""Test a file log file analysis with 4 players."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
# analyze the log file
# RPh PFPh MPh DFPh
# --- ----- ------ ------
@ -212,18 +206,13 @@ def test_4players( webapp, webdriver ):
find_child( "#lfa button.ui-dialog-titlebar-close" ).click()
# run the tests
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------
@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
def test_multiple_files( webapp, webdriver ):
"""Test analyzing multiple log files."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
def check_color_pickers( expected ):
"""Check which color pickers are being presented to the user."""
find_child( "#lfa .options button.player-colors" ).click()
@ -234,6 +223,9 @@ def test_multiple_files( webapp, webdriver ):
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
# NOTE: The "1a" and "1b" log files have the same players (Alice and Bob), but the "2" log file
# has Bob and Chuck.
# multiple-1a multiple-1b multiple-2
@ -375,18 +367,13 @@ def test_multiple_files( webapp, webdriver ):
find_child( "#lfa button.ui-dialog-titlebar-close" ).click()
# run the tests
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------
@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
def test_hotness_report( webapp, webdriver ):
"""Test generating the hotness popup."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1 )
def unload_report():
"""Unload the hotness popup."""
find_child( "#lfa .hotness img.dice" ).click()
@ -400,6 +387,9 @@ def test_hotness_report( webapp, webdriver ):
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1 )
# load the test log files
# vlog #1 vlog #2
# =============== ===============
@ -460,20 +450,18 @@ def test_hotness_report( webapp, webdriver ):
}
# run the tests
run_vassal_tests( control_tests, do_test, False )
run_vassal_tests( webapp, do_test, all_combos=False )
# ---------------------------------------------------------------------
@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
def test_3d6( webapp, webdriver ):
"""Test scenarios that use the 3d6 extension."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1, lfa_tables=1 )
# analyze the log file
_analyze_vlogs( "3d6.vlog" )
@ -506,18 +494,13 @@ def test_3d6( webapp, webdriver ):
find_child( "#lfa button.ui-dialog-titlebar-close" ).click()
# run the tests
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------
@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
def test_banner_updates( webapp, webdriver ):
"""Test updating the banner."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1 )
def check_banner( roll_type ):
"""Check the banner."""
assert find_child( "#lfa .banner .title" ).text == "Log File Analysis test"
@ -526,6 +509,9 @@ def test_banner_updates( webapp, webdriver ):
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1 )
# analyze the log file
_analyze_vlogs( "banner-updates.vlog" )
@ -540,20 +526,18 @@ def test_banner_updates( webapp, webdriver ):
find_child( "#lfa button.ui-dialog-titlebar-close" ).click()
# run the tests
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test, all_combos=False )
# ---------------------------------------------------------------------
@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
def test_download_data( webapp, webdriver ):
"""Test downloading the data."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1, lfa_persistence=1 )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1, lfa_persistence=1 )
# analyze the log file
_analyze_vlogs( "download-test.vlog" )
@ -580,20 +564,18 @@ def test_download_data( webapp, webdriver ):
]
# run the test
run_vassal_tests( control_tests, do_test, False )
run_vassal_tests( webapp, do_test, all_combos=False )
# ---------------------------------------------------------------------
@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
def test_custom_labels( webapp, webdriver ):
"""Test custom labels in the log file."""
# initialize
control_tests = init_webapp( webapp, webdriver, vlog_persistence=1, lfa_persistence=1 )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vlog_persistence=1, lfa_persistence=1 )
# analyze the log file
_analyze_vlogs( "custom-labels.vlog" )
@ -619,7 +601,7 @@ def test_custom_labels( webapp, webdriver ):
]
# run the test
run_vassal_tests( control_tests, do_test, False )
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------

@ -14,9 +14,8 @@ def test_national_capabilities_reports( webapp, webdriver ):
"""Check the national capabilities reports."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
# initialize
check_dir = os.path.join( os.path.split(__file__)[0], "fixtures/nat-caps/" )
@ -77,9 +76,7 @@ def test_national_capabilities_reports( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def _get_nat_caps( webapp, webdriver,
nat, theater, year, month
): #pylint: disable=too-many-locals
def _get_nat_caps( webapp, webdriver, nat, theater, year, month ): #pylint: disable=too-many-locals
"""Get a national capabilities snippet."""
# get the snippet
@ -197,9 +194,8 @@ def test_time_based_national_capabilities( webapp, webdriver ):
"""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
def check_notes( nat, theater, month, year, expected ):
"""Check the national capabilities notes."""

@ -96,6 +96,7 @@ def test_nationality_specific( webapp, webdriver ): #pylint: disable=too-many-lo
def do_check_snippets( btn, date, expected, warning ):
"""Check that snippets are being generated correctly."""
# change the scenario date, check that the button is displayed correctly
set_scenario_date( "{:02}/01/{:04}".format( date[1], date[0] ) )
select_tab( "ob1" )
@ -105,10 +106,12 @@ def test_nationality_specific( webapp, webdriver ): #pylint: disable=too-many-lo
assert "inactive" in classes
else:
assert "inactive" not in classes
# test snippet generation
marker = set_stored_msg_marker( "_last-warning_" )
btn.click()
wait_for_clipboard( 2, expected )
# check if a warning was issued
last_warning = get_stored_msg( "_last-warning_" )
if warning:

@ -2,7 +2,6 @@
import re
import pytest
from selenium.webdriver.common.action_chains import ActionChains
from vasl_templates.webapp.tests.utils import init_webapp, select_tab, \
@ -13,20 +12,14 @@ from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_online_images( webapp, webdriver ):
"""Test using online images in VASL scenarios."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" ) \
.set_default_template_pack( dname="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", None )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -73,20 +66,14 @@ def test_online_images( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_multiple_images( webapp, webdriver ):
"""Test handling of VASL counters that have multiple images."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" ) \
.set_default_template_pack( dname="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", None )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -129,24 +116,14 @@ def test_multiple_images( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
)
def test_extensions( webapp, webdriver ):
"""Test handling of VASL counters in extensions."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" ) \
.set_default_template_pack( dname="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {

@ -44,9 +44,7 @@ def test_scenario_persistence( webapp, webdriver ): #pylint: disable=too-many-st
"""Test loading/saving scenarios."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
init_webapp( webapp, webdriver, scenario_persistence=1 )
def check_ob_tabs( *args ):
"""Check that the OB tabs have been set correctly."""

@ -367,7 +367,10 @@ def test_roar_matching( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@pytest.mark.skipif( pytest.config.option.server_url is not None, reason="--server-url specified" ) #pylint: disable=no-member
@pytest.mark.skipif(
pytest.config.option.webapp_url is not None, #pylint: disable=no-member
reason = "Can't test against a remote webapp server."
)
def test_roar_matching2( webapp, webdriver ):
"""Test matching scenarios with ROAR scenarios."""
@ -542,13 +545,11 @@ def test_scenario_linking( webapp, webdriver ):
# ---------------------------------------------------------------------
@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
def test_scenario_upload( webapp, webdriver ):
"""Test uploading scenarios to the ASL Scenario Archive."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
def do_upload( prep_upload, expect_ask ):
"""Upload the scenario to our test endpoint."""
@ -564,17 +565,17 @@ def test_scenario_upload( webapp, webdriver ):
prep_upload( dlg )
# start the upload
control_tests.reset_last_asa_upload()
webapp.control_tests.reset_last_asa_upload()
find_child( "button.upload", dlg ).click()
if expect_ask:
dlg = wait_for_elem( 2, ".ui-dialog.ask" )
find_child( "button.ok", dlg ).click()
# wait for the upload to be processed
asa_upload = wait_for( 5, control_tests.get_last_asa_upload )
assert asa_upload["user"] == user_name
assert asa_upload["token"] == api_token
return asa_upload
last_asa_upload = wait_for( 5, webapp.control_tests.get_last_asa_upload )
assert last_asa_upload["user"] == user_name
assert last_asa_upload["token"] == api_token
return last_asa_upload
user_name, api_token = "joe", "xyz123"
def prep_upload( dlg ):
@ -621,13 +622,15 @@ def test_scenario_upload( webapp, webdriver ):
# test uploading a VASL save file
def do_test(): #pylint: disable=missing-docstring
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
dlg = _do_scenario_search( "full", [1], webdriver )
find_child( "button.import", dlg ).click()
asa_upload = do_upload( prep_upload2, False )
assert isinstance( asa_upload["vt_setup"], dict )
assert asa_upload["vasl_setup"][:3] == "PK:"
assert asa_upload["screenshot"][:5] == "JPEG:"
run_vassal_tests( control_tests, do_test, True )
last_asa_upload = do_upload( prep_upload2, False )
assert isinstance( last_asa_upload["vt_setup"], dict )
assert last_asa_upload["vasl_setup"][:2] == b"PK"
assert last_asa_upload["screenshot"][:2] == b"\xff\xd8" \
and last_asa_upload["screenshot"][-2:] == b"\xff\xd9" # nb: these are the magic numbers for JPEG's
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------

@ -1,5 +1,7 @@
""" Test HTML snippet generation. """
import base64
import pytest
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
@ -18,9 +20,8 @@ def test_snippet_ids( webapp, webdriver ):
"""Check that snippet ID's are generated correctly."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load a scenario (so that we get some sortable's)
scenario_data = {
@ -228,9 +229,8 @@ def test_edit_templates( webapp, webdriver ):
"""Test editing templates."""
# initialize
init_webapp( webapp, webdriver, edit_template_links=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, edit_template_links=1 )
ob_setups = {
1: find_child( "#ob_setups-sortable_1" ),
2: find_child( "#ob_setups-sortable_2" )
@ -320,12 +320,12 @@ def test_snippet_images( webapp, webdriver ):
"""Test generating snippet images."""
# initialize
control_tests = init_webapp( webapp, webdriver, scenario_persistence=1, snippet_image_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1, snippet_image_persistence=1 )
# check if there is a webdriver configured
if "WEBDRIVER_PATH" not in control_tests.get_app_config():
remote_app_config = webapp.control_tests.get_app_config()
if "WEBDRIVER_PATH" not in remote_app_config:
return
# load a test scenario
@ -359,10 +359,11 @@ def test_snippet_images( webapp, webdriver ):
# wait for the snippet image to be generated
wait_for( 20, lambda: ret_buffer.get_attribute( "value" ) )
fname, img_data = ret_buffer.get_attribute( "value" ).split( "|", 1 )
img_data = base64.b64decode( img_data )
# check the results
assert fname == expected_fname
last_snippet_image = control_tests.get_last_snippet_image()
last_snippet_image = webapp.control_tests.get_last_snippet_image()
assert img_data == last_snippet_image
def do_simple_test( template_id, expected_fname ): #pylint: disable=missing-docstring

@ -6,8 +6,6 @@ import base64
import re
import random
import pytest
from vasl_templates.webapp.utils import TempFile
from vasl_templates.webapp.tests.test_vehicles_ordnance import add_vo
from vasl_templates.webapp.tests.utils import \
@ -22,9 +20,8 @@ def test_individual_files( webapp, webdriver ):
"""Test loading individual template files."""
# initialize
init_webapp( webapp, webdriver, template_pack_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, template_pack_persistence=1 )
set_player( 1, "german" )
set_player( 2, "russian" )
@ -52,9 +49,8 @@ def test_zip_files( webapp, webdriver ):
"""Test loading ZIP'ed template packs."""
# initialize
init_webapp( webapp, webdriver, template_pack_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, template_pack_persistence=1 )
set_player( 1, "german" )
set_player( 2, "russian" )
@ -87,11 +83,10 @@ def test_new_default_template_pack( webapp, webdriver ):
"""Test changing the default template pack."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_default_template_pack( dname="template-packs/new-default/" ) \
.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests \
.set_default_template_pack( "new-default/" ) \
.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver )
# check that the new templates are being used
_do_test_default_template_pack( webdriver )
@ -103,11 +98,10 @@ def test_new_default_template_pack_zip( webapp, webdriver ):
zip_data = make_zip_from_files( "new-default" )
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_default_template_pack( bin_data=zip_data ) \
.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests \
.set_default_template_pack( zip_data ) \
.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver )
# check that the new templates are being used
_do_test_default_template_pack( webdriver )
@ -157,7 +151,6 @@ def test_nationality_data( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_missing_templates( webapp, webdriver ):
"""Test UI updates for missing templates."""

@ -3,7 +3,6 @@
import json
import re
import pytest
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys
@ -25,9 +24,8 @@ def test_include_vasl_images_in_snippets( webapp, webdriver ):
"""Test including VASL counter images in snippets."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
set_user_settings( { "scenario-images-source": SCENARIO_IMAGES_SOURCE_THIS_PROGRAM } )
# add a vehicle
@ -65,9 +63,8 @@ def test_include_flags_in_snippets( webapp, webdriver ):
"""Test including flags in snippets."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
# prepare the scenario
set_player( 1, "german" )
@ -185,9 +182,8 @@ def test_hide_unavailable_ma_notes( webapp, webdriver ):
"""Test showing/hiding unavailable multi-applicable notes."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test vehicle
load_scenario( {
@ -237,9 +233,8 @@ def test_vo_notes_as_images( webapp, webdriver ):
"""Test showing vehicle/ordnance notes as HTML/images."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test vehicle
load_scenario( {
@ -285,50 +280,43 @@ def test_vo_notes_as_images( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif( not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason="--vasl-mods not specified"
)
def test_alternate_webapp_base_url( webapp, webdriver ):
"""Test changing the webapp base URL."""
# initialize
def _init_webapp(): #pylint: disable=missing-docstring
return init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="test" )
)
control_tests = _init_webapp()
# enable images
set_user_settings( {
"scenario-images-source": SCENARIO_IMAGES_SOURCE_THIS_PROGRAM,
"include-vasl-images-in-snippets": True,
"include-flags-in-snippets": True,
"custom-list-bullets": True,
"vo-notes-as-images": True,
} )
def do_test( expected ): #pylint: disable=missing-docstring
# load the scenario
load_scenario( {
"SCENARIO_NAME": "test scenario",
"SCENARIO_DATE": "01/01/1940",
"VICTORY_CONDITIONS": "Just do it!",
"SCENARIO_NOTES": [ { "caption": "Scenario note #1" } ],
"SSR": [ "SSR #1", "SSR #2", "SSR #3" ],
"PLAYER_1": "german",
"OB_SETUPS_1": [ { "caption": "OB setup note 1" } ],
"OB_NOTES_1": [ { "caption": "OB note 1" } ],
"OB_VEHICLES_1": [ { "name": "PzKpfw VG" } ],
"OB_ORDNANCE_1": [ { "name": "8.8cm PaK 43" } ],
"PLAYER_2": "russian",
"OB_SETUPS_2": [ { "caption": "OB setup note 2" } ],
"OB_NOTES_2": [ { "caption": "OB note 2" } ],
"OB_VEHICLES_2": [ { "name": "T-34/85" } ],
"OB_ORDNANCE_2": [ { "name": "82mm BM obr. 37" } ],
} )
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# enable images
set_user_settings( {
"scenario-images-source": SCENARIO_IMAGES_SOURCE_THIS_PROGRAM,
"include-vasl-images-in-snippets": True,
"include-flags-in-snippets": True,
"custom-list-bullets": True,
"vo-notes-as-images": True,
} )
# load the scenario
load_scenario( {
"SCENARIO_NAME": "test scenario",
"SCENARIO_DATE": "01/01/1940",
"VICTORY_CONDITIONS": "Just do it!",
"SCENARIO_NOTES": [ { "caption": "Scenario note #1" } ],
"SSR": [ "SSR #1", "SSR #2", "SSR #3" ],
"PLAYER_1": "german",
"OB_SETUPS_1": [ { "caption": "OB setup note 1" } ],
"OB_NOTES_1": [ { "caption": "OB note 1" } ],
"OB_VEHICLES_1": [ { "name": "PzKpfw VG" } ],
"OB_ORDNANCE_1": [ { "name": "8.8cm PaK 43" } ],
"PLAYER_2": "russian",
"OB_SETUPS_2": [ { "caption": "OB setup note 2" } ],
"OB_NOTES_2": [ { "caption": "OB note 2" } ],
"OB_VEHICLES_2": [ { "name": "T-34/85" } ],
"OB_ORDNANCE_2": [ { "name": "82mm BM obr. 37" } ],
} )
def do_test( expected ): #pylint: disable=missing-docstring
# generate each snippet
snippet_btns = find_snippet_buttons()
for tab_id,btns in snippet_btns.items():
@ -346,12 +334,10 @@ def test_alternate_webapp_base_url( webapp, webdriver ):
do_test( webapp.base_url + "/" )
# test with a custom base URL
try:
control_tests.set_app_config( key="ALTERNATE_WEBAPP_BASE_URL", val="http://ALT-BASE-URL" )
_init_webapp()
do_test( "http://ALT-BASE-URL/" )
finally:
control_tests.set_app_config( key="ALTERNATE_WEBAPP_BASE_URL", val=None )
webapp.control_tests.set_app_config_val(
key="ALTERNATE_WEBAPP_BASE_URL", val="http://ALT-BASE-URL"
)
do_test( "http://ALT-BASE-URL/" )
# ---------------------------------------------------------------------

@ -5,36 +5,32 @@ import zipfile
import typing
import re
import pytest
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from vasl_templates.webapp.utils import TempFile
from vasl_templates.webapp.tests.utils import init_webapp, refresh_webapp, set_player, select_tab, new_scenario, \
from vasl_templates.webapp.tests.utils import init_webapp, set_player, select_tab, new_scenario, \
find_child, find_children, wait_for_clipboard
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario
from vasl_templates.webapp.tests.test_vehicles_ordnance import add_vo
_TEST_VASL_EXTN_FNAME = "test-vasl-extension.zip"
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_load_vasl_extensions( webapp, webdriver ):
"""Test loading VASL extensions."""
# initialize
control_tests = init_webapp( webapp, webdriver )
def do_test( build_info_fname, build_info, expected ): #pylint: disable=missing-docstring
# create the test VASL extension
_set_test_vasl_extn( control_tests, build_info, build_info_fname )
extn_fname = _set_test_vasl_extn( webapp, build_info, build_info_fname )
# reload the webapp
control_tests.set_vasl_mod( vmod="random", extns_dtype="test" )
_check_warning_msgs( control_tests, expected )
webapp.control_tests.set_vasl_version( "random", "{TEMP_DIR}" )
init_webapp( webapp, webdriver )
expected = expected.replace( "{EXTN-FNAME}", extn_fname )
_check_warning_msgs( webapp, expected )
# try loading an extension that has no buildFile
do_test( "foo", "<foo />", "Missing buildFile:" )
@ -49,75 +45,63 @@ def test_load_vasl_extensions( webapp, webdriver ):
# try loading an extension with an unknown ID
do_test( "buildFile", '<VASSAL.build.module.ModuleExtension version="v0.1" extensionId="unknown" />',
"Not accepting test.zip: no extension info for unknown/v0.1"
"Not accepting {EXTN-FNAME}: no extension info for unknown/v0.1"
)
# try loading something that's not a ZIP file
control_tests.set_test_vasl_extn( fname="test.zip", bin_data=b"This is not a ZIP file." ) \
.set_vasl_mod( vmod="random", extns_dtype="test" )
_check_warning_msgs( control_tests, "Can't check VASL extension (not a ZIP file):" )
webapp.control_tests \
.save_temp_file( _TEST_VASL_EXTN_FNAME, b"This is not a ZIP file." ) \
.set_vasl_version( "random", "{TEMP_DIR}" )
init_webapp( webapp, webdriver )
_check_warning_msgs( webapp, "Can't check VASL extension (not a ZIP file):" )
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_vasl_extension_info( webapp, webdriver ):
"""Test matching VASL extensions with our extension info files."""
# initialize
control_tests = init_webapp( webapp, webdriver )
# prepare our test VASL extension
fname = os.path.join( os.path.split(__file__)[0], "fixtures/vasl-extensions/test-extn.xml" )
_set_test_vasl_extn( control_tests, open(fname,"r").read() )
extn_fname = _set_test_vasl_extn( webapp, open(fname,"r").read() )
def do_test( dtype, expected ): #pylint: disable=missing-docstring
control_tests.set_data_dir( dtype="real" ) \
.set_vasl_extn_info_dir( dtype=dtype ) \
.set_vasl_mod( vmod="random", extns_dtype="test" )
_check_warning_msgs( control_tests, expected )
def do_test( dname, expected ): #pylint: disable=missing-docstring
webapp.control_tests \
.set_vasl_version( "random", "{TEMP_DIR}" ) \
.set_vasl_extn_info_dir( dname )
init_webapp( webapp, webdriver )
_check_warning_msgs( webapp, expected )
# try loading the VASL extension, with no matching extension info
do_test( "mismatched-id",
"Not accepting test.zip: no extension info for test/v0.1"
"Not accepting {}: no extension info for test/v0.1".format( extn_fname )
)
do_test( "mismatched-version",
"Not accepting test.zip: no extension info for test/v0.1"
"Not accepting {}: no extension info for test/v0.1".format( extn_fname )
)
# try loading the VASL extension, with matching extension info
do_test( "good-match", None )
extns = control_tests.get_vasl_extns()
extns = webapp.control_tests.get_vasl_extns()
assert len(extns) == 1
extn = extns[0]
assert os.path.basename( extn[0] ) == _TEST_VASL_EXTN_FNAME
assert extn[1] == { "extensionId": "test", "version": "v0.1" }
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vo_notes, #pylint: disable=no-member
reason = "--vo-notes not specified"
)
def test_dedupe_ma_notes( webapp, webdriver ):
"""Test deduping multi-applicable notes."""
# check if the remote webapp server supports this test
if not webapp.control_tests.has_capability( "chapter-h" ):
return
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" ) \
.set_vo_notes_dir( dtype="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" ) \
.set_vo_notes_dir( "{REAL}" )
init_webapp( webapp, webdriver )
def do_test( vehicles, expected ): #pylint: disable=missing-docstring
# add the specified vehicles
@ -171,22 +155,9 @@ def test_dedupe_ma_notes( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
)
def test_kgs_extensions( webapp, webdriver ):
"""Test the KGS extension."""
# initialize
control_tests = init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
def check_counter_images( veh_name, expected ):
"""Check the counter images available for the specified vehicle."""
@ -226,10 +197,10 @@ def test_kgs_extensions( webapp, webdriver ):
def do_test( enable_extns ): #pylint: disable=missing-docstring
# initialize
control_tests.set_vasl_mod( vmod="random",
extns_dtype = "real" if enable_extns else None
)
refresh_webapp( webdriver )
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" if enable_extns else None )
init_webapp( webapp, webdriver )
set_player( 2, "russian" )
# check the Matilda II(b)
@ -248,28 +219,19 @@ def test_kgs_extensions( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vo_notes, #pylint: disable=no-member
reason = "--vo-notes not specified"
)
def test_bfp_extensions( webapp, webdriver ):
"""Test the Bounding Fire extension."""
# check if the remote webapp server supports this test
if not webapp.control_tests.has_capability( "chapter-h" ):
return
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" ) \
.set_vo_notes_dir( dtype="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" ) \
.set_vo_notes_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -318,28 +280,19 @@ def test_bfp_extensions( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vo_notes, #pylint: disable=no-member
reason = "--vo-notes not specified"
)
def test_bfp_extensions2( webapp, webdriver ):
"""Test the Bounding Fire extension (Operation Cobra counters)."""
# check if the remote webapp server supports this test
if not webapp.control_tests.has_capability( "chapter-h" ):
return
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" ) \
.set_vo_notes_dir( dtype="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" ) \
.set_vo_notes_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -386,7 +339,7 @@ def test_bfp_extensions2( webapp, webdriver ):
# ---------------------------------------------------------------------
def _set_test_vasl_extn( control_tests, build_info, build_info_fname="buildFile" ):
def _set_test_vasl_extn( webapp, build_info, build_info_fname="buildFile" ):
"""Install a test VASL extension file."""
with TempFile() as temp_file:
with zipfile.ZipFile( temp_file.name, "w" ) as zip_file:
@ -394,11 +347,13 @@ def _set_test_vasl_extn( control_tests, build_info, build_info_fname="buildFile"
temp_file.close( delete=False )
with open( temp_file.name, "rb" ) as fp:
zip_data = fp.read()
control_tests.set_test_vasl_extn( fname="test.zip", bin_data=zip_data )
fname = _TEST_VASL_EXTN_FNAME
webapp.control_tests.save_temp_file( fname, zip_data )
return fname
def _check_warning_msgs( control_tests, expected ):
def _check_warning_msgs( webapp, expected ):
"""Check that the startup warning messages are what we expect."""
warnings = control_tests.get_vasl_mod_warnings()
warnings = webapp.control_tests.get_vasl_mod_warnings()
if expected:
assert len(warnings) == 1
if isinstance( expected, typing.re.Pattern ):

@ -9,32 +9,25 @@ import typing.re #pylint: disable=import-error
import pytest
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, compare_version_strings
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, select_menu_option, get_stored_msg, set_stored_msg, set_stored_msg_marker, wait_for, \
new_scenario, set_player
from vasl_templates.webapp.tests.test_scenario_persistence import load_scenario, load_scenario_params, save_scenario, \
assert_scenario_params_complete
# ---------------------------------------------------------------------
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_full_update( webapp, webdriver ):
"""Test updating a scenario that contains the full set of snippets."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, no_app_config_snippet_params=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
def do_test( enable_vo_notes ): #pylint: disable=missing-docstring
# initialize
control_tests.set_vo_notes_dir( dtype = "test" if enable_vo_notes else None )
refresh_webapp( webdriver )
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vo_notes_dir( "{TEST}" if enable_vo_notes else None )
init_webapp( webapp, webdriver, vsav_persistence=1, no_app_config_snippet_params=1 )
# load the scenario fields
SCENARIO_PARAMS = {
@ -105,7 +98,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,
# 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" )
vsav_dump = _dump_vsav( control_tests, fname )
vsav_dump = _dump_vsav( webapp, fname )
_check_vsav_dump( vsav_dump, {
"scenario": "Somewhere",
"players": re.compile( r"American:.*Belgian:" ),
@ -141,7 +134,7 @@ def test_full_update( webapp, webdriver ):
# check the results
temp_file.write( updated_vsav_data )
temp_file.close( delete=False )
updated_vsav_dump = _dump_vsav( control_tests, temp_file.name )
updated_vsav_dump = _dump_vsav( webapp, temp_file.name )
expected = {
"scenario": "Modified scenario name (<>{}\"'\\)",
"players": re.compile( r"American:.*Belgian:" ),
@ -200,39 +193,33 @@ def test_full_update( webapp, webdriver ):
assert updated_vsav_data == b"No changes."
# run the test against all versions of VASSAL+VASL
run_vassal_tests( control_tests, lambda: do_test(True), True )
run_vassal_tests( webapp, lambda: do_test(True) )
# run the test again (once) with no Chapter H vehicle/ordnance notes
run_vassal_tests( control_tests, lambda: do_test(False), False )
run_vassal_tests( webapp, lambda: do_test(False), all_combos=False )
# ---------------------------------------------------------------------
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_latw_autocreate( webapp, webdriver ):
"""Test auto-creation of LATW labels."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
# NOTE: We're only interested in what happens with the LATW labels, we ignore everything else.
ignore_labels = [ "scenario", "players", "victory_conditions" ]
def do_test(): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1 )
# check the VASL scenario
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/empty.vsav" )
vsav_dump = _dump_vsav( control_tests, fname )
vsav_dump = _dump_vsav( webapp, fname )
_check_vsav_dump( vsav_dump, {}, ignore_labels )
# update the scenario (German/Russian, no date)
load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3 }
)
updated_vsav_dump = _update_vsav_and_dump( webapp, fname, { "created": 3 } )
_check_vsav_dump( updated_vsav_dump, {
# nb: no LATW labels should have been created
}, ignore_labels )
@ -241,9 +228,7 @@ def test_latw_autocreate( webapp, webdriver ):
load_scenario_params( {
"scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "10/01/1943" }
} )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 4 }
)
updated_vsav_dump = _update_vsav_and_dump( webapp, fname, { "created": 4 } )
_check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust",
}, ignore_labels )
@ -252,18 +237,14 @@ def test_latw_autocreate( webapp, webdriver ):
load_scenario_params( {
"scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "01/01/1944" }
} )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 5 }
)
updated_vsav_dump = _update_vsav_and_dump( webapp, fname, { "created": 5 } )
_check_vsav_dump( updated_vsav_dump, {
"german/pf": "Panzerfaust", "german/atmm": "ATMM check:",
}, ignore_labels )
# update the scenario (British/American, no date)
load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3 }
)
updated_vsav_dump = _update_vsav_and_dump( webapp, fname, { "created": 3 } )
_check_vsav_dump( updated_vsav_dump, {
# nb: no LATW labels should have been created
}, ignore_labels )
@ -272,9 +253,7 @@ def test_latw_autocreate( webapp, webdriver ):
load_scenario_params( {
"scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1945" }
} )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
{ "created": 3 }
)
updated_vsav_dump = _update_vsav_and_dump( webapp, fname, { "created": 3 } )
_check_vsav_dump( updated_vsav_dump, {
# nb: no LATW labels should have been created
}, ignore_labels )
@ -283,29 +262,25 @@ def test_latw_autocreate( webapp, webdriver ):
# NOTE: We're testing the logic in the front/back-ends that determine whether LATW labels
# get created/updated/deleted, not the interaction with VASSAL, so we don't need to test
# against every VASSAL+VASL combination (although we can, if we want, but it'll be slow!)
run_vassal_tests( control_tests, do_test, False )
run_vassal_tests( webapp, do_test, all_combos=False )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_latw_update( webapp, webdriver ):
"""Test updating of LATW labels."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
# NOTE: We're only interested in what happens with the LATW labels, we ignore everything else.
ignore_labels = [ "scenario", "players", "victory_conditions" ]
def do_test(): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1 )
# check the VASL scenario
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw.vsav" )
vsav_dump = _dump_vsav( control_tests, fname )
vsav_dump = _dump_vsav( webapp, fname )
_check_vsav_dump( vsav_dump, {
"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
@ -318,7 +293,7 @@ def test_latw_update( webapp, webdriver ):
# 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.
# NOTE: Same thing happened when we factored out the common CSS into common.css :-/ Sigh...
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 3, "updated": 5 }
)
_check_vsav_dump( updated_vsav_dump, {
@ -333,7 +308,7 @@ def test_latw_update( webapp, webdriver ):
load_scenario_params( {
"scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1943" }
} )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 3, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump, {
@ -348,24 +323,21 @@ def test_latw_update( webapp, webdriver ):
# NOTE: We're testing the logic in the front/back-ends that determine whether LATW labels
# get created/updated/deleted, not the interaction with VASSAL, so we don't need to test
# against every VASSAL+VASL combination (although we can, if we want, but it'll be slow!)
run_vassal_tests( control_tests, do_test, False )
run_vassal_tests( webapp, do_test, all_combos=False )
# ---------------------------------------------------------------------
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_dump_vsav( webapp, webdriver ):
"""Test dumping a scenario."""
# initialize
control_tests = init_webapp( webapp, webdriver )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver )
# dump the VASL scenario
fname = os.path.join( os.path.split(__file__)[0], "fixtures/dump-vsav/labels.vsav" )
vsav_dump = _dump_vsav( control_tests, fname )
vsav_dump = _dump_vsav( webapp, fname )
# check the result
fname = change_extn( fname, ".txt" )
@ -373,31 +345,25 @@ def test_dump_vsav( webapp, webdriver ):
assert vsav_dump == expected
# run the test against all versions of VASSAL+VASL
run_vassal_tests( control_tests, do_test, True )
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_update_legacy_labels( webapp, webdriver ):
"""Test detection and updating of legacy labels."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
def do_test( enable_vo_notes ): #pylint: disable=missing-docstring
# initialize
control_tests.set_vo_notes_dir( dtype = "test" if enable_vo_notes else None )
refresh_webapp( webdriver )
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vo_notes_dir( "{TEST}" if enable_vo_notes else None )
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
# dump the VASL scenario
# 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" )
vsav_dump = _dump_vsav( control_tests, fname )
vsav_dump = _dump_vsav( webapp, fname )
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" in lbl ] ) == 0 #pylint: disable=len-as-condition
@ -407,7 +373,7 @@ def test_update_legacy_labels( webapp, webdriver ):
saved_scenario = json.load( open( fname2, "r" ) )
load_scenario( saved_scenario )
expected = 5 if enable_vo_notes else 1
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": expected, "updated": 20 }
)
@ -457,31 +423,27 @@ def test_update_legacy_labels( webapp, webdriver ):
expected["german/ob_ordnance_ma_notes_2"] = r"<span class='key'>N:</span> Unavailable."
_check_vsav_dump( updated_vsav_dump, expected )
# run the test
run_vassal_tests( control_tests, lambda: do_test(True), False )
# run the test against all versions of VASSAL+VASL
run_vassal_tests( webapp, lambda: do_test(True) )
# run the test again (once) with no Chapter H vehicle/ordnance notes
run_vassal_tests( control_tests, lambda: do_test(False), False )
run_vassal_tests( webapp, lambda: do_test(False), all_combos=False )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_update_legacy_latw_labels( webapp, webdriver ):
"""Test detection and updating of legacy LATW labels."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
def do_test(): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
# dump the VASL scenario
# 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" )
vsav_dump = _dump_vsav( control_tests, fname )
vsav_dump = _dump_vsav( webapp, fname )
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" in lbl ] ) == 0 #pylint: disable=len-as-condition
@ -493,7 +455,7 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
load_scenario_params( {
"scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "12/31/1945" }
} )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 3, "updated": 5 }
)
_check_vsav_dump( updated_vsav_dump, {
@ -508,7 +470,7 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
load_scenario_params( {
"scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1945" }
} )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 3, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump, {
@ -521,7 +483,7 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
# update the VSAV (some LATW are active)
load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 3, "updated": 5 }
)
_check_vsav_dump( updated_vsav_dump, {
@ -534,7 +496,7 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
# update the VSAV (some LATW are active)
load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 3, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump, {
@ -545,43 +507,37 @@ def test_update_legacy_latw_labels( webapp, webdriver ):
# nb: the legacy labels left in place: the scenario comment, the PF/PSK/ATMM, MOL/MOL-P and BAZ labels
assert len( [ lbl for lbl in labels if "vasl-templates:id" not in lbl ] ) == 6
# run the test
run_vassal_tests( control_tests, do_test, False )
# run the test against all versions of VASSAL+VASL
run_vassal_tests( webapp, do_test )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@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( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_player_owned_labels( webapp, webdriver ):
"""Test how we update labels owned by different player nationalities."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
# initialize
load_scenario_params( {
"scenario": {
"SCENARIO_NAME": "Player-owned labels",
"SCENARIO_DATE": "01/01/1940",
"PLAYER_1": "german",
"PLAYER_2": "american",
},
"ob1": { "OB_SETUPS_1": [ { "caption": "german setup #1" } ] },
"ob2": { "OB_SETUPS_2": [ { "caption": "american setup #1" } ] },
} )
def do_test(): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1 )
load_scenario_params( {
"scenario": {
"SCENARIO_NAME": "Player-owned labels",
"SCENARIO_DATE": "01/01/1940",
"PLAYER_1": "german",
"PLAYER_2": "american",
},
"ob1": { "OB_SETUPS_1": [ { "caption": "german setup #1" } ] },
"ob2": { "OB_SETUPS_2": [ { "caption": "american setup #1" } ] },
} )
# update a legacy scenario (i.e. labels have *not* been tagged with their owner player nationality)
# NOTE: We expect to see 4 labels updated:
# - the 2 OB setup labels (they will get the new-style ID's)
# - scenario (timestamp)
# - players (new American player)
fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels-legacy.vsav" )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "updated": 4 }
)
_check_vsav_dump( updated_vsav_dump , {
@ -597,7 +553,7 @@ def test_player_owned_labels( webapp, webdriver ):
# - players (new American player)
# 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" )
updated_vsav_dump = _update_vsav_and_dump( control_tests, fname,
updated_vsav_dump = _update_vsav_and_dump( webapp, fname,
{ "created": 1, "updated": 2 }
)
_check_vsav_dump( updated_vsav_dump , {
@ -607,25 +563,19 @@ def test_player_owned_labels( webapp, webdriver ):
}, ignore=["scenario","players","victory_conditions"] )
# run the test against all versions of VASSAL+VASL
run_vassal_tests( control_tests, do_test, True )
run_vassal_tests( webapp, do_test )
# ---------------------------------------------------------------------
@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.vasl_extensions, reason="--vasl-extensions not specified" ) #pylint: disable=no-member
def test_analyze_vsav( webapp, webdriver ):
"""Test analyzing a scenario."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" )
)
def do_test(): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
# analyze a basic scenario
new_scenario()
set_player( 1, "german" )
@ -693,20 +643,19 @@ def test_analyze_vsav( webapp, webdriver ):
)
# run the test against all versions of VASSAL+VASL
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test, vasl_extns_type="{REAL}" )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@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
def test_analyze_vsav_hip_concealed( webapp, webdriver ):
"""Test analyzing a scenario that contains HIP and concealed units."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
def do_test(): #pylint: disable=missing-docstring
# initialize
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
# do the test
# NOTE: The test scenario contains hidden/concealed units belonging to the Russians and Germans,
# but because the owning user is test/password, they should be ignored (unless you configure VASSAL
# with these credentials, so don't do that :-/).
@ -718,22 +667,20 @@ def test_analyze_vsav_hip_concealed( webapp, webdriver ):
)
# run the test against all versions of VASSAL+VASL
run_vassal_tests( control_tests, do_test, not pytest.config.option.short_tests ) #pylint: disable=no-member
run_vassal_tests( webapp, do_test )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@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
def test_reverse_remapped_gpids( webapp, webdriver ):
"""Test reverse mapping of GPID's."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
def do_test(): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
# do the test
new_scenario()
set_player( 1, "american" )
set_player( 2, "croatian" )
@ -744,25 +691,20 @@ def test_reverse_remapped_gpids( webapp, webdriver ):
)
# run the test against all versions of VASSAL+VASL
run_vassal_tests( control_tests, do_test,
not pytest.config.option.short_tests, #pylint: disable=no-member
min_vasl_version="6.5.0"
)
run_vassal_tests( webapp, do_test, min_vasl_version="6.5.0" )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@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
def test_vo_entry_selection_for_theater( webapp, webdriver ):
"""Test selection of vehicle/ordnance entries by theater."""
# initialize
control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" )
)
def do_test( theater, expected ): #pylint: disable=missing-docstring
# initialize
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, vsav_persistence=1, scenario_persistence=1 )
# do the test
new_scenario()
load_scenario_params( { "scenario": {
"SCENARIO_THEATER": theater,
@ -793,30 +735,24 @@ def test_vo_entry_selection_for_theater( webapp, webdriver ):
# NOTE The other variants always get imported as K:FW counters.
("kfw-un-common/o:002","12689/0"), ("kfw-un-common/o:002","11391/0"), ("kfw-un-common/o:002","11440/0")
] )
run_vassal_tests( control_tests, do_tests, True, min_vasl_version="6.5.0" )
run_vassal_tests( webapp, do_tests, min_vasl_version="6.5.0" )
# ---------------------------------------------------------------------
def run_vassal_tests( control_tests, func, test_all, min_vasl_version=None ):
def run_vassal_tests( webapp, func, all_combos=None, min_vasl_version=None, vasl_extns_type=None ):
"""Run the test function for each combination of VASSAL + VASL.
This is, of course, going to be insanely slow, since we need to spin up a JVM
and initialize VASSAL/VASL each time :-/
"""
# locate all VASL modules and VASSAL engines
vasl_mods = control_tests.get_vasl_mods()
vassal_engines = control_tests.get_vassal_engines()
# get the available VASSAL and VASL versions
vassal_versions = webapp.control_tests.get_vassal_versions()
vasl_versions = webapp.control_tests.get_vasl_versions()
def is_valid_combo( vassal_engine, vasl_mod ):
def is_valid_combo( vassal_version, vasl_version ):
"""Check if this is a valid combination of VASSAL and VASL."""
# NOTE: From 3.3, VASSAL no longer works with Java 8, and requires VASL 6.6.0 or later.
# FUDGE! We assume the version number is part of the VASSAL path, otherwise we would have to
# run the VASSAL shim to get the version number, and things are slow enough already :-/
mo = re.search( r"\d+\.\d+\.\d+", vassal_engine )
vassal_version = mo.group()
mo = re.search( r"\d+\.\d+\.\d+", vasl_mod )
vasl_version = mo.group()
# NOTE: From 3.3, VASSAL requires VASL 6.6.0 or later (and no longer works with Java 8).
if compare_version_strings( vassal_version, "3.3.0" ) >= 0:
if compare_version_strings( vasl_version, "6.6.0" ) < 0:
return False
@ -827,30 +763,29 @@ def run_vassal_tests( control_tests, func, test_all, min_vasl_version=None ):
# check if we want to test all VASSAL+VASL combinations (nb: if not, we test against only one combination,
# and since they all should give the same results, it doesn't matter which one.
if not test_all:
if all_combos is None:
all_combos = not pytest.config.option.short_tests #pylint: disable=no-member
if not all_combos:
for _ in range(0,100):
vasl_mod = random.choice( vasl_mods )
vassal_engine = random.choice( vassal_engines )
if is_valid_combo( vassal_engine, vasl_mod ):
vasl_mods = [ vasl_mod ]
vassal_engines = [ vassal_engine ]
vasl_version = random.choice( vasl_versions )
vassal_version = random.choice( vassal_versions )
if is_valid_combo( vassal_version, vasl_version ):
vasl_versions = [ vasl_version ]
vassal_versions = [ vassal_version ]
break
else:
assert False, "Can't find a valid combination of VASSAL and VASL."
# run the test for each VASSAL+VASL
for vassal_engine in vassal_engines:
control_tests.set_vassal_engine( vengine=vassal_engine )
for vasl_mod in vasl_mods:
# FUDGE! We assume the version number is part of the filename. Otherwise, we have to load
# the vmod, extract the buildFile, parse the XML, etc. :-/
mo = re.search( r"\d+\.\d+\.\d+", vasl_mod )
vasl_version = mo.group()
for vassal_version in vassal_versions:
for vasl_version in vasl_versions:
if min_vasl_version and compare_version_strings( vasl_version, min_vasl_version ) < 0:
continue
if not is_valid_combo( vassal_engine, vasl_mod ):
if not is_valid_combo( vassal_version, vasl_version ):
continue
control_tests.set_vasl_mod( vmod=vasl_mod )
webapp.control_tests \
.set_vassal_version( vassal_version ) \
.set_vasl_version( vasl_version, vasl_extns_type )
func()
# ---------------------------------------------------------------------
@ -859,7 +794,8 @@ def _update_vsav( fname, expected ):
"""Update a VASL scenario."""
# read the VSAV data
vsav_data = open( fname, "rb" ).read()
with open( fname, "rb" ) as fp:
vsav_data = fp.read()
# send the VSAV data to the front-end to be updated
set_stored_msg( "_vsav-persistence_", base64.b64encode( vsav_data ).decode( "utf-8" ) )
@ -867,10 +803,12 @@ def _update_vsav( fname, expected ):
_ = set_stored_msg_marker( "_last-warning_" )
select_menu_option( "update_vsav" )
# wait for the results to come back
wait_for( 2, lambda: get_stored_msg( "_vsav-persistence_" ) == "" ) # nb: wait for the front-end to receive the data
# wait for the front-end to receive the data
wait_for( 2, lambda: get_stored_msg( "_vsav-persistence_" ) == "" )
# wait for the updated data to come back
timeout = 120 if os.name == "nt" else 60
wait_for( timeout, lambda: get_stored_msg( "_vsav-persistence_" ) != "" ) # nb: wait for the updated data to arrive
wait_for( timeout, lambda: get_stored_msg( "_vsav-persistence_" ) != "" )
updated_vsav_data = get_stored_msg( "_vsav-persistence_" )
if updated_vsav_data.startswith( "ERROR: " ):
raise RuntimeError( updated_vsav_data )
@ -890,7 +828,7 @@ def _update_vsav( fname, expected ):
return updated_vsav_data
def _update_vsav_and_dump( control_tests, fname, expected ):
def _update_vsav_and_dump( webapp, fname, expected ):
"""Update a VASL scenario and dump the result."""
# update the VSAV
@ -900,15 +838,13 @@ def _update_vsav_and_dump( control_tests, fname, expected ):
with TempFile() as temp_file:
temp_file.write( updated_vsav_data )
temp_file.close( delete=False )
return _dump_vsav( control_tests, temp_file.name )
return _dump_vsav( webapp, temp_file.name )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def _dump_vsav( control_tests, fname ):
def _dump_vsav( webapp, fname ):
"""Dump a VASL scenario file."""
with open( fname, "rb" ) as fp:
vsav_data = fp.read()
return control_tests.get_vsav_dump( bin_data=vsav_data )
return webapp.control_tests.dump_vsav( fname )
def _check_vsav_dump( vsav_dump, expected, ignore=None ):
""""Check that a VASL scenario dump contains what we expect."""
@ -962,16 +898,19 @@ def _analyze_vsav( fname, expected_ob1, expected_ob2, expected_report ):
# read the VSAV data
fname = os.path.join( os.path.split(__file__)[0], "fixtures/analyze-vsav/"+fname )
vsav_data = open( fname, "rb" ).read()
with open( fname, "rb" ) as fp:
vsav_data = fp.read()
# send the VSAV data to the front-end to be analyzed
set_stored_msg( "_vsav-persistence_", base64.b64encode( vsav_data ).decode( "utf-8" ) )
prev_info_msg = set_stored_msg_marker( "_last-info_" )
prev_warning_msg = set_stored_msg_marker( "_last-warning_" )
select_menu_option( "analyze_vsav" )
# wait for the analysis to finish
wait_for( 60,
lambda: get_stored_msg("_last-info_") != prev_info_msg or get_stored_msg("_last-warning_") != prev_warning_msg
) # nb: wait for the analysis to finish
)
# check the results
saved_scenario = save_scenario()

@ -226,19 +226,12 @@ def test_variable_capabilities( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_html_names( webapp, webdriver ):
"""Test handling of vehicles/ordnance that have HTML in their name."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
def get_available_ivfs():
"""Get the PzKw IVF's available for selection."""
@ -349,17 +342,13 @@ def test_duplicate_vo_entries( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
pytest.config.option.short_tests, #pylint: disable=no-member
reason = "--short-tests specified"
) #pylint: disable=too-many-locals,too-many-branches
def test_common_vo( webapp, webdriver ):
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_common_vo( webapp, webdriver ): #pylint: disable=too-many-locals
"""Test loading of common vehicles/ordnance and landing craft."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_data_dir( dtype="real" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver )
# initialize
ALLIED_MINOR = [ "belgian", "danish", "dutch", "greek", "polish", "yugoslavian" ]
@ -479,19 +468,14 @@ def test_common_vo( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
) #pylint: disable=too-many-statements
def test_vo_images( webapp, webdriver ): #pylint: disable=too-many-statements
"""Test handling of vehicles/ordnance that have multiple images."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", None )
init_webapp( webapp, webdriver, scenario_persistence=1 )
def check_sortable2_entries( player_no, expected ):
"""Check the settings on the player's vehicles."""
@ -655,19 +639,14 @@ def test_vo_images( webapp, webdriver ): #pylint: disable=too-many-statements
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
) #pylint: disable=too-many-statements
def test_change_vo_image( webapp, webdriver ):
"""Test changing a V/O image."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", None )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# add an ISU-152
set_player( 2, "russian" )
@ -734,19 +713,12 @@ def test_change_vo_image( webapp, webdriver ):
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
def test_change_vo_image2( webapp, webdriver ):
"""Test changing the image for a V/O that has no alternative images."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random" )
)
webapp.control_tests.set_data_dir( "{REAL}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# add an 107mm GVPM
set_player( 2, "russian" )

@ -22,9 +22,8 @@ def test_vo_notes( webapp, webdriver ):
"""Test generating snippets for vehicle/ordnance notes."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -60,9 +59,8 @@ def test_ma_notes( webapp, webdriver ):
"""Test generating snippets for vehicle/ordnance multi-applicable notes."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
def do_test( player_no, nat, vo_type, vo_entries, expected ):
"""Load the specified vehicles and check the resulting snippet."""
@ -117,9 +115,8 @@ def test_ma_html_notes( webapp, webdriver ):
"""Test how we load vehicle/ordnance notes (HTML vs. PNG)."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -144,9 +141,8 @@ def test_common_vo_notes( webapp, webdriver ):
"""Test handling of Allied/Axis Minor common vehicles/ordnance."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -182,9 +178,8 @@ def test_common_vo_notes2( webapp, webdriver ):
"""Test handling of Allied/Axis Minor common vehicles/ordnance (as images)."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -221,9 +216,8 @@ def test_extra_ma_notes( webapp, webdriver ):
"""Test handling of Landing Craft and Allied/Axis Minor common vehicles/ordnance."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -297,9 +291,8 @@ def test_landing_craft_notes( webapp, webdriver ):
"""Test handling of Landing Craft notes."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -322,9 +315,8 @@ def test_update_ui( webapp, webdriver ):
"""Check that the UI is updated correctly for multi-applicable notes."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver )
def do_test( nat, veh_expected, ord_expected ):
"""Do the test."""
@ -356,9 +348,7 @@ def test_seq_ids( webapp, webdriver ):
"""Test handling of vehicle/ordnance sequence ID's."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
init_webapp( webapp, webdriver, scenario_persistence=1 )
# load the test scenario
load_scenario( {
@ -422,9 +412,8 @@ def test_special_cases( webapp, webdriver ):
"""Test special cases."""
# initialize
init_webapp( webapp, webdriver, scenario_persistence=1,
reset = lambda ct: ct.set_vo_notes_dir( dtype="test" )
)
webapp.control_tests.set_vo_notes_dir( "{TEST}" )
init_webapp( webapp, webdriver, scenario_persistence=1 )
# check that Italian Multi-Applicable Ordnance (only) Note R has a line-through
load_scenario( {
@ -442,34 +431,27 @@ def test_special_cases( webapp, webdriver ):
# ---------------------------------------------------------------------
@pytest.mark.skipif(
not pytest.config.option.vo_notes, #pylint: disable=no-member
reason = "--vo-notes not specified"
) #pylint: disable=too-many-locals,too-many-branches
# NOTE: The expected output files contain pieces from the supported extensions,
# so the VASL extensions directory must be loaded.
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
) #pylint: disable=too-many-locals
def test_vo_notes_reports( webapp, webdriver ):
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_vo_notes_reports( webapp, webdriver ): #pylint: disable=too-many-locals
"""Check the vehicle/ordnance notes reports."""
# check if the remote webapp server supports this test
if not webapp.control_tests.has_capability( "chapter-h" ):
return
# initialize
# NOTE: The server must be configured with the *real* Chapter H vehicle/ordnance notes.
vo_notes_dir = pytest.config.option.vo_notes #pylint: disable=no-member
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" ) \
.set_vo_notes_dir( dtype="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", vasl_extns_type="{REAL}" ) \
.set_vo_notes_dir( "{REAL}" )
init_webapp( webapp, webdriver )
# initialize
# FUDGE! It's not correct to get the Chapter H directory from our *local* config, we should be
# using whatever the *remote* server is using, but since this directory is not publicly available,
# we can live it. The right way to do things would be to provide a way for us to get the fixtures
# from the remote server, which is far more trouble than it's worth.
vo_notes_dir = webapp.config[ "CHAPTER_H_NOTES_DIR" ]
check_dir = os.path.join( vo_notes_dir, "tests/" )
save_dir = os.environ.get( "VO_NOTES_SAVEDIR" ) # nb: define this to save the generated reports

@ -15,25 +15,15 @@ from vasl_templates.webapp.tests.utils import init_webapp, get_nationalities, fi
# ---------------------------------------------------------------------
# NOTE: The expected output files contain pieces from the supported extensions,
# so the VASL extensions directory must be loaded.
@pytest.mark.skipif(
not pytest.config.option.vasl_mods, #pylint: disable=no-member
reason = "--vasl-mods not specified"
)
@pytest.mark.skipif(
not pytest.config.option.vasl_extensions, #pylint: disable=no-member
reason = "--vasl-extensions not specified"
) #pylint: disable=too-many-statements,too-many-locals
@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member
def test_vo_reports( webapp, webdriver ): #pylint: disable=too-many-locals
"""Check the vehicle/ordnance reports."""
# initialize
init_webapp( webapp, webdriver,
reset = lambda ct:
ct.set_data_dir( dtype="real" ) \
.set_vasl_mod( vmod="random", extns_dtype="real" )
)
webapp.control_tests \
.set_data_dir( "{REAL}" ) \
.set_vasl_version( "random", "{REAL}" )
init_webapp( webapp, webdriver )
# initialize
check_dir = os.path.join( os.path.split(__file__)[0], "fixtures/vo-reports/" )

@ -11,14 +11,11 @@ from collections import defaultdict
import lxml.html
import pytest
from PyQt5.QtWidgets import QApplication
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException, WebDriverException
from vasl_templates.webapp.tests.remote import ControlTests
# standard templates
_STD_TEMPLATES = {
"scenario": [
@ -61,45 +58,14 @@ def init_webapp( webapp, webdriver, **options ):
_webapp = webapp
_webdriver = webdriver
# reset the server
# NOTE: We have to do this manually, since we can't use pytest's monkeypatch'ing,
# since we could be talking to a remote server (see ControlTests for more details).
control_tests = ControlTests( webapp )
control_tests \
.set_data_dir( dtype="test" ) \
.set_default_scenario( fname=None ) \
.set_default_template_pack( dname=None ) \
.set_vasl_extn_info_dir( dtype=None ) \
.set_vasl_mod( vmod=None, extns_dtype=None ) \
.set_vassal_engine( vengine=None ) \
.set_vo_notes_dir( dtype=None ) \
.set_user_files_dir( dtype=None ) \
.set_roar_scenario_index( fname="roar-scenario-index.json" ) \
.set_scenario_index( fname="asl-scenario-archive.json" ) \
.set_app_config( key="MAP_URL", val="MAP:[{LAT},{LONG}]" ) \
.set_app_config( key="DISABLE_LFA_HOTNESS_FADEIN", val=True )
if "reset" in options:
options.pop( "reset" )( control_tests )
# force the default template pack to be reloaded (using the new settings)
control_tests.reset_template_pack()
# load the webapp
options[ "force-reinit" ] = 1 # nb: force the webapp to re-initialize
webdriver.get( webapp.url_for( "main", **options ) )
_wait_for_webapp()
# reset the user settings
webdriver.delete_all_cookies()
return control_tests
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def refresh_webapp( webdriver ):
"""Refresh the webapp."""
webdriver.refresh()
_wait_for_webapp()
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def _wait_for_webapp():
@ -569,6 +535,7 @@ def _get_clipboard() :
"""
if pytest.config.option.use_clipboard: #pylint: disable=no-member
global _pyqt_app
from PyQt5.QtWidgets import QApplication
if _pyqt_app is None:
_pyqt_app = QApplication( [] )
clipboard = QApplication.clipboard()

@ -6,6 +6,7 @@ import io
import tempfile
import pathlib
import math
import re
from collections import defaultdict
from flask import request, Response, send_file
@ -211,6 +212,19 @@ def parse_int( val, default=None ):
# ---------------------------------------------------------------------
def compare_version_strings( lhs, rhs ):
"""Compare two version strings."""
def parse( val ): #pylint: disable=missing-docstring
mo = re.search( r"^(\d+)\.(\d+)\.(\d+)$", val )
return ( int(mo.group(1)), int(mo.group(2)), int(mo.group(3)) )
lhs, rhs = parse(lhs), parse(rhs)
if lhs < rhs:
return -1
elif lhs > rhs:
return +1
else:
return 0
def friendly_fractions( val, postfix=None, postfix2=None ):
"""Convert decimal values to more friendly fractions."""
if val is None:

@ -13,21 +13,23 @@ _logger = logging.getLogger( "vasl_mod" )
from vasl_templates.webapp import app, globvars
from vasl_templates.webapp.config.constants import DATA_DIR
from vasl_templates.webapp.vo import get_vo_listings
from vasl_templates.webapp.utils import compare_version_strings
SUPPORTED_VASL_MOD_VERSIONS = [ "6.6.0", "6.6.1" ]
SUPPORTED_VASL_MOD_VERSIONS_DISPLAY = "6.6.0-.1"
warnings = [] # nb: for the test suite
_warnings = [] # nb: for the test suite
# ---------------------------------------------------------------------
def set_vasl_mod( vmod_fname, msg_store ):
"""Install a new global VaslMod object."""
globvars.vasl_mod = None
global _warnings
_warnings = []
if vmod_fname:
# load and install the specified VASL module
# NOTE: The Docker container configures this setting via an environment variable.
extns_dir = app.config.get( "VASL_EXTNS_DIR", os.environ.get("VASL_EXTNS_DIR") )
extns_dir = app.config.get( "VASL_EXTNS_DIR" )
extns = _load_vasl_extns( extns_dir, msg_store )
try:
vasl_mod = VaslMod( vmod_fname, DATA_DIR, extns )
@ -62,7 +64,7 @@ def _load_vasl_extns( extn_dir, msg_store ): #pylint: disable=too-many-locals,to
def log_warning( fmt, *args, **kwargs ): #pylint: disable=missing-docstring
soft = kwargs.pop( "soft", False )
msg = fmt.format( *args, **kwargs )
warnings.append( msg )
_warnings.append( msg )
if soft:
_logger.info( "%s", msg )
else:
@ -444,20 +446,6 @@ def get_vo_gpids( vasl_mod ):
return gpids
def compare_version_strings( lhs, rhs ):
"""Compare two version strings."""
def parse( val ): #pylint: disable=missing-docstring
mo = re.search( r"^(\d+)\.(\d+)\.(\d+)$", val )
return ( int(mo.group(1)), int(mo.group(2)), int(mo.group(3)) )
lhs, rhs = parse(lhs), parse(rhs)
if lhs < rhs:
return -1
elif lhs > rhs:
return +1
else:
return 0
# ---------------------------------------------------------------------
# VASL 6.4.3 removed several PieceSlot's. There's no comment for the commmit (0a27c24)

@ -17,9 +17,9 @@ from flask import request, jsonify
from vasl_templates.webapp import app, globvars
from vasl_templates.webapp.config.constants import BASE_DIR, IS_FROZEN
from vasl_templates.webapp.utils import TempFile, SimpleError
from vasl_templates.webapp.utils import TempFile, SimpleError, compare_version_strings
from vasl_templates.webapp.webdriver import WebDriver
from vasl_templates.webapp.vasl_mod import get_reverse_remapped_gpid, compare_version_strings
from vasl_templates.webapp.vasl_mod import get_reverse_remapped_gpid
# NOTE: VASSAL dropped support for Java 8 from 3.3.0. The first version of VASL that supported
# the later versions of Java was 6.6.0, but it was compiled against VASSAL 3.4.2, so we don't
@ -471,8 +471,7 @@ class VassalShim:
@staticmethod
def get_boards_dir():
"""Get the configured boards directory."""
# NOTE: The Docker container configures this setting via an environment variable.
boards_dir = app.config.get( "BOARDS_DIR", os.environ.get("VASL_BOARDS_DIR") )
boards_dir = app.config.get( "BOARDS_DIR" )
if not boards_dir:
raise SimpleError( "The VASL boards directory has not been configured." )
if not os.path.isdir( boards_dir ):
@ -532,8 +531,7 @@ class VassalShim:
@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") )
return app.config.get( "VASSAL_DIR" )
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

@ -9,6 +9,7 @@ from flask import request, render_template, jsonify, abort
from vasl_templates.webapp import app, globvars
from vasl_templates.webapp.config.constants import DATA_DIR
from vasl_templates.webapp.vo_utils import copy_vo_entry, add_vo_comments, apply_extn_info, make_vo_index
from vasl_templates.webapp import vo_utils as webapp_vo_utils
_kfw_listings = { "vehicles": {}, "ordnance": {} }
@ -106,6 +107,7 @@ def _do_load_vo_listings( vasl_mod, vo_type, merge_common, real_data_dir, msg_st
del listings[ minor_type+"-common" ]
# add vehicle/ordnance comments (based on what notes they have)
webapp_vo_utils._vo_comments = None #pylint: disable=protected-access
add_vo_comments( listings, vo_type, msg_store )
add_vo_comments( _kfw_listings[vo_type], vo_type, msg_store )

@ -33,8 +33,7 @@ def load_vo_notes( msg_store ): #pylint: disable=too-many-statements,too-many-lo
"""Load the Chapter H vehicle/ordnance notes."""
# locate the data directory
# NOTE: The Docker container configures this setting via an environment variable.
dname = app.config.get( "CHAPTER_H_NOTES_DIR", os.environ.get("CHAPTER_H_NOTES_DIR") )
dname = app.config.get( "CHAPTER_H_NOTES_DIR" )
if dname:
# NOTE: If the Chapter H directory has been configured but is incorrect, we want to keep going,
# since this may well happen when running in a container (the directory has to be always configured,

Loading…
Cancel
Save