• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright 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
17import difflib
18import filecmp
19import getopt
20import logging
21import os
22import shutil
23import subprocess
24import sys
25import unittest
26
27from vts.utils.python.common import cmd_utils
28
29
30class VtscTester(unittest.TestCase):
31    """Integration test runner for vtsc in generating the driver/profiler code.
32
33    Runs vtsc with specified mode on a bunch of files and compares the output
34    results with canonical ones. Exit code is 0 iff all tests pass.
35    Note: need to run the script from the source root to preserve the correct
36          path.
37
38    Usage:
39        python test_vtsc.py path_to_vtsc canonical_dir output_dir
40
41    example:
42        python test/vts/compilation_tools/vtsc/test/test_vtsc.py vtsc
43        test/vts/compilation_tools/vtsc/test/golden/ temp_output
44
45    Attributes:
46        _hidl_gen_path: the path to run hidl-gen
47        _vtsc_path: the path to run vtsc.
48        _canonical_dir: root directory contains canonical files for comparison.
49        _output_dir: root directory that stores all output files.
50        _errors: number of errors generates during the test.
51        _temp_dir: temp dir to store the .vts file generated by hidl-gen.
52    """
53
54    def __init__(self, testName, hidl_gen_path, vtsc_path, canonical_dir,
55                 output_dir):
56        super(VtscTester, self).__init__(testName)
57        self._hidl_gen_path = hidl_gen_path
58        self._vtsc_path = vtsc_path
59        self._canonical_dir = canonical_dir
60        self._output_dir = output_dir
61        self._errors = 0
62        self._temp_dir = "test/vts/specification/hal"
63
64    def setUp(self):
65        """Removes output dir to prevent interference from previous runs."""
66        self.RemoveOutputDir()
67
68    def tearDown(self):
69        """If successful, removes the output dir for clean-up."""
70        if self._errors == 0:
71            self.RemoveOutputDir()
72
73    def testAll(self):
74        """Run all tests. """
75        self.TestDriver()
76        self.TestProfiler()
77        self.TestFuzzer()
78        self.assertEqual(self._errors, 0)
79
80    def TestDriver(self):
81        """Run tests for DRIVER mode. """
82        logging.info("Running TestDriver test case.")
83        # Tests for Hidl Hals.
84        self.GenerateVtsFile("android.hardware.nfc@1.0")
85        for component_name in ["Nfc", "types", "NfcClientCallback"]:
86            self.RunTest("DRIVER",
87                         os.path.join(self._temp_dir, component_name + ".vts"),
88                         "%s.driver.cpp" % component_name)
89        # Tests for conventional Hals.
90        for package_path, component_name in zip(
91            ["camera/2.1", "bluetooth/1.0", "bluetooth/1.0", "wifi/1.0"],
92            ["CameraHalV2", "BluetoothHalV1",
93             "BluetoothHalV1bt_interface_t", "WifiHalV1"]):
94            self.RunTest(
95                "DRIVER",
96                "test/vts/specification/hal/conventional/%s/%s.vts" % (
97                    package_path, component_name),
98                "%s.driver.cpp" % component_name)
99        # Tests for shared libraries.
100        for component_name in ["libcV1"]:
101            self.RunTest(
102                "DRIVER",
103                "test/vts/specification/lib/ndk/bionic/1.0/%s.vts" % component_name,
104                "%s.driver.cpp" % component_name)
105
106    def TestProfiler(self):
107        """Run tests for PROFILER mode. """
108        logging.info("Running TestProfiler test case.")
109        self.GenerateVtsFile("android.hardware.nfc@1.0")
110        for component_name in ["Nfc", "types", "NfcClientCallback"]:
111            self.RunTest("PROFILER",
112                         os.path.join(self._temp_dir, component_name + ".vts"),
113                         "%s.profiler.cpp" % component_name)
114
115    def TestFuzzer(self):
116        """Run tests for Fuzzer mode. """
117        logging.info("Running TestProfiler test case.")
118        self.GenerateVtsFile("android.hardware.renderscript@1.0")
119        for component_name in ["Context", "Device", "types"]:
120            self.RunTest("FUZZER",
121                         os.path.join(self._temp_dir, component_name + ".vts"),
122                         "%s.fuzzer.cpp" % component_name, "SOURCE")
123
124    def RunFuzzerTest(self, mode, vts_file_path, source_file_name):
125        vtsc_cmd = [
126            self._vtsc_path, "-m" + mode, vts_file_path,
127            os.path.join(self._output_dir, mode),
128            os.path.join(self._output_dir, mode, "")
129        ]
130        return_code = cmd_utils.RunCommand(vtsc_cmd)
131
132        canonical_source_file = os.path.join(self._canonical_dir, mode,
133                                             source_file_name)
134        output_source_file = os.path.join(self._output_dir, mode,
135                                          source_file_name)
136        self.CompareOutputFile(output_source_file, canonical_source_file)
137
138    def GenerateVtsFile(self, hal_package_name):
139        """Run hidl-gen to generate the .vts files for the give hal package.
140
141        Args:
142            hal_package_name: name of hal package e.g. android.hardware.nfc@1.0
143        """
144        hidl_gen_cmd = [
145            self._hidl_gen_path, "-o" + self._temp_dir, "-Lvts",
146            "-randroid.hardware:hardware/interfaces",
147            "-randroid.hidl:system/libhidl/transport", hal_package_name
148        ]
149        return_code = cmd_utils.RunCommand(hidl_gen_cmd)
150        if (return_code != 0):
151            self.Error("Fail to execute command: %s" % hidl_gen_cmd)
152        [hal_name, hal_version] = hal_package_name.split("@")
153        output_dir = os.path.join(self._temp_dir,
154                                  hal_name.replace(".", "/"), hal_version)
155        for file in os.listdir(output_dir):
156            if file.endswith(".vts"):
157                os.rename(
158                    os.path.join(output_dir, file),
159                    os.path.join(self._temp_dir, file))
160
161    def RunTest(self, mode, vts_file_path, source_file_name, file_type="BOTH"):
162        """Run vtsc with given mode for the give vts file and compare the
163           output results.
164
165        Args:
166            mode: the vtsc mode for generated code. e.g. DRIVER / PROFILER.
167            vts_file_path: path of the input vts file.
168            source_file_name: name of the generated source file.
169            file_type: type of file e.g. HEADER / SOURCE / BOTH.
170        """
171        vtsc_cmd = [
172            self._vtsc_path, "-m" + mode, vts_file_path,
173            os.path.join(self._output_dir, mode),
174            os.path.join(self._output_dir, mode, source_file_name)
175        ]
176        return_code = cmd_utils.RunCommand(vtsc_cmd)
177        if (return_code != 0):
178            self.Error("Fail to execute command: %s" % vtsc_cmd)
179
180        if (file_type == "HEADER" or file_type == "BOTH"):
181            header_file_name = vts_file_path + ".h"
182            canonical_header_file = os.path.join(self._canonical_dir, mode,
183                                                 header_file_name)
184            output_header_file = os.path.join(self._output_dir, mode,
185                                              header_file_name)
186            self.CompareOutputFile(output_header_file, canonical_header_file)
187        if (file_type == "SOURCE" or file_type == "BOTH"):
188            canonical_source_file = os.path.join(self._canonical_dir, mode,
189                                                 source_file_name)
190            output_source_file = os.path.join(self._output_dir, mode,
191                                              source_file_name)
192            self.CompareOutputFile(output_source_file, canonical_source_file)
193        else:
194            self.Error("No such file_type: %s" % file_type)
195
196    def CompareOutputFile(self, output_file, canonical_file):
197        """Compares a given file and the corresponding one under canonical_dir.
198
199        Args:
200            canonical_file: name of the canonical file.
201            output_file: name of the output file.
202        """
203        if not os.path.isfile(canonical_file):
204            self.Error("Generated unexpected file: %s (for %s)" %
205                       (output_file, canonical_file))
206        else:
207            if not filecmp.cmp(output_file, canonical_file):
208                self.Error(
209                    "output file: %s does not match the canonical_file: "
210                    "%s" % (output_file, canonical_file))
211                self.PrintDiffFiles(output_file, canonical_file)
212
213    def PrintDiffFiles(self, output_file, canonical_file):
214        with open(output_file, 'r') as file1:
215            with open(canonical_file, 'r') as file2:
216                diff = difflib.unified_diff(
217                    file1.readlines(),
218                    file2.readlines(),
219                    fromfile=output_file,
220                    tofile=canonical_file)
221        for line in diff:
222            logging.error(line)
223
224    def Error(self, string):
225        """Prints an error message and increments error count."""
226        logging.error(string)
227        self._errors += 1
228
229    def RemoveOutputDir(self):
230        """Remove the output_dir if it exists."""
231        if os.path.exists(self._output_dir):
232            logging.info("rm -rf %s", self._output_dir)
233            shutil.rmtree(self._output_dir)
234        logging.info("temp_dir %s not cleared", self._temp_dir)
235
236
237if __name__ == "__main__":
238    # Default values of the input parameter, could be overridden by command.
239    vtsc_path = "vtsc"
240    canonical_dir = "test/vts/compilation_tools/vtsc/test/golden/"
241    output_dir = "test/vts/compilation_tools/vtsc/test/temp_coutput/"
242    # Parse the arguments and set the provided value for
243    # hidl-gen/vtsc_path/canonical_dar/output_dir.
244    try:
245        opts, _ = getopt.getopt(sys.argv[1:], "h:p:c:o:")
246    except getopt.GetoptError, err:
247        print "Usage: python test_vtsc.py [-h hidl_gen_path] [-p vtsc_path] " \
248              "[-c canonical_dir] [-o output_dir]"
249        sys.exit(1)
250    for opt, val in opts:
251        if opt == "-h":
252            hidl_gen_path = val
253        elif opt == "-p":
254            vtsc_path = val
255        elif opt == "-c":
256            canonical_dir = val
257        elif opt == "-o":
258            output_dir = val
259        else:
260            print "unhandled option %s" % (opt, )
261            sys.exit(1)
262
263    suite = unittest.TestSuite()
264    suite.addTest(
265        VtscTester('testAll', hidl_gen_path, vtsc_path, canonical_dir,
266                   output_dir))
267    result = unittest.TextTestRunner(verbosity=2).run(suite)
268    if not result.wasSuccessful():
269        sys.exit(-1)
270