|
2 | 2 |
|
3 | 3 | import asyncio
|
4 | 4 | from datetime import datetime, timedelta, timezone
|
5 |
| -from typing import TYPE_CHECKING |
| 5 | +from typing import TYPE_CHECKING, Any |
| 6 | +from unittest import mock |
| 7 | +from unittest.mock import AsyncMock |
6 | 8 |
|
7 | 9 | import pytest
|
8 | 10 | from playwright.async_api import Browser, Playwright, async_playwright
|
9 | 11 |
|
10 |
| -from crawlee.browsers import PlaywrightBrowserController |
| 12 | +from crawlee.browsers import PlaywrightBrowserController, PlaywrightBrowserPlugin, PlaywrightPersistentBrowser |
11 | 13 |
|
12 | 14 | if TYPE_CHECKING:
|
13 | 15 | from collections.abc import AsyncGenerator
|
@@ -106,3 +108,28 @@ async def test_close_browser_with_open_pages(browser: Browser) -> None:
|
106 | 108 |
|
107 | 109 | assert controller.pages_count == 0
|
108 | 110 | assert not controller.is_browser_connected
|
| 111 | + |
| 112 | + |
| 113 | +async def test_memory_leak_on_concurrent_context_creation() -> None: |
| 114 | + """Test that only one browser context is created when multiple pages are opened concurrently.""" |
| 115 | + |
| 116 | + # Prepare mocked browser with relevant methods and attributes |
| 117 | + mocked_browser = AsyncMock() |
| 118 | + mocked_context_launcher = AsyncMock() |
| 119 | + async def delayed_launch_persistent_context(*args: Any, **kwargs: Any) -> AsyncMock: |
| 120 | + """Ensure that both calls to create context overlap in time.""" |
| 121 | + await asyncio.sleep(5) # Simulate delay in creation to make sure race condition happens |
| 122 | + return await mocked_context_launcher(*args, **kwargs) |
| 123 | + mocked_browser.launch_persistent_context = delayed_launch_persistent_context |
| 124 | + |
| 125 | + # Create minimal instance of PlaywrightBrowserController with mocked browser |
| 126 | + controller = PlaywrightBrowserController( |
| 127 | + PlaywrightPersistentBrowser(mocked_browser,None, {}), |
| 128 | + header_generator=None, |
| 129 | + fingerprint_generator=None |
| 130 | + ) |
| 131 | + |
| 132 | + # Both calls will try to create browser context at the same time, but only one context should be created. |
| 133 | + await asyncio.gather(controller.new_page(), controller.new_page()) |
| 134 | + |
| 135 | + assert mocked_context_launcher.call_count == 1 |
0 commit comments