Files @ b4b79380b3d0
Branch filter:

Location: conntrackt/testproject/functional_tests/base.py

branko
CONNT-34: Implemented base functional test class:

- Added helper class to be used as common ancestor for all functional
tests.
- Added missing headers.
- Updated hgignore file to exclude the geckodriver.log.
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017 Branko Majic
#
# This file is part of Django Conntrackt.
#
# Django Conntrackt is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# Django Conntrackt is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Django Conntrackt.  If not, see <http://www.gnu.org/licenses/>.
#


# Standard library imports.
import os
import time

# Django imports.
from django.contrib.staticfiles.testing import StaticLiveServerTestCase

# Third-party library imports.
from selenium.common.exceptions import WebDriverException
from selenium import webdriver


# Maximum amount of time to wait before assuming a test has
# failed. Used as default for functions that wait.
MAX_WAIT = 10

# Delay between subsequent runs of function when using the wait
# decorator.
EXECUTION_DELAY = 0.5

# Base directory where the tests are located at.
BASE_DIR = os.path.dirname(os.path.abspath(__file__))

# Base directory where the test binaries are located at.
TEST_BINARIES_DIR = os.path.join(BASE_DIR, "..", "..", "test_binaries")

# Location of Firefox binary.
FIREFOX_BINARY = os.path.join(TEST_BINARIES_DIR, "firefox", "firefox")

# Location of geckodriver binary.
GECKODRIVER_BINARY = os.path.join(TEST_BINARIES_DIR, "geckodriver")


def wait(fn, execution_delay=EXECUTION_DELAY, max_wait=MAX_WAIT):
    """
    Decorator that will produce a wrapper function that will run the
    specified function repeatedly as long as all of the following
    conditions have been met:

    - Function throws either AssertionError or
      selenium.common.exceptions.WebDriverException.
    - Combined runtime of all function calls does not exceed the
      maximum wait time.

    As soon as the original function returns without any thrown
    exceptions, the wrapper function will return its result.

    If the function throws any other exception beyond the ones listed
    above, this exception will get propagated.

    Arguments:

      fn
        Function that should be run.

      max_wait
        Maximum time in seconds to wait for function to execute
        successfully.

      execution_delay
        Delay in seconds between subsequent function runs. Used to
        prevent high CPU usage.

    Returns:

      Wrapper function around passed-in function. The wrapper
      function, provided it executes without failure, returns the
      result of original function.
    """

    def modified_fn(*args, **kwargs):
        start_time = time.time()

        while True:
            try:
                return fn(*args, **kwargs)
            except (AssertionError, WebDriverException) as e:
                if time.time() - start_time > max_wait:
                    raise e
                time.sleep(execution_delay)

    return modified_fn


class FunctionalTest(StaticLiveServerTestCase):
    """
    Helper test class that provides some convenience when writing
    functional tests.

    During test set-up, the class will:

    - Set-up a browser instance, storing it in property self.browser.

    During the test tear-down, the class will:

    - Destroy the browser instance.

    In addition to this, the class provides a convenience method
    wait_for which can be used to wait for a function to execute
    within a time slot and assert something, as described for the wait
    decorator.
    """

    def setUp(self):
        self.browser = webdriver.Firefox(firefox_binary=FIREFOX_BINARY, executable_path=GECKODRIVER_BINARY)

    def tearDown(self):
        self.browser.quit()
        super(FunctionalTest, self).tearDown()

    @wait
    def wait_for(self, fn):
        return fn()