1# Copyright 2023 The Chromium Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import annotations 6 7import abc 8import datetime as dt 9import logging 10from typing import TYPE_CHECKING, Sequence 11 12from crossbench.path import safe_filename 13 14if TYPE_CHECKING: 15 from crossbench.runner.run import Run 16 from crossbench.types import JsonDict 17 18 19class Story(abc.ABC): 20 @classmethod 21 @abc.abstractmethod 22 def all_story_names(cls) -> Sequence[str]: 23 pass 24 25 def __init__(self, 26 name: str, 27 duration: dt.timedelta = dt.timedelta(seconds=15)): 28 assert name, "Invalid page name" 29 self._name = safe_filename(name) 30 self._duration = duration 31 if self._duration: 32 assert self._duration.total_seconds() > 0, ( 33 f"Duration must be non-empty, but got: {duration}") 34 35 @property 36 def name(self) -> str: 37 return self._name 38 39 @property 40 def duration(self) -> dt.timedelta: 41 return self._duration 42 43 def details_json(self) -> JsonDict: 44 return {"name": self.name, "duration": self.duration.total_seconds()} 45 46 def log_run_details(self, run: Run) -> None: 47 logging.info("STORY: %s", self) 48 timing = run.timing 49 logging.info("STORY DURATION: expected=%s timeout=%s", 50 timing.timedelta(self.duration), 51 timing.timeout_timedelta(self.duration)) 52 53 def setup(self, run: Run) -> None: 54 """Setup work for a story that is not part of the main workload should 55 be put in this method. Probes can skip measuring this section. 56 i.e selecting substories to run. 57 """ 58 59 @abc.abstractmethod 60 def run(self, run: Run) -> None: 61 """The main workload of a story that is measured by all Probes. 62 """ 63 64 def teardown(self, run: Run) -> None: 65 """Cleanup work for a story that is not part of the main workload should 66 be put in this method. Probes can skip measuring this section. 67 """ 68 69 def __str__(self) -> str: 70 return f"Story(name={self.name})" 71