# 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. from __future__ import annotations import abc import datetime as dt import logging from typing import TYPE_CHECKING, Sequence from crossbench.path import safe_filename if TYPE_CHECKING: from crossbench.runner.run import Run from crossbench.types import JsonDict class Story(abc.ABC): @classmethod @abc.abstractmethod def all_story_names(cls) -> Sequence[str]: pass def __init__(self, name: str, duration: dt.timedelta = dt.timedelta(seconds=15)): assert name, "Invalid page name" self._name = safe_filename(name) self._duration = duration if self._duration: assert self._duration.total_seconds() > 0, ( f"Duration must be non-empty, but got: {duration}") @property def name(self) -> str: return self._name @property def duration(self) -> dt.timedelta: return self._duration def details_json(self) -> JsonDict: return {"name": self.name, "duration": self.duration.total_seconds()} def log_run_details(self, run: Run) -> None: logging.info("STORY: %s", self) timing = run.timing logging.info("STORY DURATION: expected=%s timeout=%s", timing.timedelta(self.duration), timing.timeout_timedelta(self.duration)) def setup(self, run: Run) -> None: """Setup work for a story that is not part of the main workload should be put in this method. Probes can skip measuring this section. i.e selecting substories to run. """ @abc.abstractmethod def run(self, run: Run) -> None: """The main workload of a story that is measured by all Probes. """ def teardown(self, run: Run) -> None: """Cleanup work for a story that is not part of the main workload should be put in this method. Probes can skip measuring this section. """ def __str__(self) -> str: return f"Story(name={self.name})"