| #!/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 filecmp |
| import logging |
| import os |
| import shutil |
| import subprocess |
| import sys |
| import unittest |
| |
| class Tester(unittest.TestCase): |
| """Integration test runner for hidl-gen. |
| Runs hidl-gen on a bunch of packages/files and compares the out results with |
| canonical ones. Exit code is 0 iff all tests pass. |
| |
| Usage: |
| python test_output.py path_to_hidl_gen canonical_dir output_dir (interface_root) |
| If interface_root is not provided, the default package "andorid.hardwre" and |
| the path root defined in $TOP will be used. |
| |
| Attributes: |
| _hidl_gen_path: the path to run hidl-gen. |
| _canonical_dir: root directory contains canonical files for comparison. |
| _output_dir: root directory that stores all output files. |
| _interface_root: package:path root for the interface. |
| e.g., android.hardware:hardware/interfaces |
| _errors: number of errors generates during the test. |
| """ |
| def __init__(self, testName, hidl_gen_path, canonical_dir, output_dir, interface_root=''): |
| super(Tester, self).__init__(testName) |
| self._hidl_gen_path = hidl_gen_path |
| self._canonical_dir = canonical_dir |
| self._output_dir = output_dir |
| self._interface_root = interface_root |
| self._errors = 0 |
| |
| 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.TestVtsOutput() |
| self.TestCppOutput() |
| self.TestJavaOutput() |
| |
| def TestVtsOutput(self): |
| """Runs hidl-gen to generate vts file and verify the output.""" |
| self.RunTest("vts", "android.hardware.nfc@1.0", |
| "android/hardware/nfc/1.0") |
| self.assertEqual(self._errors, 0) |
| |
| def TestCppOutput(self): |
| """Runs hidl-gen to generate c++ file and verify the output.""" |
| # self.RunTest("c++", "android.hardware.nfc@1.0", |
| # "android/hardware/nfc/1.0") |
| self.RunTest("c++", "android.hardware.tests.expression@1.0", |
| "android/hardware/tests/expression/1.0") |
| self.assertEqual(self._errors, 0) |
| |
| def TestJavaOutput(self): |
| self.RunTest("java", "android.hardware.tests.expression@1.0", |
| "android/hardware/tests/expression/1.0") |
| self.assertEqual(self._errors, 0) |
| |
| def RunTest(self, language, fqname, package_path): |
| """Run hidl-gen with given language and fqname, and compare the results. |
| |
| Args: |
| language: the target language for the output code. e.g. c++, vts. |
| fqname: fully qualified name of the input files. |
| For single file input, follow the format: |
| package@version::fileName. |
| For directory input, follow the format: package@version. |
| example: android.hardware.nfc@1.0::INfc.hal |
| package_path: path for the package in fqname. |
| example: android/hardware/nfc/1.0 |
| """ |
| if self._interface_root != '': |
| hidl_gen_cmd = [self._hidl_gen_path, "-o", self._output_dir, "-L", |
| language, "-r", self._interface_root, fqname] |
| else: |
| hidl_gen_cmd = [self._hidl_gen_path, "-o", self._output_dir, "-L", |
| language, fqname] |
| self.RunCommand(hidl_gen_cmd) |
| |
| output_dir = os.path.join(self._output_dir, package_path) |
| canonical_dir = os.path.join(self._canonical_dir, package_path) |
| self.CompareOutput(output_dir, canonical_dir) |
| |
| def RunCommand(self, command): |
| """Runs a unix command and stashes the result.""" |
| proc = subprocess.Popen(command, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| (stdout, stderr) = proc.communicate() |
| if proc.returncode != 0: |
| self.Error(("Fail to execute command: %s" % command + |
| (" (stdout: %s\n stderr: %s\n)" % (stdout, stderr)))) |
| |
| def CompareOutput(self, output_dir, canonical_dir): |
| """Compare the files under output_dir with those under canonical_dir. |
| |
| Args: |
| output_dir: directory that stores output files. |
| canonical_dir: directory contains canonical files for comparison. |
| """ |
| for file_name in os.listdir(output_dir): |
| logging.info("comparing file: %s" % file) |
| self.CompareFile(file_name, output_dir, canonical_dir) |
| |
| def CompareFile(self, file_name, output_dir, canonical_dir): |
| """Compares a given file and the corresponding one under canonical_dir. |
| |
| Args: |
| file_name: name of file for comparison. |
| output_dir: directory that stores output files. |
| canonical_dir: directory contains canonical files for comparison. |
| """ |
| canonical_file = os.path.join(canonical_dir, file_name) |
| output_file = os.path.join(output_dir, file_name) |
| if not os.path.isfile(canonical_file): |
| self.Error("Generated unexpected file: %s" % output_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)) |
| |
| 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 __name__ == "__main__": |
| if len(sys.argv) != 5 and len(sys.argv) !=4: |
| print "Usage: python test_output.py hidl_gen_path canonical_dir " \ |
| "output_dir (interface_root)" |
| sys.exit(1) |
| suite = unittest.TestSuite() |
| if len(sys.argv) == 5: |
| suite.addTest(Tester('testAll', sys.argv[1],sys.argv[2], sys.argv[3], sys.argv[4])) |
| else: |
| suite.addTest(Tester('testAll', sys.argv[1],sys.argv[2], sys.argv[3])) |
| result = unittest.TextTestRunner(verbosity=2).run(suite) |
| if not result.wasSuccessful(): |
| sys.exit(-1) |