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 tags: 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 grouping_keys: A dict of grouping keys that will be added to values computed 28 on this story. 29 """ 30 31 def __init__(self, shared_state_class, name='', tags=None, 32 is_local=False, make_javascript_deterministic=True, 33 grouping_keys=None, platform_specific=False): 34 """ 35 Args: 36 make_javascript_deterministic: Whether JavaScript performed on 37 the page is made deterministic across multiple runs. This 38 requires that the web content is served via Web Page Replay 39 to take effect. This setting does not affect stories containing no web 40 content or where the HTTP MIME type is not text/html.See also: 41 _InjectScripts method in third_party/web-page-replay/httpclient.py. 42 platform_specific: Boolean indicating if a separate web page replay 43 recording is required on each platform. 44 """ 45 assert issubclass(shared_state_class, 46 shared_state_module.SharedState) 47 self._shared_state_class = shared_state_class 48 self._name = name 49 self._platform_specific = platform_specific 50 global _next_story_id 51 self._id = _next_story_id 52 _next_story_id += 1 53 if tags is None: 54 tags = set() 55 elif isinstance(tags, list): 56 tags = set(tags) 57 else: 58 assert isinstance(tags, set) 59 self._tags = tags 60 self._is_local = is_local 61 self._make_javascript_deterministic = make_javascript_deterministic 62 if grouping_keys is None: 63 grouping_keys = {} 64 else: 65 assert isinstance(grouping_keys, dict) 66 self._grouping_keys = grouping_keys 67 68 def Run(self, shared_state): 69 """Execute the interactions with the applications and/or platforms.""" 70 raise NotImplementedError 71 72 @property 73 def tags(self): 74 return self._tags 75 76 @property 77 def shared_state_class(self): 78 return self._shared_state_class 79 80 @property 81 def id(self): 82 return self._id 83 84 @property 85 def name(self): 86 return self._name 87 88 @property 89 def grouping_keys(self): 90 return self._grouping_keys 91 92 @property 93 def display_name_and_grouping_key_tuple(self): 94 return self.display_name, tuple(self.grouping_keys.iteritems()) 95 96 def AsDict(self): 97 """Converts a story object to a dict suitable for JSON output.""" 98 d = { 99 'id': self._id, 100 } 101 if self._name: 102 d['name'] = self._name 103 return d 104 105 @property 106 def file_safe_name(self): 107 """A version of display_name that's safe to use as a filename. 108 109 The default implementation sanitizes special characters with underscores, 110 but it's okay to override it with a more specific implementation in 111 subclasses. 112 """ 113 # This fail-safe implementation is safe for subclasses to override. 114 return re.sub('[^a-zA-Z0-9]', '_', self.display_name) 115 116 @property 117 def display_name(self): 118 if self.name: 119 return self.name 120 else: 121 return self.__class__.__name__ 122 123 @property 124 def is_local(self): 125 """Returns True iff this story does not require network.""" 126 return self._is_local 127 128 @property 129 def serving_dir(self): 130 """Returns the absolute path to a directory with hash files to data that 131 should be updated from cloud storage, or None if no files need to be 132 updated. 133 """ 134 return None 135 136 @property 137 def make_javascript_deterministic(self): 138 return self._make_javascript_deterministic 139 140 @property 141 def platform_specific(self): 142 return self._platform_specific 143