diff --git a/.pylintrc b/.pylintrc index a51285d..00856c6 100644 --- a/.pylintrc +++ b/.pylintrc @@ -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. diff --git a/Dockerfile b/Dockerfile index b41d862..7c41ec6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/conftest.py b/conftest.py index 326a08c..d0170da 100644 --- a/conftest.py +++ b/conftest.py @@ -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 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docker/config/debug.cfg b/docker/config/debug.cfg index 9f06056..397503b 100644 --- a/docker/config/debug.cfg +++ b/docker/config/debug.cfg @@ -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/ diff --git a/docker/config/site.cfg b/docker/config/site.cfg index a00fca3..2893e35 100644 --- a/docker/config/site.cfg +++ b/docker/config/site.cfg @@ -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 diff --git a/docker/run.sh b/docker/run.sh index 090418e..05af6cd 100755 --- a/docker/run.sh +++ b/docker/run.sh @@ -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 diff --git a/requirements-dev.txt b/requirements-dev.txt index 7548d4c..346de95 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -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 diff --git a/run-container.sh b/run-container.sh index eed7d63..251542b 100755 --- a/run-container.sh +++ b/run-container.sh @@ -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]} diff --git a/vasl_templates/main.py b/vasl_templates/main.py index ce77393..e0516d3 100755 --- a/vasl_templates/main.py +++ b/vasl_templates/main.py @@ -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 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vasl_templates/tools/webdriver_stress_test.py b/vasl_templates/tools/webdriver_stress_test.py index 3764486..a0e3267 100755 --- a/vasl_templates/tools/webdriver_stress_test.py +++ b/vasl_templates/tools/webdriver_stress_test.py @@ -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 ) diff --git a/vasl_templates/webapp/__init__.py b/vasl_templates/webapp/__init__.py index 3889885..cc4aab0 100644 --- a/vasl_templates/webapp/__init__.py +++ b/vasl_templates/webapp/__init__.py @@ -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 ) diff --git a/vasl_templates/webapp/config/debug.cfg.example b/vasl_templates/webapp/config/debug.cfg.example new file mode 100644 index 0000000..ba8d626 --- /dev/null +++ b/vasl_templates/webapp/config/debug.cfg.example @@ -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 = ... diff --git a/vasl_templates/webapp/config/site.cfg.example b/vasl_templates/webapp/config/site.cfg.example index 244aabe..b5f638b 100644 --- a/vasl_templates/webapp/config/site.cfg.example +++ b/vasl_templates/webapp/config/site.cfg.example @@ -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... diff --git a/vasl_templates/webapp/downloads.py b/vasl_templates/webapp/downloads.py index 57e63d4..12d0bd9 100644 --- a/vasl_templates/webapp/downloads.py +++ b/vasl_templates/webapp/downloads.py @@ -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" } diff --git a/vasl_templates/webapp/files.py b/vasl_templates/webapp/files.py index 06618c1..bda0280 100644 --- a/vasl_templates/webapp/files.py +++ b/vasl_templates/webapp/files.py @@ -60,8 +60,7 @@ class FileServer: @app.route( "/user/" ) 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 ): diff --git a/vasl_templates/webapp/main.py b/vasl_templates/webapp/main.py index 17ba581..15deefe 100644 --- a/vasl_templates/webapp/main.py +++ b/vasl_templates/webapp/main.py @@ -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 "" diff --git a/vasl_templates/webapp/run_server.py b/vasl_templates/webapp/run_server.py index 5ea9d48..36b4f73 100755 --- a/vasl_templates/webapp/run_server.py +++ b/vasl_templates/webapp/run_server.py @@ -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 diff --git a/vasl_templates/webapp/testing.py b/vasl_templates/webapp/testing.py deleted file mode 100644 index 6c51992..0000000 --- a/vasl_templates/webapp/testing.py +++ /dev/null @@ -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/" ) -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" diff --git a/vasl_templates/webapp/tests/compile-proto.sh b/vasl_templates/webapp/tests/compile-proto.sh new file mode 100755 index 0000000..a7ddb16 --- /dev/null +++ b/vasl_templates/webapp/tests/compile-proto.sh @@ -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 diff --git a/vasl_templates/webapp/tests/control_tests.py b/vasl_templates/webapp/tests/control_tests.py new file mode 100644 index 0000000..3256685 --- /dev/null +++ b/vasl_templates/webapp/tests/control_tests.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 diff --git a/vasl_templates/webapp/tests/control_tests_servicer.py b/vasl_templates/webapp/tests/control_tests_servicer.py new file mode 100644 index 0000000..c8c0206 --- /dev/null +++ b/vasl_templates/webapp/tests/control_tests_servicer.py @@ -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 ) diff --git a/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/BFP_v403.vmdx b/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/BFP_v403.vmdx new file mode 100644 index 0000000..3077e0a Binary files /dev/null and b/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/BFP_v403.vmdx differ diff --git a/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/kgs-v1.1.mdx b/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/kgs-v1.1.mdx new file mode 100644 index 0000000..639f7a6 Binary files /dev/null and b/vasl_templates/webapp/tests/fixtures/vasl-extensions/real/kgs-v1.1.mdx differ diff --git a/vasl_templates/webapp/tests/proto/__init__.py b/vasl_templates/webapp/tests/proto/__init__.py new file mode 100644 index 0000000..066049f --- /dev/null +++ b/vasl_templates/webapp/tests/proto/__init__.py @@ -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() diff --git a/vasl_templates/webapp/tests/proto/control_tests.proto b/vasl_templates/webapp/tests/proto/control_tests.proto new file mode 100644 index 0000000..be1749d --- /dev/null +++ b/vasl_templates/webapp/tests/proto/control_tests.proto @@ -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 ) ; +} diff --git a/vasl_templates/webapp/tests/proto/delete_app_config_val_request.py b/vasl_templates/webapp/tests/proto/delete_app_config_val_request.py new file mode 100644 index 0000000..b55e78c --- /dev/null +++ b/vasl_templates/webapp/tests/proto/delete_app_config_val_request.py @@ -0,0 +1,7 @@ +""" Injected functions for DeleteAppConfigValRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a DeleteAppConfigValRequest as a brief string.""" + return self.key diff --git a/vasl_templates/webapp/tests/proto/dump_vsav_request.py b/vasl_templates/webapp/tests/proto/dump_vsav_request.py new file mode 100644 index 0000000..5b2a8e5 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/dump_vsav_request.py @@ -0,0 +1,7 @@ +""" Injected functions for DumpVsavRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a DumpVsavRequest as a brief string.""" + return "#bytes={}".format( len(self.vsavData) ) diff --git a/vasl_templates/webapp/tests/proto/generated/control_tests_pb2.py b/vasl_templates/webapp/tests/proto/generated/control_tests_pb2.py new file mode 100644 index 0000000..18c38a9 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/generated/control_tests_pb2.py @@ -0,0 +1,1554 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: control_tests.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='control_tests.proto', + package='', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x13\x63ontrol_tests.proto\x1a\x1bgoogle/protobuf/empty.proto\"*\n\x12StartTestsResponse\x12\x14\n\x0c\x63\x61pabilities\x18\x01 \x03(\t\"3\n\x19GetVassalVersionsResponse\x12\x16\n\x0evassalVersions\x18\x01 \x03(\t\"0\n\x17SetVassalVersionRequest\x12\x15\n\rvassalVersion\x18\x01 \x01(\t\"/\n\x17GetVaslVersionsResponse\x12\x14\n\x0cvaslVersions\x18\x01 \x03(\t\"\x9c\x01\n\x15SetVaslVersionRequest\x12\x13\n\x0bvaslVersion\x18\x01 \x01(\t\x12;\n\rvaslExtnsType\x18\x02 \x01(\x0e\x32$.SetVaslVersionRequest.VaslExtnsType\"1\n\rVaslExtnsType\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04REAL\x10\x01\x12\x0c\n\x08TEMP_DIR\x10\x02\"-\n\x14GetVaslExtnsResponse\x12\x15\n\rvaslExtnsJson\x18\x01 \x01(\t\",\n\x19SetVaslExtnInfoDirRequest\x12\x0f\n\x07\x64irName\x18\x01 \x01(\t\"6\n\x18SetGpidRemappingsRequest\x12\x1a\n\x12gpidRemappingsJson\x18\x01 \x01(\t\".\n\x1aGetVaslModWarningsResponse\x12\x10\n\x08warnings\x18\x01 \x03(\t\"_\n\x11SetDataDirRequest\x12+\n\x07\x64irType\x18\x01 \x01(\x0e\x32\x1a.SetDataDirRequest.DirType\"\x1d\n\x07\x44irType\x12\x08\n\x04TEST\x10\x00\x12\x08\n\x04REAL\x10\x01\"-\n\x19SetDefaultScenarioRequest\x12\x10\n\x08\x66ileName\x18\x01 \x01(\t\"\xc9\x01\n\x1dSetDefaultTemplatePackRequest\x12K\n\x10templatePackType\x18\x01 \x01(\x0e\x32/.SetDefaultTemplatePackRequest.TemplatePackTypeH\x00\x12\x11\n\x07\x64irName\x18\x02 \x01(\tH\x00\x12\x11\n\x07zipData\x18\x03 \x01(\x0cH\x00\")\n\x10TemplatePackType\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\x08\n\x04REAL\x10\x01\x42\n\n\x08tp_oneof\"w\n\x18SetVehOrdNotesDirRequest\x12\x32\n\x07\x64irType\x18\x01 \x01(\x0e\x32!.SetVehOrdNotesDirRequest.DirType\"\'\n\x07\x44irType\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04TEST\x10\x01\x12\x08\n\x04REAL\x10\x02\"*\n\x16SetUserFilesDirRequest\x12\x10\n\x08\x64irOrUrl\x18\x01 \x01(\t\".\n\x1aSetAsaScenarioIndexRequest\x12\x10\n\x08\x66ileName\x18\x01 \x01(\t\"/\n\x1bSetRoarScenarioIndexRequest\x12\x10\n\x08\x66ileName\x18\x01 \x01(\t\"0\n\x1bGetLastSnippetImageResponse\x12\x11\n\timageData\x18\x01 \x01(\x0c\"2\n\x18GetLastAsaUploadResponse\x12\x16\n\x0elastUploadJson\x18\x01 \x01(\t\"#\n\x0f\x44umpVsavRequest\x12\x10\n\x08vsavData\x18\x01 \x01(\x0c\"$\n\x10\x44umpVsavResponse\x12\x10\n\x08vsavDump\x18\x01 \x01(\t\"+\n\x14GetVaslPiecesRequest\x12\x13\n\x0bvaslVersion\x18\x01 \x01(\t\"9\n\x15GetVaslPiecesResponse\x12\x11\n\tpieceDump\x18\x01 \x01(\t\x12\r\n\x05gpids\x18\x02 \x03(\t\"-\n\x14GetAppConfigResponse\x12\x15\n\rappConfigJson\x18\x01 \x01(\t\"h\n\x16SetAppConfigValRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x10\n\x06strVal\x18\x02 \x01(\tH\x00\x12\x10\n\x06intVal\x18\x03 \x01(\x05H\x00\x12\x11\n\x07\x62oolVal\x18\x04 \x01(\x08H\x00\x42\n\n\x08\x61\x63_oneof\"(\n\x19\x44\x65leteAppConfigValRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\"5\n\x13SaveTempFileRequest\x12\x10\n\x08\x66ileName\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x32\x89\x0e\n\x0c\x43ontrolTests\x12\x39\n\nstartTests\x12\x16.google.protobuf.Empty\x1a\x13.StartTestsResponse\x12:\n\x08\x65ndTests\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12G\n\x11getVassalVersions\x12\x16.google.protobuf.Empty\x1a\x1a.GetVassalVersionsResponse\x12\x44\n\x10setVassalVersion\x12\x18.SetVassalVersionRequest\x1a\x16.google.protobuf.Empty\x12\x43\n\x0fgetVaslVersions\x12\x16.google.protobuf.Empty\x1a\x18.GetVaslVersionsResponse\x12@\n\x0esetVaslVersion\x12\x16.SetVaslVersionRequest\x1a\x16.google.protobuf.Empty\x12=\n\x0cgetVaslExtns\x12\x16.google.protobuf.Empty\x1a\x15.GetVaslExtnsResponse\x12H\n\x12setVaslExtnInfoDir\x12\x1a.SetVaslExtnInfoDirRequest\x1a\x16.google.protobuf.Empty\x12\x46\n\x11setGpidRemappings\x12\x19.SetGpidRemappingsRequest\x1a\x16.google.protobuf.Empty\x12I\n\x12getVaslModWarnings\x12\x16.google.protobuf.Empty\x1a\x1b.GetVaslModWarningsResponse\x12\x38\n\nsetDataDir\x12\x12.SetDataDirRequest\x1a\x16.google.protobuf.Empty\x12H\n\x12setDefaultScenario\x12\x1a.SetDefaultScenarioRequest\x1a\x16.google.protobuf.Empty\x12P\n\x16setDefaultTemplatePack\x12\x1e.SetDefaultTemplatePackRequest\x1a\x16.google.protobuf.Empty\x12\x46\n\x11setVehOrdNotesDir\x12\x19.SetVehOrdNotesDirRequest\x1a\x16.google.protobuf.Empty\x12\x42\n\x0fsetUserFilesDir\x12\x17.SetUserFilesDirRequest\x1a\x16.google.protobuf.Empty\x12J\n\x13setAsaScenarioIndex\x12\x1b.SetAsaScenarioIndexRequest\x1a\x16.google.protobuf.Empty\x12L\n\x14setRoarScenarioIndex\x12\x1c.SetRoarScenarioIndexRequest\x1a\x16.google.protobuf.Empty\x12K\n\x13getLastSnippetImage\x12\x16.google.protobuf.Empty\x1a\x1c.GetLastSnippetImageResponse\x12\x44\n\x12resetLastAsaUpload\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12\x45\n\x10getLastAsaUpload\x12\x16.google.protobuf.Empty\x1a\x19.GetLastAsaUploadResponse\x12/\n\x08\x64umpVsav\x12\x10.DumpVsavRequest\x1a\x11.DumpVsavResponse\x12>\n\rgetVaslPieces\x12\x15.GetVaslPiecesRequest\x1a\x16.GetVaslPiecesResponse\x12=\n\x0cgetAppConfig\x12\x16.google.protobuf.Empty\x1a\x15.GetAppConfigResponse\x12\x42\n\x0fsetAppConfigVal\x12\x17.SetAppConfigValRequest\x1a\x16.google.protobuf.Empty\x12H\n\x12\x64\x65leteAppConfigVal\x12\x1a.DeleteAppConfigValRequest\x1a\x16.google.protobuf.Empty\x12<\n\x0csaveTempFile\x12\x14.SaveTempFileRequest\x1a\x16.google.protobuf.Emptyb\x06proto3' + , + dependencies=[google_dot_protobuf_dot_empty__pb2.DESCRIPTOR,]) + + + +_SETVASLVERSIONREQUEST_VASLEXTNSTYPE = _descriptor.EnumDescriptor( + name='VaslExtnsType', + full_name='SetVaslVersionRequest.VaslExtnsType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='REAL', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TEMP_DIR', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=356, + serialized_end=405, +) +_sym_db.RegisterEnumDescriptor(_SETVASLVERSIONREQUEST_VASLEXTNSTYPE) + +_SETDATADIRREQUEST_DIRTYPE = _descriptor.EnumDescriptor( + name='DirType', + full_name='SetDataDirRequest.DirType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='TEST', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='REAL', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=670, + serialized_end=699, +) +_sym_db.RegisterEnumDescriptor(_SETDATADIRREQUEST_DIRTYPE) + +_SETDEFAULTTEMPLATEPACKREQUEST_TEMPLATEPACKTYPE = _descriptor.EnumDescriptor( + name='TemplatePackType', + full_name='SetDefaultTemplatePackRequest.TemplatePackType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEFAULT', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='REAL', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=897, + serialized_end=938, +) +_sym_db.RegisterEnumDescriptor(_SETDEFAULTTEMPLATEPACKREQUEST_TEMPLATEPACKTYPE) + +_SETVEHORDNOTESDIRREQUEST_DIRTYPE = _descriptor.EnumDescriptor( + name='DirType', + full_name='SetVehOrdNotesDirRequest.DirType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='NONE', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TEST', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='REAL', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=1032, + serialized_end=1071, +) +_sym_db.RegisterEnumDescriptor(_SETVEHORDNOTESDIRREQUEST_DIRTYPE) + + +_STARTTESTSRESPONSE = _descriptor.Descriptor( + name='StartTestsResponse', + full_name='StartTestsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='capabilities', full_name='StartTestsResponse.capabilities', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=52, + serialized_end=94, +) + + +_GETVASSALVERSIONSRESPONSE = _descriptor.Descriptor( + name='GetVassalVersionsResponse', + full_name='GetVassalVersionsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vassalVersions', full_name='GetVassalVersionsResponse.vassalVersions', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=96, + serialized_end=147, +) + + +_SETVASSALVERSIONREQUEST = _descriptor.Descriptor( + name='SetVassalVersionRequest', + full_name='SetVassalVersionRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vassalVersion', full_name='SetVassalVersionRequest.vassalVersion', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=149, + serialized_end=197, +) + + +_GETVASLVERSIONSRESPONSE = _descriptor.Descriptor( + name='GetVaslVersionsResponse', + full_name='GetVaslVersionsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vaslVersions', full_name='GetVaslVersionsResponse.vaslVersions', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=199, + serialized_end=246, +) + + +_SETVASLVERSIONREQUEST = _descriptor.Descriptor( + name='SetVaslVersionRequest', + full_name='SetVaslVersionRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vaslVersion', full_name='SetVaslVersionRequest.vaslVersion', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='vaslExtnsType', full_name='SetVaslVersionRequest.vaslExtnsType', index=1, + number=2, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _SETVASLVERSIONREQUEST_VASLEXTNSTYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=249, + serialized_end=405, +) + + +_GETVASLEXTNSRESPONSE = _descriptor.Descriptor( + name='GetVaslExtnsResponse', + full_name='GetVaslExtnsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vaslExtnsJson', full_name='GetVaslExtnsResponse.vaslExtnsJson', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=407, + serialized_end=452, +) + + +_SETVASLEXTNINFODIRREQUEST = _descriptor.Descriptor( + name='SetVaslExtnInfoDirRequest', + full_name='SetVaslExtnInfoDirRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='dirName', full_name='SetVaslExtnInfoDirRequest.dirName', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=454, + serialized_end=498, +) + + +_SETGPIDREMAPPINGSREQUEST = _descriptor.Descriptor( + name='SetGpidRemappingsRequest', + full_name='SetGpidRemappingsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gpidRemappingsJson', full_name='SetGpidRemappingsRequest.gpidRemappingsJson', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=500, + serialized_end=554, +) + + +_GETVASLMODWARNINGSRESPONSE = _descriptor.Descriptor( + name='GetVaslModWarningsResponse', + full_name='GetVaslModWarningsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='warnings', full_name='GetVaslModWarningsResponse.warnings', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=556, + serialized_end=602, +) + + +_SETDATADIRREQUEST = _descriptor.Descriptor( + name='SetDataDirRequest', + full_name='SetDataDirRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='dirType', full_name='SetDataDirRequest.dirType', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _SETDATADIRREQUEST_DIRTYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=604, + serialized_end=699, +) + + +_SETDEFAULTSCENARIOREQUEST = _descriptor.Descriptor( + name='SetDefaultScenarioRequest', + full_name='SetDefaultScenarioRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='fileName', full_name='SetDefaultScenarioRequest.fileName', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=701, + serialized_end=746, +) + + +_SETDEFAULTTEMPLATEPACKREQUEST = _descriptor.Descriptor( + name='SetDefaultTemplatePackRequest', + full_name='SetDefaultTemplatePackRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='templatePackType', full_name='SetDefaultTemplatePackRequest.templatePackType', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dirName', full_name='SetDefaultTemplatePackRequest.dirName', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='zipData', full_name='SetDefaultTemplatePackRequest.zipData', index=2, + number=3, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _SETDEFAULTTEMPLATEPACKREQUEST_TEMPLATEPACKTYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='tp_oneof', full_name='SetDefaultTemplatePackRequest.tp_oneof', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=749, + serialized_end=950, +) + + +_SETVEHORDNOTESDIRREQUEST = _descriptor.Descriptor( + name='SetVehOrdNotesDirRequest', + full_name='SetVehOrdNotesDirRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='dirType', full_name='SetVehOrdNotesDirRequest.dirType', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _SETVEHORDNOTESDIRREQUEST_DIRTYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=952, + serialized_end=1071, +) + + +_SETUSERFILESDIRREQUEST = _descriptor.Descriptor( + name='SetUserFilesDirRequest', + full_name='SetUserFilesDirRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='dirOrUrl', full_name='SetUserFilesDirRequest.dirOrUrl', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1073, + serialized_end=1115, +) + + +_SETASASCENARIOINDEXREQUEST = _descriptor.Descriptor( + name='SetAsaScenarioIndexRequest', + full_name='SetAsaScenarioIndexRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='fileName', full_name='SetAsaScenarioIndexRequest.fileName', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1117, + serialized_end=1163, +) + + +_SETROARSCENARIOINDEXREQUEST = _descriptor.Descriptor( + name='SetRoarScenarioIndexRequest', + full_name='SetRoarScenarioIndexRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='fileName', full_name='SetRoarScenarioIndexRequest.fileName', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1165, + serialized_end=1212, +) + + +_GETLASTSNIPPETIMAGERESPONSE = _descriptor.Descriptor( + name='GetLastSnippetImageResponse', + full_name='GetLastSnippetImageResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='imageData', full_name='GetLastSnippetImageResponse.imageData', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1214, + serialized_end=1262, +) + + +_GETLASTASAUPLOADRESPONSE = _descriptor.Descriptor( + name='GetLastAsaUploadResponse', + full_name='GetLastAsaUploadResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='lastUploadJson', full_name='GetLastAsaUploadResponse.lastUploadJson', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1264, + serialized_end=1314, +) + + +_DUMPVSAVREQUEST = _descriptor.Descriptor( + name='DumpVsavRequest', + full_name='DumpVsavRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vsavData', full_name='DumpVsavRequest.vsavData', index=0, + number=1, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1316, + serialized_end=1351, +) + + +_DUMPVSAVRESPONSE = _descriptor.Descriptor( + name='DumpVsavResponse', + full_name='DumpVsavResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vsavDump', full_name='DumpVsavResponse.vsavDump', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1353, + serialized_end=1389, +) + + +_GETVASLPIECESREQUEST = _descriptor.Descriptor( + name='GetVaslPiecesRequest', + full_name='GetVaslPiecesRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='vaslVersion', full_name='GetVaslPiecesRequest.vaslVersion', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1391, + serialized_end=1434, +) + + +_GETVASLPIECESRESPONSE = _descriptor.Descriptor( + name='GetVaslPiecesResponse', + full_name='GetVaslPiecesResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='pieceDump', full_name='GetVaslPiecesResponse.pieceDump', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='gpids', full_name='GetVaslPiecesResponse.gpids', index=1, + number=2, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1436, + serialized_end=1493, +) + + +_GETAPPCONFIGRESPONSE = _descriptor.Descriptor( + name='GetAppConfigResponse', + full_name='GetAppConfigResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='appConfigJson', full_name='GetAppConfigResponse.appConfigJson', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1495, + serialized_end=1540, +) + + +_SETAPPCONFIGVALREQUEST = _descriptor.Descriptor( + name='SetAppConfigValRequest', + full_name='SetAppConfigValRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='SetAppConfigValRequest.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='strVal', full_name='SetAppConfigValRequest.strVal', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='intVal', full_name='SetAppConfigValRequest.intVal', index=2, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='boolVal', full_name='SetAppConfigValRequest.boolVal', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='ac_oneof', full_name='SetAppConfigValRequest.ac_oneof', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=1542, + serialized_end=1646, +) + + +_DELETEAPPCONFIGVALREQUEST = _descriptor.Descriptor( + name='DeleteAppConfigValRequest', + full_name='DeleteAppConfigValRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', full_name='DeleteAppConfigValRequest.key', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1648, + serialized_end=1688, +) + + +_SAVETEMPFILEREQUEST = _descriptor.Descriptor( + name='SaveTempFileRequest', + full_name='SaveTempFileRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='fileName', full_name='SaveTempFileRequest.fileName', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='data', full_name='SaveTempFileRequest.data', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1690, + serialized_end=1743, +) + +_SETVASLVERSIONREQUEST.fields_by_name['vaslExtnsType'].enum_type = _SETVASLVERSIONREQUEST_VASLEXTNSTYPE +_SETVASLVERSIONREQUEST_VASLEXTNSTYPE.containing_type = _SETVASLVERSIONREQUEST +_SETDATADIRREQUEST.fields_by_name['dirType'].enum_type = _SETDATADIRREQUEST_DIRTYPE +_SETDATADIRREQUEST_DIRTYPE.containing_type = _SETDATADIRREQUEST +_SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['templatePackType'].enum_type = _SETDEFAULTTEMPLATEPACKREQUEST_TEMPLATEPACKTYPE +_SETDEFAULTTEMPLATEPACKREQUEST_TEMPLATEPACKTYPE.containing_type = _SETDEFAULTTEMPLATEPACKREQUEST +_SETDEFAULTTEMPLATEPACKREQUEST.oneofs_by_name['tp_oneof'].fields.append( + _SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['templatePackType']) +_SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['templatePackType'].containing_oneof = _SETDEFAULTTEMPLATEPACKREQUEST.oneofs_by_name['tp_oneof'] +_SETDEFAULTTEMPLATEPACKREQUEST.oneofs_by_name['tp_oneof'].fields.append( + _SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['dirName']) +_SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['dirName'].containing_oneof = _SETDEFAULTTEMPLATEPACKREQUEST.oneofs_by_name['tp_oneof'] +_SETDEFAULTTEMPLATEPACKREQUEST.oneofs_by_name['tp_oneof'].fields.append( + _SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['zipData']) +_SETDEFAULTTEMPLATEPACKREQUEST.fields_by_name['zipData'].containing_oneof = _SETDEFAULTTEMPLATEPACKREQUEST.oneofs_by_name['tp_oneof'] +_SETVEHORDNOTESDIRREQUEST.fields_by_name['dirType'].enum_type = _SETVEHORDNOTESDIRREQUEST_DIRTYPE +_SETVEHORDNOTESDIRREQUEST_DIRTYPE.containing_type = _SETVEHORDNOTESDIRREQUEST +_SETAPPCONFIGVALREQUEST.oneofs_by_name['ac_oneof'].fields.append( + _SETAPPCONFIGVALREQUEST.fields_by_name['strVal']) +_SETAPPCONFIGVALREQUEST.fields_by_name['strVal'].containing_oneof = _SETAPPCONFIGVALREQUEST.oneofs_by_name['ac_oneof'] +_SETAPPCONFIGVALREQUEST.oneofs_by_name['ac_oneof'].fields.append( + _SETAPPCONFIGVALREQUEST.fields_by_name['intVal']) +_SETAPPCONFIGVALREQUEST.fields_by_name['intVal'].containing_oneof = _SETAPPCONFIGVALREQUEST.oneofs_by_name['ac_oneof'] +_SETAPPCONFIGVALREQUEST.oneofs_by_name['ac_oneof'].fields.append( + _SETAPPCONFIGVALREQUEST.fields_by_name['boolVal']) +_SETAPPCONFIGVALREQUEST.fields_by_name['boolVal'].containing_oneof = _SETAPPCONFIGVALREQUEST.oneofs_by_name['ac_oneof'] +DESCRIPTOR.message_types_by_name['StartTestsResponse'] = _STARTTESTSRESPONSE +DESCRIPTOR.message_types_by_name['GetVassalVersionsResponse'] = _GETVASSALVERSIONSRESPONSE +DESCRIPTOR.message_types_by_name['SetVassalVersionRequest'] = _SETVASSALVERSIONREQUEST +DESCRIPTOR.message_types_by_name['GetVaslVersionsResponse'] = _GETVASLVERSIONSRESPONSE +DESCRIPTOR.message_types_by_name['SetVaslVersionRequest'] = _SETVASLVERSIONREQUEST +DESCRIPTOR.message_types_by_name['GetVaslExtnsResponse'] = _GETVASLEXTNSRESPONSE +DESCRIPTOR.message_types_by_name['SetVaslExtnInfoDirRequest'] = _SETVASLEXTNINFODIRREQUEST +DESCRIPTOR.message_types_by_name['SetGpidRemappingsRequest'] = _SETGPIDREMAPPINGSREQUEST +DESCRIPTOR.message_types_by_name['GetVaslModWarningsResponse'] = _GETVASLMODWARNINGSRESPONSE +DESCRIPTOR.message_types_by_name['SetDataDirRequest'] = _SETDATADIRREQUEST +DESCRIPTOR.message_types_by_name['SetDefaultScenarioRequest'] = _SETDEFAULTSCENARIOREQUEST +DESCRIPTOR.message_types_by_name['SetDefaultTemplatePackRequest'] = _SETDEFAULTTEMPLATEPACKREQUEST +DESCRIPTOR.message_types_by_name['SetVehOrdNotesDirRequest'] = _SETVEHORDNOTESDIRREQUEST +DESCRIPTOR.message_types_by_name['SetUserFilesDirRequest'] = _SETUSERFILESDIRREQUEST +DESCRIPTOR.message_types_by_name['SetAsaScenarioIndexRequest'] = _SETASASCENARIOINDEXREQUEST +DESCRIPTOR.message_types_by_name['SetRoarScenarioIndexRequest'] = _SETROARSCENARIOINDEXREQUEST +DESCRIPTOR.message_types_by_name['GetLastSnippetImageResponse'] = _GETLASTSNIPPETIMAGERESPONSE +DESCRIPTOR.message_types_by_name['GetLastAsaUploadResponse'] = _GETLASTASAUPLOADRESPONSE +DESCRIPTOR.message_types_by_name['DumpVsavRequest'] = _DUMPVSAVREQUEST +DESCRIPTOR.message_types_by_name['DumpVsavResponse'] = _DUMPVSAVRESPONSE +DESCRIPTOR.message_types_by_name['GetVaslPiecesRequest'] = _GETVASLPIECESREQUEST +DESCRIPTOR.message_types_by_name['GetVaslPiecesResponse'] = _GETVASLPIECESRESPONSE +DESCRIPTOR.message_types_by_name['GetAppConfigResponse'] = _GETAPPCONFIGRESPONSE +DESCRIPTOR.message_types_by_name['SetAppConfigValRequest'] = _SETAPPCONFIGVALREQUEST +DESCRIPTOR.message_types_by_name['DeleteAppConfigValRequest'] = _DELETEAPPCONFIGVALREQUEST +DESCRIPTOR.message_types_by_name['SaveTempFileRequest'] = _SAVETEMPFILEREQUEST +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +StartTestsResponse = _reflection.GeneratedProtocolMessageType('StartTestsResponse', (_message.Message,), { + 'DESCRIPTOR' : _STARTTESTSRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:StartTestsResponse) + }) +_sym_db.RegisterMessage(StartTestsResponse) + +GetVassalVersionsResponse = _reflection.GeneratedProtocolMessageType('GetVassalVersionsResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETVASSALVERSIONSRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetVassalVersionsResponse) + }) +_sym_db.RegisterMessage(GetVassalVersionsResponse) + +SetVassalVersionRequest = _reflection.GeneratedProtocolMessageType('SetVassalVersionRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETVASSALVERSIONREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetVassalVersionRequest) + }) +_sym_db.RegisterMessage(SetVassalVersionRequest) + +GetVaslVersionsResponse = _reflection.GeneratedProtocolMessageType('GetVaslVersionsResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETVASLVERSIONSRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetVaslVersionsResponse) + }) +_sym_db.RegisterMessage(GetVaslVersionsResponse) + +SetVaslVersionRequest = _reflection.GeneratedProtocolMessageType('SetVaslVersionRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETVASLVERSIONREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetVaslVersionRequest) + }) +_sym_db.RegisterMessage(SetVaslVersionRequest) + +GetVaslExtnsResponse = _reflection.GeneratedProtocolMessageType('GetVaslExtnsResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETVASLEXTNSRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetVaslExtnsResponse) + }) +_sym_db.RegisterMessage(GetVaslExtnsResponse) + +SetVaslExtnInfoDirRequest = _reflection.GeneratedProtocolMessageType('SetVaslExtnInfoDirRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETVASLEXTNINFODIRREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetVaslExtnInfoDirRequest) + }) +_sym_db.RegisterMessage(SetVaslExtnInfoDirRequest) + +SetGpidRemappingsRequest = _reflection.GeneratedProtocolMessageType('SetGpidRemappingsRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETGPIDREMAPPINGSREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetGpidRemappingsRequest) + }) +_sym_db.RegisterMessage(SetGpidRemappingsRequest) + +GetVaslModWarningsResponse = _reflection.GeneratedProtocolMessageType('GetVaslModWarningsResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETVASLMODWARNINGSRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetVaslModWarningsResponse) + }) +_sym_db.RegisterMessage(GetVaslModWarningsResponse) + +SetDataDirRequest = _reflection.GeneratedProtocolMessageType('SetDataDirRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETDATADIRREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetDataDirRequest) + }) +_sym_db.RegisterMessage(SetDataDirRequest) + +SetDefaultScenarioRequest = _reflection.GeneratedProtocolMessageType('SetDefaultScenarioRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETDEFAULTSCENARIOREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetDefaultScenarioRequest) + }) +_sym_db.RegisterMessage(SetDefaultScenarioRequest) + +SetDefaultTemplatePackRequest = _reflection.GeneratedProtocolMessageType('SetDefaultTemplatePackRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETDEFAULTTEMPLATEPACKREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetDefaultTemplatePackRequest) + }) +_sym_db.RegisterMessage(SetDefaultTemplatePackRequest) + +SetVehOrdNotesDirRequest = _reflection.GeneratedProtocolMessageType('SetVehOrdNotesDirRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETVEHORDNOTESDIRREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetVehOrdNotesDirRequest) + }) +_sym_db.RegisterMessage(SetVehOrdNotesDirRequest) + +SetUserFilesDirRequest = _reflection.GeneratedProtocolMessageType('SetUserFilesDirRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETUSERFILESDIRREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetUserFilesDirRequest) + }) +_sym_db.RegisterMessage(SetUserFilesDirRequest) + +SetAsaScenarioIndexRequest = _reflection.GeneratedProtocolMessageType('SetAsaScenarioIndexRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETASASCENARIOINDEXREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetAsaScenarioIndexRequest) + }) +_sym_db.RegisterMessage(SetAsaScenarioIndexRequest) + +SetRoarScenarioIndexRequest = _reflection.GeneratedProtocolMessageType('SetRoarScenarioIndexRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETROARSCENARIOINDEXREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetRoarScenarioIndexRequest) + }) +_sym_db.RegisterMessage(SetRoarScenarioIndexRequest) + +GetLastSnippetImageResponse = _reflection.GeneratedProtocolMessageType('GetLastSnippetImageResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETLASTSNIPPETIMAGERESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetLastSnippetImageResponse) + }) +_sym_db.RegisterMessage(GetLastSnippetImageResponse) + +GetLastAsaUploadResponse = _reflection.GeneratedProtocolMessageType('GetLastAsaUploadResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETLASTASAUPLOADRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetLastAsaUploadResponse) + }) +_sym_db.RegisterMessage(GetLastAsaUploadResponse) + +DumpVsavRequest = _reflection.GeneratedProtocolMessageType('DumpVsavRequest', (_message.Message,), { + 'DESCRIPTOR' : _DUMPVSAVREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:DumpVsavRequest) + }) +_sym_db.RegisterMessage(DumpVsavRequest) + +DumpVsavResponse = _reflection.GeneratedProtocolMessageType('DumpVsavResponse', (_message.Message,), { + 'DESCRIPTOR' : _DUMPVSAVRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:DumpVsavResponse) + }) +_sym_db.RegisterMessage(DumpVsavResponse) + +GetVaslPiecesRequest = _reflection.GeneratedProtocolMessageType('GetVaslPiecesRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETVASLPIECESREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetVaslPiecesRequest) + }) +_sym_db.RegisterMessage(GetVaslPiecesRequest) + +GetVaslPiecesResponse = _reflection.GeneratedProtocolMessageType('GetVaslPiecesResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETVASLPIECESRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetVaslPiecesResponse) + }) +_sym_db.RegisterMessage(GetVaslPiecesResponse) + +GetAppConfigResponse = _reflection.GeneratedProtocolMessageType('GetAppConfigResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETAPPCONFIGRESPONSE, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:GetAppConfigResponse) + }) +_sym_db.RegisterMessage(GetAppConfigResponse) + +SetAppConfigValRequest = _reflection.GeneratedProtocolMessageType('SetAppConfigValRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETAPPCONFIGVALREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SetAppConfigValRequest) + }) +_sym_db.RegisterMessage(SetAppConfigValRequest) + +DeleteAppConfigValRequest = _reflection.GeneratedProtocolMessageType('DeleteAppConfigValRequest', (_message.Message,), { + 'DESCRIPTOR' : _DELETEAPPCONFIGVALREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:DeleteAppConfigValRequest) + }) +_sym_db.RegisterMessage(DeleteAppConfigValRequest) + +SaveTempFileRequest = _reflection.GeneratedProtocolMessageType('SaveTempFileRequest', (_message.Message,), { + 'DESCRIPTOR' : _SAVETEMPFILEREQUEST, + '__module__' : 'control_tests_pb2' + # @@protoc_insertion_point(class_scope:SaveTempFileRequest) + }) +_sym_db.RegisterMessage(SaveTempFileRequest) + + + +_CONTROLTESTS = _descriptor.ServiceDescriptor( + name='ControlTests', + full_name='ControlTests', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=1746, + serialized_end=3547, + methods=[ + _descriptor.MethodDescriptor( + name='startTests', + full_name='ControlTests.startTests', + index=0, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_STARTTESTSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='endTests', + full_name='ControlTests.endTests', + index=1, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getVassalVersions', + full_name='ControlTests.getVassalVersions', + index=2, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETVASSALVERSIONSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setVassalVersion', + full_name='ControlTests.setVassalVersion', + index=3, + containing_service=None, + input_type=_SETVASSALVERSIONREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getVaslVersions', + full_name='ControlTests.getVaslVersions', + index=4, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETVASLVERSIONSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setVaslVersion', + full_name='ControlTests.setVaslVersion', + index=5, + containing_service=None, + input_type=_SETVASLVERSIONREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getVaslExtns', + full_name='ControlTests.getVaslExtns', + index=6, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETVASLEXTNSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setVaslExtnInfoDir', + full_name='ControlTests.setVaslExtnInfoDir', + index=7, + containing_service=None, + input_type=_SETVASLEXTNINFODIRREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setGpidRemappings', + full_name='ControlTests.setGpidRemappings', + index=8, + containing_service=None, + input_type=_SETGPIDREMAPPINGSREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getVaslModWarnings', + full_name='ControlTests.getVaslModWarnings', + index=9, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETVASLMODWARNINGSRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setDataDir', + full_name='ControlTests.setDataDir', + index=10, + containing_service=None, + input_type=_SETDATADIRREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setDefaultScenario', + full_name='ControlTests.setDefaultScenario', + index=11, + containing_service=None, + input_type=_SETDEFAULTSCENARIOREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setDefaultTemplatePack', + full_name='ControlTests.setDefaultTemplatePack', + index=12, + containing_service=None, + input_type=_SETDEFAULTTEMPLATEPACKREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setVehOrdNotesDir', + full_name='ControlTests.setVehOrdNotesDir', + index=13, + containing_service=None, + input_type=_SETVEHORDNOTESDIRREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setUserFilesDir', + full_name='ControlTests.setUserFilesDir', + index=14, + containing_service=None, + input_type=_SETUSERFILESDIRREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setAsaScenarioIndex', + full_name='ControlTests.setAsaScenarioIndex', + index=15, + containing_service=None, + input_type=_SETASASCENARIOINDEXREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setRoarScenarioIndex', + full_name='ControlTests.setRoarScenarioIndex', + index=16, + containing_service=None, + input_type=_SETROARSCENARIOINDEXREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getLastSnippetImage', + full_name='ControlTests.getLastSnippetImage', + index=17, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETLASTSNIPPETIMAGERESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='resetLastAsaUpload', + full_name='ControlTests.resetLastAsaUpload', + index=18, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getLastAsaUpload', + full_name='ControlTests.getLastAsaUpload', + index=19, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETLASTASAUPLOADRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='dumpVsav', + full_name='ControlTests.dumpVsav', + index=20, + containing_service=None, + input_type=_DUMPVSAVREQUEST, + output_type=_DUMPVSAVRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getVaslPieces', + full_name='ControlTests.getVaslPieces', + index=21, + containing_service=None, + input_type=_GETVASLPIECESREQUEST, + output_type=_GETVASLPIECESRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='getAppConfig', + full_name='ControlTests.getAppConfig', + index=22, + containing_service=None, + input_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + output_type=_GETAPPCONFIGRESPONSE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='setAppConfigVal', + full_name='ControlTests.setAppConfigVal', + index=23, + containing_service=None, + input_type=_SETAPPCONFIGVALREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='deleteAppConfigVal', + full_name='ControlTests.deleteAppConfigVal', + index=24, + containing_service=None, + input_type=_DELETEAPPCONFIGVALREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='saveTempFile', + full_name='ControlTests.saveTempFile', + index=25, + containing_service=None, + input_type=_SAVETEMPFILEREQUEST, + output_type=google_dot_protobuf_dot_empty__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTROLTESTS) + +DESCRIPTOR.services_by_name['ControlTests'] = _CONTROLTESTS + +# @@protoc_insertion_point(module_scope) diff --git a/vasl_templates/webapp/tests/proto/generated/control_tests_pb2_grpc.py b/vasl_templates/webapp/tests/proto/generated/control_tests_pb2_grpc.py new file mode 100644 index 0000000..4795481 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/generated/control_tests_pb2_grpc.py @@ -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) diff --git a/vasl_templates/webapp/tests/proto/get_vasl_pieces_request.py b/vasl_templates/webapp/tests/proto/get_vasl_pieces_request.py new file mode 100644 index 0000000..3881f0b --- /dev/null +++ b/vasl_templates/webapp/tests/proto/get_vasl_pieces_request.py @@ -0,0 +1,7 @@ +""" Injected functions for GetVaslPiecesRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a GetVaslPiecesRequest as a brief string.""" + return self.vaslVersion diff --git a/vasl_templates/webapp/tests/proto/save_temp_file_request.py b/vasl_templates/webapp/tests/proto/save_temp_file_request.py new file mode 100644 index 0000000..35f7df3 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/save_temp_file_request.py @@ -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) ) diff --git a/vasl_templates/webapp/tests/proto/set_app_config_val_request.py b/vasl_templates/webapp/tests/proto/set_app_config_val_request.py new file mode 100644 index 0000000..0a58fcb --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_app_config_val_request.py @@ -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 ) diff --git a/vasl_templates/webapp/tests/proto/set_asa_scenario_index_request.py b/vasl_templates/webapp/tests/proto/set_asa_scenario_index_request.py new file mode 100644 index 0000000..bb31326 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_asa_scenario_index_request.py @@ -0,0 +1,7 @@ +""" Injected functions for SetAsaScenarioIndexRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a SetAsaScenarioIndexRequest as a brief string.""" + return self.fileName diff --git a/vasl_templates/webapp/tests/proto/set_data_dir_request.py b/vasl_templates/webapp/tests/proto/set_data_dir_request.py new file mode 100644 index 0000000..97431a2 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_data_dir_request.py @@ -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 diff --git a/vasl_templates/webapp/tests/proto/set_default_scenario_request.py b/vasl_templates/webapp/tests/proto/set_default_scenario_request.py new file mode 100644 index 0000000..ff5cb89 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_default_scenario_request.py @@ -0,0 +1,7 @@ +""" Injected functions for SetDefaultScenarioRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a SetDefaultScenarioRequest as a brief string.""" + return self.fileName diff --git a/vasl_templates/webapp/tests/proto/set_default_template_pack_request.py b/vasl_templates/webapp/tests/proto/set_default_template_pack_request.py new file mode 100644 index 0000000..1b7426d --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_default_template_pack_request.py @@ -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() diff --git a/vasl_templates/webapp/tests/proto/set_roar_scenario_index_request.py b/vasl_templates/webapp/tests/proto/set_roar_scenario_index_request.py new file mode 100644 index 0000000..9409309 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_roar_scenario_index_request.py @@ -0,0 +1,7 @@ +""" Injected functions for SetRoarScenarioIndexRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a SetRoarScenarioIndexRequest as a brief string.""" + return self.fileName diff --git a/vasl_templates/webapp/tests/proto/set_user_files_dir_request.py b/vasl_templates/webapp/tests/proto/set_user_files_dir_request.py new file mode 100644 index 0000000..8a31acb --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_user_files_dir_request.py @@ -0,0 +1,7 @@ +""" Injected functions for SetUserFilesDirRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a SetUserFilesDirRequest as a brief string.""" + return self.dirOrUrl diff --git a/vasl_templates/webapp/tests/proto/set_vasl_extn_info_dir_request.py b/vasl_templates/webapp/tests/proto/set_vasl_extn_info_dir_request.py new file mode 100644 index 0000000..7ba3b07 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_vasl_extn_info_dir_request.py @@ -0,0 +1,7 @@ +""" Injected functions for SetVaslExtnInfoDirRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a SetVaslExtnInfoDirRequest as a brief string.""" + return self.dirName diff --git a/vasl_templates/webapp/tests/proto/set_vasl_version_request.py b/vasl_templates/webapp/tests/proto/set_vasl_version_request.py new file mode 100644 index 0000000..f13ae4c --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_vasl_version_request.py @@ -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 + ) diff --git a/vasl_templates/webapp/tests/proto/set_vassal_version_request.py b/vasl_templates/webapp/tests/proto/set_vassal_version_request.py new file mode 100644 index 0000000..4340007 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_vassal_version_request.py @@ -0,0 +1,7 @@ +""" Injected functions for SetVassalVersionRequest. """ + +# --------------------------------------------------------------------- + +def brief( self ): + """Return a SetVassalVersionRequest as a brief string.""" + return self.vassalVersion diff --git a/vasl_templates/webapp/tests/proto/set_veh_ord_notes_dir_request.py b/vasl_templates/webapp/tests/proto/set_veh_ord_notes_dir_request.py new file mode 100644 index 0000000..1b3a375 --- /dev/null +++ b/vasl_templates/webapp/tests/proto/set_veh_ord_notes_dir_request.py @@ -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 diff --git a/vasl_templates/webapp/tests/proto/utils.py b/vasl_templates/webapp/tests/proto/utils.py new file mode 100644 index 0000000..0cca02f --- /dev/null +++ b/vasl_templates/webapp/tests/proto/utils.py @@ -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] ) diff --git a/vasl_templates/webapp/tests/remote.py b/vasl_templates/webapp/tests/remote.py deleted file mode 100644 index 7007d01..0000000 --- a/vasl_templates/webapp/tests/remote.py +++ /dev/null @@ -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 diff --git a/vasl_templates/webapp/tests/test_capabilities.py b/vasl_templates/webapp/tests/test_capabilities.py index 37c9977..1fb0f4f 100644 --- a/vasl_templates/webapp/tests/test_capabilities.py +++ b/vasl_templates/webapp/tests/test_capabilities.py @@ -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 diff --git a/vasl_templates/webapp/tests/test_comments.py b/vasl_templates/webapp/tests/test_comments.py index e280e83..8f0ef59 100644 --- a/vasl_templates/webapp/tests/test_comments.py +++ b/vasl_templates/webapp/tests/test_comments.py @@ -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() diff --git a/vasl_templates/webapp/tests/test_counters.py b/vasl_templates/webapp/tests/test_counters.py index 669702f..31c3263 100644 --- a/vasl_templates/webapp/tests/test_counters.py +++ b/vasl_templates/webapp/tests/test_counters.py @@ -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: diff --git a/vasl_templates/webapp/tests/test_default_scenario.py b/vasl_templates/webapp/tests/test_default_scenario.py index e54e520..736a679 100644 --- a/vasl_templates/webapp/tests/test_default_scenario.py +++ b/vasl_templates/webapp/tests/test_default_scenario.py @@ -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']" ) diff --git a/vasl_templates/webapp/tests/test_dirty_scenario_checks.py b/vasl_templates/webapp/tests/test_dirty_scenario_checks.py index f1c0819..a0087c2 100644 --- a/vasl_templates/webapp/tests/test_dirty_scenario_checks.py +++ b/vasl_templates/webapp/tests/test_dirty_scenario_checks.py @@ -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 = { diff --git a/vasl_templates/webapp/tests/test_extras_templates.py b/vasl_templates/webapp/tests/test_extras_templates.py index 7931263..cdca0f7 100644 --- a/vasl_templates/webapp/tests/test_extras_templates.py +++ b/vasl_templates/webapp/tests/test_extras_templates.py @@ -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 diff --git a/vasl_templates/webapp/tests/test_files.py b/vasl_templates/webapp/tests/test_files.py index 77f4961..12bc2ee 100644 --- a/vasl_templates/webapp/tests/test_files.py +++ b/vasl_templates/webapp/tests/test_files.py @@ -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']" ) diff --git a/vasl_templates/webapp/tests/test_jshint.py b/vasl_templates/webapp/tests/test_jshint.py index 1f6ecbb..aa4c241 100644 --- a/vasl_templates/webapp/tests/test_jshint.py +++ b/vasl_templates/webapp/tests/test_jshint.py @@ -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) ], diff --git a/vasl_templates/webapp/tests/test_lfa.py b/vasl_templates/webapp/tests/test_lfa.py index c586648..0ffe477 100644 --- a/vasl_templates/webapp/tests/test_lfa.py +++ b/vasl_templates/webapp/tests/test_lfa.py @@ -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 ) # --------------------------------------------------------------------- diff --git a/vasl_templates/webapp/tests/test_national_capabilities.py b/vasl_templates/webapp/tests/test_national_capabilities.py index e3fd1c8..22994b5 100644 --- a/vasl_templates/webapp/tests/test_national_capabilities.py +++ b/vasl_templates/webapp/tests/test_national_capabilities.py @@ -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.""" diff --git a/vasl_templates/webapp/tests/test_ob.py b/vasl_templates/webapp/tests/test_ob.py index 2d8322e..86ec990 100644 --- a/vasl_templates/webapp/tests/test_ob.py +++ b/vasl_templates/webapp/tests/test_ob.py @@ -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: diff --git a/vasl_templates/webapp/tests/test_online_images.py b/vasl_templates/webapp/tests/test_online_images.py index 32d25c2..c184367 100644 --- a/vasl_templates/webapp/tests/test_online_images.py +++ b/vasl_templates/webapp/tests/test_online_images.py @@ -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( { diff --git a/vasl_templates/webapp/tests/test_scenario_persistence.py b/vasl_templates/webapp/tests/test_scenario_persistence.py index 694b107..8b3e6b0 100644 --- a/vasl_templates/webapp/tests/test_scenario_persistence.py +++ b/vasl_templates/webapp/tests/test_scenario_persistence.py @@ -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.""" diff --git a/vasl_templates/webapp/tests/test_scenario_search.py b/vasl_templates/webapp/tests/test_scenario_search.py index b0d0ec2..3528081 100644 --- a/vasl_templates/webapp/tests/test_scenario_search.py +++ b/vasl_templates/webapp/tests/test_scenario_search.py @@ -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 ) # --------------------------------------------------------------------- diff --git a/vasl_templates/webapp/tests/test_snippets.py b/vasl_templates/webapp/tests/test_snippets.py index c1e31e4..46cdf83 100644 --- a/vasl_templates/webapp/tests/test_snippets.py +++ b/vasl_templates/webapp/tests/test_snippets.py @@ -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 diff --git a/vasl_templates/webapp/tests/test_template_packs.py b/vasl_templates/webapp/tests/test_template_packs.py index 9a53bd2..bdaa9e0 100644 --- a/vasl_templates/webapp/tests/test_template_packs.py +++ b/vasl_templates/webapp/tests/test_template_packs.py @@ -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.""" diff --git a/vasl_templates/webapp/tests/test_user_settings.py b/vasl_templates/webapp/tests/test_user_settings.py index 9a9249b..42a1bed 100644 --- a/vasl_templates/webapp/tests/test_user_settings.py +++ b/vasl_templates/webapp/tests/test_user_settings.py @@ -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/" ) # --------------------------------------------------------------------- diff --git a/vasl_templates/webapp/tests/test_vasl_extensions.py b/vasl_templates/webapp/tests/test_vasl_extensions.py index 0574752..40d6b5b 100644 --- a/vasl_templates/webapp/tests/test_vasl_extensions.py +++ b/vasl_templates/webapp/tests/test_vasl_extensions.py @@ -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", "", "Missing buildFile:" ) @@ -49,75 +45,63 @@ def test_load_vasl_extensions( webapp, webdriver ): # try loading an extension with an unknown ID do_test( "buildFile", '', - "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 ): diff --git a/vasl_templates/webapp/tests/test_vassal.py b/vasl_templates/webapp/tests/test_vassal.py index 00e44ae..7d063b2 100644 --- a/vasl_templates/webapp/tests/test_vassal.py +++ b/vasl_templates/webapp/tests/test_vassal.py @@ -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"N: 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() diff --git a/vasl_templates/webapp/tests/test_vehicles_ordnance.py b/vasl_templates/webapp/tests/test_vehicles_ordnance.py index 5a737bf..4ad5dab 100644 --- a/vasl_templates/webapp/tests/test_vehicles_ordnance.py +++ b/vasl_templates/webapp/tests/test_vehicles_ordnance.py @@ -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" ) diff --git a/vasl_templates/webapp/tests/test_vo_notes.py b/vasl_templates/webapp/tests/test_vo_notes.py index 5fcfc64..6b0fae5 100644 --- a/vasl_templates/webapp/tests/test_vo_notes.py +++ b/vasl_templates/webapp/tests/test_vo_notes.py @@ -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 diff --git a/vasl_templates/webapp/tests/test_vo_reports.py b/vasl_templates/webapp/tests/test_vo_reports.py index c92ffbf..96eb158 100644 --- a/vasl_templates/webapp/tests/test_vo_reports.py +++ b/vasl_templates/webapp/tests/test_vo_reports.py @@ -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/" ) diff --git a/vasl_templates/webapp/tests/utils.py b/vasl_templates/webapp/tests/utils.py index 2bf68be..13ad1b0 100644 --- a/vasl_templates/webapp/tests/utils.py +++ b/vasl_templates/webapp/tests/utils.py @@ -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() diff --git a/vasl_templates/webapp/utils.py b/vasl_templates/webapp/utils.py index 747d6c2..1781999 100644 --- a/vasl_templates/webapp/utils.py +++ b/vasl_templates/webapp/utils.py @@ -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: diff --git a/vasl_templates/webapp/vasl_mod.py b/vasl_templates/webapp/vasl_mod.py index 615ddd2..fb7a0a9 100644 --- a/vasl_templates/webapp/vasl_mod.py +++ b/vasl_templates/webapp/vasl_mod.py @@ -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) diff --git a/vasl_templates/webapp/vassal.py b/vasl_templates/webapp/vassal.py index fc5f08f..421f5a8 100644 --- a/vasl_templates/webapp/vassal.py +++ b/vasl_templates/webapp/vassal.py @@ -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" ) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vasl_templates/webapp/vo.py b/vasl_templates/webapp/vo.py index 4e372d9..26e2287 100644 --- a/vasl_templates/webapp/vo.py +++ b/vasl_templates/webapp/vo.py @@ -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 ) diff --git a/vasl_templates/webapp/vo_notes.py b/vasl_templates/webapp/vo_notes.py index c2eaeb1..7011e9f 100644 --- a/vasl_templates/webapp/vo_notes.py +++ b/vasl_templates/webapp/vo_notes.py @@ -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,