1#!/usr/bin/env python
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19
20from vts.runners.host import asserts
21from vts.runners.host import base_test
22from vts.runners.host import const
23from vts.runners.host import keys
24from vts.runners.host import test_runner
25from vts.utils.python.controllers import android_device
26from vts.utils.python.os import path_utils
27
28from vts.testcases.kernel.linux_kselftest import kselftest_config as config
29
30class LinuxKselftestTest(base_test.BaseTestClass):
31    """Runs Linux Kselftest test cases against Android OS kernel.
32
33    Attributes:
34        _dut: AndroidDevice, the device under test as config
35        _shell: ShellMirrorObject, shell mirror
36        _testcases: string list, list of testcases to run
37    """
38    _32BIT = 32
39    _64BIT = 64
40
41    def setUpClass(self):
42        """Creates a remote shell instance, and copies data files."""
43        required_params = [
44            keys.ConfigKeys.IKEY_DATA_FILE_PATH,
45            config.ConfigKeys.TEST_TYPE
46        ]
47        self.getUserParams(required_params)
48
49        logging.info("%s: %s", keys.ConfigKeys.IKEY_DATA_FILE_PATH,
50            self.data_file_path)
51
52        self._dut = self.android_devices[0]
53        self._shell = self._dut.shell
54
55        if self.test_type == "presubmit":
56            self._testcases = config.KSFT_CASES_PRESUBMIT
57        elif self.test_type == "stable":
58            self._testcases = config.KSFT_CASES_STABLE
59        elif self.test_type == "staging":
60            self._testcases = config.KSFT_CASES_STAGING
61        else:
62            asserts.fail("Test config is incorrect!")
63
64    def tearDownClass(self):
65        """Deletes all copied data."""
66        self._shell.Execute("rm -rf %s" % config.KSFT_DIR)
67
68    def PushFiles(self, n_bit):
69        """adb pushes related file to target.
70
71        Args:
72            n_bit: _32BIT or 32 for 32-bit tests;
73                _64BIT or 64 for 64-bit tests;
74        """
75        self._shell.Execute("mkdir %s -p" % config.KSFT_DIR)
76        test_bit = 'nativetest'
77        if n_bit == self._64BIT:
78            test_bit += '64'
79        self._dut.adb.push("%s/DATA/%s/linux-kselftest/. %s" %
80            (self.data_file_path, test_bit, config.KSFT_DIR))
81
82    def PreTestSetup(self):
83        """Sets up test before running."""
84        # This sed command makes shell scripts compatible wiht android shell.
85        sed_pattern = [
86            's?/bin/echo?echo?',
87            's?#!/bin/sh?#!/system/bin/sh?',
88            's?#!/bin/bash?#!/system/bin/sh?'
89        ]
90        sed_cmd = 'sed %s' % ' '.join(
91            ['-i -e ' + ('"%s"' % p) for p in sed_pattern])
92
93        # This grep command is used to identify shell scripts.
94        grep_pattern = [
95           'bin/sh',
96           'bin/bash'
97        ]
98        grep_cmd = 'grep -l %s' % ' '.join(
99            ['-e ' + ('"%s"' % p) for p in grep_pattern])
100
101        # This applies sed_cmd to every shell script.
102        cmd = 'find %s -type f | xargs %s | xargs %s' % (
103            config.KSFT_DIR, grep_cmd, sed_cmd)
104        result = self._shell.Execute(cmd)
105
106        asserts.assertFalse(
107            any(result[const.EXIT_CODE]),
108            "Error: pre-test setup failed.")
109
110    def RunTestcase(self, testcase):
111        """Runs the given testcase and asserts the result.
112
113        Args:
114            testcase: a LinuxKselftestTestcase object, specifies which
115                test case to run.
116        """
117        if not testcase:
118            asserts.skip("Test is not supported on this abi.")
119
120        chmod_cmd = "chmod -R 755 %s" % path_utils.JoinTargetPath(
121            config.KSFT_DIR, testcase.testsuite)
122        cd_cmd = "cd %s" % path_utils.JoinTargetPath(
123            config.KSFT_DIR, testcase.testsuite)
124
125        cmd = [
126            chmod_cmd,
127            "%s && %s" % (cd_cmd, testcase.test_cmd)
128        ]
129        logging.info("Executing: %s", cmd)
130
131        result = self._shell.Execute(cmd)
132        logging.info("EXIT_CODE: %s:", result[const.EXIT_CODE])
133
134        asserts.assertFalse(
135            any(result[const.EXIT_CODE]),
136            "%s failed." % testcase.testname)
137
138    def TestNBits(self, n_bit):
139        """Runs all 32-bit or all 64-bit tests.
140
141        Args:
142            n_bit: _32BIT or 32 for 32-bit tests;
143                _64BIT or 64 for 64-bit tests;
144        """
145        self.PushFiles(n_bit)
146        self.PreTestSetup()
147
148        cpu_abi = self._dut.cpu_abi
149        relevant_testcases = filter(
150            lambda x: x.IsRelevant(cpu_abi, n_bit),
151            self._testcases)
152
153        self.runGeneratedTests(
154            test_func=self.RunTestcase,
155            settings=relevant_testcases,
156            name_func=lambda testcase: "%s_%sbit" % (
157                testcase.testname.replace('/','_'), n_bit))
158
159    def generate32BitTests(self):
160        """Runs all 32-bit tests."""
161        self.TestNBits(self._32BIT)
162
163    def generate64BitTests(self):
164        """Runs all 64-bit tests."""
165        if self._dut.is64Bit:
166            self.TestNBits(self._64BIT)
167
168if __name__ == "__main__":
169    test_runner.main()
170