#!/usr/bin/env python
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import logging

from vts.runners.host import asserts
from vts.runners.host import base_test
from vts.runners.host import const
from vts.runners.host import keys
from vts.runners.host import test_runner
from vts.utils.python.controllers import android_device
from vts.utils.python.os import path_utils

from vts.testcases.kernel.linux_kselftest import kselftest_config as config

class LinuxKselftestTest(base_test.BaseTestClass):
    """Runs Linux Kselftest test cases against Android OS kernel.

    Attributes:
        _dut: AndroidDevice, the device under test as config
        _shell: ShellMirrorObject, shell mirror
        _testcases: string list, list of testcases to run
    """
    _32BIT = 32
    _64BIT = 64

    def setUpClass(self):
        """Creates a remote shell instance, and copies data files."""
        required_params = [
            keys.ConfigKeys.IKEY_DATA_FILE_PATH,
            config.ConfigKeys.TEST_TYPE
        ]
        self.getUserParams(required_params)

        logging.info("%s: %s", keys.ConfigKeys.IKEY_DATA_FILE_PATH,
            self.data_file_path)

        self._dut = self.android_devices[0]
        self._shell = self._dut.shell

        if self.test_type == "presubmit":
            self._testcases = config.KSFT_CASES_PRESUBMIT
        elif self.test_type == "stable":
            self._testcases = config.KSFT_CASES_STABLE
        elif self.test_type == "staging":
            self._testcases = config.KSFT_CASES_STAGING
        else:
            asserts.fail("Test config is incorrect!")

    def tearDownClass(self):
        """Deletes all copied data."""
        self._shell.Execute("rm -rf %s" % config.KSFT_DIR)

    def PushFiles(self, n_bit):
        """adb pushes related file to target.

        Args:
            n_bit: _32BIT or 32 for 32-bit tests;
                _64BIT or 64 for 64-bit tests;
        """
        self._shell.Execute("mkdir %s -p" % config.KSFT_DIR)
        test_bit = 'nativetest'
        if n_bit == self._64BIT:
            test_bit += '64'
        self._dut.adb.push("%s/DATA/%s/linux-kselftest/. %s" %
            (self.data_file_path, test_bit, config.KSFT_DIR))

    def PreTestSetup(self):
        """Sets up test before running."""
        # This sed command makes shell scripts compatible wiht android shell.
        sed_pattern = [
            's?/bin/echo?echo?',
            's?#!/bin/sh?#!/system/bin/sh?',
            's?#!/bin/bash?#!/system/bin/sh?'
        ]
        sed_cmd = 'sed %s' % ' '.join(
            ['-i -e ' + ('"%s"' % p) for p in sed_pattern])

        # This grep command is used to identify shell scripts.
        grep_pattern = [
           'bin/sh',
           'bin/bash'
        ]
        grep_cmd = 'grep -l %s' % ' '.join(
            ['-e ' + ('"%s"' % p) for p in grep_pattern])

        # This applies sed_cmd to every shell script.
        cmd = 'find %s -type f | xargs %s | xargs %s' % (
            config.KSFT_DIR, grep_cmd, sed_cmd)
        result = self._shell.Execute(cmd)

        asserts.assertFalse(
            any(result[const.EXIT_CODE]),
            "Error: pre-test setup failed.")

    def RunTestcase(self, testcase):
        """Runs the given testcase and asserts the result.

        Args:
            testcase: a LinuxKselftestTestcase object, specifies which
                test case to run.
        """
        if not testcase:
            asserts.skip("Test is not supported on this abi.")

        chmod_cmd = "chmod -R 755 %s" % path_utils.JoinTargetPath(
            config.KSFT_DIR, testcase.testsuite)
        cd_cmd = "cd %s" % path_utils.JoinTargetPath(
            config.KSFT_DIR, testcase.testsuite)

        cmd = [
            chmod_cmd,
            "%s && %s" % (cd_cmd, testcase.test_cmd)
        ]
        logging.info("Executing: %s", cmd)

        result = self._shell.Execute(cmd)
        logging.info("EXIT_CODE: %s:", result[const.EXIT_CODE])

        asserts.assertFalse(
            any(result[const.EXIT_CODE]),
            "%s failed." % testcase.testname)

    def TestNBits(self, n_bit):
        """Runs all 32-bit or all 64-bit tests.

        Args:
            n_bit: _32BIT or 32 for 32-bit tests;
                _64BIT or 64 for 64-bit tests;
        """
        self.PushFiles(n_bit)
        self.PreTestSetup()

        cpu_abi = self._dut.cpu_abi
        relevant_testcases = filter(
            lambda x: x.IsRelevant(cpu_abi, n_bit),
            self._testcases)

        self.runGeneratedTests(
            test_func=self.RunTestcase,
            settings=relevant_testcases,
            name_func=lambda testcase: "%s_%sbit" % (
                testcase.testname.replace('/','_'), n_bit))

    def generate32BitTests(self):
        """Runs all 32-bit tests."""
        self.TestNBits(self._32BIT)

    def generate64BitTests(self):
        """Runs all 64-bit tests."""
        if self._dut.is64Bit:
            self.TestNBits(self._64BIT)

if __name__ == "__main__":
    test_runner.main()