blob: 7a8e7d35d7a3d9f7410be33cc8c7e5fb3bd0ee5d [file] [log] [blame]
# Copyright 2022 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import annotations
import abc
import logging
import pathlib
import re
from typing import Iterable, Optional, Union, TYPE_CHECKING
from urllib.parse import urlparse
import crossbench as cb
import crossbench.stories as stories
import crossbench.benchmarks.base as benchmarks
class Page(stories.Story, metaclass=abc.ABCMeta):
class LivePage(Page):
_DURATION_RE = re.compile(r"((\d*[.])?\d+)s?")
def story_names(cls):
return tuple( for page in PAGE_LIST)
def from_names(cls, name_or_url_list, separate=True):
if len(name_or_url_list) == 1 and name_or_url_list[0] == "all":
pages = PAGE_LIST
pages = cls._resolve_name_or_urls(name_or_url_list)
# Check if we have unique domain names for better short names
urls = list(urlparse(page.url) for page in pages)
hostnames = set(url.hostname for url in urls)
if len(hostnames) == len(urls):
pages = cls._resolve_name_or_urls(name_or_url_list, use_hostname=True)
if not separate and len(pages) > 1:
combined_name = "_".join( for page in pages)
pages = (CombinedPage(pages, combined_name),)"PAGES: %s", list(map(str, pages)))
return pages
def _resolve_name_or_urls(cls, name_or_url_list, use_hostname=False):
pages = []
page = None
for value in name_or_url_list:
if value in PAGES:
page = PAGES[value]
elif "://" in value:
name = value
url = value
if use_hostname:
name = urlparse(url).hostname
page = LivePage(name, url)
# Use the last created page and set the duration on it
assert page is not None, (
f"Duration '{value}' has to follow a URL or page-name.")
match = cls._DURATION_RE.match(value)
assert match, f"Duration '{value}' is not a number."
duration = float(
assert duration > 0, ("Duration should be positive. "
f"Got duration={duration} page={}")
page.duration = duration
return pages
def __init__(self, name, url, duration=15):
super().__init__(name, duration)
assert url, "Invalid page url"
self.url = url
def details_json(self):
result = super().details_json()
result["url"] = str(self.url)
return result
def run(self, run):
run.browser.show_url(run.runner, self.url)
run.runner.wait(self.duration + 1)
def __str__(self):
return f"Page(name={}, url={self.url})"
class CombinedPage(Page):
def story_names(cls):
raise NotImplementedError()
def from_names(cls, names, separate=False):
raise NotImplementedError()
def __init__(self, pages, name="combined"):
assert len(pages), "No sub-pages provided for CombinedPage"
assert len(pages) > 1, "Combined Page needs more than one page"
self._pages = pages
duration = sum(page.duration for page in pages)
super().__init__(name, duration)
self.url = None
def details_json(self):
result = super().details_json()
result["pages"] = list(page.details_json() for page in self._pages)
return result
def run(self, run):
for page in self._pages:
def __str__(self):
combined_name = ",".join( for page in self._pages)
return f"CombinedPage({combined_name})"
LivePage("amazon", "", 5),
LivePage("bing", "", 5),
LivePage("caf", "", 6),
LivePage("cnn", "", 7),
LivePage("ecma262", "", 10),
LivePage("expedia", "", 7),
LivePage("facebook", "", 8),
LivePage("maps", "", 10),
LivePage("microsoft", "", 6),
LivePage("provincial", "", 6),
LivePage("sueddeutsche", "", 8),
LivePage("timesofindia", "", 8),
LivePage("twitter", "", 6),
PAGES = { page for page in PAGE_LIST}
class PageLoadBenchmark(benchmarks.SubStoryBenchmark):
Benchmark runner for loading pages.
Use --urls/--stories to either choose from an existing set of pages, or direct
URLs. After each page you can also specify a custom wait/load duration in
seconds. Multiple URLs/page names can be provided as a comma-separated list.
Use --separate to load each page individually.
NAME = "loading"
def add_cli_parser(cls, subparsers):
parser = super().add_cli_parser(subparsers)
help="List of urls and durations to load: url,seconds,...")
return parser
def __init__(self,
stories: Union[Page, Iterable[Page]],
duration: Optional[float] = None):
if isinstance(stories, Page):
stories = [stories]
for story in stories:
assert isinstance(story, Page)
if duration is not None:
assert duration > 0, f"Invalid page duration={duration}s"
story.duration = duration