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 inspect 19import logging 20import os 21import re 22import subprocess 23import sys 24 25from vts.runners.host import asserts 26from vts.runners.host import base_test 27from vts.runners.host import const 28from vts.runners.host import test_runner 29 30 31class CameraITSTest(base_test.BaseTestClass): 32 """Running CameraITS tests in VTS. 33 34 Attributes: 35 _TABLET_TYPES: a list of strings, known tablet product types. 36 dut1: AndroidDevice instance, for 1st device whose front-facing camera 37 is tested. 38 dut2: AndroidDevice instance, for 2nd device whose back-facing camera 39 is tested. 40 display_device: AndroidDevice instance, for a tablet device used as 41 a display screen. 42 """ 43 _TABLET_TYPES = ["dragon"] 44 45 # TODO: use config file to pass in: 46 # - serial for dut and screen 47 # - camera id 48 # so that we can run other test scenes with ITS-in-a-box 49 def setUpClass(self): 50 """Setup ITS running python environment and check for required python modules 51 """ 52 # When VTS lab infra is used, the provided device order is: 53 # DUT (front-facing camera), 54 # DUT (back-facing camera), 55 # Tablet (display). 56 # This order shall be preserved when a custom test serving infra is 57 # used. 58 self.dut1 = self.android_devices[0] 59 self.dut2 = self.android_devices[1] 60 self.display_device = self.android_devices[2] 61 # In a local run (e.g., using a VTS test harness), the device order 62 # can be arbitrary. So tablet is detected and chosen as a display 63 # device. Similarly, we need a mechanism to detect DUT which uses 64 # front-facing camera (that can be done here or inside another layer). 65 66 if self.dut1.product_type in self._TABLET_TYPES: 67 temp_device = self.dut1 68 self.dut1 = self.dut2 69 self.dut2 = self.dut3 70 self.display_device = temp_device 71 elif self.dut2.product_type in self._TABLET_TYPES: 72 temp_device = self.dut2 73 self.dut2 = self.display_device 74 self.display_device = temp_device 75 76 self.device_arg = "device=%s" % self.dut1.serial 77 self.display_device_arg = "chart=%s" % self.display_device.serial 78 self.its_path = str( 79 os.path.abspath(os.path.join(self.data_file_path, 'CameraITS'))) 80 logging.info("cwd: %s", os.getcwd()) 81 logging.info("its_path: %s", self.its_path) 82 self.out_path = logging.log_path 83 os.environ["CAMERA_ITS_TOP"] = self.its_path 84 # Below module check code assumes tradefed is running python 2.7 85 # If tradefed switches to python3, then we will be checking modules in python3 while ITS 86 # scripts should be ran in 2.7. 87 if sys.version_info[:2] != (2, 7): 88 logging.warning("Python version %s found; " 89 "CameraITSTest only tested with Python 2.7." % 90 (str(sys.version_info[:3]))) 91 logging.info("===============================") 92 logging.info("Python path is: %s" % (sys.executable)) 93 logging.info("PYTHONPATH env is: " + os.environ["PYTHONPATH"]) 94 import PIL 95 if hasattr(PIL, "__version__"): 96 logging.info("PIL version is %s", PIL.__version__) 97 logging.info("PIL path is " + inspect.getfile(PIL)) 98 from PIL import Image 99 logging.info("Image path is " + inspect.getfile(Image)) 100 import numpy 101 logging.info("numpy version is " + numpy.__version__) 102 logging.info("numpy path is " + inspect.getfile(numpy)) 103 import scipy 104 logging.info("scipy version is " + scipy.__version__) 105 logging.info("scipy path is " + inspect.getfile(scipy)) 106 import matplotlib 107 logging.info("matplotlib version is " + matplotlib.__version__) 108 logging.info("matplotlib path is " + inspect.getfile(matplotlib)) 109 from matplotlib import pylab 110 logging.info("pylab path is " + inspect.getfile(pylab)) 111 logging.info("===============================") 112 modules = [ 113 "numpy", "PIL", "Image", "matplotlib", "pylab", "scipy.stats", 114 "scipy.spatial" 115 ] 116 for m in modules: 117 try: 118 if m == "Image": 119 # Image modules are now imported from PIL 120 exec ("from PIL import Image") 121 elif m == "pylab": 122 exec ("from matplotlib import pylab") 123 else: 124 exec ("import " + m) 125 except ImportError as e: 126 asserts.fail("Cannot import python module %s: %s" % (m, 127 str(e))) 128 129 # Add ITS module path to path 130 its_path = os.path.join(self.its_path, "pymodules") 131 env_python_path = os.environ["PYTHONPATH"] 132 self.pythonpath = env_python_path if its_path in env_python_path else \ 133 "%s:%s" % (its_path, env_python_path) 134 os.environ["PYTHONPATH"] = self.pythonpath 135 logging.info("new PYTHONPATH: %s", self.pythonpath) 136 137 def RunTestcase(self, testpath): 138 """Runs the given testcase and asserts the result. 139 140 Args: 141 testpath: string, format tests/[scenename]/[testname].py 142 """ 143 testname = re.split("/|\.", testpath)[-2] 144 cmd = [ 145 'python', os.path.join(self.its_path, testpath), self.device_arg, 146 self.display_device_arg 147 ] 148 outdir = self.out_path 149 outpath = os.path.join(outdir, testname + "_stdout.txt") 150 errpath = os.path.join(outdir, testname + "_stderr.txt") 151 logging.info("cwd: %s", os.getcwd()) 152 logging.info("cmd: %s", cmd) 153 logging.info("outpath: %s", outpath) 154 logging.info("errpath: %s", errpath) 155 with open(outpath, "w") as fout, open(errpath, "w") as ferr: 156 retcode = subprocess.call( 157 cmd, stderr=ferr, stdout=fout, cwd=outdir) 158 if retcode != 0 and retcode != 101: 159 # Dump all logs to host log if the test failed 160 with open(outpath, "r") as fout, open(errpath, "r") as ferr: 161 logging.info(fout.read()) 162 logging.error(ferr.read()) 163 164 asserts.assertTrue(retcode == 0 or retcode == 101, 165 "ITS %s retcode %d" % (testname, retcode)) 166 167 def FetchTestPaths(self, scene): 168 """Returns a list of test paths for a given test scene. 169 170 Args: 171 scnee: one of ITS test scene name. 172 """ 173 its_path = self.its_path 174 paths = [ 175 os.path.join("tests", scene, s) 176 for s in os.listdir(os.path.join(its_path, "tests", scene)) 177 if s[-3:] == ".py" and s[:4] == "test" 178 ] 179 paths.sort() 180 return paths 181 182 def generateAllTestCases(self): 183 # Unused packages: 184 # * No plan to support in the near future 185 # - "dng_noise_model" 186 # - "inprog" 187 # - "rolling_shutter_skew" 188 # * Not for ITS box (but for diffuser plate) 189 # - "scene5" 190 # * Not for ITS box (but for sensor_fusion rotation rig, high-end only) 191 # - "sensor_fusion" 192 # * Add those scenes to the below list after scene swapping is ported: 193 # "scene1" 194 # "scene2" 195 # "scene3" 196 # "scene4" 197 for test_package_name in ["scene0"]: 198 testpaths = self.FetchTestPaths(test_package_name) 199 self.runGeneratedTests( 200 test_func=self.RunTestcase, 201 settings=testpaths, 202 name_func=lambda path: "%s_%s" % (re.split("/|\.", path)[-3], re.split("/|\.", path)[-2])) 203 204 205if __name__ == "__main__": 206 test_runner.main() 207