File: test_gc.py

package info (click to toggle)
textual 2.1.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 55,084 kB
  • sloc: python: 85,423; lisp: 1,669; makefile: 101
file content (87 lines) | stat: -rw-r--r-- 2,072 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import asyncio
import gc

import pytest

from textual.app import App, ComposeResult
from textual.containers import Vertical
from textual.widgets import Footer, Header, Label


def count_nodes() -> int:
    """Count number of references to DOMNodes."""
    dom_nodes = [
        obj
        for obj in gc.get_objects()
        if any(cls.__name__ == "DOMNode" for cls in obj.__class__.__mro__)
    ]
    print(dom_nodes)
    return len(dom_nodes)


async def run_app() -> None:
    """Run a dummy app."""

    class DummyApp(App):
        """Dummy app with a few widgets."""

        def compose(self) -> ComposeResult:
            yield Header()
            with Vertical():
                yield Label("foo")
                yield Label("bar")
            yield Footer()

    app = DummyApp()

    async with app.run_test() as pilot:
        # We should have a bunch of DOMNodes while the test is running
        assert count_nodes() > 0
        await pilot.press("ctrl+c")

    assert not app._running

    # Force a GC collection
    gc.collect()

    # After the test, all DOMNodes will have been torn down
    assert count_nodes() == 1


async def _count_app_nodes() -> None:
    """Regression test for https://github.com/Textualize/textual/issues/4959"""

    # Should be no DOMNodes yet
    assert count_nodes() == 0

    await run_app()
    await asyncio.sleep(0)

    gc.collect()

    nodes_remaining = count_nodes()

    if nodes_remaining:
        print("NODES REMAINING")

        import objgraph

        objgraph.show_backrefs(
            [
                obj
                for obj in gc.get_objects()
                if any(cls.__name__ == "App" for cls in obj.__class__.__mro__)
            ],
            filename="graph.png",
            max_depth=15,
        )

    assert nodes_remaining == 0


# It looks like PyTest holds on to references to DOMNodes
# So this will only pass if ran in isolation
@pytest.mark.xfail
async def test_gc():
    """Regression test for https://github.com/Textualize/textual/issues/4959"""
    await _count_app_nodes()