import logging
import traceback
from time import sleep
import os
import pickle
import urllib

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common import proxy

from IPython import embed
from brome.core.settings import BROME_CONFIG
from brome.core.stateful import Stateful
from brome.core.proxy_driver import ProxyDriver
from brome.core.configurator import (
    test_config_to_dict,
    parse_brome_config_from_browser_config
)
from brome.model.testinstance import Testinstance
from brome.model.testbatch import Testbatch
from brome.model.testresult import Testresult
from brome.model.testcrash import Testcrash
from brome.model.test import Test
from brome.core import exceptions
from brome.core.utils import (
    DbSessionContext,
    utcnow,
    say,
    create_dir_if_doesnt_exist,
    string_to_filename
)


class BaseTest(object):
    """Base test class used to init the driver, load and save state, start and
        stop video recording

    Attributes:
        runner (object): the runner that created the BaseTest instance
        name (str): the name of the test
        index (int): the index of the test
        browser_config (object): the browser config; used to init the driver
        test_batch_id (int): the test batch id
    """

    def __init__(self, runner, name, index, browser_config, test_batch_id, **kwargs):  # noqa
        self._runner = runner
        self._name = name
        self._index = index
        self._browser_config = browser_config
        self._test_batch_id = test_batch_id
        self._localhost_instance = kwargs.get('localhost_instance')
        self._log_file_path = ''


        browser_config = parse_brome_config_from_browser_config(self._browser_config.config)
        for key in iter(BROME_CONFIG):
            if key in browser_config:
                BROME_CONFIG[key].update(browser_config[key])

        with DbSessionContext(BROME_CONFIG['database']['mongo_database_name']) as session:  # noqa
            test_batch = session.query(Testbatch)\
                .filter(Testbatch.mongo_id == test_batch_id)\
                .one()
            self._test_batch_friendly_name = test_batch.friendly_name

        self._crash_error = False

        # TEST BATCH DIRECTORY
        if self._runner.runner_dir:
            self._runner_dir = self._runner.runner_dir
        else:
            self._runner_dir = ''

        # NETWORK CAPTURE
        if self._browser_config.get('enable_proxy'):
            self._network_capture_file_relative_path = os.path.join(
                self._runner.relative_runner_dir,
                'network_capture',
                string_to_filename('%s.data' % self._name)
            )
        else:
            self._network_capture_file_relative_path = ''

        # LOGGING
        self.configure_logger()

        # DRIVER
        self.pdriver = self.init_driver()

        # ASSIGN THE TEST NAME TO THE INSTANCE
        if self._browser_config.location in ['ec2', 'virtualbox']:
            self._runner.resolve_instance_by_ip(
                self.pdriver.get_ip_of_node()
            ).testname = self._name

        # TEST RESULT DIRECTORY
        self.configure_test_result_dir()

        # DRIVER RESOLUTION
        self.pdriver.configure_resolution()

        # VIDEO RECORDING
        self.start_video_recording()
        starting_timestamp = utcnow()

        # TEST KWARGS
        self._test_config = test_config_to_dict(
            BROME_CONFIG['runner'].get('test_config')
        )

        with DbSessionContext(BROME_CONFIG['database']['mongo_database_name']) as session:  # noqa

            extra_data = {}
            if self._browser_config.location in 'ec2':
                self._private_ip = self.pdriver.get_ip_of_node()
                extra_data['instance_private_ip'] = self._runner.instances_ip[self._private_ip].private_ip  # noqa
                extra_data['instance_public_ip'] = self._runner.instances_ip[self._private_ip].public_ip  # noqa
                extra_data['instance_public_dns'] = self._runner.instances_ip[self._private_ip].public_dns  # noqa
                extra_data['instance_private_dns'] = self._runner.instances_ip[self._private_ip].private_dns  # noqa
            elif self._browser_config.location in 'virtualbox':
                self._private_ip = self._browser_config.get('ip')
                extra_data['instance_private_ip'] = self._private_ip

            capabilities = {
                'browserName': self.pdriver.capabilities['browserName'],
                'platform': self.pdriver.capabilities['platform'],
                'version': self.pdriver.capabilities['version']
            }
            test_instance = Testinstance()
            test_instance.name = self._name
            test_instance.starting_timestamp = starting_timestamp
            test_instance.browser_capabilities = capabilities
            test_instance.browser_id = self.pdriver.get_id()
            test_instance.test_batch_id = self._test_batch_id
            test_instance.extra_data = extra_data
            if self._runner.root_test_result_dir:
                test_instance.log_file_path = self._relative_log_file_path
                test_instance.root_path = self._runner.root_test_result_dir
                test_instance.network_capture_path = self._network_capture_file_relative_path  # noqa
                test_instance.video_capture_path = self._video_capture_file_relative_path  # noqa
            else:
                test_instance.log_file_path = ''
                test_instance.root_path = ''
                test_instance.network_capture_path = ''
                test_instance.video_capture_path = ''

            session.save(test_instance, safe=True)

            self._test_instance_id = test_instance.get_uid()

        # START PROXY
        if self._browser_config.location == 'ec2':
            instance = self._runner.resolve_instance_by_ip(self._private_ip)
            if instance.browser_config.get('enable_proxy'):
                instance.start_proxy()

        # FEATURE DETECTION
        self.detect_feature()

    def detect_feature(self):
        with DbSessionContext(BROME_CONFIG['database']['mongo_database_name']) as session:  # noqa
            test_batch = session.query(Testbatch)\
                .filter(Testbatch.mongo_id == self._test_batch_id).one()

            # SESSION VIDEO CAPTURE
            if self._browser_config.get('record_session'):
                test_batch.feature_session_video_capture = True

            # NETWORK CAPTURE
            if self._browser_config.get('enable_proxy'):
                test_batch.feature_network_capture = True

            # SCREENSHOT
            if self._runner_dir:
                test_batch.feature_style_quality = True
                test_batch.feature_screenshots = True

            # BOT DIARY
            if BROME_CONFIG['bot_diary']['enable_bot_diary']:
                test_batch.feature_bot_diaries = False

            # TODO VNC

            session.save(test_batch, safe=True)

    def start_video_recording(self):
        self._video_capture_file_relative_path = ''

        # TODO support localhost and more
        if self._browser_config.get('record_session') and \
                self._browser_config.location in ['ec2', 'virtualbox']:
            self.info_log("Starting screen capture...")

            video_capture_file = string_to_filename(
                '%s.mp4' % (self._name.replace(' ', '_'))
            )

            self._video_capture_file_path = os.path.join(
                self._video_recording_dir,
                video_capture_file
            )

            self._video_capture_file_relative_path = os.path.join(
                self._video_recording_relative_dir,
                video_capture_file
            )

            private_ip = self.pdriver.get_ip_of_node()
            instance = self._runner.resolve_instance_by_ip(private_ip)
            instance.start_video_recording(
                self._video_capture_file_path,
                video_capture_file
            )

    def stop_video_recording(self):
        if self._browser_config.get('record_session'):
            self.info_log("Stopping the screen capture...")

            instance = self._runner.resolve_instance_by_ip(self._private_ip)
            instance.stop_video_recording()

    def init_driver(self, retry=30):
        """Init driver will instanciate a webdriver according to the browser config

        First a webdriver is instanciate
            according to the provided browser config
        then the webdriver is wrapped in the brome proxy_driver

        Args:
            retry (int): the number of time the init driver will retry to
                init the driver if it fail
                            default: 10

        Returns:
            proxy_driver instance

        Raises:
            Exception InitDriverException
            Exception InvalidBrowserName

        """

        # LOCAL
        if self._browser_config.location == 'localhost':

            def get_proxy():
                mitm_proxy = "localhost:%s" % self._localhost_instance.proxy_port  # noqa

                return proxy.Proxy({
                    'proxyType': proxy.ProxyType.MANUAL,
                    'httpProxy': mitm_proxy,
                    'sslProxy': mitm_proxy
                })

            # CHROME
            if self._browser_config.get('browserName').lower() == 'chrome':
                chrome_options = webdriver.ChromeOptions()

                if self._browser_config.get('mobile_emulation'):
                    chrome_options.add_experimental_option(
                        "mobileEmulation",
                        self._browser_config.get('mobile_emulation')
                    )
                elif self._browser_config.get('enable_proxy'):
                    chrome_options.add_argument(
                        "--proxy-server={0}".format(get_proxy())
                    )

                driver = webdriver.Chrome(chrome_options=chrome_options)

            elif self._browser_config.get('browserName').lower() \
                    in ['firefox', 'phantomjs'] \
                    and self._browser_config.get('enable_proxy'):

                # NOTE http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp#using-a-proxy  # noqa
                # FIREFOX
                if self._browser_config.get('browserName').lower() \
                        == 'firefox':
                    profile = webdriver.FirefoxProfile()
                    profile.set_proxy(proxy=get_proxy())
                    driver = webdriver.Firefox(firefox_profile=profile)

                # PHANTOMJS
                elif self._browser_config.get('browserName').lower() \
                        == 'phantomjs':

                    # TODO investigate why this doesnt work in phantomjs
                    """
                    #WAY 1
                    #http://stackoverflow.com/questions/14699718/how-do-i-set-a-proxy-for-phantomjs-ghostdriver-in-python-webdriver
                    service_args = [
                    '--proxy=127.0.0.1:%s'%self._localhost_instance.proxy_port,
                    '--proxy-type=http',
                    ]
                    driver = webdriver.PhantomJS(service_args=service_args)

                    #WAY 2
                    desired_cap = self._browser_config.config
                    proxy.add_to_capabilities(desired_cap)
                    driver = webdriver.PhantomJS(desired_capabilities = desired_cap) # noqa
                    """
                    raise NotImplemented()

                # NOTE: AFAIK Safari doesn't support proxy
                # https://code.google.com/p/selenium/issues/detail?id=5051
            else:
                try:
                    driver = getattr(
                        webdriver,
                        self._browser_config.get('browserName')
                    )()
                except AttributeError:
                    raise exceptions.InvalidBrowserName(
                        "The browserName('%s') is invalid" %
                        self._browser_config.get('browserName')
                    )

        # APPIUM
        elif self._browser_config.location == 'appium':
            driver = webdriver.Remote(
                command_executor='http://127.0.0.1:4723/wd/hub',
                desired_capabilities=self._browser_config.config
            )

        # SAUCELABS
        elif self._browser_config.location == 'saucelabs':
            driver = webdriver.Remote(
                command_executor='http://%s:%s@ondemand.saucelabs.com:80/wd/hub' % (  # noqa
                    BROME_CONFIG['saucelabs']['username'],
                    BROME_CONFIG['saucelabs']['key']
                ),
                desired_capabilities=self._browser_config.config
            )

        # BROWSERSTACK
        elif self._browser_config.location == 'browserstack':
            driver = webdriver.Remote(
                command_executor='http://%s:%s@hub.browserstack.com:80/wd/hub' % (  # noqa
                    BROME_CONFIG['browserstack']['username'],
                    BROME_CONFIG['browserstack']['key']
                ),
                desired_capabilities=self._browser_config.config
            )

        # REMOTE
        elif self._browser_config.location in ['virtualbox', 'ec2']:
            config = self._browser_config.config

            desired_cap = {}
            desired_cap['browserName'] = config.get('browserName')
            desired_cap['platform'] = config.get('platform')
            desired_cap['javascriptEnabled'] = True

            if config.get('enable_proxy'):
                mitm_proxy = "localhost:%s" % config.get('proxy_port', 8080)

                _proxy = proxy.Proxy({
                    'proxyType': proxy.ProxyType.MANUAL,
                    'httpProxy': mitm_proxy,
                    'sslProxy': mitm_proxy
                })

            if desired_cap['browserName'].lower() == "chrome":
                chrome_options = Options()
                chrome_options.add_argument("--test-type")
                chrome_options.add_argument("--disable-application-cache")

                if config.get('enable_proxy'):
                    chrome_options.add_argument(
                        '--proxy-server=%s' % mitm_proxy
                    )
                if config.get('mobile_emulation'):
                    chrome_options.add_experimental_option(
                        "mobileEmulation",
                        config.get('mobile_emulation')
                    )


                desired_cap = chrome_options.to_capabilities()
                desired_cap['platform'] = config.get('platform')

            try:
                command_executor = "http://%s:%s/wd/hub" % (  # noqa
                    BROME_CONFIG['grid_runner']['selenium_server_ip'],
                    BROME_CONFIG['grid_runner']['selenium_server_port']
                )

                if desired_cap['browserName'].lower() == "firefox" \
                        and config.get('enable_proxy'):
                    profile = webdriver.FirefoxProfile()
                    profile.set_proxy(proxy=_proxy)
                    driver = webdriver.Remote(
                            browser_profile=profile,
                            desired_capabilities=desired_cap,
                            command_executor=command_executor)
                else:
                    driver = webdriver.Remote(
                            command_executor=command_executor,
                            desired_capabilities=desired_cap
                    )

                self.info_log('Got a session')

            except Exception as e:
                if str(e).find("Error forwarding the new session") != -1:
                    if retry:

                        self.info_log("Waiting 5 sec because the pool doesn't contain the needed browser.")  # noqa

                        sleep(5)

                        return self.init_driver(retry=(retry - 1))
                    else:
                        raise exceptions.InitDriverException("Cannot get the driver")  # noqa

        # Wrap the driver in the brome proxy driver and return it
        return ProxyDriver(
            driver=driver,
            test_instance=self,
            runner=self._runner
        )

    def delete_state(self, state_pickle=None):
        """This will delete the pickle that hold the saved test state

        Args:
            state_pickle (path): the path of the pickle that will be delete
        """
        if not state_pickle:
            state_pickle = self.get_state_pickle_path()

        if os.path.isfile(state_pickle):
            os.remove(state_pickle)
            self.info_log("State deleted: %s" % state_pickle)

    def get_state_pickle_path(self):
        """Return the state's pickle path

        The project:url config need to be set in order to
            extract the server name. Each server will have it own state
        The test name is used to name the state.

        E.g.: the test named "test 1" that run with the project:url
            ("http://www.example.com") with have a state named:
        tests/states/test_1_example.com.pkl

        Returns: str (path)
        """

        # Extract the server name
        server = urllib.parse.urlparse(
            BROME_CONFIG['project']['url']
        ).netloc

        if BROME_CONFIG['project']['test_batch_result_path']:
            states_dir = os.path.join(
                BROME_CONFIG['project']['absolute_path'],
                BROME_CONFIG['project']['script_folder_name'],
                "states"
            )
            create_dir_if_doesnt_exist(states_dir)

            # TODO should be configurable
            state_pickle_path = os.path.join(
                states_dir,
                string_to_filename(
                    '%s_%s.pkl' %
                    (
                        self._name.replace(' ', '_'),
                        server
                    )
                )
            )

            return state_pickle_path
        else:
            return False

    def save_state(self):
        """Save the state in a pickle

        First remove all the instance variables that are private (_*).
        Then remove the pdriver instance variable.
        Finally cleanup the state using the Stateful.cleanup_state function.
        The cleanup is recursive.

        The test state will be saved to the path returned by the
            get_state_pickle_path.

        Returns: None
        """
        self.info_log("Saving state...")

        state = {}

        # Remove all the instance variables that are private
        state = {key: value for (key, value) in iter(self.__dict__.items()) if key[0] != '_'}  # noqa

        # Remove the pdriver
        del state['pdriver']

        # Cleanup the state
        effective_state = Stateful.cleanup_state(state)

        # Save the state
        state_pickle_path = self.get_state_pickle_path()
        if state_pickle_path:
            with open(state_pickle_path, 'wb') as fd:
                pickle.dump(effective_state, fd)

            self.info_log("State saved: %s" % state_pickle_path)

    def load_state(self):
        """Load the state pickle into the instance object if a state is found

        The state will be loaded in the basetest instance if a state exist.
        The pdriver will be set on each instance that inherited from Stateful
        These instance can be in a dict or a list. It is not recursive tho.

        If the instance has an after_load method, this method will be called
        E.g.:
        class Base(object):
            def after_load(self):
                self.app = self.pdriver.app

        Returns: bool True if a state was found; False if no state was found;
        """
        self.info_log("Loading state...")

        # Set the pdriver for an object that inherited from Stateful
        def set_pdriver(value):
            # TODO support class that inherited Stateful
            if Stateful in value.__class__.__bases__:
                value.pdriver = self.pdriver
                if hasattr(value, 'after_load'):
                    value.after_load()
            elif type(value) is list:
                for v in value:
                    set_pdriver(v)
            elif type(value) is dict:
                for k, v in iter(value.items()):
                    set_pdriver(v)

        # Load the state pickle
        state_pickle_path = self.get_state_pickle_path()
        if state_pickle_path:
            if os.path.isfile(state_pickle_path):
                with open(state_pickle_path, 'rb') as fd:
                    state = pickle.load(fd)
            else:
                self.info_log("No state found.")

                return False
        else:
            self.info_log("No state found.")
            return False

        # Set the pdriver
        for key, value in iter(state.items()):
            set_pdriver(value)

        # Update the instance variable dictionary
        self.__dict__.update(state)

        self.info_log("State loaded.")

        return True

    def configure_logger(self):
        """Configure the logger for the test

        Configure the logger from the logger_test config:
            logger_test:level
            logger_test:streamlogger
            logger_test:filelogger
            logger_test:format

        See documentation: http://brome.readthedocs.org/en/release/configuration.html#logger-test  # noqa

        If the config project:test_batch_result_path is set to False then the
            filelogger will not be configured.

        Returns: None
        """

        # Logger name
        logger_name = self._name

        # Log directory
        if self._runner_dir:
            self._test_log_dir = os.path.join(
                self._runner_dir,
                "logs"
            )
            self._relative_test_log_dir = os.path.join(
                self._runner.relative_runner_dir,
                "logs"
            )
        else:
            self._test_log_dir = ''
            self._relative_test_log_dir = ''

        # Logger
        self._logger = logging.getLogger(logger_name)

        # Create the log directory
        if self._runner_dir:
            create_dir_if_doesnt_exist(self._test_log_dir)

        # Format
        format_ = BROME_CONFIG['logger_test']['format']

        # Stream logger
        if BROME_CONFIG['logger_test']['streamlogger']:
            sh = logging.StreamHandler()
            stream_formatter = logging.Formatter(format_)
            sh.setFormatter(stream_formatter)
            self._logger.addHandler(sh)

        # File logger
        if self._runner_dir:
            if BROME_CONFIG['logger_test']['filelogger']:
                test_name = string_to_filename(self._name)
                self._log_file_path = os.path.join(
                    self._test_log_dir,
                    "%s_%s.log" %
                    (test_name, self._browser_config.get('browserName'))
                )
                self._relative_log_file_path = os.path.join(
                    self._relative_test_log_dir,
                    "%s_%s.log" %
                    (test_name, self._browser_config.get('browserName'))
                )
                fh = logging.FileHandler(
                    self._log_file_path
                )
                file_formatter = logging.Formatter(format_)
                fh.setFormatter(file_formatter)
                self._logger.addHandler(fh)

        # Set level
        self._logger.setLevel(getattr(
            logging, BROME_CONFIG['logger_test']['level'])
        )

    def get_logger_dict(self):
        return {
            'batchid': self._test_batch_friendly_name,
            'testname': u"%s" % self._name
        }

    def debug_log(self, msg):
        self._logger.debug(u"[debug]%s" % msg, extra=self.get_logger_dict())

    def info_log(self, msg):
        self._logger.info(u"%s" % msg, extra=self.get_logger_dict())

    def warning_log(self, msg):
        self._logger.warning(
            u"[warning]%s" % msg,
            extra=self.get_logger_dict()
        )

    def error_log(self, msg):
        self._logger.error(u"[error]%s" % msg, extra=self.get_logger_dict())

    def critical_log(self, msg):
        self._logger.critical(
            u"[critical]%s" % msg,
            extra=self.get_logger_dict()
        )

    def execute(self):
        try:
            self.before_run()

            if self._test_config.get("delete_state"):
                state_pickle_path = self.get_state_pickle_path()
                if state_pickle_path:
                    self.delete_state(state_pickle_path)
                else:
                    self.info_log('No state to delete!')

            if not self.load_state():
                if hasattr(self, 'create_state'):
                    ret = self.create_state()
                    if ret:
                        self.save_state()

            self.run(**self._test_config)

            self.after_run()

        except Exception as e:
            self.error_log('Test failed')

            tb = traceback.format_exc()

            self.error_log('Crash: %s' % tb)

            self.fail(tb)
        finally:
            self.end()

    def end(self):
        self.info_log("Test ended")

        # STOP PROXY
        if self._browser_config.location == 'ec2':
            instance = self._runner.resolve_instance_by_ip(self._private_ip)
            if instance.browser_config.get('enable_proxy'):
                instance.stop_proxy()

        self.stop_video_recording()
        ending_timestamp = utcnow()

        self.quit_driver()

        if BROME_CONFIG['runner']['play_sound_on_test_finished']:
            say(BROME_CONFIG['runner']['sound_on_test_finished'])

        ending_timestamp = ending_timestamp
        with DbSessionContext(BROME_CONFIG['database']['mongo_database_name']) as session:  # noqa
            test_instance = session.query(Testinstance)\
                .filter(Testinstance.mongo_id == self._test_instance_id).one()
            test_instance.ending_timestamp = ending_timestamp
            test_instance.terminated = True
            session.save(test_instance, safe=True)

        self.info_log("Ending timestamp: %s" % ending_timestamp)

    def quit_driver(self):
        self.info_log("Quitting the browser...")
        try:
            self.pdriver.quit()
        except Exception as e:
            self.error_log('Exception driver.quit(): %s' % str(e))

        """
        if hasattr(self, 'browsermobserver'):
            with open("temp.har", "w") as fd:
                fd.write(json.dumps(self.proxy.har))

            self.browsermobserver.stop()
        """

    def before_run(self):
        pass

    def after_run(self):
        pass

    def fail(self, tb):
        if BROME_CONFIG['runner']['play_sound_on_test_crash']:
            say(BROME_CONFIG['runner']['sound_on_test_crash'])

        if BROME_CONFIG['runner']['embed_on_test_crash']:
            self.pdriver.embed()

        if self.pdriver.bot_diary:
            if not self.pdriver.bot_diary.is_empty():
                traceback = tb
                tb = '\nBot Diary:\n'
                tb += self.pdriver.bot_diary.get_section(-1)[-1] + '\n'
                tb += 'And it failed\n'
                tb += str(traceback)

        self._crash_error = '[!]%s %s crashed: %s' % (self._name, self._browser_config.get_id(), str(tb))  # noqa

        self.create_crash_report(tb)

    def create_crash_report(self, tb):
        self.info_log('Creating a crash report')

        crash_name = "%s - %s" % (self.pdriver.get_id(join_char=' ', browser_version_join_char='.'), self._name)  # noqa

        extra_data = {}

        # JAVASCRIPT ERROR
        extra_data['javascript_error'] = self.pdriver.get_javascript_error()

        crash_screenshot_relative_path = ''
        if self._runner_dir:
            crash_screenshot_path = os.path.join(
                self._crash_report_dir,
                string_to_filename('%s.png' % crash_name)
            )

            crash_screenshot_relative_path = os.path.join(
                self._crash_report_relative_dir,
                string_to_filename('%s.png' % crash_name)
            )

            # CRASH LOG
            with open(os.path.join(self._crash_report_dir, string_to_filename('%s.log' % crash_name)), 'w') as f:  # noqa
                f.write(str(tb))

            # CRASH SCREENSHOT
            self.pdriver.take_screenshot(screenshot_path=crash_screenshot_path)

        # CRASH OBJECT
        with DbSessionContext(BROME_CONFIG['database']['mongo_database_name']) as session:  # noqa
            capabilities = {
                'browserName': self.pdriver.capabilities['browserName'],
                'platform': self.pdriver.capabilities['platform'],
                'version': self.pdriver.capabilities['version']
            }
            test_crash = Testcrash()
            test_crash.title = self._name
            test_crash.browser_capabilities = capabilities
            test_crash.browser_id = self.pdriver.get_id()
            test_crash.timestamp = utcnow()
            test_crash.trace = str(tb)
            if self._runner.root_test_result_dir:
                test_crash.root_path = self._runner.root_test_result_dir
                test_crash.screenshot_path = crash_screenshot_relative_path
                test_crash.video_capture_path = self._video_capture_file_relative_path  # noqa
            else:
                test_crash.root_path = ''
                test_crash.screenshot_path = ''
                test_crash.video_capture_path = ''
            test_crash.extra_data = extra_data
            test_crash.test_instance_id = self._test_instance_id
            test_crash.test_batch_id = self._test_batch_id

            session.save(test_crash, safe=True)

    def configure_test_result_dir(self):

        if not self._runner_dir:
            return

        # CRASH DIRECTORY
        self._crash_report_dir = os.path.join(
            self._runner_dir,
            'crashes'
        )
        create_dir_if_doesnt_exist(self._crash_report_dir)

        self._crash_report_relative_dir = os.path.join(
            self._runner.relative_runner_dir,
            'crashes'
        )

        # ASSERTION SCREENSHOT DIRECTORY
        self._assertion_screenshot_relative_dir = os.path.join(
            self._runner.relative_runner_dir,
            'assertion_screenshots',
            self.pdriver.get_id(join_char='_')
        )

        self._assertion_screenshot_dir = os.path.join(
            self._runner_dir,
            'assertion_screenshots',
            self.pdriver.get_id(join_char='_')
        )
        create_dir_if_doesnt_exist(self._assertion_screenshot_dir)

        # SCREENSHOT DIRECTORY
        self._screenshot_relative_dir = os.path.join(
            self._runner.relative_runner_dir,
            'screenshots',
            self.pdriver.get_id(join_char='_')
        )

        self._screenshot_dir = os.path.join(
            self._runner_dir,
            'screenshots',
            self.pdriver.get_id(join_char='_')
        )
        create_dir_if_doesnt_exist(self._screenshot_dir)

        # QUALITY SCREENSHOT DIRECTORY
        self._quality_screenshot_relative_dir = os.path.join(
            self._runner.relative_runner_dir,
            'quality_screenshots',
            self.pdriver.get_id(join_char='_')
        )

        self._quality_screenshot_dir = os.path.join(
            self._runner_dir,
            'quality_screenshots',
            self.pdriver.get_id(join_char='_')
        )
        create_dir_if_doesnt_exist(self._quality_screenshot_dir)

        # VIDEO RECORDING DIRECTORY
        if self._browser_config.get('record_session'):
            self._video_recording_dir = os.path.join(
                self._runner_dir,
                'video_recording',
                self.pdriver.get_id(join_char='_')
            )
            create_dir_if_doesnt_exist(self._video_recording_dir)

            self._video_recording_relative_dir = os.path.join(
                self._runner.relative_runner_dir,
                'video_recording',
                self.pdriver.get_id(join_char='_')
            )

        # NETWORK CAPTURE DIRECTORY
        if self._browser_config.get('enable_proxy'):
            self._network_capture_dir = os.path.join(
                self._runner_dir,
                'network_capture'
            )
            create_dir_if_doesnt_exist(self._network_capture_dir)

    def get_test_result_summary(self):
        results = []

        with DbSessionContext(BROME_CONFIG['database']['mongo_database_name']) as session:  # noqa
            base_query = session.query(Testresult).filter(Testresult.test_instance_id == self._test_instance_id).filter(Testresult.browser_id == self.pdriver.get_id())  # noqa
            total_test = base_query.count()
            total_test_successful = base_query.filter(Testresult.result == True).count()  # noqa
            base_query = session.query(Testresult).filter(Testresult.test_instance_id == self._test_instance_id).filter(Testresult.browser_id == self.pdriver.get_id())  # noqa
            total_test_failed = base_query.filter(Testresult.result == False).count()  # noqa
            base_query = session.query(Testresult)\
                .filter(Testresult.test_instance_id == self._test_instance_id)\
                .filter(Testresult.browser_id == self.pdriver.get_id())
            failed_tests = base_query.filter(Testresult.result == False).all()  # noqa

            results.append('Total_test: %s; Total_test_successful: %s; Total_test_failed: %s' % (total_test, total_test_successful, total_test_failed))  # noqa

            failed_tests_title = []
            for failed_test in failed_tests:
                if failed_test.title not in failed_tests_title:
                    failed_tests_title.append(failed_test.title)
                    query = session.query(Test)\
                        .filter(Test.mongo_id == failed_test.test_id)
                    if query.count():
                        test = query.one()
                        test_id = test.test_id
                    else:
                        test_id = 'n/a'

                    results.append('[%s] %s' % (test_id, failed_test.title))

        return results
