1# -*- coding: utf-8 -*- 2""" 3test_events.py 4~~~~~~~~~~~~~~ 5 6Specific tests for any function that is logically self-contained as part of 7events.py. 8""" 9import inspect 10import sys 11 12from hypothesis import given 13from hypothesis.strategies import ( 14 integers, lists, tuples 15) 16import pytest 17 18import h2.errors 19import h2.events 20import h2.settings 21 22 23# We define a fairly complex Hypothesis strategy here. We want to build a list 24# of two tuples of (Setting, value). For Setting we want to make sure we can 25# handle settings that the rest of hyper knows nothing about, so we want to 26# use integers from 0 to (2**16-1). For values, they're from 0 to (2**32-1). 27# Define that strategy here for clarity. 28SETTINGS_STRATEGY = lists( 29 tuples( 30 integers(min_value=0, max_value=2**16-1), 31 integers(min_value=0, max_value=2**32-1), 32 ) 33) 34 35 36class TestRemoteSettingsChanged(object): 37 """ 38 Validate the function of the RemoteSettingsChanged event. 39 """ 40 @given(SETTINGS_STRATEGY) 41 def test_building_settings_from_scratch(self, settings_list): 42 """ 43 Missing old settings are defaulted to None. 44 """ 45 settings_dict = dict(settings_list) 46 e = h2.events.RemoteSettingsChanged.from_settings( 47 old_settings={}, 48 new_settings=settings_dict, 49 ) 50 51 for setting, new_value in settings_dict.items(): 52 assert e.changed_settings[setting].setting == setting 53 assert e.changed_settings[setting].original_value is None 54 assert e.changed_settings[setting].new_value == new_value 55 56 @given(SETTINGS_STRATEGY, SETTINGS_STRATEGY) 57 def test_only_reports_changed_settings(self, 58 old_settings_list, 59 new_settings_list): 60 """ 61 Settings that were not changed are not reported. 62 """ 63 old_settings_dict = dict(old_settings_list) 64 new_settings_dict = dict(new_settings_list) 65 e = h2.events.RemoteSettingsChanged.from_settings( 66 old_settings=old_settings_dict, 67 new_settings=new_settings_dict, 68 ) 69 70 assert len(e.changed_settings) == len(new_settings_dict) 71 assert ( 72 sorted(list(e.changed_settings.keys())) == 73 sorted(list(new_settings_dict.keys())) 74 ) 75 76 @given(SETTINGS_STRATEGY, SETTINGS_STRATEGY) 77 def test_correctly_reports_changed_settings(self, 78 old_settings_list, 79 new_settings_list): 80 """ 81 Settings that are changed are correctly reported. 82 """ 83 old_settings_dict = dict(old_settings_list) 84 new_settings_dict = dict(new_settings_list) 85 e = h2.events.RemoteSettingsChanged.from_settings( 86 old_settings=old_settings_dict, 87 new_settings=new_settings_dict, 88 ) 89 90 for setting, new_value in new_settings_dict.items(): 91 original_value = old_settings_dict.get(setting) 92 assert e.changed_settings[setting].setting == setting 93 assert e.changed_settings[setting].original_value == original_value 94 assert e.changed_settings[setting].new_value == new_value 95 96 97class TestEventReprs(object): 98 """ 99 Events have useful representations. 100 """ 101 example_request_headers = [ 102 (':authority', 'example.com'), 103 (':path', '/'), 104 (':scheme', 'https'), 105 (':method', 'GET'), 106 ] 107 example_informational_headers = [ 108 (':status', '100'), 109 ('server', 'fake-serv/0.1.0') 110 ] 111 example_response_headers = [ 112 (':status', '200'), 113 ('server', 'fake-serv/0.1.0') 114 ] 115 116 def test_requestreceived_repr(self): 117 """ 118 RequestReceived has a useful debug representation. 119 """ 120 e = h2.events.RequestReceived() 121 e.stream_id = 5 122 e.headers = self.example_request_headers 123 124 assert repr(e) == ( 125 "<RequestReceived stream_id:5, headers:[" 126 "(':authority', 'example.com'), " 127 "(':path', '/'), " 128 "(':scheme', 'https'), " 129 "(':method', 'GET')]>" 130 ) 131 132 def test_responsereceived_repr(self): 133 """ 134 ResponseReceived has a useful debug representation. 135 """ 136 e = h2.events.ResponseReceived() 137 e.stream_id = 500 138 e.headers = self.example_response_headers 139 140 assert repr(e) == ( 141 "<ResponseReceived stream_id:500, headers:[" 142 "(':status', '200'), " 143 "('server', 'fake-serv/0.1.0')]>" 144 ) 145 146 def test_trailersreceived_repr(self): 147 """ 148 TrailersReceived has a useful debug representation. 149 """ 150 e = h2.events.TrailersReceived() 151 e.stream_id = 62 152 e.headers = self.example_response_headers 153 154 assert repr(e) == ( 155 "<TrailersReceived stream_id:62, headers:[" 156 "(':status', '200'), " 157 "('server', 'fake-serv/0.1.0')]>" 158 ) 159 160 def test_informationalresponsereceived_repr(self): 161 """ 162 InformationalResponseReceived has a useful debug representation. 163 """ 164 e = h2.events.InformationalResponseReceived() 165 e.stream_id = 62 166 e.headers = self.example_informational_headers 167 168 assert repr(e) == ( 169 "<InformationalResponseReceived stream_id:62, headers:[" 170 "(':status', '100'), " 171 "('server', 'fake-serv/0.1.0')]>" 172 ) 173 174 def test_datareceived_repr(self): 175 """ 176 DataReceived has a useful debug representation. 177 """ 178 e = h2.events.DataReceived() 179 e.stream_id = 888 180 e.data = b"abcdefghijklmnopqrstuvwxyz" 181 e.flow_controlled_length = 88 182 183 assert repr(e) == ( 184 "<DataReceived stream_id:888, flow_controlled_length:88, " 185 "data:6162636465666768696a6b6c6d6e6f7071727374>" 186 ) 187 188 def test_windowupdated_repr(self): 189 """ 190 WindowUpdated has a useful debug representation. 191 """ 192 e = h2.events.WindowUpdated() 193 e.stream_id = 0 194 e.delta = 2**16 195 196 assert repr(e) == "<WindowUpdated stream_id:0, delta:65536>" 197 198 def test_remotesettingschanged_repr(self): 199 """ 200 RemoteSettingsChanged has a useful debug representation. 201 """ 202 e = h2.events.RemoteSettingsChanged() 203 e.changed_settings = { 204 h2.settings.SettingCodes.INITIAL_WINDOW_SIZE: 205 h2.settings.ChangedSetting( 206 h2.settings.SettingCodes.INITIAL_WINDOW_SIZE, 2**16, 2**15 207 ), 208 } 209 210 assert repr(e) == ( 211 "<RemoteSettingsChanged changed_settings:{ChangedSetting(" 212 "setting=SettingCodes.INITIAL_WINDOW_SIZE, original_value=65536, " 213 "new_value=32768)}>" 214 ) 215 216 def test_pingreceived_repr(self): 217 """ 218 PingReceived has a useful debug representation. 219 """ 220 e = h2.events.PingReceived() 221 e.ping_data = b'abcdefgh' 222 223 assert repr(e) == "<PingReceived ping_data:6162636465666768>" 224 225 def test_pingackreceived_repr(self): 226 """ 227 PingAckReceived has a useful debug representation. 228 """ 229 e = h2.events.PingAckReceived() 230 e.ping_data = b'abcdefgh' 231 232 assert repr(e) == "<PingAckReceived ping_data:6162636465666768>" 233 234 def test_streamended_repr(self): 235 """ 236 StreamEnded has a useful debug representation. 237 """ 238 e = h2.events.StreamEnded() 239 e.stream_id = 99 240 241 assert repr(e) == "<StreamEnded stream_id:99>" 242 243 def test_streamreset_repr(self): 244 """ 245 StreamEnded has a useful debug representation. 246 """ 247 e = h2.events.StreamReset() 248 e.stream_id = 919 249 e.error_code = h2.errors.ErrorCodes.ENHANCE_YOUR_CALM 250 e.remote_reset = False 251 252 assert repr(e) == ( 253 "<StreamReset stream_id:919, " 254 "error_code:ErrorCodes.ENHANCE_YOUR_CALM, remote_reset:False>" 255 ) 256 257 def test_pushedstreamreceived_repr(self): 258 """ 259 PushedStreamReceived has a useful debug representation. 260 """ 261 e = h2.events.PushedStreamReceived() 262 e.pushed_stream_id = 50 263 e.parent_stream_id = 11 264 e.headers = self.example_request_headers 265 266 assert repr(e) == ( 267 "<PushedStreamReceived pushed_stream_id:50, parent_stream_id:11, " 268 "headers:[" 269 "(':authority', 'example.com'), " 270 "(':path', '/'), " 271 "(':scheme', 'https'), " 272 "(':method', 'GET')]>" 273 ) 274 275 def test_settingsacknowledged_repr(self): 276 """ 277 SettingsAcknowledged has a useful debug representation. 278 """ 279 e = h2.events.SettingsAcknowledged() 280 e.changed_settings = { 281 h2.settings.SettingCodes.INITIAL_WINDOW_SIZE: 282 h2.settings.ChangedSetting( 283 h2.settings.SettingCodes.INITIAL_WINDOW_SIZE, 2**16, 2**15 284 ), 285 } 286 287 assert repr(e) == ( 288 "<SettingsAcknowledged changed_settings:{ChangedSetting(" 289 "setting=SettingCodes.INITIAL_WINDOW_SIZE, original_value=65536, " 290 "new_value=32768)}>" 291 ) 292 293 def test_priorityupdated_repr(self): 294 """ 295 PriorityUpdated has a useful debug representation. 296 """ 297 e = h2.events.PriorityUpdated() 298 e.stream_id = 87 299 e.weight = 32 300 e.depends_on = 8 301 e.exclusive = True 302 303 assert repr(e) == ( 304 "<PriorityUpdated stream_id:87, weight:32, depends_on:8, " 305 "exclusive:True>" 306 ) 307 308 @pytest.mark.parametrize("additional_data,data_repr", [ 309 (None, "None"), 310 (b'some data', "736f6d652064617461") 311 ]) 312 def test_connectionterminated_repr(self, additional_data, data_repr): 313 """ 314 ConnectionTerminated has a useful debug representation. 315 """ 316 e = h2.events.ConnectionTerminated() 317 e.error_code = h2.errors.ErrorCodes.INADEQUATE_SECURITY 318 e.last_stream_id = 33 319 e.additional_data = additional_data 320 321 assert repr(e) == ( 322 "<ConnectionTerminated error_code:ErrorCodes.INADEQUATE_SECURITY, " 323 "last_stream_id:33, additional_data:%s>" % data_repr 324 ) 325 326 def test_alternativeserviceavailable_repr(self): 327 """ 328 AlternativeServiceAvailable has a useful debug representation. 329 """ 330 e = h2.events.AlternativeServiceAvailable() 331 e.origin = b"example.com" 332 e.field_value = b'h2=":8000"; ma=60' 333 334 assert repr(e) == ( 335 '<AlternativeServiceAvailable origin:example.com, ' 336 'field_value:h2=":8000"; ma=60>' 337 ) 338 339 def test_unknownframereceived_repr(self): 340 """ 341 UnknownFrameReceived has a useful debug representation. 342 """ 343 e = h2.events.UnknownFrameReceived() 344 assert repr(e) == '<UnknownFrameReceived>' 345 346 347def all_events(): 348 """ 349 Generates all the classes (i.e., events) defined in h2.events. 350 """ 351 for _, obj in inspect.getmembers(sys.modules['h2.events']): 352 353 # We are only interested in objects that are defined in h2.events; 354 # objects that are imported from other modules are not of interest. 355 if hasattr(obj, '__module__') and (obj.__module__ != 'h2.events'): 356 continue 357 358 if inspect.isclass(obj): 359 yield obj 360 361 362@pytest.mark.parametrize('event', all_events()) 363def test_all_events_subclass_from_event(event): 364 """ 365 Every event defined in h2.events subclasses from h2.events.Event. 366 """ 367 assert (event is h2.events.Event) or issubclass(event, h2.events.Event) 368