1# Copyright 2014 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Verifies different combinations of output formats."""
15
16
17import logging
18import os
19
20from mobly import test_runner
21
22import its_base_test
23import camera_properties_utils
24import capture_request_utils
25import its_session_utils
26import target_exposure_utils
27
28
29_AUTO_REQUEST_MODE = 1
30_NAME = os.path.splitext(os.path.basename(__file__))[0]
31_STOP_AT_FIRST_FAILURE = False  # change to True to have test break @ 1st FAIL
32
33
34class FormatCombosTest(its_base_test.ItsBaseTest):
35  """Test different combinations of output formats.
36
37  Note the test does not require a specific target but does perform
38  both automatic and manual captures so it requires a fixed scene
39  where 3A can converge.
40  """
41
42  def test_format_combos(self):
43    with its_session_utils.ItsSession(
44        device_id=self.dut.serial,
45        camera_id=self.camera_id,
46        hidden_physical_id=self.hidden_physical_id) as cam:
47
48      props = cam.get_camera_properties()
49      props = cam.override_with_hidden_physical_camera_props(props)
50
51      # Load chart for scene
52      its_session_utils.load_scene(
53          cam, props, self.scene, self.tablet, self.chart_distance)
54
55      successes = []
56      failures = []
57
58      # Up to 2 possible request types: auto and manual
59      req_aut = capture_request_utils.auto_capture_request()
60      reqs = [req_aut]
61      if camera_properties_utils.compute_target_exposure(props):
62        e, s = target_exposure_utils.get_target_exposure_combos(
63            self.log_path, cam)['midExposureTime']
64        req_man = capture_request_utils.manual_capture_request(s, e)
65        reqs.append(req_man)
66
67      # Up to 10 different combos of output formats.
68      # Some are single surfaces and some are multiple surfaces.
69      wyuv, hyuv = capture_request_utils.get_available_output_sizes(
70          'yuv', props)[-1]
71      wjpg, hjpg = capture_request_utils.get_available_output_sizes(
72          'jpg', props)[-1]
73      fmt_yuv_prev = {'format': 'yuv', 'width': wyuv, 'height': hyuv}
74      fmt_yuv_full = {'format': 'yuv'}
75      fmt_jpg_prev = {'format': 'jpeg', 'width': wjpg, 'height': hjpg}
76      fmt_jpg_full = {'format': 'jpeg'}
77      fmt_combos = [
78          [fmt_yuv_prev],
79          [fmt_yuv_full],
80          [fmt_jpg_prev],
81          [fmt_jpg_full],
82          [fmt_yuv_prev, fmt_jpg_prev],
83          [fmt_yuv_prev, fmt_jpg_full],
84      ]
85      if camera_properties_utils.raw16(props):
86        fmt_raw_full = {'format': 'raw'}
87        fmt_combos.extend([
88            [fmt_raw_full],
89            [fmt_yuv_prev, fmt_raw_full],
90            [fmt_yuv_prev, fmt_jpg_prev, fmt_raw_full],
91            [fmt_yuv_prev, fmt_jpg_full, fmt_raw_full]])
92
93      if camera_properties_utils.y8(props):
94        wy8, hy8 = capture_request_utils.get_available_output_sizes(
95            'y8', props)[-1]
96        fmt_y8_prev = {'format': 'y8', 'width': wy8, 'height': hy8}
97        fmt_y8_full = {'format': 'y8'}
98        fmt_combos.extend([
99            [fmt_y8_prev],
100            [fmt_y8_full]])
101
102      # Two different burst lengths: single frame and 3 frames.
103      burst_lens = [1, 3]
104
105      # Run through 2xlen(fmt_combos)x2 different combinations.
106      n = 0
107      for r, req in enumerate(reqs):
108        if req['android.control.mode'] == _AUTO_REQUEST_MODE:
109          req_str = 'auto'
110        else:
111          req_str = 'manual'
112        for f, fmt_combo in enumerate(fmt_combos):
113          for b, burst_len in enumerate(burst_lens):
114            try:
115              cam.do_capture([req] * burst_len, fmt_combo)
116              successes.append((n, r, f, b))
117              logging.debug('Success[%02d]', n)
118              logging.debug('req: %s', req_str)
119              logging.debug('fmt: %s', str(fmt_combo))
120              logging.debug('burst_len: %d\n', burst_len)
121
122            except Exception as e:  # pylint: disable=broad-except
123              logging.error(e)
124              logging.error('Failure[%02d]', n)
125              logging.debug('req: %s', req_str)
126              logging.error('fmt: %s', str(fmt_combo))
127              logging.error('burst_len: %d\n', burst_len)
128              failures.append((n, r, f, b))
129              if _STOP_AT_FIRST_FAILURE:
130                raise AssertionError(
131                    f'Capture fail at combo req: {req_str}, fmt: {fmt_combo}, '
132                    f'burst: {burst_len}') from e
133            n += 1
134
135      num_fail = len(failures)
136      num_success = len(successes)
137      num_total = len(reqs)*len(fmt_combos)*len(burst_lens)
138      num_not_run = num_total - num_success - num_fail
139
140      logging.debug('Success: %d / %d', num_success, num_total)
141
142      # assert all combinations successfully capture
143      if num_fail != 0:
144        raise AssertionError(f'Number of fails: {num_fail} / {num_total}')
145      if num_not_run > 0:
146        raise AssertionError(f'Number of combos not run: {num_not_run}')
147
148if __name__ == '__main__':
149  test_runner.main()
150