普通文本  |  218行  |  8.3 KB

#
# 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 copy
import logging
import itertools
import operator

from vts.runners.host import const
from vts.utils.python.common import cmd_utils
from vts.utils.python.os import path_utils

from vts.testcases.kernel.ltp.shell_environment import shell_environment
from vts.testcases.kernel.ltp import ltp_enums
from vts.testcases.kernel.ltp import ltp_configs
from vts.testcases.kernel.ltp import requirements


class EnvironmentRequirementChecker(object):
    """LTP testcase environment checker.

    This class contains a dictionary for some known environment
    requirements for a set of test cases and several environment
    check functions to be mapped with. All check functions' results
    are cached in a dictionary for multiple use.

    Attributes:
        _REQUIREMENT_DEFINITIONS: dictionary {string, obj}, a map between
            requirement name and the actual definition class object
        _result_cache: dictionary {requirement_check_method_name:
            (bool, string)}, a map between check method name and cached result
            tuples (boolean, note)
        _executable_available: dict {string, bool}, a map between executable
                                path and its existance on target
        _shell_env: ShellEnvironment object, which checks and sets
            shell environments given a shell mirror
        shell: shell mirror object, can be used to execute shell
            commands on target side through runner
        ltp_bin_host_path: string, host path of ltp binary
    """

    def __init__(self, shell):
        self.shell = shell
        self._result_cache = {}
        self._executable_available = {}
        self._shell_env = shell_environment.ShellEnvironment(self.shell)
        self._REQUIREMENT_DEFINITIONS = requirements.GetRequrementDefinitions()

    @property
    def shell(self):
        """Get the runner's shell mirror object to execute commands"""
        return self._shell

    @shell.setter
    def shell(self, shell):
        """Set the runner's shell mirror object to execute commands"""
        self._shell = shell

    def Cleanup(self):
        """Run all cleanup jobs at the end of tests"""
        self._shell_env.Cleanup()

    def GetRequirements(self, test_case):
        """Get a list of requirements for a fiven test case

        Args:
            test_case: TestCase object, the test case to query
        """
        result = copy.copy(ltp_configs.REQUIREMENT_FOR_ALL)

        result.extend(
            rule
            for rule, tests in ltp_configs.REQUIREMENTS_TO_TESTCASE.iteritems()
            if test_case.fullname in tests)

        result.extend(
            rule
            for rule, tests in ltp_configs.REQUIREMENT_TO_TESTSUITE.iteritems()
            if test_case.testsuite in tests)

        return list(set(result))

    def Check(self, test_case):
        """Check whether a given test case's requirement has been satisfied.
        Skip the test if not.

        If check failed, this method returns False and the reason is set
        to test_case.note.

        Args:
            test_case: TestCase object, a given test case to check

        Returns:
            True if check pass; False otherwise
        """
        if (test_case.requirement_state ==
                ltp_enums.RequirementState.UNSATISFIED or
                not self.TestBinaryExists(test_case)):
            return False

        for requirement in self.GetRequirements(test_case):
            if requirement not in self._result_cache:
                definitions = self._REQUIREMENT_DEFINITIONS[requirement]
                self._result_cache[
                    requirement] = self._shell_env.ExecuteDefinitions(
                        definitions)

            result, note = self._result_cache[requirement]
            logging.info("Result for %s's requirement %s is %s", test_case,
                         requirement, result)
            if result is False:
                test_case.requirement_state = ltp_enums.RequirementState.UNSATISFIED
                test_case.note = note
                return False

        test_case.requirement_state = ltp_enums.RequirementState.SATISFIED
        return True

    def CheckAllTestCaseExecutables(self, test_cases):
        """Run a batch job to check executable exists and set permissions.

        The result will be stored in self._executable_available for use in
        TestBinaryExists method.

        Args:
            test_case: list of TestCase objects.
        """
        executables_generators = (
            test_case.GetRequiredExecutablePaths(self.ltp_bin_host_path)
            for test_case in test_cases)
        executables = list(
            set(itertools.chain.from_iterable(executables_generators)))

        # Set all executables executable permission using chmod.
        logging.info("Setting permissions on device")
        permission_command = "chmod 775 %s" % path_utils.JoinTargetPath(
            ltp_configs.LTPBINPATH, '*')
        permission_result = self.shell.Execute(permission_command)
        if permission_result[const.EXIT_CODE][0]:
            logging.error("Permission command '%s' failed.",
                          permission_command)

        # Check existence of all executables used in test definition.
        # Some executables needed by test cases but not listed in test
        # definition will not be checked here
        executable_exists_commands = [
            "ls %s" % executable for executable in executables
            if executable not in ltp_configs.INTERNAL_BINS
        ]
        logging.info("Checking binary existence on host: %s",
                     executable_exists_commands)

        cmd_results = cmd_utils.ExecuteShellCommand(executable_exists_commands)
        executable_exists_results = map(operator.not_,
                                        cmd_results[cmd_utils.EXIT_CODE])
        logging.info("Finished checking binary existence on host: %s",
                     cmd_results)

        self._executable_available = dict(
            zip(executables, executable_exists_results))

        # Check whether all the internal binaries in path needed exist
        bin_path_exist_commands = ["which %s" % bin
                                   for bin in ltp_configs.INTERNAL_BINS]
        bin_path_results = map(
            operator.not_,
            self.shell.Execute(bin_path_exist_commands)[const.EXIT_CODE])

        bin_path_results = map(
            operator.not_,
            self.shell.Execute(bin_path_exist_commands)[const.EXIT_CODE])

        self._executable_available.update(
            dict(zip(ltp_configs.INTERNAL_BINS, bin_path_results)))

    def TestBinaryExists(self, test_case):
        """Check whether the given test case's binary exists.

        Args:
            test_case: TestCase, the object representing the test case

        Return:
            True if exists, False otherwise
        """
        if test_case.requirement_state == ltp_enums.RequirementState.UNSATISFIED:
            logging.warn("[Checker] Attempting to run test case that has "
                         "already been checked and requirement not satisfied."
                         "%s" % test_case)
            return False

        executables = test_case.GetRequiredExecutablePaths(
            self.ltp_bin_host_path)
        results = [self._executable_available[executable]
                   for executable in executables]

        if not all(results):
            test_case.requirement_state = ltp_enums.RequirementState.UNSATISFIED
            test_case.note = "Some executables not exist: {}".format(
                zip(executables, results))
            logging.error("[Checker] Binary existance check failed for {}. "
                          "Reason: {}".format(test_case, test_case.note))
            return False
        else:
            return True