Skip to content

GUI filter tests are timing-dependent and fail under load #8

Closed

Description

@NaNIsaac

Symptom

In a full QT_QPA_PLATFORM=offscreen uv run pytest run, the filter tests in tests/test_gui.py fail intermittently (seen 2026-10-22 on main and on a branch with only uv.lock changes), for example:

  • test_filter_narrows_the_listing
  • test_filter_survives_changing_folder
  • test_escape_clears_the_filter_and_returns_to_the_tree
  • test_clearing_the_filter_restores_everything

Run on their own (pytest tests/test_gui.py -k filter) they pass.

Cause

The tests wait a fixed time with pump(300) and then read the listing. The listing fills asynchronously (the directory model loads in the background) and the filter applies after the 120 ms single-shot _filter_timer in gui/main_window.py. On a loaded machine, 300 ms is not always enough, so the assertion reads a listing that is not populated or not filtered yet.

Reproduce

Run the whole suite while the machine is busy (for example with another test suite running in parallel). A full run took about 200 s here and 3 of the filter tests failed.

Fix

Replace the fixed pump(ms) before each listing assertion with a wait-until helper that pumps events until the expected condition holds or a timeout (a few seconds) passes, and fails with the last listing on timeout.

Activity

  1. NaNIsaac commented on Oct 22, 2026

    @NaNIsaac
    OwnerAuthor

    Checked on the branch of #22 (6badf80), Windows, QT_QPA_PLATFORM=offscreen, with 12 busy-loop processes on a 12-thread machine so every core was at 100 %. One pytest process per run.

    Selection Runs Result
    tests/test_gui.py -k "filter or escape or clearing" (the four tests of this issue plus the escape test) 20 20 passed, 22 to 69 s each
    the whole tests/test_gui.py, -x 10 3 passed; 7 stopped at test_arrow_keys_move_the_preview (below)

    The filter tests never failed: in the whole-file runs they come before line 438, so every one of the 10 runs passed them. That matches the measurement in #14 (cause: windows and file models never deleted; fixed in #22), so what this issue describes, filter tests failing under load, is the same problem and #22 fixes it. Closing as a duplicate of #14.

    The whole-file runs do show a different weakness, filed separately: test_arrow_keys_move_the_preview still polls for the model for a fixed 2 s (for _ in range(20): pump(100)), and under full CPU load the folder is not populated in time (assert 0 >= 2). The script is b16_repeat_under_load.py in the ops scratchpad; not in the repository.

  2. NaNIsaac commented on Oct 22, 2026

    @NaNIsaac
    OwnerAuthor

    The separate issue for the arrow-key tests is #24.

  3. NaNIsaac commented on Oct 22, 2026

    @NaNIsaac
    OwnerAuthor

    Reopening: the controlled rerun registered in advance (lab backlog B16b) failed once on main.

    Rule, fixed before the runs: 20 runs of the filter tests (-k "filter or escape or clearing", 6 tests) under CPU load from 12 busy-loop processes and nothing else; 20/20 on main means the earlier failures came from other jobs' load and this stays closed, a single failure means #22 did not finish the job and #8 reopens.

    Result (2026-10-23, Windows 11, Ryzen 5 7500F, 12 logical cores, offscreen Qt, docs/b16_repeat_under_load.py --runs 20):

    branch passed failed failing test
    main 1e27ef4 (#22 merged) 19 1 run 10: test_clearing_the_filter_restores_everything
    fix/gui-arrow-key-waits a5dd6eb (#26) 19 1 run 4: test_filter_keeps_folders_visible

    Both failures print the same state after the 10 s wait:

    listing=[] rowCount=0 canFetchMore=False root=<the test folder> rootPath=<the same folder>
    nameFilters=[...as expected...] timer_active=False
    

    The root path, the name filters and the filter timer are all as the test set them; only the listing is empty. That is not the stale-state symptom of #14. A likely reading, not verified: with every core busy, QFileSystemModel's background gatherer did not deliver the folder within 10 s.

    Caveat on the condition: load snapshots before and after the runs also showed the Everything file indexer (about one core over 3 s, at 05:45 and at the end) and Windows Defender (1.0-1.7 cores), so "nothing but the busy loops" was not fully met. The registered verdict stands as written; this is recorded next to it, not instead of it.

    Next test that would separate the two readings: the same 20 runs with one core left free (11 workers) or with the wait raised to 30 s. If the failures go away, the cause is gatherer latency under saturation (a test timing budget), not a state leak.

  4. NaNIsaac commented on Oct 22, 2026

    @NaNIsaac
    OwnerAuthor

    Controlled rerun B16c (registered in advance, run 2026-10-23 07:03-07:20 with nothing else on the machine; the Everything indexer was running but idle, no other process above 0.6 core): (a) 11 load workers, 10 s wait: 20/20; (b) 12 load workers, 30 s wait: 20/20. Registered verdict: gathering delay under CPU saturation, not state leakage. The timeout change is in the PR that references this issue; it closes this when merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions