1# Copyright (c) 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import errno
6import fnmatch
7import hashlib
8import logging
9import os
10
11from autotest_lib.client.bin import utils
12from autotest_lib.client.common_lib import error
13from autotest_lib.client.common_lib import file_utils
14from autotest_lib.client.cros import chrome_binary_test
15from autotest_lib.client.cros.video import helper_logger
16
17DOWNLOAD_BASE = 'http://commondatastorage.googleapis.com/chromiumos-test-assets-public/'
18BINARY = 'video_encode_accelerator_unittest'
19
20def _remove_if_exists(filepath):
21    try:
22        os.remove(filepath)
23    except OSError, e:
24        if e.errno != errno.ENOENT: # no such file
25            raise
26
27
28def _download_video(download_path, local_file):
29    url = '%s%s' % (DOWNLOAD_BASE, download_path)
30    logging.info('download "%s" to "%s"', url, local_file)
31
32    file_utils.download_file(url, local_file)
33
34    with open(local_file, 'r') as r:
35        md5sum = hashlib.md5(r.read()).hexdigest()
36        if md5sum not in download_path:
37            raise error.TestError('unmatched md5 sum: %s' % md5sum)
38
39
40class video_VideoEncodeAccelerator(chrome_binary_test.ChromeBinaryTest):
41    """
42    This test is a wrapper of the chrome unittest binary:
43    video_encode_accelerator_unittest.
44    """
45
46    version = 1
47
48    def get_filter_option(self, profile, size):
49        """Get option of filtering test.
50
51        @param profile: The profile to encode into.
52        @param size: The size of test stream in pair format (width, height).
53        """
54
55        # Profiles used in blacklist to filter test for specific profiles.
56        H264 = 1
57        VP8 = 11
58        VP9 = 12
59
60        blacklist = {
61                # (board, profile, size): [tests to skip...]
62
63                # "board" supports Unix shell-type wildcards.
64
65                # Use None for "profile" or "size" to indicate no filter on it.
66
67                # It is possible to match multiple keys for board/profile/size
68                # in the blacklist, e.g. veyron_minnie could match both
69                # "veyron_*" and "veyron_minnie".
70
71                # rk3399 doesn't support HW encode for plane sizes not multiple
72                # of cache line.
73                ('kevin', None, None): ['CacheLineUnalignedInputTest/*'],
74                ('bob', None, None): ['CacheLineUnalignedInputTest/*'],
75
76                # Still high failure rate of VP8 EncoderPerf for veyrons,
77                # disable it for now. crbug/720386
78                ('veyron_*', VP8, None): ['EncoderPerf/*'],
79
80                # Disable mid_stream_bitrate_switch test cases for elm/hana.
81                # crbug/725087
82                ('elm', None, None): ['MidStreamParamSwitchBitrate/*',
83                                      'MultipleEncoders/*'],
84                ('hana', None, None): ['MidStreamParamSwitchBitrate/*',
85                                       'MultipleEncoders/*'],
86
87                # Around 40% failure on elm and hana 320x180 test stream.
88                # crbug/728906
89                ('elm', H264, (320, 180)): ['ForceBitrate/*'],
90                ('elm', VP8, (320, 180)): ['ForceBitrate/*'],
91                ('hana', H264, (320, 180)): ['ForceBitrate/*'],
92                ('hana', VP8, (320, 180)): ['ForceBitrate/*'],
93                }
94
95        board = utils.get_current_board()
96
97        filter_list = []
98        for (board_key, profile_key, size_key), value in blacklist.items():
99            if (fnmatch.fnmatch(board, board_key) and
100                (profile_key is None or profile == profile_key) and
101                (size_key is None or size == size_key)):
102                filter_list += value
103
104        if filter_list:
105            return '-' + ':'.join(filter_list)
106
107        return ''
108
109    @helper_logger.video_log_wrapper
110    @chrome_binary_test.nuke_chrome
111    def run_once(self, in_cloud, streams, profile, gtest_filter=None):
112        """Runs video_encode_accelerator_unittest on the streams.
113
114        @param in_cloud: Input file needs to be downloaded first.
115        @param streams: The test streams for video_encode_accelerator_unittest.
116        @param profile: The profile to encode into.
117        @param gtest_filter: test case filter.
118
119        @raises error.TestFail for video_encode_accelerator_unittest failures.
120        """
121
122        last_test_failure = None
123        for path, width, height, bit_rate in streams:
124            if in_cloud:
125                input_path = os.path.join(self.tmpdir, path.split('/')[-1])
126                _download_video(path, input_path)
127            else:
128                input_path = os.path.join(self.cr_source_dir, path)
129
130            output_path = os.path.join(self.tmpdir,
131                    '%s.out' % input_path.split('/')[-1])
132
133            cmd_line_list = []
134            cmd_line_list.append('--test_stream_data="%s:%s:%s:%s:%s:%s"' % (
135                    input_path, width, height, profile, output_path, bit_rate))
136            cmd_line_list.append(helper_logger.chrome_vmodule_flag())
137            cmd_line_list.append('--ozone-platform=gbm')
138
139            # Command line |gtest_filter| can override get_filter_option().
140            predefined_filter = self.get_filter_option(profile, (width, height))
141            if gtest_filter and predefined_filter:
142                logging.warning('predefined gtest filter is suppressed: %s',
143                    predefined_filter)
144                applied_filter = gtest_filter
145            else:
146                applied_filter = predefined_filter
147            if applied_filter:
148                cmd_line_list.append('--gtest_filter="%s"' % applied_filter)
149
150            cmd_line = ' '.join(cmd_line_list)
151            try:
152                self.run_chrome_test_binary(BINARY, cmd_line, as_chronos=False)
153            except error.TestFail as test_failure:
154                # Continue to run the remaining test streams and raise
155                # the last failure after finishing all streams.
156                logging.exception('error while encoding %s', input_path)
157                last_test_failure = test_failure
158            finally:
159                # Remove the downloaded video
160                if in_cloud:
161                    _remove_if_exists(input_path)
162                _remove_if_exists(output_path)
163
164        if last_test_failure:
165            raise last_test_failure
166