#!/usr/bin/env python
# Copyright (C) 2010 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#     * Neither the Google name nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""This is a test implementation of the Port interface that generates the
   correct output for every test. It can be used for perf testing, because
   it is pretty much a lower limit on how fast a port can possibly run.

   This implementation acts as a wrapper around a real port (the real port
   is held as a delegate object). To specify which port, use the port name
   'dryrun-XXX' (e.g., 'dryrun-chromium-mac-leopard'). If you use just
   'dryrun', it uses the default port.

   Note that because this is really acting as a wrapper around the underlying
   port, you must be able to run the underlying port as well
   (check_build() and check_sys_deps() must pass and auxiliary binaries
   like layout_test_helper and httpd must work).

   This implementation also modifies the test expectations so that all
   tests are either SKIPPED or expected to PASS."""

from __future__ import with_statement

import sys

import base
import factory


def _read_file(path, mode='r'):
    """Return the contents of a file as a string.

    Returns '' if anything goes wrong, instead of throwing an IOError.

    """
    contents = ''
    try:
        with open(path, mode) as f:
            contents = f.read()
    except IOError:
        pass
    return contents


def _write_file(path, contents, mode='w'):
    """Write the string to the specified path.

    Returns nothing if the write fails, instead of raising an IOError.

    """
    try:
        with open(path, mode) as f:
            f.write(contents)
    except IOError:
        pass


class DryRunPort(object):
    """DryRun implementation of the Port interface."""

    def __init__(self, port_name=None, options=None):
        pfx = 'dryrun-'
        if port_name.startswith(pfx):
            port_name = port_name[len(pfx):]
        else:
            port_name = None
        self.__delegate = factory.get(port_name, options)

    def __getattr__(self, name):
        return getattr(self.__delegate, name)

    def check_build(self, needs_http):
        return True

    def check_sys_deps(self, needs_http):
        return True

    def start_helper(self):
        pass

    def start_http_server(self):
        pass

    def start_websocket_server(self):
        pass

    def stop_helper(self):
        pass

    def stop_http_server(self):
        pass

    def stop_websocket_server(self):
        pass

    def create_driver(self, image_path, options):
        return DryrunDriver(self, image_path, options)


class DryrunDriver(base.Driver):
    """Dryrun implementation of the DumpRenderTree / Driver interface."""

    def __init__(self, port, image_path, test_driver_options):
        self._port = port
        self._driver_options = test_driver_options
        self._image_path = image_path
        self._layout_tests_dir = None

    def poll(self):
        return None

    def returncode(self):
        return 0

    def run_test(self, uri, timeoutms, image_hash):
        test_name = self._uri_to_test(uri)

        text_filename = self._port.expected_filename(test_name, '.txt')
        text_output = _read_file(text_filename)

        if image_hash:
            image_filename = self._port.expected_filename(test_name, '.png')
            image = _read_file(image_filename, 'rb')
            if self._image_path:
                _write_file(self._image_path, image)
            hash_filename = self._port.expected_filename(test_name,
                '.checksum')
            hash = _read_file(hash_filename)
        else:
            hash = None
        return (False, False, hash, text_output, None)

    def start(self):
        pass

    def stop(self):
        pass

    def _uri_to_test(self, uri):
        """Return the base layout test name for a given URI.

        This returns the test name for a given URI, e.g., if you passed in
        "file:///src/LayoutTests/fast/html/keygen.html" it would return
        "fast/html/keygen.html".

        """
        if not self._layout_tests_dir:
            self._layout_tests_dir = self._port.layout_tests_dir()
        test = uri

        if uri.startswith("file:///"):
            if sys.platform == 'win32':
                test = test.replace('file:///', '')
                test = test.replace('/', '\\')
            else:
                test = test.replace('file://', '')
            return test
        elif uri.startswith("http://127.0.0.1:8880/"):
            # websocket tests
            test = test.replace('http://127.0.0.1:8880/',
                                self._layout_tests_dir + '/')
            return test
        elif uri.startswith("http://"):
            # regular HTTP test
            test = test.replace('http://127.0.0.1:8000/',
                                self._layout_tests_dir + '/http/tests/')
            return test
        elif uri.startswith("https://"):
            test = test.replace('https://127.0.0.1:8443/',
                                self._layout_tests_dir + '/http/tests/')
            return test
        else:
            raise NotImplementedError('unknown url type: %s' % uri)
