# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1.  Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 2.  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.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.

"""Contains the entry method for test-webkitpy."""

import logging
import os
import sys
import unittest

import webkitpy


_log = logging.getLogger(__name__)


class Tester(object):

    """Discovers and runs webkitpy unit tests."""

    def _find_unittest_files(self, webkitpy_dir):
        """Return a list of paths to all unit-test files."""
        unittest_paths = []  # Return value.

        for dir_path, dir_names, file_names in os.walk(webkitpy_dir):
            for file_name in file_names:
                if not file_name.endswith("_unittest.py"):
                    continue
                unittest_path = os.path.join(dir_path, file_name)
                unittest_paths.append(unittest_path)

        return unittest_paths

    def _modules_from_paths(self, webkitpy_dir, paths):
        """Return a list of fully-qualified module names given paths."""
        webkitpy_dir = os.path.abspath(webkitpy_dir)
        webkitpy_name = os.path.split(webkitpy_dir)[1]  # Equals "webkitpy".

        prefix_length = len(webkitpy_dir)

        modules = []
        for path in paths:
            path = os.path.abspath(path)
            # This gives us, for example: /common/config/ports_unittest.py
            rel_path = path[prefix_length:]
            # This gives us, for example: /common/config/ports_unittest
            rel_path = os.path.splitext(rel_path)[0]

            parts = []
            while True:
                (rel_path, tail) = os.path.split(rel_path)
                if not tail:
                    break
                parts.insert(0, tail)
            # We now have, for example: common.config.ports_unittest
            parts.insert(0, webkitpy_name)  # Put "webkitpy" at the beginning.
            module = ".".join(parts)
            modules.append(module)

        return modules

    def run_tests(self, sys_argv):
        """Run the unit tests in all *_unittest.py modules in webkitpy.

        This method excludes "webkitpy.common.checkout.scm_unittest" unless
        the --all option is the second element of sys_argv.

        Args:
          sys_argv: A reference to sys.argv.

        """
        if len(sys_argv) > 1 and not sys_argv[-1].startswith("-"):
            # Then explicit modules or test names were provided, which
            # the unittest module is equipped to handle.
            unittest.main(argv=sys_argv, module=None)
            # No need to return since unitttest.main() exits.

        # Otherwise, auto-detect all unit tests.

        webkitpy_dir = os.path.dirname(webkitpy.__file__)
        unittest_paths = self._find_unittest_files(webkitpy_dir)

        modules = self._modules_from_paths(webkitpy_dir, unittest_paths)
        modules.sort()

        # This is a sanity check to ensure that the unit-test discovery
        # methods are working.
        if len(modules) < 1:
            raise Exception("No unit-test modules found.")

        for module in modules:
            _log.debug("Found: %s" % module)

        # FIXME: This is a hack, but I'm tired of commenting out the test.
        #        See https://bugs.webkit.org/show_bug.cgi?id=31818
        if len(sys_argv) > 1 and sys.argv[1] == "--all":
            sys.argv.remove("--all")
        else:
            excluded_module = "webkitpy.common.checkout.scm_unittest"
            _log.info("Excluding: %s (use --all to include)" % excluded_module)
            modules.remove(excluded_module)

        sys_argv.extend(modules)

        # We pass None for the module because we do not want the unittest
        # module to resolve module names relative to a given module.
        # (This would require importing all of the unittest modules from
        # this module.)  See the loadTestsFromName() method of the
        # unittest.TestLoader class for more details on this parameter.
        unittest.main(argv=sys_argv, module=None)
