Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Andrew/chromium root #153

Merged
merged 8 commits into from
Oct 25, 2024
Merged

Andrew/chromium root #153

merged 8 commits into from
Oct 25, 2024

Conversation

ayjayt
Copy link
Collaborator

@ayjayt ayjayt commented Oct 24, 2024

meant as a solution for:
plotly/Kaleido#211

could possibly ease problems in:
plotly/Kaleido#209

  • Make sure to bump dependency version in plotly/kaleido

Leads to new issue: #152

@ayjayt ayjayt requested a review from gvwilson October 24, 2024 20:09
@ayjayt
Copy link
Collaborator Author

ayjayt commented Oct 24, 2024

Lets run through testing before review

@ayjayt
Copy link
Collaborator Author

ayjayt commented Oct 24, 2024

This does prove that integration tests are necessary on kaleido, because
a) disabling gpu seems to not stop the gpu init process, which still fails,

but it doesn't cause a hang-and-kill anymore

@ayjayt ayjayt requested a review from neyberson October 24, 2024 22:45
Copy link
Collaborator

@neyberson neyberson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing this branch, sometimes the test_placeholder.py fails, here is the output of the test in cmd (windows 10):

(app_dev_tools_12) C:\Users\neybe\Desktop\Pozo\choreographer>pytest -vvv
================================================= test session starts =================================================
platform win32 -- Python 3.12.4, pytest-8.3.3, pluggy-1.5.0 -- C:\Users\neybe\.conda\envs\app_dev_tools_12\python.exe
cachedir: .pytest_cache
benchmark: 4.0.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: C:\Users\neybe\Desktop\Pozo\choreographer
configfile: pyproject.toml
plugins: dash-2.18.0, asyncio-0.24.0, benchmark-4.0.0, timeout-2.3.1, xdist-3.6.1
asyncio: mode=Mode.STRICT, default_loop_scope=function
collected 36 items

tests/test_browser.py::test_create_and_close_tab PASSED                                                          [  2%]
tests/test_browser.py::test_create_and_close_session read_jsons (blocking):
PASSED                                                      [  5%]
tests/test_browser.py::test_browser_write_json PASSED                                                            [  8%]
tests/test_browser.py::test_browser_send_command PASSED                                                          [ 11%]
tests/test_browser.py::test_populate_targets PASSED                                                              [ 13%]
tests/test_browser.py::test_get_tab read_jsons (blocking):
PASSED                                                                       [ 16%]
tests/test_placeholder.py::test_placeholder FAILED                                                               [ 19%]
tests/test_placeholder.py::test_placeholder ERROR                                                                [ 19%]
tests/test_process.py::test_context[headless-debug-debug_browser] PASSED                                         [ 22%]
tests/test_process.py::test_context[headless-debug-] PASSED                                                      [ 25%]
tests/test_process.py::test_context[headless--debug_browser] PASSED                                              [ 27%]
tests/test_process.py::test_context[headless--] PASSED                                                           [ 30%]
tests/test_process.py::test_context[-debug-debug_browser] PASSED                                                 [ 33%]
tests/test_process.py::test_context[-debug-] PASSED                                                              [ 36%]
tests/test_process.py::test_context[--debug_browser] PASSED                                                      [ 38%]
tests/test_process.py::test_context[--] PASSED                                                                   [ 41%]
tests/test_process.py::test_no_context[headless-debug-debug_browser] PASSED                                      [ 44%]
tests/test_process.py::test_no_context[headless-debug-] PASSED                                                   [ 47%]
tests/test_process.py::test_no_context[headless--debug_browser] PASSED                                           [ 50%]
tests/test_process.py::test_no_context[headless--] PASSED                                                        [ 52%]
tests/test_process.py::test_no_context[-debug-debug_browser] PASSED                                              [ 55%]
tests/test_process.py::test_no_context[-debug-] PASSED                                                           [ 58%]
tests/test_process.py::test_no_context[--debug_browser] PASSED                                                   [ 61%]
tests/test_process.py::test_no_context[--] PASSED                                                                [ 63%]
tests/test_process.py::test_watchdog[headless-debug-debug_browser] PASSED                                        [ 66%]
tests/test_process.py::test_watchdog[headless-debug-] PASSED                                                     [ 69%]
tests/test_process.py::test_watchdog[headless--debug_browser] PASSED                                             [ 72%]
tests/test_process.py::test_watchdog[headless--] PASSED                                                          [ 75%]
tests/test_process.py::test_watchdog[-debug-debug_browser] PASSED                                                [ 77%]
tests/test_process.py::test_watchdog[-debug-] PASSED                                                             [ 80%]
tests/test_process.py::test_watchdog[--debug_browser] PASSED                                                     [ 83%]
tests/test_process.py::test_watchdog[--] PASSED                                                                  [ 86%]
tests/test_session.py::test_send_command PASSED                                                                  [ 88%]
tests/test_tab.py::test_create_and_close_session PASSED                                                          [ 91%]
tests/test_tab.py::test_send_command read_jsons (blocking):
PASSED                                                                      [ 94%]
tests/test_tab.py::test_subscribe_once {"method":"Page.frameNavigated","params":{"frame":{"id":"577D08C96E81E1E61E38BDDD9DFCAE1D","loaderId":"59BBAA8B5C704BCD0C4759DAC51C85DF","url":"about:blank","domainAndRegistry":"","securityOrigin":"://","mimeType":"text/html","adFrameStatus":{"adFrameType":"none"},"secureContextType":"InsecureScheme","crossOriginIsolatedContextType":"NotIsolated","gatedAPIFeatures":[]},"type":"Navigation"},"sessionId":"B0B875155DAD3195378129E96DD39C81"}
read_jsons: {'method': 'Page.frameNavigated', 'params': {'frame': {'id': '577D08C96E81E1E61E38BDDD9DFCAE1D', 'loaderId': '59BBAA8B5C704BCD0C4759DAC51C85DF', 'url': 'about:blank', 'domainAndRegistry': '', 'securityOrigin': '://', 'mimeType': 'text/html', 'adFrameStatus': {'adFrameType': 'none'}, 'secureContextType': 'InsecureScheme', 'crossOriginIsolatedContextType': 'NotIsolated', 'gatedAPIFeatures': []}, 'type': 'Navigation'}, 'sessionId': 'B0B875155DAD3195378129E96DD39C81'}PASSED                                                                    [ 97%]
tests/test_tab.py::test_subscribe_and_unsubscribe PASSED{"method":"Page.frameNavigated","params":{"frame":{"id":"F2B9C52C976CCEB43836F17C86DDBBF2","loaderId":"0BF602C30979DF680ABCF69C424F2181","url":"about:blank","domainAndRegistry":"","securityOrigin":"://","mimeType":"text/html","adFrameStatus":{"adFrameType":"none"},"secureContextType":"InsecureScheme","crossOriginIsolatedContextType":"NotIsolated","gatedAPIFeatures":[]},"type":"Navigation"},"sessionId":"D9B0FA7EB9C2E0D00AD67D2B34EA6705"}
                                                         [100%]read_jsons: {'method': 'Page.frameNavigated', 'params': {'frame': {'id': 'F2B9C52C976CCEB43836F17C86DDBBF2', 'loaderId': '0BF602C30979DF680ABCF69C424F2181', 'url': 'about:blank', 'domainAndRegistry': '', 'securityOrigin': '://', 'mimeType': 'text/html', 'adFrameStatus': {'adFrameType': 'none'}, 'secureContextType': 'InsecureScheme', 'crossOriginIsolatedContextType': 'NotIsolated', 'gatedAPIFeatures': []}, 'type': 'Navigation'}, 'sessionId': 'D9B0FA7EB9C2E0D00AD67D2B34EA6705'}


======================================================= ERRORS ========================================================
________________________________________ ERROR at teardown of test_placeholder ________________________________________

request = <SubRequest 'browser' for <Coroutine test_placeholder>>

    @pytest_asyncio.fixture(scope="function", loop_scope="function")
    async def browser(request):
        # this needs also to be set by command line TODO
        headless = request.config.getoption("--headless")
        debug = request.config.get_verbosity() > 2
        browser = await choreo.Browser(
            headless=headless, debug=debug, debug_browser=debug
        )
        yield browser
>       await browser.close()

tests\conftest.py:43:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    async def close_task():
        if self.lock.locked():
            return
        await self.lock.acquire()
        if not self.future_self.done():
            self.future_self.set_exception(BrowserFailedError("Close() was called before the browser finished opening- maybe it crashed?"))
        for future in self.futures.values():
>           future.set_exception(BrowserClosedError("Command not completed because browser closed."))
E           asyncio.exceptions.InvalidStateError: invalid state

..\..\..\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\browser.py:422: InvalidStateError
------------------------------------------------ Captured stderr setup ------------------------------------------------
DEBUG REPORT:
BROWSER_PATH: C:\Program Files\Google\Chrome\Application\chrome.exe
USER_DATA_DIR: C:\Users\neybe\AppData\Local\Temp\tmp0zgto7t1
read_jsons (blocking):
write_json:Starting watchdog

write_json: {"id": 0, "method": "Target.getTargets"}
wrote_json.
Old Headless mode will be removed from the Chrome binary soon. Please use the new Headless mode (https://developer.chrome.com/docs/chromium/new-headless) or the chrome-headless-shell which is a standalone implementation of the old Headless mode (https://developer.chrome.com/blog/chrome-headless-shell).

{"id":0,"result":{"targetInfos":[]}}
read_jsons: {'id': 0, 'result': {'targetInfos': []}}
run_read_loop() found future for key ('', 0)
DEBUG REPORT:
BROWSER_PATH: C:\Program Files\Google\Chrome\Application\chrome.exe
USER_DATA_DIR: C:\Users\neybe\AppData\Local\Temp\tmp0zgto7t1
read_jsons (blocking):
write_json:Starting watchdog

write_json: {"id": 0, "method": "Target.getTargets"}
wrote_json.
{"id":0,"result":{"targetInfos":[]}}
read_jsons: {'id': 0, 'result': {'targetInfos': []}}
run_read_loop() found future for key ('', 0)
read_jsons (blocking):
------------------------------------------------ Captured stdout call -------------------------------------------------


------------------------------------------------ Captured stderr call -------------------------------------------------
write_json:
write_json: {"id": 1, "method": "Target.getTargets"}
wrote_json.
write_json:
write_json: {"id": 1, "method": "Target.getTargets"}
wrote_json.
-------------------------------------------------- Captured log call --------------------------------------------------
ERROR    asyncio:base_events.py:1821 Task exception was never retrieved
future: <Task finished name='Task-94' coro=<Browser.run_read_loop.<locals>.read_loop() done, defined at C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\browser.py:602> exception=ValueError('I/O operation on closed file.')>
Traceback (most recent call last):
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\browser.py", line 604, in read_loop
    responses = await self.loop.run_in_executor(
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\concurrent\futures\thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\pipe.py", line 76, in read_jsons
    print(f"read_jsons ({'blocking' if blocking else 'not blocking'}):", file=sys.stderr)
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\_pytest\capture.py", line 198, in write
    super().write(s)
ValueError: I/O operation on closed file.
====================================================== FAILURES =======================================================
__________________________________________________ test_placeholder ___________________________________________________

fut = <coroutine object test_placeholder at 0x000001C80CEB3740>, timeout = 5

    async def wait_for(fut, timeout):
        """Wait for the single Future or coroutine to complete, with timeout.

        Coroutine will be wrapped in Task.

        Returns result of the Future or coroutine.  When a timeout occurs,
        it cancels the task and raises TimeoutError.  To avoid the task
        cancellation, wrap it in shield().

        If the wait is cancelled, the task is also cancelled.

        If the task suppresses the cancellation and returns a value instead,
        that value is returned.

        This function is a coroutine.
        """
        # The special case for timeout <= 0 is for the following case:
        #
        # async def test_waitfor():
        #     func_started = False
        #
        #     async def func():
        #         nonlocal func_started
        #         func_started = True
        #
        #     try:
        #         await asyncio.wait_for(func(), 0)
        #     except asyncio.TimeoutError:
        #         assert not func_started
        #     else:
        #         assert False
        #
        # asyncio.run(test_waitfor())


        if timeout is not None and timeout <= 0:
            fut = ensure_future(fut)

            if fut.done():
                return fut.result()

            await _cancel_and_wait(fut)
            try:
                return fut.result()
            except exceptions.CancelledError as exc:
                raise TimeoutError from exc

        async with timeouts.timeout(timeout):
>           return await fut

..\..\..\.conda\envs\app_dev_tools_12\Lib\asyncio\tasks.py:520:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

capteesys = <_pytest.capture.CaptureFixture object at 0x000001C80D0A5460>
browser = <choreographer.browser.Browser object at 0x000001C80D0A45F0>

    async def test_placeholder(capteesys, browser):
        print("")
>       assert "result" in await browser.send_command("Target.getTargets")
E       asyncio.exceptions.CancelledError

tests\test_placeholder.py:10: CancelledError

The above exception was the direct cause of the following exception:

args = ()
kwargs = {'browser': <choreographer.browser.Browser object at 0x000001C80D0A45F0>, 'capteesys': <_pytest.capture.CaptureFixture object at 0x000001C80D0A5460>}

    async def wrapped_test_fn(*args, **kwargs):
        try:
>           return await asyncio.wait_for(
                    raw_test_fn(*args, **kwargs), timeout=timeout
                        )

tests\conftest.py:67:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\..\..\.conda\envs\app_dev_tools_12\Lib\asyncio\tasks.py:519: in wait_for
    async with timeouts.timeout(timeout):
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <Timeout [expired]>, exc_type = <class 'asyncio.exceptions.CancelledError'>, exc_val = CancelledError()
exc_tb = <traceback object at 0x000001C80D055CC0>

    async def __aexit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],
    ) -> Optional[bool]:
        assert self._state in (_State.ENTERED, _State.EXPIRING)

        if self._timeout_handler is not None:
            self._timeout_handler.cancel()
            self._timeout_handler = None

        if self._state is _State.EXPIRING:
            self._state = _State.EXPIRED

            if self._task.uncancel() <= self._cancelling and exc_type is exceptions.CancelledError:
                # Since there are no new cancel requests, we're
                # handling this.
>               raise TimeoutError from exc_val
E               TimeoutError

..\..\..\.conda\envs\app_dev_tools_12\Lib\asyncio\timeouts.py:115: TimeoutError

During handling of the above exception, another exception occurred:

args = ()
kwargs = {'browser': <choreographer.browser.Browser object at 0x000001C80D0A45F0>, 'capteesys': <_pytest.capture.CaptureFixture object at 0x000001C80D0A5460>}

    async def wrapped_test_fn(*args, **kwargs):
        try:
            return await asyncio.wait_for(
                    raw_test_fn(*args, **kwargs), timeout=timeout
                        )
        except TimeoutError:
>           pytest.fail(f"Test {item.name} failed a timeout. This can be extended, but shouldn't be. See conftest.py.")
E           Failed: Test test_placeholder failed a timeout. This can be extended, but shouldn't be. See conftest.py.

tests\conftest.py:71: Failed
------------------------------------------------ Captured stderr setup ------------------------------------------------
DEBUG REPORT:
BROWSER_PATH: C:\Program Files\Google\Chrome\Application\chrome.exe
USER_DATA_DIR: C:\Users\neybe\AppData\Local\Temp\tmp0zgto7t1
read_jsons (blocking):
write_json:Starting watchdog

write_json: {"id": 0, "method": "Target.getTargets"}
wrote_json.
Old Headless mode will be removed from the Chrome binary soon. Please use the new Headless mode (https://developer.chrome.com/docs/chromium/new-headless) or the chrome-headless-shell which is a standalone implementation of the old Headless mode (https://developer.chrome.com/blog/chrome-headless-shell).

{"id":0,"result":{"targetInfos":[]}}
read_jsons: {'id': 0, 'result': {'targetInfos': []}}
run_read_loop() found future for key ('', 0)
DEBUG REPORT:
BROWSER_PATH: C:\Program Files\Google\Chrome\Application\chrome.exe
USER_DATA_DIR: C:\Users\neybe\AppData\Local\Temp\tmp0zgto7t1
read_jsons (blocking):
write_json:Starting watchdog

write_json: {"id": 0, "method": "Target.getTargets"}
wrote_json.
{"id":0,"result":{"targetInfos":[]}}
read_jsons: {'id': 0, 'result': {'targetInfos': []}}
run_read_loop() found future for key ('', 0)
read_jsons (blocking):
------------------------------------------------ Captured stdout call -------------------------------------------------


------------------------------------------------ Captured stderr call -------------------------------------------------
write_json:
write_json: {"id": 1, "method": "Target.getTargets"}
wrote_json.
write_json:
write_json: {"id": 1, "method": "Target.getTargets"}
wrote_json.
-------------------------------------------------- Captured log call --------------------------------------------------
ERROR    asyncio:base_events.py:1821 Task exception was never retrieved
future: <Task finished name='Task-94' coro=<Browser.run_read_loop.<locals>.read_loop() done, defined at C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\browser.py:602> exception=ValueError('I/O operation on closed file.')>
Traceback (most recent call last):
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\browser.py", line 604, in read_loop
    responses = await self.loop.run_in_executor(
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\concurrent\futures\thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\pipe.py", line 76, in read_jsons
    print(f"read_jsons ({'blocking' if blocking else 'not blocking'}):", file=sys.stderr)
  File "C:\Users\neybe\.conda\envs\app_dev_tools_12\Lib\site-packages\_pytest\capture.py", line 198, in write
    super().write(s)
ValueError: I/O operation on closed file.
=============================================== short test summary info ===============================================
FAILED tests/test_placeholder.py::test_placeholder - Failed: Test test_placeholder failed a timeout. This can be extended, but shouldn't be. See conftest.py.
ERROR tests/test_placeholder.py::test_placeholder - asyncio.exceptions.InvalidStateError: invalid state
======================================= 1 failed, 35 passed, 1 error in 30.45s ========================================

@ayjayt
Copy link
Collaborator Author

ayjayt commented Oct 25, 2024

@neyberson the test is reporting:

    async def close_task():
        if self.lock.locked():
            return
        await self.lock.acquire()
        if not self.future_self.done():
            self.future_self.set_exception(BrowserFailedError("Close() was called before the browser finished opening- maybe it crashed?"))
        for future in self.futures.values():
>           future.set_exception(BrowserClosedError("Command not completed because browser closed."))
E           asyncio.exceptions.InvalidStateError: invalid state

..\..\..\.conda\envs\app_dev_tools_12\Lib\site-packages\choreographer\browser.py:422: InvalidStateError

But browser.py:422 for this branch should look like this:

                    self.future_self.set_exception(BrowserFailedError("Close() was called before the browser finished opening- maybe it crashed?"))
                for future in self.futures.values():
                    if future.done(): continue
                    future.set_exception(BrowserClosedError("Command not completed because browser closed."))
                for session in self.sessions.values():
                    for future in session.subscriptions_futures.values():
                        if future.done(): continue
                        future.set_exception(BrowserClosedError("Event not complete because browser closed."))
                for tab in self.tabs.values():

which means you are testing an old verison or different branch. Please review what you are testing.

@ayjayt ayjayt merged commit 35e13aa into main Oct 25, 2024
4 checks passed
@ayjayt ayjayt deleted the andrew/chromium-root branch October 25, 2024 18:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants