xref: /aosp_15_r20/external/openscreen/cast/standalone_e2e.py (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1*3f982cf4SFabien Sanglard#!/usr/bin/env python3
2*3f982cf4SFabien Sanglard# Copyright 2021 The Chromium Authors. All rights reserved.
3*3f982cf4SFabien Sanglard# Use of this source code is governed by a BSD-style license that can be
4*3f982cf4SFabien Sanglard# found in the LICENSE file.
5*3f982cf4SFabien Sanglard"""
6*3f982cf4SFabien SanglardThis script is intended to cover end to end testing for the standalone sender
7*3f982cf4SFabien Sanglardand receiver executables in cast. This ensures that the basic functionality of
8*3f982cf4SFabien Sanglardthese executables is not impaired, such as the TLS/UDP connections and encoding
9*3f982cf4SFabien Sanglardand decoding video.
10*3f982cf4SFabien Sanglard"""
11*3f982cf4SFabien Sanglard
12*3f982cf4SFabien Sanglardimport argparse
13*3f982cf4SFabien Sanglardimport os
14*3f982cf4SFabien Sanglardimport pathlib
15*3f982cf4SFabien Sanglardimport logging
16*3f982cf4SFabien Sanglardimport subprocess
17*3f982cf4SFabien Sanglardimport sys
18*3f982cf4SFabien Sanglardimport time
19*3f982cf4SFabien Sanglardimport unittest
20*3f982cf4SFabien Sanglardimport ssl
21*3f982cf4SFabien Sanglardfrom collections import namedtuple
22*3f982cf4SFabien Sanglard
23*3f982cf4SFabien Sanglardfrom enum import IntEnum, IntFlag
24*3f982cf4SFabien Sanglardfrom urllib import request
25*3f982cf4SFabien Sanglard
26*3f982cf4SFabien Sanglard# Environment variables that can be overridden to set test properties.
27*3f982cf4SFabien SanglardROOT_ENVVAR = 'OPENSCREEN_ROOT_DIR'
28*3f982cf4SFabien SanglardBUILD_ENVVAR = 'OPENSCREEN_BUILD_DIR'
29*3f982cf4SFabien SanglardLIBAOM_ENVVAR = 'OPENSCREEN_HAVE_LIBAOM'
30*3f982cf4SFabien Sanglard
31*3f982cf4SFabien SanglardTEST_VIDEO_NAME = 'Contador_Glam.mp4'
32*3f982cf4SFabien Sanglard# NOTE: we use the HTTP protocol instead of HTTPS due to certificate issues
33*3f982cf4SFabien Sanglard# in the legacy urllib.request API.
34*3f982cf4SFabien SanglardTEST_VIDEO_URL = ('https://storage.googleapis.com/openscreen_standalone/' +
35*3f982cf4SFabien Sanglard                  TEST_VIDEO_NAME)
36*3f982cf4SFabien Sanglard
37*3f982cf4SFabien SanglardPROCESS_TIMEOUT = 15  # seconds
38*3f982cf4SFabien Sanglard
39*3f982cf4SFabien Sanglard# Open Screen test certificates expire after 3 days. We crop this slightly (by
40*3f982cf4SFabien Sanglard# 8 hours) to account for potential errors in time calculations.
41*3f982cf4SFabien SanglardCERT_EXPIRY_AGE = (3 * 24 - 8) * 60 * 60
42*3f982cf4SFabien Sanglard
43*3f982cf4SFabien Sanglard# These properties are based on compiled settings in Open Screen, and should
44*3f982cf4SFabien Sanglard# not change without updating this file.
45*3f982cf4SFabien SanglardTEST_CERT_NAME = 'generated_root_cast_receiver.crt'
46*3f982cf4SFabien SanglardTEST_KEY_NAME = 'generated_root_cast_receiver.key'
47*3f982cf4SFabien SanglardSENDER_BINARY_NAME = 'cast_sender'
48*3f982cf4SFabien SanglardRECEIVER_BINARY_NAME = 'cast_receiver'
49*3f982cf4SFabien Sanglard
50*3f982cf4SFabien SanglardEXPECTED_RECEIVER_MESSAGES = [
51*3f982cf4SFabien Sanglard    "CastService is running.", "Found codec: opus (known to FFMPEG as opus)",
52*3f982cf4SFabien Sanglard    "Successfully negotiated a session, creating SDL players.",
53*3f982cf4SFabien Sanglard    "Receivers are currently destroying, resetting SDL players."
54*3f982cf4SFabien Sanglard]
55*3f982cf4SFabien Sanglard
56*3f982cf4SFabien Sanglardclass VideoCodec(IntEnum):
57*3f982cf4SFabien Sanglard  """There are different messages printed by the receiver depending on the codec
58*3f982cf4SFabien Sanglard  chosen. """
59*3f982cf4SFabien Sanglard  Vp8 = 0
60*3f982cf4SFabien Sanglard  Vp9 = 1
61*3f982cf4SFabien Sanglard  Av1 = 2
62*3f982cf4SFabien Sanglard
63*3f982cf4SFabien SanglardVIDEO_CODEC_SPECIFIC_RECEIVER_MESSAGES = [
64*3f982cf4SFabien Sanglard  "Found codec: vp8 (known to FFMPEG as vp8)",
65*3f982cf4SFabien Sanglard  "Found codec: vp9 (known to FFMPEG as vp9)",
66*3f982cf4SFabien Sanglard  "Found codec: libaom-av1 (known to FFMPEG as av1)"
67*3f982cf4SFabien Sanglard]
68*3f982cf4SFabien Sanglard
69*3f982cf4SFabien SanglardEXPECTED_SENDER_MESSAGES = [
70*3f982cf4SFabien Sanglard    "Launching Mirroring App on the Cast Receiver",
71*3f982cf4SFabien Sanglard    "Max allowed media bitrate (audio + video) will be",
72*3f982cf4SFabien Sanglard    "Contador_Glam.mp4 (starts in one second)...",
73*3f982cf4SFabien Sanglard    "The video capturer has reached the end of the media stream.",
74*3f982cf4SFabien Sanglard    "The audio capturer has reached the end of the media stream.",
75*3f982cf4SFabien Sanglard    "Video complete. Exiting...", "Shutting down..."
76*3f982cf4SFabien Sanglard]
77*3f982cf4SFabien Sanglard
78*3f982cf4SFabien SanglardMISSING_LOG_MESSAGE = """Missing an expected message from either the sender
79*3f982cf4SFabien Sanglardor receiver. This either means that one of the binaries misbehaved, or you
80*3f982cf4SFabien Sanglardchanged or deleted one of the log messages used for validation. Please ensure
81*3f982cf4SFabien Sanglardthat the necessary log messages are left unchanged, or update this
82*3f982cf4SFabien Sanglardtest suite's expectations."""
83*3f982cf4SFabien Sanglard
84*3f982cf4SFabien SanglardDESCRIPTION = """Runs end to end tests for the standalone Cast Streaming sender
85*3f982cf4SFabien Sanglardand receiver. By default, this script assumes it is being ran from a current
86*3f982cf4SFabien Sanglardworking directory inside Open Screen's source directory, and uses
87*3f982cf4SFabien Sanglard<root_dir>/out/Default as the build directory. To override these, set the
88*3f982cf4SFabien SanglardOPENSCREEN_ROOT_DIR and OPENSCREEN_BUILD_DIR environment variables. If the root
89*3f982cf4SFabien Sanglarddirectory is set and the build directory is not,
90*3f982cf4SFabien Sanglard<OPENSCREEN_ROOT_DIR>/out/Default will be used. In addition, if LibAOM is
91*3f982cf4SFabien Sanglardinstalled, one can choose to run AV1 tests by defining the
92*3f982cf4SFabien SanglardOPENSCREEN_HAVE_LIBAOM environment variable.
93*3f982cf4SFabien Sanglard
94*3f982cf4SFabien SanglardSee below for the the help output generated by the `unittest` package."""
95*3f982cf4SFabien Sanglard
96*3f982cf4SFabien Sanglard
97*3f982cf4SFabien Sanglarddef _set_log_level(is_verbose):
98*3f982cf4SFabien Sanglard    """Sets the logging level, either DEBUG or ERROR as appropriate."""
99*3f982cf4SFabien Sanglard    level = logging.DEBUG if is_verbose else logging.INFO
100*3f982cf4SFabien Sanglard    logging.basicConfig(stream=sys.stdout, level=level)
101*3f982cf4SFabien Sanglard
102*3f982cf4SFabien Sanglard
103*3f982cf4SFabien Sanglarddef _get_loopback_adapter_name():
104*3f982cf4SFabien Sanglard    """Retrieves the name of the loopback adapter (lo on Linux/lo0 on Mac)."""
105*3f982cf4SFabien Sanglard    if sys.platform == 'linux' or sys.platform == 'linux2':
106*3f982cf4SFabien Sanglard        return 'lo'
107*3f982cf4SFabien Sanglard    if sys.platform == 'darwin':
108*3f982cf4SFabien Sanglard        return 'lo0'
109*3f982cf4SFabien Sanglard    return None
110*3f982cf4SFabien Sanglard
111*3f982cf4SFabien Sanglard
112*3f982cf4SFabien Sanglarddef _get_file_age_in_seconds(path):
113*3f982cf4SFabien Sanglard    """Get the age of a given file in seconds"""
114*3f982cf4SFabien Sanglard    # Time is stored in seconds since epoch
115*3f982cf4SFabien Sanglard    file_last_modified = 0
116*3f982cf4SFabien Sanglard    if path.exists():
117*3f982cf4SFabien Sanglard        file_last_modified = path.stat().st_mtime
118*3f982cf4SFabien Sanglard    return time.time() - file_last_modified
119*3f982cf4SFabien Sanglard
120*3f982cf4SFabien Sanglard
121*3f982cf4SFabien Sanglarddef _get_build_paths():
122*3f982cf4SFabien Sanglard    """Gets the root and build paths (either default or from the environment
123*3f982cf4SFabien Sanglard    variables), and sets related paths to binaries and files."""
124*3f982cf4SFabien Sanglard    root_path = pathlib.Path(
125*3f982cf4SFabien Sanglard    os.environ[ROOT_ENVVAR] if os.getenv(ROOT_ENVVAR) else subprocess.
126*3f982cf4SFabien Sanglard    getoutput('git rev-parse --show-toplevel'))
127*3f982cf4SFabien Sanglard    assert root_path.exists(), 'Could not find openscreen root!'
128*3f982cf4SFabien Sanglard
129*3f982cf4SFabien Sanglard    build_path = pathlib.Path(os.environ[BUILD_ENVVAR]) if os.getenv(
130*3f982cf4SFabien Sanglard        BUILD_ENVVAR) else root_path.joinpath('out',
131*3f982cf4SFabien Sanglard                                                    'Default').resolve()
132*3f982cf4SFabien Sanglard    assert build_path.exists(), 'Could not find openscreen build!'
133*3f982cf4SFabien Sanglard
134*3f982cf4SFabien Sanglard    BuildPaths = namedtuple("BuildPaths",
135*3f982cf4SFabien Sanglard                            "root build test_video cast_receiver cast_sender")
136*3f982cf4SFabien Sanglard    return BuildPaths(root = root_path,
137*3f982cf4SFabien Sanglard        build = build_path,
138*3f982cf4SFabien Sanglard        test_video = build_path.joinpath(TEST_VIDEO_NAME).resolve(),
139*3f982cf4SFabien Sanglard        cast_receiver = build_path.joinpath(RECEIVER_BINARY_NAME).resolve(),
140*3f982cf4SFabien Sanglard        cast_sender = build_path.joinpath(SENDER_BINARY_NAME).resolve()
141*3f982cf4SFabien Sanglard        )
142*3f982cf4SFabien Sanglard
143*3f982cf4SFabien Sanglard
144*3f982cf4SFabien Sanglardclass TestFlags(IntFlag):
145*3f982cf4SFabien Sanglard    """
146*3f982cf4SFabien Sanglard    Test flags, primarily used to control sender and receiver configuration
147*3f982cf4SFabien Sanglard    to test different features of the standalone libraries.
148*3f982cf4SFabien Sanglard    """
149*3f982cf4SFabien Sanglard    UseRemoting = 1
150*3f982cf4SFabien Sanglard    UseAndroidHack = 2
151*3f982cf4SFabien Sanglard
152*3f982cf4SFabien Sanglard
153*3f982cf4SFabien Sanglardclass StandaloneCastTest(unittest.TestCase):
154*3f982cf4SFabien Sanglard    """
155*3f982cf4SFabien Sanglard    Test class for setting up and running end to end tests on the
156*3f982cf4SFabien Sanglard    standalone sender and receiver binaries. This class uses the unittest
157*3f982cf4SFabien Sanglard    package, so methods that are executed as tests all have named prefixed
158*3f982cf4SFabien Sanglard    with "test_".
159*3f982cf4SFabien Sanglard
160*3f982cf4SFabien Sanglard    This suite sets the current working directory to the root of the Open
161*3f982cf4SFabien Sanglard    Screen repository, and references all files from the root directory.
162*3f982cf4SFabien Sanglard    Generated certificates should always be in |cls.build_paths.root|.
163*3f982cf4SFabien Sanglard    """
164*3f982cf4SFabien Sanglard
165*3f982cf4SFabien Sanglard    @classmethod
166*3f982cf4SFabien Sanglard    def setUpClass(cls):
167*3f982cf4SFabien Sanglard        """Shared setup method for all tests, handles one-time updates."""
168*3f982cf4SFabien Sanglard        cls.build_paths = _get_build_paths()
169*3f982cf4SFabien Sanglard        os.chdir(cls.build_paths.root)
170*3f982cf4SFabien Sanglard        cls.download_video()
171*3f982cf4SFabien Sanglard        cls.generate_certificates()
172*3f982cf4SFabien Sanglard
173*3f982cf4SFabien Sanglard    @classmethod
174*3f982cf4SFabien Sanglard    def download_video(cls):
175*3f982cf4SFabien Sanglard        """Downloads the test video from Google storage."""
176*3f982cf4SFabien Sanglard        if os.path.exists(cls.build_paths.test_video):
177*3f982cf4SFabien Sanglard            logging.debug('Video already exists, skipping download...')
178*3f982cf4SFabien Sanglard            return
179*3f982cf4SFabien Sanglard
180*3f982cf4SFabien Sanglard        logging.debug('Downloading video from %s', TEST_VIDEO_URL)
181*3f982cf4SFabien Sanglard        with request.urlopen(TEST_VIDEO_URL, context=ssl.SSLContext()) as url:
182*3f982cf4SFabien Sanglard            with open(cls.build_paths.test_video, 'wb') as file:
183*3f982cf4SFabien Sanglard                file.write(url.read())
184*3f982cf4SFabien Sanglard
185*3f982cf4SFabien Sanglard    @classmethod
186*3f982cf4SFabien Sanglard    def generate_certificates(cls):
187*3f982cf4SFabien Sanglard        """Generates test certificates using the cast receiver."""
188*3f982cf4SFabien Sanglard        cert_age = _get_file_age_in_seconds(pathlib.Path(TEST_CERT_NAME))
189*3f982cf4SFabien Sanglard        key_age = _get_file_age_in_seconds(pathlib.Path(TEST_KEY_NAME))
190*3f982cf4SFabien Sanglard        if cert_age < CERT_EXPIRY_AGE and key_age < CERT_EXPIRY_AGE:
191*3f982cf4SFabien Sanglard            logging.debug('Credentials are up to date...')
192*3f982cf4SFabien Sanglard            return
193*3f982cf4SFabien Sanglard
194*3f982cf4SFabien Sanglard        logging.debug('Credentials out of date, generating new ones...')
195*3f982cf4SFabien Sanglard        try:
196*3f982cf4SFabien Sanglard            subprocess.check_output(
197*3f982cf4SFabien Sanglard                [
198*3f982cf4SFabien Sanglard                    cls.build_paths.cast_receiver,
199*3f982cf4SFabien Sanglard                    '-g',  # Generate certificate and private key.
200*3f982cf4SFabien Sanglard                    '-v'  # Enable verbose logging.
201*3f982cf4SFabien Sanglard                ],
202*3f982cf4SFabien Sanglard                stderr=subprocess.STDOUT)
203*3f982cf4SFabien Sanglard        except subprocess.CalledProcessError as e:
204*3f982cf4SFabien Sanglard            print('Generation failed with output: ', e.output.decode())
205*3f982cf4SFabien Sanglard            raise
206*3f982cf4SFabien Sanglard
207*3f982cf4SFabien Sanglard    def launch_receiver(self):
208*3f982cf4SFabien Sanglard        """Launches the receiver process with discovery disabled."""
209*3f982cf4SFabien Sanglard        logging.debug('Launching the receiver application...')
210*3f982cf4SFabien Sanglard        loopback = _get_loopback_adapter_name()
211*3f982cf4SFabien Sanglard        self.assertTrue(loopback)
212*3f982cf4SFabien Sanglard
213*3f982cf4SFabien Sanglard        #pylint: disable = consider-using-with
214*3f982cf4SFabien Sanglard        return subprocess.Popen(
215*3f982cf4SFabien Sanglard            [
216*3f982cf4SFabien Sanglard                self.build_paths.cast_receiver,
217*3f982cf4SFabien Sanglard                '-d',
218*3f982cf4SFabien Sanglard                TEST_CERT_NAME,
219*3f982cf4SFabien Sanglard                '-p',
220*3f982cf4SFabien Sanglard                TEST_KEY_NAME,
221*3f982cf4SFabien Sanglard                '-x',  # Skip discovery, only necessary on Mac OS X.
222*3f982cf4SFabien Sanglard                '-v',  # Enable verbose logging.
223*3f982cf4SFabien Sanglard                loopback
224*3f982cf4SFabien Sanglard            ],
225*3f982cf4SFabien Sanglard            stdout=subprocess.PIPE,
226*3f982cf4SFabien Sanglard            stderr=subprocess.PIPE)
227*3f982cf4SFabien Sanglard
228*3f982cf4SFabien Sanglard    def launch_sender(self, flags, codec=None):
229*3f982cf4SFabien Sanglard        """Launches the sender process, running the test video file once."""
230*3f982cf4SFabien Sanglard        logging.debug('Launching the sender application...')
231*3f982cf4SFabien Sanglard        command = [
232*3f982cf4SFabien Sanglard            self.build_paths.cast_sender,
233*3f982cf4SFabien Sanglard            '127.0.0.1:8010',
234*3f982cf4SFabien Sanglard            self.build_paths.test_video,
235*3f982cf4SFabien Sanglard            '-d',
236*3f982cf4SFabien Sanglard            TEST_CERT_NAME,
237*3f982cf4SFabien Sanglard            '-n'  # Only play the video once, and then exit.
238*3f982cf4SFabien Sanglard        ]
239*3f982cf4SFabien Sanglard        if TestFlags.UseAndroidHack in flags:
240*3f982cf4SFabien Sanglard            command.append('-a')
241*3f982cf4SFabien Sanglard        if TestFlags.UseRemoting in flags:
242*3f982cf4SFabien Sanglard            command.append('-r')
243*3f982cf4SFabien Sanglard
244*3f982cf4SFabien Sanglard        # The standalone sender sends VP8 if no codec command line argument is
245*3f982cf4SFabien Sanglard        # passed.
246*3f982cf4SFabien Sanglard        if codec:
247*3f982cf4SFabien Sanglard          command.append('-c')
248*3f982cf4SFabien Sanglard          if codec == VideoCodec.Vp8:
249*3f982cf4SFabien Sanglard            command.append('vp8')
250*3f982cf4SFabien Sanglard          elif codec == VideoCodec.Vp9:
251*3f982cf4SFabien Sanglard              command.append('vp9')
252*3f982cf4SFabien Sanglard          else:
253*3f982cf4SFabien Sanglard              self.assertTrue(codec == VideoCodec.Av1)
254*3f982cf4SFabien Sanglard              command.append('av1')
255*3f982cf4SFabien Sanglard
256*3f982cf4SFabien Sanglard        #pylint: disable = consider-using-with
257*3f982cf4SFabien Sanglard        return subprocess.Popen(command,
258*3f982cf4SFabien Sanglard                                stdout=subprocess.PIPE,
259*3f982cf4SFabien Sanglard                                stderr=subprocess.PIPE)
260*3f982cf4SFabien Sanglard
261*3f982cf4SFabien Sanglard    def check_logs(self, logs, codec=None):
262*3f982cf4SFabien Sanglard        """Checks that the outputted logs contain expected behavior."""
263*3f982cf4SFabien Sanglard
264*3f982cf4SFabien Sanglard        # If a codec was not provided, we should make sure that the standalone
265*3f982cf4SFabien Sanglard        # sender sent VP8.
266*3f982cf4SFabien Sanglard        if codec == None:
267*3f982cf4SFabien Sanglard          codec = VideoCodec.Vp8
268*3f982cf4SFabien Sanglard
269*3f982cf4SFabien Sanglard        for message in (EXPECTED_RECEIVER_MESSAGES +
270*3f982cf4SFabien Sanglard                        [VIDEO_CODEC_SPECIFIC_RECEIVER_MESSAGES[codec]]):
271*3f982cf4SFabien Sanglard            self.assertTrue(
272*3f982cf4SFabien Sanglard                message in logs[0],
273*3f982cf4SFabien Sanglard                'Missing log message: {}.\n{}'.format(message,
274*3f982cf4SFabien Sanglard                                                      MISSING_LOG_MESSAGE))
275*3f982cf4SFabien Sanglard        for message in EXPECTED_SENDER_MESSAGES:
276*3f982cf4SFabien Sanglard            self.assertTrue(
277*3f982cf4SFabien Sanglard                message in logs[1],
278*3f982cf4SFabien Sanglard                'Missing log message: {}.\n{}'.format(message,
279*3f982cf4SFabien Sanglard                                                      MISSING_LOG_MESSAGE))
280*3f982cf4SFabien Sanglard        for log, prefix in logs, ["[ERROR:", "[FATAL:"]:
281*3f982cf4SFabien Sanglard            self.assertTrue(prefix not in log, "Logs contained an error")
282*3f982cf4SFabien Sanglard        logging.debug('Finished validating log output')
283*3f982cf4SFabien Sanglard
284*3f982cf4SFabien Sanglard    def get_output(self, flags, codec=None):
285*3f982cf4SFabien Sanglard        """Launches the sender and receiver, and handles exit output."""
286*3f982cf4SFabien Sanglard        receiver_process = self.launch_receiver()
287*3f982cf4SFabien Sanglard        logging.debug('Letting the receiver start up...')
288*3f982cf4SFabien Sanglard        time.sleep(3)
289*3f982cf4SFabien Sanglard        sender_process = self.launch_sender(flags, codec)
290*3f982cf4SFabien Sanglard
291*3f982cf4SFabien Sanglard        logging.debug('Launched sender PID %i and receiver PID %i...',
292*3f982cf4SFabien Sanglard            sender_process.pid, receiver_process.pid)
293*3f982cf4SFabien Sanglard        logging.debug('collating output...')
294*3f982cf4SFabien Sanglard        output = (receiver_process.communicate(
295*3f982cf4SFabien Sanglard            timeout=PROCESS_TIMEOUT)[1].decode('utf-8'),
296*3f982cf4SFabien Sanglard                  sender_process.communicate(
297*3f982cf4SFabien Sanglard                      timeout=PROCESS_TIMEOUT)[1].decode('utf-8'))
298*3f982cf4SFabien Sanglard
299*3f982cf4SFabien Sanglard        # TODO(issuetracker.google.com/194292855): standalones should exit zero.
300*3f982cf4SFabien Sanglard        # Remoting causes the sender to exit with code -4.
301*3f982cf4SFabien Sanglard        if not TestFlags.UseRemoting in flags:
302*3f982cf4SFabien Sanglard            self.assertEqual(sender_process.returncode, 0,
303*3f982cf4SFabien Sanglard                             'sender had non-zero exit code')
304*3f982cf4SFabien Sanglard        return output
305*3f982cf4SFabien Sanglard
306*3f982cf4SFabien Sanglard    def test_golden_case(self):
307*3f982cf4SFabien Sanglard        """Tests that when settings are normal, things work end to end."""
308*3f982cf4SFabien Sanglard        output = self.get_output([])
309*3f982cf4SFabien Sanglard        self.check_logs(output)
310*3f982cf4SFabien Sanglard
311*3f982cf4SFabien Sanglard    def test_remoting(self):
312*3f982cf4SFabien Sanglard        """Tests that basic remoting works."""
313*3f982cf4SFabien Sanglard        output = self.get_output(TestFlags.UseRemoting)
314*3f982cf4SFabien Sanglard        self.check_logs(output)
315*3f982cf4SFabien Sanglard
316*3f982cf4SFabien Sanglard    def test_with_android_hack(self):
317*3f982cf4SFabien Sanglard        """Tests that things work when the Android RTP hack is enabled."""
318*3f982cf4SFabien Sanglard        output = self.get_output(TestFlags.UseAndroidHack)
319*3f982cf4SFabien Sanglard        self.check_logs(output)
320*3f982cf4SFabien Sanglard
321*3f982cf4SFabien Sanglard    def test_vp8_flag(self):
322*3f982cf4SFabien Sanglard      """Tests that the VP8 flag works with standard settings."""
323*3f982cf4SFabien Sanglard      output = self.get_output([], VideoCodec.Vp8)
324*3f982cf4SFabien Sanglard      self.check_logs(output, VideoCodec.Vp8)
325*3f982cf4SFabien Sanglard
326*3f982cf4SFabien Sanglard    def test_vp9_flag(self):
327*3f982cf4SFabien Sanglard      """Tests that the VP9 flag works with standard settings."""
328*3f982cf4SFabien Sanglard      output = self.get_output([], VideoCodec.Vp9)
329*3f982cf4SFabien Sanglard      self.check_logs(output, VideoCodec.Vp9)
330*3f982cf4SFabien Sanglard
331*3f982cf4SFabien Sanglard    @unittest.skipUnless(os.getenv(LIBAOM_ENVVAR),
332*3f982cf4SFabien Sanglard                        'Skipping AV1 test since LibAOM not installed.')
333*3f982cf4SFabien Sanglard    def test_av1_flag(self):
334*3f982cf4SFabien Sanglard      """Tests that the AV1 flag works with standard settings."""
335*3f982cf4SFabien Sanglard      output = self.get_output([], VideoCodec.Av1)
336*3f982cf4SFabien Sanglard      self.check_logs(output, VideoCodec.Av1)
337*3f982cf4SFabien Sanglard
338*3f982cf4SFabien Sanglard
339*3f982cf4SFabien Sanglarddef parse_args():
340*3f982cf4SFabien Sanglard    """Parses the command line arguments and sets up the logging module."""
341*3f982cf4SFabien Sanglard    # NOTE for future developers: the `unittest` module will complain if it is
342*3f982cf4SFabien Sanglard    # passed any args that it doesn't understand. If any Open Screen-specific
343*3f982cf4SFabien Sanglard    # command line arguments are added in the future, they should be cropped
344*3f982cf4SFabien Sanglard    # from sys.argv before |unittest.main()| is called.
345*3f982cf4SFabien Sanglard    parser = argparse.ArgumentParser(description=DESCRIPTION)
346*3f982cf4SFabien Sanglard    parser.add_argument('-v',
347*3f982cf4SFabien Sanglard                        '--verbose',
348*3f982cf4SFabien Sanglard                        help='enable debug logging',
349*3f982cf4SFabien Sanglard                        action='store_true')
350*3f982cf4SFabien Sanglard
351*3f982cf4SFabien Sanglard    parsed_args = parser.parse_args(sys.argv[1:])
352*3f982cf4SFabien Sanglard    _set_log_level(parsed_args.verbose)
353*3f982cf4SFabien Sanglard
354*3f982cf4SFabien Sanglard
355*3f982cf4SFabien Sanglardif __name__ == '__main__':
356*3f982cf4SFabien Sanglard    parse_args()
357*3f982cf4SFabien Sanglard    unittest.main()
358