# Copyright 2014 Intel Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import ConfigParser
import os
import subprocess
import xml.etree.cElementTree as ET

# Piglit modules
from framework import core, grouptools
from framework.profile import Test, TestProfile

__all__ = ['profile']


def get_test_case(root, root_group, outputfile):
    """Parser the test case list of Google Android CTS,
    and store the test case list to dEQP-GLES3-cases.txt
    """
    for child in root:
        root_group.append(child.get('name'))
        if child.tag == "Test":
            outputfile.write('TEST: {}\n'.format('.'.join(root_group)))
            del root_group[-1]
        else:
            get_test_case(child, root_group, outputfile)
            del root_group[-1]


def load_test_hierarchy(input, output):
    """Google have added a subset of dEQP to CTS test, the case list is stored
    at some xml files. Such as: com.drawelements.deqp.gles3.xml This function
    is used to parser the file, and generate a new dEQP-GLES3-cases.txt which
    only contain the subset of dEQP.
    """
    tree = ET.parse(input)
    root = tree.getroot()
    root_group = []
    with open(output, 'w') as f:
        get_test_case(root, root_group, f)


def get_option(env_varname, config_option):
    """Query the given environment variable and then piglit.conf for the
    option. Return None if the option is unset.
    """
    opt = os.environ.get(env_varname, None)
    if opt is not None:
        return opt

    try:
        opt = core.PIGLIT_CONFIG.get(config_option[0], config_option[1])
    except ConfigParser.NoSectionError:
        pass
    except ConfigParser.NoOptionError:
        pass

    return opt


# Path to the deqp-gles3 executable.
DEQP_GLES3_EXE = get_option('PIGLIT_DEQP_GLES3_EXE', ('deqp-gles3', 'exe'))

# Path to the xml file which contained the case list of the subset of dEQP
# and marked as must pass in CTS.
DEQP_MUSTPASS = get_option('PIGLIT_DEQP_MUSTPASS',
                           ('deqp-gles3', 'mustpasslist'))

DEQP_GLES3_EXTRA_ARGS = get_option('PIGLIT_DEQP_GLES3_EXTRA_ARGS',
                                   ('deqp-gles3', 'extra_args'))


def gen_caselist_txt():
    """Generate dEQP-GLES3-cases.txt and return its path."""
    # dEQP is stupid (2014-12-07):
    #   1. To generate the caselist file, dEQP requires that the process's
    #      current directory must be that same as that of the executable.
    #      Otherwise, it fails to find its data files.
    #   2. dEQP creates the caselist file in the process's current directory
    #      and provides no option to change its location.
    #   3. dEQP creates a GL context when generating the caselist. Therefore,
    #      the caselist must be generated on the test target rather than the
    #      build host. In other words, when the build host and test target
    #      differ then we cannot pre-generate the caselist on the build host:
    #      we must *dynamically* generate it during the testrun.
    basedir = os.path.dirname(DEQP_GLES3_EXE)
    caselist_path = os.path.join(basedir, 'dEQP-GLES3-cases.txt')
    subprocess.check_call(
        [DEQP_GLES3_EXE, '--deqp-runmode=txt-caselist'],
        cwd=basedir)
    if DEQP_MUSTPASS is not None:
        load_test_hierarchy(DEQP_MUSTPASS, caselist_path)
    assert os.path.exists(caselist_path)
    return caselist_path


def iter_deqp_test_cases():
    """Iterate over original dEQP GLES3 testcase names."""
    caselist_path = gen_caselist_txt()
    with open(caselist_path) as caselist_file:
        for i, line in enumerate(caselist_file):
            if line.startswith('GROUP:'):
                continue
            elif line.startswith('TEST:'):
                yield line[len('TEST:'):].strip()
            else:
                raise Exception(
                    '{0}:{1}: ill-formed line'.format(caselist_path, i))


class DEQPTest(Test):

    __RESULT_MAP = {"Pass": "pass",
                    "Fail": "fail",
                    "QualityWarning": "warn",
                    "InternalError": "fail",
                    "Crash": "crash",
                    "NotSupported": "skip"}

    def __init__(self, case_name):
        command = [DEQP_GLES3_EXE, '--deqp-case=' + case_name]
        if DEQP_GLES3_EXTRA_ARGS is not None:
            command.extend(DEQP_GLES3_EXTRA_ARGS.split())
        super(DEQPTest, self).__init__(command)

        # dEQP's working directory must be the same as that of the executable,
        # otherwise it cannot find its data files (2014-12-07).
        self.cwd = os.path.dirname(DEQP_GLES3_EXE)

    def interpret_result(self):
        if self.result['returncode'] != 0:
            self.result['result'] = 'fail'
            return

        for line in self.result['out'].split('\n'):
            line = line.lstrip()
            for k, v in DEQPTest.__RESULT_MAP.iteritems():
                if line.startswith(k):
                    self.result['result'] = v
                    return

        # We failed to parse the test output. Fallback to 'fail'.
        self.result['result'] = 'fail'


def add_tests():
    if DEQP_GLES3_EXE is None:
        return

    for deqp_testname in iter_deqp_test_cases():
        # dEQP uses '.' as the testgroup separator.
        piglit_testname = deqp_testname.replace('.', grouptools.SEPARATOR)

        test = DEQPTest(deqp_testname)
        profile.test_list[piglit_testname] = test


profile = TestProfile()
add_tests()