1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import re 6 7from telemetry.story import shared_state as shared_state_module 8 9_next_story_id = 0 10 11 12class Story(object): 13 """A class styled on unittest.TestCase for creating story tests. 14 15 Tests should override Run to maybe start the application and perform actions 16 on it. To share state between different tests, one can define a 17 shared_state which contains hooks that will be called before and 18 after mutiple stories run and in between runs. 19 20 Args: 21 shared_state_class: subclass of telemetry.story.shared_state.SharedState. 22 name: string name of this story that can be used for identifying this story 23 in results output. 24 labels: A list or set of string labels that are used for filtering. See 25 story.story_filter for more information. 26 is_local: If True, the story does not require network. 27 """ 28 29 def __init__(self, shared_state_class, name='', labels=None, 30 is_local=False, make_javascript_deterministic=True): 31 """ 32 Args: 33 make_javascript_deterministic: Whether JavaScript performed on 34 the page is made deterministic across multiple runs. This 35 requires that the web content is served via Web Page Replay 36 to take effect. This setting does not affect stories containing no web 37 content or where the HTTP MIME type is not text/html.See also: 38 _InjectScripts method in third_party/webpagereplay/httpclient.py. 39 """ 40 assert issubclass(shared_state_class, 41 shared_state_module.SharedState) 42 self._shared_state_class = shared_state_class 43 self._name = name 44 global _next_story_id 45 self._id = _next_story_id 46 _next_story_id += 1 47 if labels is None: 48 labels = set([]) 49 elif isinstance(labels, list): 50 labels = set(labels) 51 else: 52 assert isinstance(labels, set) 53 self._labels = labels 54 self._is_local = is_local 55 self._make_javascript_deterministic = make_javascript_deterministic 56 57 def Run(self, shared_state): 58 """Execute the interactions with the applications and/or platforms.""" 59 raise NotImplementedError 60 61 @property 62 def labels(self): 63 return self._labels 64 65 @property 66 def shared_state_class(self): 67 return self._shared_state_class 68 69 @property 70 def id(self): 71 return self._id 72 73 @property 74 def name(self): 75 return self._name 76 77 def AsDict(self): 78 """Converts a story object to a dict suitable for JSON output.""" 79 d = { 80 'id': self._id, 81 } 82 if self._name: 83 d['name'] = self._name 84 return d 85 86 @property 87 def file_safe_name(self): 88 """A version of display_name that's safe to use as a filename. 89 90 The default implementation sanitizes special characters with underscores, 91 but it's okay to override it with a more specific implementation in 92 subclasses. 93 """ 94 # This fail-safe implementation is safe for subclasses to override. 95 return re.sub('[^a-zA-Z0-9]', '_', self.display_name) 96 97 @property 98 def display_name(self): 99 if self.name: 100 return self.name 101 else: 102 return self.__class__.__name__ 103 104 @property 105 def is_local(self): 106 """Returns True iff this story does not require network.""" 107 return self._is_local 108 109 @property 110 def serving_dir(self): 111 """Returns the absolute path to a directory with hash files to data that 112 should be updated from cloud storage, or None if no files need to be 113 updated. 114 """ 115 return None 116 117 @property 118 def make_javascript_deterministic(self): 119 return self._make_javascript_deterministic 120