|
18 | 18 | import itertools |
19 | 19 | import operator as op |
20 | 20 | import os |
| 21 | +import psutil |
21 | 22 | import threading |
22 | 23 | import time |
23 | 24 |
|
@@ -46,7 +47,7 @@ def publisher(): |
46 | 47 | yield pubsub_v1.PublisherClient() |
47 | 48 |
|
48 | 49 |
|
49 | | -@pytest.fixture(scope=u"module") |
| 50 | +@pytest.fixture(scope="module") |
50 | 51 | def subscriber(): |
51 | 52 | yield pubsub_v1.SubscriberClient() |
52 | 53 |
|
@@ -383,6 +384,54 @@ def test_managing_subscription_iam_policy( |
383 | 384 | assert bindings[1].members == ["group:cloud-logs@google.com"] |
384 | 385 |
|
385 | 386 |
|
| 387 | +def test_subscriber_not_leaking_open_sockets( |
| 388 | + publisher, topic_path, subscription_path, cleanup |
| 389 | +): |
| 390 | + # Make sure the topic and the supscription get deleted. |
| 391 | + # NOTE: Since subscriber client will be closed in the test, we should not |
| 392 | + # use the shared `subscriber` fixture, but instead construct a new client |
| 393 | + # in this test. |
| 394 | + # Also, since the client will get closed, we need another subscriber client |
| 395 | + # to clean up the subscription. We also need to make sure that auxiliary |
| 396 | + # subscriber releases the sockets, too. |
| 397 | + subscriber = pubsub_v1.SubscriberClient() |
| 398 | + subscriber_2 = pubsub_v1.SubscriberClient() |
| 399 | + cleanup.append((subscriber_2.delete_subscription, subscription_path)) |
| 400 | + |
| 401 | + def one_arg_close(subscriber): # the cleanup helper expects exactly one argument |
| 402 | + subscriber.close() |
| 403 | + |
| 404 | + cleanup.append((one_arg_close, subscriber_2)) |
| 405 | + cleanup.append((publisher.delete_topic, topic_path)) |
| 406 | + |
| 407 | + # Create topic before starting to track connection count (any sockets opened |
| 408 | + # by the publisher client are not counted by this test). |
| 409 | + publisher.create_topic(topic_path) |
| 410 | + |
| 411 | + current_process = psutil.Process() |
| 412 | + conn_count_start = len(current_process.connections()) |
| 413 | + |
| 414 | + # Publish a few messages, then synchronously pull them and check that |
| 415 | + # no sockets are leaked. |
| 416 | + with subscriber: |
| 417 | + subscriber.create_subscription(name=subscription_path, topic=topic_path) |
| 418 | + |
| 419 | + # Publish a few messages, wait for the publish to succeed. |
| 420 | + publish_futures = [ |
| 421 | + publisher.publish(topic_path, u"message {}".format(i).encode()) |
| 422 | + for i in range(1, 4) |
| 423 | + ] |
| 424 | + for future in publish_futures: |
| 425 | + future.result() |
| 426 | + |
| 427 | + # Synchronously pull messages. |
| 428 | + response = subscriber.pull(subscription_path, max_messages=3) |
| 429 | + assert len(response.received_messages) == 3 |
| 430 | + |
| 431 | + conn_count_end = len(current_process.connections()) |
| 432 | + assert conn_count_end == conn_count_start |
| 433 | + |
| 434 | + |
386 | 435 | class TestStreamingPull(object): |
387 | 436 | def test_streaming_pull_callback_error_propagation( |
388 | 437 | self, publisher, topic_path, subscriber, subscription_path, cleanup |
|
0 commit comments