blob: 1e7f3d52095239ca3749df62192630b4384b404e [file] [log] [blame]
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import base64
import functools
import os
import shutil
import subprocess
import sys
import attr
import pytest
from selenium.webdriver.remote.webelement import WebElement
from typing import Tuple
# The module skia_gold_common is relative to its own path, add "build" dir
# to the search path.
from chrome.test.variations.test_utils import SRC_DIR
sys.path.append(os.path.join(SRC_DIR, 'build'))
from skia_gold_common import output_managerless_skia_gold_session as omsgs
from skia_gold_common import skia_gold_properties as sgp
from skia_gold_common import skia_gold_session_manager as sgsm
from skia_gold_common.skia_gold_session import SkiaGoldSession
_CORPUS ='finch-smoke-tests'
@functools.lru_cache
def _get_skia_gold_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
sgp.SkiaGoldProperties.AddCommandLineArguments(parser)
skia_options, _ = parser.parse_known_args()
return skia_options
class _VariationsSkiaGoldSessionManager(sgsm.SkiaGoldSessionManager):
@staticmethod
def GetSessionClass():
return omsgs.OutputManagerlessSkiaGoldSession
@attr.attrs()
class VariationsSkiaGoldUtil:
"""Wrapper test util class for skia gold API."""
img_dir: str = attr.attrib()
skia_gold_session: SkiaGoldSession = attr.attrib()
test_name: str = attr.attrib()
use_luci: bool = attr.attrib()
def _png_file_for_name(self, name: str) -> str:
"""Returns a file name that should be used for diff comparison."""
return os.path.join(self.img_dir, f'{name}.png')
@staticmethod
def screenshot_from_element(ele: WebElement) -> bytes:
"""Convenient method to screenshot an selement."""
return base64.b64decode(ele.screenshot_as_base64)
def compare(self, name: str, png_data: bytes) -> Tuple[int, str]:
"""Compares image using skia gold API.
It saves png data into a file first and compares using `goldctl`. The image
can be inspected after test runs. It runs locally or on a bot, and only
upload gold images for triaging on a bot.
"""
png_file = self._png_file_for_name(name)
with open(png_file, "wb") as f:
f.write(png_data)
image_name = f'{self.test_name}:{name}'
status, error_msg = self.skia_gold_session.RunComparison(
name=image_name, png_file=png_file, use_luci=self.use_luci)
return status, (
f'{error_msg}'
f'{self.skia_gold_session.GetTriageLinks(image_name)}'
)
@pytest.fixture
def skia_gold_util(
request: pytest.FixtureRequest,
tmp_path_factory: pytest.TempPathFactory
) -> VariationsSkiaGoldUtil:
"""Returns VariationsSkiaGoldUtil to help compare gold images."""
# Doesn't generate skia name for parameterization yet
assert not hasattr(request, 'param')
skia_tmp_dir = tmp_path_factory.mktemp('skia_gold', True)
skia_img_dir = tmp_path_factory.mktemp('skia_img', True)
skia_gold_properties = sgp.SkiaGoldProperties(
args=_get_skia_gold_args())
skia_gold_session_manager = _VariationsSkiaGoldSessionManager(
skia_tmp_dir,
skia_gold_properties
)
config = request.config
session = skia_gold_session_manager.GetSkiaGoldSession({
'platform': config.getoption('target_platform'),
'channel': config.getoption('channel'),
}, _CORPUS)
test_file = os.path.relpath(request.module.__file__, SRC_DIR)
try:
util = VariationsSkiaGoldUtil(
img_dir=skia_img_dir,
skia_gold_session=session,
test_name=f'{test_file}:{request.node.name}',
use_luci=(not skia_gold_properties.local_pixel_tests))
yield util
finally:
# TODO(b/280321923):
# Keep the img dir in case of test failures when running locally.
shutil.rmtree(skia_tmp_dir, ignore_errors=True)
shutil.rmtree(skia_img_dir, ignore_errors=True)