1#!/usr/bin/env python3 2# 3# Copyright 2019 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import logging 18import time 19import traceback 20from datetime import datetime, timedelta 21from threading import Timer 22 23from blueberry.tests.gd.cert.behavior import when, wait_until 24from blueberry.tests.gd.cert.behavior import IHasBehaviors 25from blueberry.tests.gd.cert.behavior import anything 26from blueberry.tests.gd.cert.behavior import SingleArgumentBehavior 27from blueberry.tests.gd.cert.behavior import ReplyStage 28from blueberry.tests.gd.cert.event_stream import EventStream, FilteringEventStream 29from blueberry.tests.gd.cert.metadata import metadata 30from blueberry.tests.gd.cert.truth import assertThat 31import hci_packets as hci 32 33from mobly import asserts 34from mobly import signals 35from mobly import test_runner 36from mobly import base_test 37 38 39class BogusProto: 40 41 class BogusType: 42 43 def __init__(self): 44 self.name = "BogusProto" 45 self.is_extension = False 46 self.cpp_type = False 47 48 def type(self): 49 return 'BogusRpc' 50 51 def label(self): 52 return "label" 53 54 class BogusDescriptor: 55 56 def __init__(self, name): 57 self.full_name = name 58 59 def __init__(self, value): 60 self.value_ = value 61 self.DESCRIPTOR = BogusProto.BogusDescriptor(str(value)) 62 63 def __str__(self): 64 return "BogusRpc value = " + str(self.value_) 65 66 def ListFields(self): 67 for field in [BogusProto.BogusType()]: 68 yield [field, self.value_] 69 70 71class FetchEvents: 72 73 def __init__(self, events, delay_ms): 74 self.events_ = events 75 self.sleep_time_ = (delay_ms * 1.0) / 1000 76 self.index_ = 0 77 self.done_ = False 78 self.then_ = datetime.now() 79 80 def __iter__(self): 81 for event in self.events_: 82 time.sleep(self.sleep_time_) 83 if self.done_: 84 return 85 logging.debug("yielding %d" % event) 86 yield BogusProto(event) 87 88 def done(self): 89 return self.done_ 90 91 def cancel(self): 92 logging.debug("cancel") 93 self.done_ = True 94 return None 95 96 97class TestBehaviors(object): 98 99 def __init__(self, parent): 100 self.test_request_behavior = SingleArgumentBehavior(lambda: TestBehaviors.TestRequestReplyStage(parent)) 101 102 def test_request(self, matcher): 103 return self.test_request_behavior.begin(matcher) 104 105 class TestRequestReplyStage(ReplyStage): 106 107 def __init__(self, parent): 108 self._parent = parent 109 110 def increment_count(self): 111 self._commit(lambda obj: self._increment_count(obj)) 112 return self 113 114 def _increment_count(self, obj): 115 self._parent.count += 1 116 self._parent.captured.append(obj) 117 118 119class ObjectWithBehaviors(IHasBehaviors): 120 121 def __init__(self): 122 self.behaviors = TestBehaviors(self) 123 self.count = 0 124 self.captured = [] 125 self.unhandled_count = 0 126 127 def get_behaviors(self): 128 return self.behaviors 129 130 def increment_unhandled(self): 131 self.unhandled_count += 1 132 133 134class CertSelfTest(base_test.BaseTestClass): 135 136 def setup_test(self): 137 return True 138 139 def teardown_test(self): 140 return True 141 142 def test_assert_occurs_at_least_passes(self): 143 with EventStream(FetchEvents(events=[1, 2, 3, 1, 2, 3], delay_ms=40)) as event_stream: 144 event_stream.assert_event_occurs(lambda data: data.value_ == 1, 145 timeout=timedelta(milliseconds=300), 146 at_least_times=2) 147 148 def test_assert_occurs_passes(self): 149 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 150 event_stream.assert_event_occurs(lambda data: data.value_ == 1, timeout=timedelta(seconds=1)) 151 152 def test_assert_occurs_fails(self): 153 try: 154 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 155 event_stream.assert_event_occurs(lambda data: data.value_ == 4, timeout=timedelta(seconds=1)) 156 except Exception as e: 157 logging.debug(e) 158 return True # Failed as expected 159 return False 160 161 def test_assert_occurs_at_most_passes(self): 162 with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=50)) as event_stream: 163 event_stream.assert_event_occurs_at_most(lambda data: data.value_ < 4, 164 timeout=timedelta(seconds=1), 165 at_most_times=3) 166 167 def test_assert_occurs_at_most_fails(self): 168 try: 169 with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=50)) as event_stream: 170 event_stream.assert_event_occurs_at_most(lambda data: data.value_ > 1, 171 timeout=timedelta(seconds=1), 172 at_most_times=2) 173 except Exception as e: 174 logging.debug(e) 175 return True # Failed as expected 176 return False 177 178 def test_skip_a_test(self): 179 asserts.skip("Skipping this test because it's blocked by b/xyz") 180 assert False 181 182 def test_nested_packets(self): 183 handle = 123 184 inside = hci.ReadScanEnable() 185 logging.debug(inside.serialize()) 186 logging.debug("building outside") 187 outside = hci.Acl(handle=handle, 188 packet_boundary_flag=hci.PacketBoundaryFlag.FIRST_NON_AUTOMATICALLY_FLUSHABLE, 189 broadcast_flag=hci.BroadcastFlag.POINT_TO_POINT, 190 payload=inside.serialize()) 191 logging.debug(outside.serialize()) 192 logging.debug("Done!") 193 194 def test_assertThat_boolean_success(self): 195 assertThat(True).isTrue() 196 assertThat(False).isFalse() 197 198 def test_assertThat_boolean_falseIsTrue(self): 199 try: 200 assertThat(False).isTrue() 201 except Exception as e: 202 return True 203 return False 204 205 def test_assertThat_boolean_trueIsFalse(self): 206 try: 207 assertThat(True).isFalse() 208 except Exception as e: 209 return True 210 return False 211 212 def test_assertThat_object_success(self): 213 assertThat("this").isEqualTo("this") 214 assertThat("this").isNotEqualTo("that") 215 assertThat(None).isNone() 216 assertThat("this").isNotNone() 217 218 def test_assertThat_object_isEqualToFails(self): 219 try: 220 assertThat("this").isEqualTo("that") 221 except Exception as e: 222 return True 223 return False 224 225 def test_assertThat_object_isNotEqualToFails(self): 226 try: 227 assertThat("this").isNotEqualTo("this") 228 except Exception as e: 229 return True 230 return False 231 232 def test_assertThat_object_isNoneFails(self): 233 try: 234 assertThat("this").isNone() 235 except Exception as e: 236 return True 237 return False 238 239 def test_assertThat_object_isNotNoneFails(self): 240 try: 241 assertThat(None).isNotNone() 242 except Exception as e: 243 return True 244 return False 245 246 def test_assertThat_eventStream_emits_passes(self): 247 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 248 assertThat(event_stream).emits(lambda data: data.value_ == 1) 249 250 def test_assertThat_eventStream_emits_then_passes(self): 251 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 252 assertThat(event_stream).emits(lambda data: data.value_ == 1).then(lambda data: data.value_ == 3) 253 254 def test_assertThat_eventStream_emits_fails(self): 255 try: 256 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 257 assertThat(event_stream).emits(lambda data: data.value_ == 4) 258 except Exception as e: 259 logging.debug(e) 260 return True # Failed as expected 261 return False 262 263 def test_assertThat_eventStream_emits_then_fails(self): 264 try: 265 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 266 assertThat(event_stream).emits(lambda data: data.value_ == 1).emits(lambda data: data.value_ == 4) 267 except Exception as e: 268 logging.debug(e) 269 return True # Failed as expected 270 return False 271 272 def test_assertThat_eventStream_emitsInOrder_passes(self): 273 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 274 assertThat(event_stream).emits(lambda data: data.value_ == 1, lambda data: data.value_ == 2).inOrder() 275 276 def test_assertThat_eventStream_emitsInAnyOrder_passes(self): 277 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 278 assertThat(event_stream).emits( 279 lambda data: data.value_ == 2, 280 lambda data: data.value_ == 1).inAnyOrder().then(lambda data: data.value_ == 3) 281 282 def test_assertThat_eventStream_emitsInOrder_fails(self): 283 try: 284 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 285 assertThat(event_stream).emits(lambda data: data.value_ == 2, lambda data: data.value_ == 1).inOrder() 286 except Exception as e: 287 logging.debug(e) 288 return True # Failed as expected 289 return False 290 291 def test_assertThat_eventStream_emitsInAnyOrder_fails(self): 292 try: 293 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 294 assertThat(event_stream).emits(lambda data: data.value_ == 4, 295 lambda data: data.value_ == 1).inAnyOrder() 296 except Exception as e: 297 logging.debug(e) 298 return True # Failed as expected 299 return False 300 301 def test_assertThat_emitsNone_passes(self): 302 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 303 assertThat(event_stream).emitsNone(lambda data: data.value_ == 4, timeout=timedelta(seconds=0.15)).thenNone( 304 lambda data: data.value_ == 5, timeout=timedelta(seconds=0.15)) 305 306 def test_assertThat_emitsNone_passes_after_1_second(self): 307 with EventStream(FetchEvents(events=[1, 2, 3, 4], delay_ms=400)) as event_stream: 308 assertThat(event_stream).emitsNone(lambda data: data.value_ == 4, timeout=timedelta(seconds=1)) 309 310 def test_assertThat_emitsNone_fails(self): 311 try: 312 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 313 assertThat(event_stream).emitsNone(lambda data: data.value_ == 2, timeout=timedelta(seconds=1)) 314 except Exception as e: 315 logging.debug(e) 316 return True # Failed as expected 317 return False 318 319 def test_assertThat_emitsNone_zero_passes(self): 320 with EventStream(FetchEvents(events=[], delay_ms=50)) as event_stream: 321 assertThat(event_stream).emitsNone(timeout=timedelta(milliseconds=10)).thenNone(timeout=timedelta( 322 milliseconds=10)) 323 324 def test_assertThat_emitsNone_zero_passes_after_one_second(self): 325 with EventStream(FetchEvents([1], delay_ms=1500)) as event_stream: 326 assertThat(event_stream).emitsNone(timeout=timedelta(seconds=1.0)) 327 328 def test_assertThat_emitsNone_zero_fails(self): 329 try: 330 with EventStream(FetchEvents(events=[17], delay_ms=50)) as event_stream: 331 assertThat(event_stream).emitsNone(timeout=timedelta(seconds=1)) 332 except Exception as e: 333 logging.debug(e) 334 return True # Failed as expected 335 return False 336 337 def test_filtering_event_stream_none_filter_function(self): 338 with EventStream(FetchEvents(events=[1, 2, 3], delay_ms=50)) as event_stream: 339 filtered_event_stream = FilteringEventStream(event_stream, None) 340 assertThat(filtered_event_stream) \ 341 .emits(lambda data: data.value_ == 1) \ 342 .then(lambda data: data.value_ == 3) 343 344 def test_metadata_empty(self): 345 346 @metadata() 347 def simple_pass_test(arg): 348 pass 349 350 try: 351 simple_pass_test(1) 352 except signals.TestFailure: 353 pass 354 except Exception as e: 355 asserts.fail("@metadata() should only raise signals.TestFailure, " 356 "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) 357 else: 358 asserts.fail("@metadata() should not work") 359 360 def test_metadata_empty_no_function_call(self): 361 362 @metadata 363 def simple_pass_test(arg): 364 pass 365 366 try: 367 simple_pass_test(1) 368 except signals.TestFailure: 369 pass 370 except Exception as e: 371 asserts.fail("@metadata should only raise signals.TestFailure, " 372 "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) 373 else: 374 asserts.fail("@metadata should not work") 375 376 def test_metadata_pts_missing_id(self): 377 378 @metadata(pts_test_name="Hello world") 379 def simple_pass_test(arg): 380 pass 381 382 try: 383 simple_pass_test(1) 384 except signals.TestFailure: 385 pass 386 except Exception as e: 387 asserts.fail("should only raise signals.TestFailure, " 388 "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) 389 else: 390 asserts.fail("missing pts_test_id should not work") 391 392 def test_metadata_pts_missing_name(self): 393 394 @metadata(pts_test_id="A/B/C") 395 def simple_pass_test(arg): 396 pass 397 398 try: 399 simple_pass_test(1) 400 except signals.TestFailure: 401 pass 402 except Exception as e: 403 asserts.fail("should only raise signals.TestFailure, " 404 "but raised %s with msg %s instead" % (e.__class__.__name__, str(e))) 405 else: 406 asserts.fail("missing pts_test_name should not work") 407 408 def test_metadata_pts_test_id_and_description(self): 409 410 @metadata(pts_test_id="A/B/C", pts_test_name="Hello world") 411 def simple_pass_test(arg): 412 pass 413 414 try: 415 simple_pass_test(1) 416 except signals.TestPass as e: 417 asserts.assert_true("pts_test_id" in e.extras, msg=("pts_test_id not in extra: %s" % str(e.extras))) 418 asserts.assert_equal(e.extras["pts_test_id"], "A/B/C") 419 asserts.assert_true("pts_test_name" in e.extras, msg=("pts_test_name not in extra: %s" % str(e.extras))) 420 asserts.assert_equal(e.extras["pts_test_name"], "Hello world") 421 else: 422 asserts.fail("Must throw an exception using @metadata decorator") 423 424 def test_metadata_test_with_exception_stacktrace(self): 425 426 @metadata(pts_test_id="A/B/C", pts_test_name="Hello world") 427 def simple_fail_test(failure_argument): 428 raise ValueError(failure_argument) 429 430 try: 431 simple_fail_test("BEEFBEEF") 432 except signals.TestError as e: 433 asserts.assert_true("pts_test_id" in e.extras, msg=("pts_test_id not in extra: %s" % str(e.extras))) 434 asserts.assert_equal(e.extras["pts_test_id"], "A/B/C") 435 asserts.assert_true("pts_test_name" in e.extras, msg=("pts_test_name not in extra: %s" % str(e.extras))) 436 asserts.assert_equal(e.extras["pts_test_name"], "Hello world") 437 trace_str = traceback.format_exc() 438 asserts.assert_true("raise ValueError(failure_argument)" in trace_str, 439 msg="Failed test method not in error stack trace: %s" % trace_str) 440 else: 441 asserts.fail("Must throw an exception using @metadata decorator") 442 443 def test_fluent_behavior_simple(self): 444 thing = ObjectWithBehaviors() 445 446 when(thing).test_request(anything()).then().increment_count() 447 448 thing.behaviors.test_request_behavior.run("A") 449 450 assertThat(thing.count).isEqualTo(1) 451 assertThat(thing.captured).isEqualTo(["A"]) 452 453 def test_fluent_behavior__then_single__captures_one(self): 454 thing = ObjectWithBehaviors() 455 456 thing.behaviors.test_request_behavior.set_default_to_ignore() 457 458 when(thing).test_request(anything()).then().increment_count() 459 460 thing.behaviors.test_request_behavior.run("A") 461 thing.behaviors.test_request_behavior.run("A") 462 thing.behaviors.test_request_behavior.run("A") 463 464 assertThat(thing.count).isEqualTo(1) 465 assertThat(thing.captured).isEqualTo(["A"]) 466 467 def test_fluent_behavior__then_times__captures_all(self): 468 thing = ObjectWithBehaviors() 469 470 when(thing).test_request(anything()).then(times=3).increment_count() 471 472 thing.behaviors.test_request_behavior.run("A") 473 thing.behaviors.test_request_behavior.run("B") 474 thing.behaviors.test_request_behavior.run("C") 475 476 assertThat(thing.count).isEqualTo(3) 477 assertThat(thing.captured).isEqualTo(["A", "B", "C"]) 478 479 def test_fluent_behavior__always__captures_all(self): 480 thing = ObjectWithBehaviors() 481 482 when(thing).test_request(anything()).always().increment_count() 483 484 thing.behaviors.test_request_behavior.run("A") 485 thing.behaviors.test_request_behavior.run("B") 486 thing.behaviors.test_request_behavior.run("C") 487 488 assertThat(thing.count).isEqualTo(3) 489 assertThat(thing.captured).isEqualTo(["A", "B", "C"]) 490 491 def test_fluent_behavior__matcher__captures_relevant(self): 492 thing = ObjectWithBehaviors() 493 thing.behaviors.test_request_behavior.set_default_to_ignore() 494 495 when(thing).test_request(lambda obj: obj == "B").always().increment_count() 496 497 thing.behaviors.test_request_behavior.run("A") 498 thing.behaviors.test_request_behavior.run("B") 499 thing.behaviors.test_request_behavior.run("C") 500 501 assertThat(thing.count).isEqualTo(1) 502 assertThat(thing.captured).isEqualTo(["B"]) 503 504 def test_fluent_behavior__then_repeated__captures_relevant(self): 505 thing = ObjectWithBehaviors() 506 thing.behaviors.test_request_behavior.set_default_to_ignore() 507 508 when(thing).test_request(anything()).then().increment_count().increment_count() 509 510 thing.behaviors.test_request_behavior.run("A") 511 thing.behaviors.test_request_behavior.run("B") 512 thing.behaviors.test_request_behavior.run("A") 513 514 assertThat(thing.count).isEqualTo(2) 515 assertThat(thing.captured).isEqualTo(["A", "B"]) 516 517 def test_fluent_behavior__fallback__captures_relevant(self): 518 thing = ObjectWithBehaviors() 519 thing.behaviors.test_request_behavior.set_default_to_ignore() 520 521 when(thing).test_request(lambda obj: obj == "B").then(times=1).increment_count() 522 when(thing).test_request(lambda obj: obj == "C").always().increment_count() 523 524 thing.behaviors.test_request_behavior.run("A") 525 thing.behaviors.test_request_behavior.run("B") 526 thing.behaviors.test_request_behavior.run("A") 527 thing.behaviors.test_request_behavior.run("C") 528 thing.behaviors.test_request_behavior.run("B") 529 thing.behaviors.test_request_behavior.run("C") 530 531 assertThat(thing.count).isEqualTo(3) 532 assertThat(thing.captured).isEqualTo(["B", "C", "C"]) 533 534 def test_fluent_behavior__default_unhandled_crash(self): 535 thing = ObjectWithBehaviors() 536 537 when(thing).test_request(anything()).then().increment_count() 538 539 thing.behaviors.test_request_behavior.run("A") 540 try: 541 thing.behaviors.test_request_behavior.run("A") 542 except Exception as e: 543 logging.debug(e) 544 return True # Failed as expected 545 return False 546 547 def test_fluent_behavior__set_default_works(self): 548 thing = ObjectWithBehaviors() 549 thing.behaviors.test_request_behavior.set_default(lambda obj: thing.increment_unhandled()) 550 551 when(thing).test_request(anything()).then().increment_count() 552 553 thing.behaviors.test_request_behavior.run("A") 554 thing.behaviors.test_request_behavior.run("A") 555 assertThat(thing.unhandled_count).isEqualTo(1) 556 557 def test_fluent_behavior__wait_until_done(self): 558 thing = ObjectWithBehaviors() 559 is_a = lambda obj: obj == "A" 560 when(thing).test_request(is_a).then().increment_count() 561 562 closure = lambda: thing.behaviors.test_request_behavior.run("A") 563 t = Timer(0.5, closure) 564 t.start() 565 566 wait_until(thing).test_request(is_a).times(1) 567 assertThat(thing.count).isEqualTo(1) 568 assertThat(thing.captured).isEqualTo(["A"]) 569 570 def test_fluent_behavior__wait_until_done_different_lambda(self): 571 thing = ObjectWithBehaviors() 572 when(thing).test_request(lambda obj: obj == "A").then().increment_count() 573 574 closure = lambda: thing.behaviors.test_request_behavior.run("A") 575 t = Timer(0.5, closure) 576 t.start() 577 578 wait_until(thing).test_request(lambda obj: obj == "A").times(1) 579 assertThat(thing.count).isEqualTo(1) 580 assertThat(thing.captured).isEqualTo(["A"]) 581 582 def test_fluent_behavior__wait_until_done_anything(self): 583 thing = ObjectWithBehaviors() 584 when(thing).test_request(lambda obj: obj == "A").then().increment_count() 585 586 closure = lambda: thing.behaviors.test_request_behavior.run("A") 587 t = Timer(0.5, closure) 588 t.start() 589 590 wait_until(thing).test_request(anything()).times(1) 591 assertThat(thing.count).isEqualTo(1) 592 assertThat(thing.captured).isEqualTo(["A"]) 593 594 def test_fluent_behavior__wait_until_done_not_happened(self): 595 thing = ObjectWithBehaviors() 596 thing.behaviors.test_request_behavior.set_default_to_ignore() 597 when(thing).test_request(lambda obj: obj == "A").then().increment_count() 598 599 closure = lambda: thing.behaviors.test_request_behavior.run("B") 600 t = Timer(0.5, closure) 601 t.start() 602 assertThat(wait_until(thing).test_request(lambda obj: obj == "A").times(1)).isFalse() 603 604 def test_fluent_behavior__wait_until_done_with_default(self): 605 thing = ObjectWithBehaviors() 606 thing.behaviors.test_request_behavior.set_default(lambda obj: thing.increment_unhandled()) 607 608 closure = lambda: thing.behaviors.test_request_behavior.run("A") 609 t = Timer(0.5, closure) 610 t.start() 611 612 wait_until(thing).test_request(anything()).times(1) 613 assertThat(thing.unhandled_count).isEqualTo(1) 614 615 def test_fluent_behavior__wait_until_done_two_events_AA(self): 616 thing = ObjectWithBehaviors() 617 when(thing).test_request(lambda obj: obj == "A").then().increment_count().increment_count() 618 619 closure1 = lambda: thing.behaviors.test_request_behavior.run("A") 620 t1 = Timer(0.5, closure1) 621 t1.start() 622 closure2 = lambda: thing.behaviors.test_request_behavior.run("A") 623 t2 = Timer(0.5, closure2) 624 t2.start() 625 626 wait_until(thing).test_request(lambda obj: obj == "A").times(2) 627 assertThat(thing.count).isEqualTo(2) 628 assertThat(thing.captured).isEqualTo(["A", "A"]) 629 630 def test_fluent_behavior__wait_until_done_two_events_AB(self): 631 thing = ObjectWithBehaviors() 632 when(thing).test_request(anything()).always().increment_count() 633 634 closure1 = lambda: thing.behaviors.test_request_behavior.run("A") 635 t1 = Timer(0.5, closure1) 636 t1.start() 637 closure2 = lambda: thing.behaviors.test_request_behavior.run("B") 638 t2 = Timer(1, closure2) 639 t2.start() 640 641 wait_until(thing).test_request(anything()).times(2) 642 assertThat(thing.count).isEqualTo(2) 643 assertThat(thing.captured).isEqualTo(["A", "B"]) 644 645 def test_fluent_behavior__wait_until_done_only_one_event_is_done(self): 646 thing = ObjectWithBehaviors() 647 when(thing).test_request(anything()).always().increment_count() 648 649 closure1 = lambda: thing.behaviors.test_request_behavior.run("A") 650 t1 = Timer(1, closure1) 651 t1.start() 652 closure2 = lambda: thing.behaviors.test_request_behavior.run("B") 653 t2 = Timer(3, closure2) 654 t2.start() 655 assertThat(wait_until(thing).test_request(lambda obj: obj == "A").times(2)).isFalse() 656 657 658if __name__ == '__main__': 659 test_runner.main() 660