summaryrefslogtreecommitdiff
path: root/apps/CameraITS/tests/its_base_test.py
blob: daa55c651d3b5d26df25dad14e94633c2f438a40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# Copyright 2020 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 logging
import time

import its_session_utils
import lighting_control_utils
from mobly import asserts
from mobly import base_test
from mobly import utils
from mobly.controllers import android_device


ADAPTIVE_BRIGHTNESS_OFF = '0'
TABLET_CMD_DELAY_SEC = 0.5  # found empirically
TABLET_DIMMER_TIMEOUT_MS = 1800000  # this is max setting possible
CTS_VERIFIER_PKG = 'com.android.cts.verifier'
WAIT_TIME_SEC = 5
SCROLLER_TIMEOUT_MS = 3000
VALID_NUM_DEVICES = (1, 2)
NOT_YET_MANDATED_ALL = 100

# Not yet mandated tests ['test', first_api_level not yet mandatory]
# ie. ['test_test_patterns', 30] is MANDATED for first_api_level > 30
NOT_YET_MANDATED = {
    'scene0': [['test_test_patterns', 30],
               ['test_tonemap_curve', 30]],
    'scene1_1': [['test_ae_precapture_trigger', 28]],
    'scene1_2': [],
    'scene2_a': [['test_jpeg_quality', 30]],
    'scene2_b': [['test_auto_per_frame_control', NOT_YET_MANDATED_ALL]],
    'scene2_c': [],
    'scene2_d': [['test_num_faces', 30]],
    'scene2_e': [['test_num_faces', 30], ['test_continuous_picture', 30]],
    'scene3': [],
    'scene4': [],
    'scene5': [],
    'scene6': [['test_zoom', 30]],
    'sensor_fusion': [],
}

logging.getLogger('matplotlib.font_manager').disabled = True


class ItsBaseTest(base_test.BaseTestClass):
  """Base test for CameraITS tests.

  Tests inherit from this class execute in the Camera ITS automation systems.
  These systems consist of either:
    1. a device under test (dut) and an external rotation controller
    2. a device under test (dut) and one screen device(tablet)
    3. a device under test (dut) and manual charts

  Attributes:
    dut: android_device.AndroidDevice, the device under test.
    tablet: android_device.AndroidDevice, the tablet device used to display
        scenes.
  """

  def setup_class(self):
    devices = self.register_controller(android_device, min_number=1)
    self.dut = devices[0]
    self.camera = str(self.user_params['camera'])
    logging.debug('Camera_id: %s', self.camera)
    if self.user_params.get('chart_distance'):
      self.chart_distance = float(self.user_params['chart_distance'])
      logging.debug('Chart distance: %s cm', self.chart_distance)
    if (self.user_params.get('lighting_cntl') and
        self.user_params.get('lighting_ch')):
      self.lighting_cntl = self.user_params['lighting_cntl']
      self.lighting_ch = str(self.user_params['lighting_ch'])
    else:
      self.lighting_cntl = 'None'
      self.lighting_ch = '1'
    if self.user_params.get('debug_mode'):
      self.debug_mode = True if self.user_params[
          'debug_mode'] == 'True' else False
    if self.user_params.get('scene'):
      self.scene = self.user_params['scene']
    camera_id_combo = self.parse_hidden_camera_id()
    self.camera_id = camera_id_combo[0]
    if len(camera_id_combo) == 2:
      self.hidden_physical_id = camera_id_combo[1]
    else:
      self.hidden_physical_id = None

    num_devices = len(devices)
    if num_devices == 2:  # scenes [0,1,2,3,4,5,6]
      try:
        self.tablet = devices[1]
        self.tablet_screen_brightness = self.user_params['brightness']
      except KeyError:
        logging.debug('Not all tablet arguments set.')
    else:  # sensor_fusion or manual run
      try:
        self.fps = int(self.user_params['fps'])
        img_size = self.user_params['img_size'].split(',')
        self.img_w = int(img_size[0])
        self.img_h = int(img_size[1])
        self.test_length = float(self.user_params['test_length'])
        self.rotator_cntl = self.user_params['rotator_cntl']
        self.rotator_ch = str(self.user_params['rotator_ch'])
      except KeyError:
        self.tablet = None
        logging.debug('Not all arguments set. Manual run.')

    self._setup_devices(num_devices)

    arduino_serial_port = lighting_control_utils.lighting_control(
        self.lighting_cntl, self.lighting_ch)
    if arduino_serial_port:
      lighting_control_utils.set_light_brightness(
          self.lighting_ch, 255, arduino_serial_port)
      logging.debug('Light is turned ON.')

  def _setup_devices(self, num):
    """Sets up each device in parallel if more than one device."""
    if num not in VALID_NUM_DEVICES:
      raise AssertionError(
          f'Incorrect number of devices! Must be in {str(VALID_NUM_DEVICES)}')
    if num == 1:
      self.setup_dut(self.dut)
    else:
      logic = lambda d: self.setup_dut(d) if d else self.setup_tablet()
      utils.concurrent_exec(
          logic, [(self.dut,), (None,)],
          max_workers=2,
          raise_on_exception=True)

  def setup_dut(self, device):
    self.dut.adb.shell(
        'am start -n com.android.cts.verifier/.CtsVerifierActivity')
    logging.debug('Setting up device: %s', str(device))
    # Wait for the app screen to appear.
    time.sleep(WAIT_TIME_SEC)

  def setup_tablet(self):
    # KEYCODE_POWER to reset dimmer timer. KEYCODE_WAKEUP no effect if ON.
    self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_POWER'])
    time.sleep(TABLET_CMD_DELAY_SEC)
    self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_WAKEUP'])
    time.sleep(TABLET_CMD_DELAY_SEC)
    # Dismiss keyguard
    self.tablet.adb.shell(['wm', 'dismiss-keyguard'])
    time.sleep(TABLET_CMD_DELAY_SEC)
    # Turn off the adaptive brightness on tablet.
    self.tablet.adb.shell(
        ['settings', 'put', 'system', 'screen_brightness_mode',
         ADAPTIVE_BRIGHTNESS_OFF])
    # Set the screen brightness
    self.tablet.adb.shell(
        ['settings', 'put', 'system', 'screen_brightness',
         str(self.tablet_screen_brightness)])
    logging.debug('Tablet brightness set to: %s',
                  format(self.tablet_screen_brightness))
    self.tablet.adb.shell('settings put system screen_off_timeout {}'.format(
        TABLET_DIMMER_TIMEOUT_MS))
    self.set_tablet_landscape_orientation()
    self.tablet.adb.shell('am force-stop com.google.android.apps.docs')
    self.tablet.adb.shell('am force-stop com.google.android.apps.photos')
    self.tablet.adb.shell('am force-stop com.android.gallery3d')
    self.tablet.adb.shell('am force-stop com.sec.android.gallery3d')
    self.tablet.adb.shell('am force-stop com.miui.gallery')

  def set_tablet_landscape_orientation(self):
    """Sets the screen orientation to landscape.
    """
    # Get the landscape orientation value.
    # This value is different for Pixel C/Huawei/Samsung tablets.
    output = self.tablet.adb.shell('dumpsys window | grep mLandscapeRotation')
    logging.debug('dumpsys window output: %s', output.decode('utf-8').strip())
    output_list = str(output.decode('utf-8')).strip().split(' ')
    for val in output_list:
      if 'LandscapeRotation' in val:
        landscape_val = str(val.split('=')[-1])
        # For some tablets the values are in constant forms such as ROTATION_90
        if 'ROTATION_90' in landscape_val:
          landscape_val = '1'
        elif 'ROTATION_0' in landscape_val:
          landscape_val = '0'
        logging.debug('Changing the orientation to landscape mode.')
        self.tablet.adb.shell(['settings', 'put', 'system', 'user_rotation',
                               landscape_val])
        break
    logging.debug('Reported tablet orientation is: %d',
                  int(self.tablet.adb.shell(
                      'settings get system user_rotation')))

  def parse_hidden_camera_id(self):
    """Parse the string of camera ID into an array.

    Returns:
      Array with camera id and hidden_physical camera id.
    """
    camera_id_combo = self.camera.split(its_session_utils.SUB_CAMERA_SEPARATOR)
    return camera_id_combo

  def determine_not_yet_mandated_tests(self, device_id, scene):
    """Determine not_yet_mandated tests from NOT_YET_MANDATED list & phone info.

    Args:
     device_id: string of device id number.
     scene: scene to which tests belong to.

    Returns:
       dict of not yet mandated tests
    """
    # Initialize not_yet_mandated.
    not_yet_mandated = {}
    not_yet_mandated[scene] = []

    # Determine first API level for device.
    first_api_level = its_session_utils.get_first_api_level(device_id)

    # Determine which test are not yet mandated for first api level.
    tests = NOT_YET_MANDATED[scene]
    for [test, first_api_level_not_mandated] in tests:
      logging.debug('First API level %s NOT MANDATED: %d',
                    test, first_api_level_not_mandated)
      if first_api_level <= first_api_level_not_mandated:
        not_yet_mandated[scene].append(test)
    return not_yet_mandated

  def on_pass(self, record):
    logging.debug('%s on PASS.', record.test_name)

  def on_fail(self, record):
    logging.debug('%s on FAIL.', record.test_name)
    if self.user_params.get('scene'):
      not_yet_mandated_tests = self.determine_not_yet_mandated_tests(
          self.dut.serial, self.scene)
      if self.current_test_info.name in not_yet_mandated_tests[self.scene]:
        logging.debug('%s is not yet mandated.', self.current_test_info.name)
        asserts.fail('Not yet mandated test', extras='Not yet mandated test')