| #!/usr/bin/env python |
| # |
| # Copyright 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 difflib |
| import filecmp |
| import getopt |
| import logging |
| import os |
| import shutil |
| import subprocess |
| import sys |
| import unittest |
| |
| from vts.utils.python.common import cmd_utils |
| |
| |
| class VtscTester(unittest.TestCase): |
| """Integration test runner for vtsc in generating the driver/profiler code. |
| |
| Runs vtsc with specified mode on a bunch of files and compares the output |
| results with canonical ones. Exit code is 0 iff all tests pass. |
| Note: need to run the script from the source root to preserve the correct |
| path. |
| |
| Usage: |
| python test_vtsc.py path_to_vtsc canonical_dir output_dir |
| |
| example: |
| python test/vts/compilation_tools/vtsc/test/test_vtsc.py vtsc |
| test/vts/compilation_tools/vtsc/test/golden/ temp_output |
| |
| Attributes: |
| _hidl_gen_path: the path to run hidl-gen |
| _vtsc_path: the path to run vtsc. |
| _canonical_dir: root directory contains canonical files for comparison. |
| _output_dir: root directory that stores all output files. |
| _errors: number of errors generates during the test. |
| _temp_dir: temp dir to store the .vts file generated by hidl-gen. |
| """ |
| |
| def __init__(self, testName, hidl_gen_path, vtsc_path, canonical_dir, |
| output_dir): |
| super(VtscTester, self).__init__(testName) |
| self._hidl_gen_path = hidl_gen_path |
| self._vtsc_path = vtsc_path |
| self._canonical_dir = canonical_dir |
| self._output_dir = output_dir |
| self._errors = 0 |
| self._temp_dir = "test/vts/specification/hal" |
| |
| def setUp(self): |
| """Removes output dir to prevent interference from previous runs.""" |
| self.RemoveOutputDir() |
| |
| def tearDown(self): |
| """If successful, removes the output dir for clean-up.""" |
| if self._errors == 0: |
| self.RemoveOutputDir() |
| |
| def testAll(self): |
| """Run all tests. """ |
| self.TestDriver() |
| self.TestProfiler() |
| self.TestFuzzer() |
| self.assertEqual(self._errors, 0) |
| |
| def TestDriver(self): |
| """Run tests for DRIVER mode. """ |
| logging.info("Running TestDriver test case.") |
| # Tests for Hidl Hals. |
| self.GenerateVtsFile("android.hardware.nfc@1.0") |
| for component_name in ["Nfc", "types", "NfcClientCallback"]: |
| self.RunTest("DRIVER", |
| os.path.join(self._temp_dir, component_name + ".vts"), |
| "%s.driver.cpp" % component_name) |
| # Tests for conventional Hals. |
| for component_name in ["CameraHalV2", "BluetoothHalV1", |
| "BluetoothHalV1bt_interface_t", "WifiHalV1"]: |
| self.RunTest("DRIVER", |
| "test/vts/specification/hal_conventional/%s.vts" % |
| component_name, "%s.driver.cpp" % component_name) |
| # Tests for shared libraries. |
| for component_name in ["libcV1"]: |
| self.RunTest("DRIVER", "test/vts/specification/lib_bionic/%s.vts" % |
| component_name, "%s.driver.cpp" % component_name) |
| |
| def TestProfiler(self): |
| """Run tests for PROFILER mode. """ |
| logging.info("Running TestProfiler test case.") |
| self.GenerateVtsFile("android.hardware.nfc@1.0") |
| for component_name in ["Nfc", "types", "NfcClientCallback"]: |
| self.RunTest("PROFILER", |
| os.path.join(self._temp_dir, component_name + ".vts"), |
| "%s.profiler.cpp" % component_name) |
| |
| def TestFuzzer(self): |
| """Run tests for Fuzzer mode. """ |
| logging.info("Running TestProfiler test case.") |
| self.GenerateVtsFile("android.hardware.renderscript@1.0") |
| for component_name in ["Context", "Device", "types"]: |
| self.RunTest("FUZZER", |
| os.path.join(self._temp_dir, component_name + ".vts"), |
| "%s.fuzzer.cpp" % component_name, "SOURCE") |
| |
| def RunFuzzerTest(self, mode, vts_file_path, source_file_name): |
| vtsc_cmd = [ |
| self._vtsc_path, "-m" + mode, vts_file_path, |
| os.path.join(self._output_dir, mode), |
| os.path.join(self._output_dir, mode, "") |
| ] |
| return_code = cmd_utils.RunCommand(vtsc_cmd) |
| |
| canonical_source_file = os.path.join(self._canonical_dir, mode, |
| source_file_name) |
| output_source_file = os.path.join(self._output_dir, mode, |
| source_file_name) |
| self.CompareOutputFile(output_source_file, canonical_source_file) |
| |
| def GenerateVtsFile(self, hal_package_name): |
| """Run hidl-gen to generate the .vts files for the give hal package. |
| |
| Args: |
| hal_package_name: name of hal package e.g. android.hardware.nfc@1.0 |
| """ |
| hidl_gen_cmd = [ |
| self._hidl_gen_path, "-o" + self._temp_dir, "-Lvts", |
| "-randroid.hardware:hardware/interfaces", |
| "-randroid.hidl:system/libhidl/transport", hal_package_name |
| ] |
| return_code = cmd_utils.RunCommand(hidl_gen_cmd) |
| if (return_code != 0): |
| self.Error("Fail to execute command: %s" % hidl_gen_cmd) |
| [hal_name, hal_version] = hal_package_name.split("@") |
| output_dir = os.path.join(self._temp_dir, |
| hal_name.replace(".", "/"), hal_version) |
| for file in os.listdir(output_dir): |
| if file.endswith(".vts"): |
| os.rename( |
| os.path.join(output_dir, file), |
| os.path.join(self._temp_dir, file)) |
| |
| def RunTest(self, mode, vts_file_path, source_file_name, file_type="BOTH"): |
| """Run vtsc with given mode for the give vts file and compare the |
| output results. |
| |
| Args: |
| mode: the vtsc mode for generated code. e.g. DRIVER / PROFILER. |
| vts_file_path: path of the input vts file. |
| source_file_name: name of the generated source file. |
| file_type: type of file e.g. HEADER / SOURCE / BOTH. |
| """ |
| vtsc_cmd = [ |
| self._vtsc_path, "-m" + mode, vts_file_path, |
| os.path.join(self._output_dir, mode), |
| os.path.join(self._output_dir, mode, source_file_name) |
| ] |
| return_code = cmd_utils.RunCommand(vtsc_cmd) |
| if (return_code != 0): |
| self.Error("Fail to execute command: %s" % vtsc_cmd) |
| |
| if (file_type == "HEADER" or file_type == "BOTH"): |
| header_file_name = vts_file_path + ".h" |
| canonical_header_file = os.path.join(self._canonical_dir, mode, |
| header_file_name) |
| output_header_file = os.path.join(self._output_dir, mode, |
| header_file_name) |
| self.CompareOutputFile(output_header_file, canonical_header_file) |
| if (file_type == "SOURCE" or file_type == "BOTH"): |
| canonical_source_file = os.path.join(self._canonical_dir, mode, |
| source_file_name) |
| output_source_file = os.path.join(self._output_dir, mode, |
| source_file_name) |
| self.CompareOutputFile(output_source_file, canonical_source_file) |
| else: |
| self.Error("No such file_type: %s" % file_type) |
| |
| def CompareOutputFile(self, output_file, canonical_file): |
| """Compares a given file and the corresponding one under canonical_dir. |
| |
| Args: |
| canonical_file: name of the canonical file. |
| output_file: name of the output file. |
| """ |
| if not os.path.isfile(canonical_file): |
| self.Error("Generated unexpected file: %s (for %s)" % |
| (output_file, canonical_file)) |
| else: |
| if not filecmp.cmp(output_file, canonical_file): |
| self.Error( |
| "output file: %s does not match the canonical_file: " |
| "%s" % (output_file, canonical_file)) |
| self.PrintDiffFiles(output_file, canonical_file) |
| |
| def PrintDiffFiles(self, output_file, canonical_file): |
| with open(output_file, 'r') as file1: |
| with open(canonical_file, 'r') as file2: |
| diff = difflib.unified_diff( |
| file1.readlines(), |
| file2.readlines(), |
| fromfile=output_file, |
| tofile=canonical_file) |
| for line in diff: |
| logging.error(line) |
| |
| def Error(self, string): |
| """Prints an error message and increments error count.""" |
| logging.error(string) |
| self._errors += 1 |
| |
| def RemoveOutputDir(self): |
| """Remove the output_dir if it exists.""" |
| if os.path.exists(self._output_dir): |
| shutil.rmtree(self._output_dir) |
| if os.path.exists(self._temp_dir): |
| shutil.rmtree(self._temp_dir) |
| |
| |
| if __name__ == "__main__": |
| # Default values of the input parameter, could be overridden by command. |
| vtsc_path = "vtsc" |
| canonical_dir = "test/vts/compilation_tools/vtsc/test/golden/" |
| output_dir = "test/vts/compilation_tools/vtsc/test/temp_coutput/" |
| # Parse the arguments and set the provided value for |
| # hidl-gen/vtsc_path/canonical_dar/output_dir. |
| try: |
| opts, _ = getopt.getopt(sys.argv[1:], "h:p:c:o:") |
| except getopt.GetoptError, err: |
| print "Usage: python test_vtsc.py [-h hidl_gen_path] [-p vtsc_path] " \ |
| "[-c canonical_dir] [-o output_dir]" |
| sys.exit(1) |
| for opt, val in opts: |
| if opt == "-h": |
| hidl_gen_path = val |
| elif opt == "-p": |
| vtsc_path = val |
| elif opt == "-c": |
| canonical_dir = val |
| elif opt == "-o": |
| output_dir = val |
| else: |
| print "unhandled option %s" % (opt, ) |
| sys.exit(1) |
| |
| suite = unittest.TestSuite() |
| suite.addTest( |
| VtscTester('testAll', hidl_gen_path, vtsc_path, canonical_dir, |
| output_dir)) |
| result = unittest.TextTestRunner(verbosity=2).run(suite) |
| if not result.wasSuccessful(): |
| sys.exit(-1) |