""" Executes Audio / Video performance tests against a smart display device.
This script needs to be executed from the build output folder, e.g.
out/fuchsia/."""
import logging
import multiprocessing
import os
import random
import shutil
import subprocess
import sys
import time
from contextlib import AbstractContextManager
from pathlib import Path
import camera
import server
import video_analyzer
TEST_SCRIPTS_ROOT = os.path.join(os.path.dirname(__file__), '..', '..',
'build', 'fuchsia', 'test')
sys.path.append(TEST_SCRIPTS_ROOT)
import monitors
import perf_trace
import version
from chrome_driver_wrapper import ChromeDriverWrapper
from common import get_build_info, get_free_local_port, get_ip_address, ssh_run
from repeating_log import RepeatingLog
HTTP_SERVER_PORT = get_free_local_port()
LOG_DIR = os.environ.get('ISOLATED_OUTDIR', '/tmp')
TEMP_DIR = os.environ.get('TMPDIR', '/tmp')
VIDEOS = [
'480p30fpsH264_foodmarket_sync.mp4',
'480p30fpsVP9_foodmarket_sync.webm',
'480p60fpsH264_boat_sync.mp4',
'480p60fpsHEVC_boat_sync.mp4',
'480p60fpsVP9_boat_sync.mp4',
'720p24fpsH264_gangnam_sync.mp4',
'720p24fpsVP9_gangnam_sync.webm',
'720p60fpsH264_boat_yt_sync.mp4',
'720p60fpsHEVC_boat_sync.mp4',
'720p60fpsVP9_boat_yt_sync.webm',
]
HIGH_PERF_VIDEOS = [
'1080p60fpsH264_boat_yt_sync.mp4',
'1080p60fpsHEVC_boat_sync.mp4',
'1080p60fpsVP9_boat_yt_sync.webm',
]
class StartProcess(AbstractContextManager):
"""Starts a multiprocessing.Process."""
def __init__(self, target, args, terminate: bool):
self._proc = multiprocessing.Process(target=target, args=args)
self._terminate = terminate
def __enter__(self):
self._proc.start()
def __exit__(self, exc_type, exc_value, traceback):
if self._terminate:
self._proc.terminate()
self._proc.join()
if not self._terminate:
assert self._proc.exitcode == 0
def parameters_of(file: str) -> camera.Parameters:
result = camera.Parameters()
result.file = file
result.output_path = TEMP_DIR
result.max_frames = 1200
result.fps = 120
result.duration_sec = 30
if os.environ.get('CAMERA_SERIAL_NUMBER'):
result.serial_number = os.environ['CAMERA_SERIAL_NUMBER']
return result
def _wait_js_condition(driver: ChromeDriverWrapper, element,
condition: str) -> bool:
"""Waits a condition on the element once a second for at most 30 seconds,
returns True if the condition met."""
start = time.time()
while not driver.execute_script(f'return arguments[0].{condition};',
element):
if time.time() - start >= 30:
return False
time.sleep(1)
return True
def run_video_perf_test(file: str, driver: ChromeDriverWrapper,
host: str) -> None:
perf_trace.start()
driver.get(f'http://{host}:{HTTP_SERVER_PORT}/video.html?file={file}')
camera_params = parameters_of(file)
original_video = os.path.join(server.VIDEO_DIR, file)
assert camera_params.video_file != original_video
video = driver.find_element_by_id('video')
with monitors.time_consumption(file, 'video_perf', 'playback', 'loading'), \
RepeatingLog(f'Waiting for video {file} to be loaded.'):
if not _wait_js_condition(driver, video, 'readyState >= 2'):
logging.warning(
'%s may never be loaded, still go ahead to play it.', file)
monitors.average(file, 'video_perf', 'playback',
'failed_to_load').record(1)
with StartProcess(camera.start, [camera_params], False):
video.click()
with monitors.time_consumption(file, 'video_perf', 'playback', 'laggy'), \
RepeatingLog(f'Waiting for video {file} playback to finish.'):
if not _wait_js_condition(driver, video, 'ended'):
logging.warning('%s may never finish', file)
monitors.average(file, 'video_perf', 'playback',
'never_finish').record(1)
logging.warning('Video %s finished', file)
perf_trace.stop(file)
results = video_analyzer.from_original_video(camera_params.video_file,
original_video)
def record(key: str) -> None:
monitors.average(file, 'video_perf', key).record(results.get(key, -128))
record('smoothness')
record('freezing')
record('dropped_frame_count')
record('total_frame_count')
record('dropped_frame_percentage')
logging.warning('Video analysis result of %s: %s', file, results)
shutil.move(camera_params.info_file, LOG_DIR)
def main() -> int:
try:
with ChromeDriverWrapper() as driver:
logging.warning('Chrome version %s %s',
version.chrome_version_str(),
version.git_revision())
build_info = get_build_info()
logging.warning('Fuchsia build info %s', build_info)
monitors.tag(
version.chrome_version_str(), build_info.version,
version.chrome_version_str() + '/' + build_info.version)
host = '.'.join(get_ip_address(os.environ.get('FUCHSIA_NODENAME'),
ipv4_only=True).
exploded.split('.')[:-1] +
[os.environ.get('CAMERA_SERIAL_NUMBER') and
'10' or
'1'])
candidates = set(VIDEOS)
if build_info.board == 'sherlock':
candidates.update(HIGH_PERF_VIDEOS)
for file in random.sample(list(candidates), 2):
run_video_perf_test(file, driver, host)
return 0
except:
monitors.clear()
raise
finally:
monitors.dump(os.path.join(LOG_DIR, 'invocations'))
if __name__ == '__main__':
logging.warning('Running %s with env %s', sys.argv, os.environ)
with StartProcess(server.start, [HTTP_SERVER_PORT], True):
sys.exit(main())