Zhuoyao Zhang | 82b83bb | 2016-08-12 15:15:19 -0700 | [diff] [blame] | 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 | |
| 17 | import filecmp |
| 18 | import logging |
| 19 | import os |
| 20 | import shutil |
| 21 | import subprocess |
| 22 | import sys |
| 23 | import unittest |
| 24 | |
| 25 | class Tester(unittest.TestCase): |
| 26 | """Integration test runner for hidl-gen. |
| 27 | Runs hidl-gen on a bunch of packages/files and compares the out results with |
| 28 | canonical ones. Exit code is 0 iff all tests pass. |
| 29 | |
| 30 | Usage: |
| 31 | python test_output.py path_to_hidl_gen canonical_dir output_dir (interface_root) |
| 32 | If interface_root is not provided, the default package "andorid.hardwre" and |
| 33 | the path root defined in $TOP will be used. |
| 34 | |
| 35 | Attributes: |
| 36 | _hidl_gen_path: the path to run hidl-gen. |
| 37 | _canonical_dir: root directory contains canonical files for comparison. |
| 38 | _output_dir: root directory that stores all output files. |
| 39 | _interface_root: package:path root for the interface. |
| 40 | e.g., android.hardware:hardware/interfaces |
| 41 | _errors: number of errors generates during the test. |
| 42 | """ |
| 43 | def __init__(self, testName, hidl_gen_path, canonical_dir, output_dir, interface_root=''): |
| 44 | super(Tester, self).__init__(testName) |
| 45 | self._hidl_gen_path = hidl_gen_path |
| 46 | self._canonical_dir = canonical_dir |
| 47 | self._output_dir = output_dir |
| 48 | self._interface_root = interface_root |
| 49 | self._errors = 0 |
| 50 | |
| 51 | def setUp(self): |
| 52 | """Removes output dir to prevent interference from previous runs.""" |
| 53 | self.RemoveOutputDir() |
| 54 | |
| 55 | def tearDown(self): |
| 56 | """If successful, removes the output dir for clean-up.""" |
| 57 | if self._errors == 0: |
| 58 | self.RemoveOutputDir() |
| 59 | |
| 60 | def testAll(self): |
| 61 | """Run all tests. """ |
| 62 | self.TestVtsOutput() |
Yifan Hong | 5788697 | 2016-08-17 10:42:15 -0700 | [diff] [blame] | 63 | self.TestCppOutput() |
Yifan Hong | 19ca75a | 2016-08-31 10:20:03 -0700 | [diff] [blame] | 64 | self.TestJavaOutput() |
Zhuoyao Zhang | 82b83bb | 2016-08-12 15:15:19 -0700 | [diff] [blame] | 65 | |
| 66 | def TestVtsOutput(self): |
| 67 | """Runs hidl-gen to generate vts file and verify the output.""" |
| 68 | self.RunTest("vts", "android.hardware.nfc@1.0", |
| 69 | "android/hardware/nfc/1.0") |
| 70 | self.assertEqual(self._errors, 0) |
| 71 | |
| 72 | def TestCppOutput(self): |
| 73 | """Runs hidl-gen to generate c++ file and verify the output.""" |
Yifan Hong | 5788697 | 2016-08-17 10:42:15 -0700 | [diff] [blame] | 74 | # self.RunTest("c++", "android.hardware.nfc@1.0", |
| 75 | # "android/hardware/nfc/1.0") |
| 76 | self.RunTest("c++", "android.hardware.tests.expression@1.0", |
| 77 | "android/hardware/tests/expression/1.0") |
Zhuoyao Zhang | 82b83bb | 2016-08-12 15:15:19 -0700 | [diff] [blame] | 78 | self.assertEqual(self._errors, 0) |
| 79 | |
Yifan Hong | 19ca75a | 2016-08-31 10:20:03 -0700 | [diff] [blame] | 80 | def TestJavaOutput(self): |
| 81 | self.RunTest("java", "android.hardware.tests.expression@1.0", |
| 82 | "android/hardware/tests/expression/1.0") |
| 83 | self.assertEqual(self._errors, 0) |
| 84 | |
Zhuoyao Zhang | 82b83bb | 2016-08-12 15:15:19 -0700 | [diff] [blame] | 85 | def RunTest(self, language, fqname, package_path): |
| 86 | """Run hidl-gen with given language and fqname, and compare the results. |
| 87 | |
| 88 | Args: |
| 89 | language: the target language for the output code. e.g. c++, vts. |
| 90 | fqname: fully qualified name of the input files. |
| 91 | For single file input, follow the format: |
| 92 | package@version::fileName. |
| 93 | For directory input, follow the format: package@version. |
| 94 | example: android.hardware.nfc@1.0::INfc.hal |
| 95 | package_path: path for the package in fqname. |
| 96 | example: android/hardware/nfc/1.0 |
| 97 | """ |
| 98 | if self._interface_root != '': |
| 99 | hidl_gen_cmd = [self._hidl_gen_path, "-o", self._output_dir, "-L", |
| 100 | language, "-r", self._interface_root, fqname] |
| 101 | else: |
| 102 | hidl_gen_cmd = [self._hidl_gen_path, "-o", self._output_dir, "-L", |
| 103 | language, fqname] |
| 104 | self.RunCommand(hidl_gen_cmd) |
| 105 | |
| 106 | output_dir = os.path.join(self._output_dir, package_path) |
| 107 | canonical_dir = os.path.join(self._canonical_dir, package_path) |
| 108 | self.CompareOutput(output_dir, canonical_dir) |
| 109 | |
| 110 | def RunCommand(self, command): |
| 111 | """Runs a unix command and stashes the result.""" |
| 112 | proc = subprocess.Popen(command, |
| 113 | stdout=subprocess.PIPE, |
| 114 | stderr=subprocess.PIPE) |
| 115 | (stdout, stderr) = proc.communicate() |
| 116 | if proc.returncode != 0: |
| 117 | self.Error(("Fail to execute command: %s" % command + |
| 118 | (" (stdout: %s\n stderr: %s\n)" % (stdout, stderr)))) |
| 119 | |
| 120 | def CompareOutput(self, output_dir, canonical_dir): |
| 121 | """Compare the files under output_dir with those under canonical_dir. |
| 122 | |
| 123 | Args: |
| 124 | output_dir: directory that stores output files. |
| 125 | canonical_dir: directory contains canonical files for comparison. |
| 126 | """ |
| 127 | for file_name in os.listdir(output_dir): |
| 128 | logging.info("comparing file: %s" % file) |
| 129 | self.CompareFile(file_name, output_dir, canonical_dir) |
| 130 | |
| 131 | def CompareFile(self, file_name, output_dir, canonical_dir): |
| 132 | """Compares a given file and the corresponding one under canonical_dir. |
| 133 | |
| 134 | Args: |
| 135 | file_name: name of file for comparison. |
| 136 | output_dir: directory that stores output files. |
| 137 | canonical_dir: directory contains canonical files for comparison. |
| 138 | """ |
| 139 | canonical_file = os.path.join(canonical_dir, file_name) |
| 140 | output_file = os.path.join(output_dir, file_name) |
| 141 | if not os.path.isfile(canonical_file): |
| 142 | self.Error("Generated unexpected file: %s" % output_file) |
| 143 | else: |
| 144 | if not filecmp.cmp(output_file, canonical_file): |
| 145 | self.Error("output file: %s does not match the canonical_file: " |
| 146 | "%s" % (output_file, canonical_file)) |
| 147 | |
| 148 | def Error(self, string): |
| 149 | """Prints an error message and increments error count.""" |
| 150 | logging.error(string) |
| 151 | self._errors += 1 |
| 152 | |
| 153 | def RemoveOutputDir(self): |
| 154 | """Remove the output_dir if it exists.""" |
| 155 | if os.path.exists(self._output_dir): |
| 156 | shutil.rmtree(self._output_dir) |
| 157 | |
| 158 | if __name__ == "__main__": |
| 159 | if len(sys.argv) != 5 and len(sys.argv) !=4: |
| 160 | print "Usage: python test_output.py hidl_gen_path canonical_dir " \ |
| 161 | "output_dir (interface_root)" |
| 162 | sys.exit(1) |
| 163 | suite = unittest.TestSuite() |
| 164 | if len(sys.argv) == 5: |
| 165 | suite.addTest(Tester('testAll', sys.argv[1],sys.argv[2], sys.argv[3], sys.argv[4])) |
| 166 | else: |
| 167 | suite.addTest(Tester('testAll', sys.argv[1],sys.argv[2], sys.argv[3])) |
| 168 | result = unittest.TextTestRunner(verbosity=2).run(suite) |
| 169 | if not result.wasSuccessful(): |
| 170 | sys.exit(-1) |