|
4 | 4 | import pytest
|
5 | 5 |
|
6 | 6 |
|
| 7 | +# https://docs.python.org/3/whatsnew/3.5.html#pep-492-coroutines-with-async-and-await-syntax |
7 | 8 | ASYNC_AWAIT = sys.version_info >= (3, 5)
|
8 | 9 |
|
| 10 | +# https://docs.python.org/3/whatsnew/3.6.html#pep-525-asynchronous-generators |
| 11 | +ASYNC_GENERATORS = sys.version_info >= (3, 6) |
| 12 | + |
9 | 13 |
|
10 | 14 | def assert_outcomes(run_result, outcomes):
|
11 | 15 | formatted_output = format_run_result_output_for_assert(run_result)
|
@@ -47,6 +51,13 @@ def skip_if_no_async_await():
|
47 | 51 | )
|
48 | 52 |
|
49 | 53 |
|
| 54 | +def skip_if_no_async_generators(): |
| 55 | + return pytest.mark.skipif( |
| 56 | + not ASYNC_GENERATORS, |
| 57 | + reason="async generators not support on Python <3.6", |
| 58 | + ) |
| 59 | + |
| 60 | + |
50 | 61 | @pytest.fixture
|
51 | 62 | def cmd_opts(request):
|
52 | 63 | reactor = request.config.getoption("reactor", "default")
|
@@ -303,6 +314,157 @@ async def test_succeed(foo):
|
303 | 314 | assert_outcomes(rr, {"passed": 2, "failed": 1})
|
304 | 315 |
|
305 | 316 |
|
| 317 | +@skip_if_no_async_await() |
| 318 | +def test_async_fixture(testdir, cmd_opts): |
| 319 | + test_file = """ |
| 320 | + from twisted.internet import reactor, defer |
| 321 | + import pytest |
| 322 | + import pytest_twisted |
| 323 | +
|
| 324 | + @pytest_twisted.async_fixture(scope="function", params=["fs", "imap", "web"]) |
| 325 | + @pytest.mark.redgreenblue |
| 326 | + async def foo(request): |
| 327 | + d1, d2 = defer.Deferred(), defer.Deferred() |
| 328 | + reactor.callLater(0.01, d1.callback, 1) |
| 329 | + reactor.callLater(0.02, d2.callback, request.param) |
| 330 | + await d1 |
| 331 | + return d2, |
| 332 | +
|
| 333 | + @pytest_twisted.inlineCallbacks |
| 334 | + def test_succeed_blue(foo): |
| 335 | + x = yield foo[0] |
| 336 | + if x == "web": |
| 337 | + raise RuntimeError("baz") |
| 338 | + """ |
| 339 | + testdir.makepyfile(test_file) |
| 340 | + rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts) |
| 341 | + assert_outcomes(rr, {"passed": 2, "failed": 1}) |
| 342 | + |
| 343 | + |
| 344 | +@skip_if_no_async_generators() |
| 345 | +def test_async_yield_fixture_concurrent_teardown(testdir, cmd_opts): |
| 346 | + test_file = """ |
| 347 | + from twisted.internet import reactor, defer |
| 348 | + import pytest |
| 349 | + import pytest_twisted |
| 350 | +
|
| 351 | +
|
| 352 | + here = defer.Deferred() |
| 353 | + there = defer.Deferred() |
| 354 | +
|
| 355 | + @pytest_twisted.async_yield_fixture() |
| 356 | + async def this(): |
| 357 | + yield 42 |
| 358 | +
|
| 359 | + there.callback(None) |
| 360 | + reactor.callLater(5, here.cancel) |
| 361 | + await here |
| 362 | +
|
| 363 | + @pytest_twisted.async_yield_fixture() |
| 364 | + async def that(): |
| 365 | + yield 37 |
| 366 | +
|
| 367 | + here.callback(None) |
| 368 | + reactor.callLater(5, there.cancel) |
| 369 | + await there |
| 370 | +
|
| 371 | + def test_succeed(this, that): |
| 372 | + pass |
| 373 | + """ |
| 374 | + testdir.makepyfile(test_file) |
| 375 | + # TODO: add a timeout, failure just hangs indefinitely for now |
| 376 | + # https://github.com/pytest-dev/pytest/issues/4073 |
| 377 | + rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts) |
| 378 | + assert_outcomes(rr, {"passed": 1}) |
| 379 | + |
| 380 | + |
| 381 | +@skip_if_no_async_generators() |
| 382 | +def test_async_yield_fixture(testdir, cmd_opts): |
| 383 | + test_file = """ |
| 384 | + from twisted.internet import reactor, defer |
| 385 | + import pytest |
| 386 | + import pytest_twisted |
| 387 | +
|
| 388 | + @pytest_twisted.async_yield_fixture( |
| 389 | + scope="function", |
| 390 | + params=["fs", "imap", "web", "gopher", "archie"], |
| 391 | + ) |
| 392 | + async def foo(request): |
| 393 | + d1, d2 = defer.Deferred(), defer.Deferred() |
| 394 | + reactor.callLater(0.01, d1.callback, 1) |
| 395 | + reactor.callLater(0.02, d2.callback, request.param) |
| 396 | + await d1 |
| 397 | +
|
| 398 | + # Twisted doesn't allow calling back with a Deferred as a value. |
| 399 | + # This deferred is being wrapped up in a tuple to sneak through. |
| 400 | + # https://github.com/twisted/twisted/blob/c0f1394c7bfb04d97c725a353a1f678fa6a1c602/src/twisted/internet/defer.py#L459 |
| 401 | + yield d2, |
| 402 | +
|
| 403 | + if request.param == "gopher": |
| 404 | + raise RuntimeError("gaz") |
| 405 | +
|
| 406 | + if request.param == "archie": |
| 407 | + yield 42 |
| 408 | +
|
| 409 | + @pytest_twisted.inlineCallbacks |
| 410 | + def test_succeed(foo): |
| 411 | + x = yield foo[0] |
| 412 | + if x == "web": |
| 413 | + raise RuntimeError("baz") |
| 414 | + """ |
| 415 | + testdir.makepyfile(test_file) |
| 416 | + rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts) |
| 417 | + assert_outcomes(rr, {"passed": 2, "failed": 3}) |
| 418 | + |
| 419 | + |
| 420 | +@skip_if_no_async_generators() |
| 421 | +def test_async_yield_fixture_function_scope(testdir, cmd_opts): |
| 422 | + test_file = """ |
| 423 | + from twisted.internet import reactor, defer |
| 424 | + import pytest |
| 425 | + import pytest_twisted |
| 426 | +
|
| 427 | + check_me = 0 |
| 428 | +
|
| 429 | + @pytest_twisted.async_yield_fixture(scope="function") |
| 430 | + async def foo(): |
| 431 | + global check_me |
| 432 | +
|
| 433 | + if check_me != 0: |
| 434 | + raise Exception('check_me already modified before fixture run') |
| 435 | +
|
| 436 | + check_me = 1 |
| 437 | +
|
| 438 | + yield 42 |
| 439 | +
|
| 440 | + if check_me != 2: |
| 441 | + raise Exception( |
| 442 | + 'check_me not updated properly: {}'.format(check_me), |
| 443 | + ) |
| 444 | +
|
| 445 | + check_me = 0 |
| 446 | +
|
| 447 | + def test_first(foo): |
| 448 | + global check_me |
| 449 | +
|
| 450 | + assert check_me == 1 |
| 451 | + assert foo == 42 |
| 452 | +
|
| 453 | + check_me = 2 |
| 454 | +
|
| 455 | + def test_second(foo): |
| 456 | + global check_me |
| 457 | +
|
| 458 | + assert check_me == 1 |
| 459 | + assert foo == 42 |
| 460 | +
|
| 461 | + check_me = 2 |
| 462 | + """ |
| 463 | + testdir.makepyfile(test_file) |
| 464 | + rr = testdir.run(sys.executable, "-m", "pytest", "-v", *cmd_opts) |
| 465 | + assert_outcomes(rr, {"passed": 2}) |
| 466 | + |
| 467 | + |
306 | 468 | def test_blockon_in_hook(testdir, cmd_opts, request):
|
307 | 469 | skip_if_reactor_not(request, "default")
|
308 | 470 | conftest_file = """
|
|
0 commit comments