-
Notifications
You must be signed in to change notification settings - Fork 744
fix: post-merge fixes for #802 (tests, StdioBridge flakiness, focus nudge) #804
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
Merged
+147
−14
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
de38df8
fix: remove broken root rename assertion from prefab test
dsarno 595ccee
fix: remove yield frames causing StdioBridge reconnect test flakiness
dsarno 63c7e5b
fix: ensure focus nudge fires and logs correctly during test polling
dsarno ab99b9d
fix: address PR #804 review feedback
dsarno 84dbfa6
docs: add Claude Code to MCP client examples in README
dsarno File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| """Tests for focus_nudge utility — should_nudge() logic and nudge_unity_focus() gating.""" | ||
|
|
||
| import time | ||
| from unittest.mock import patch, AsyncMock | ||
|
|
||
| import pytest | ||
|
|
||
| from utils.focus_nudge import ( | ||
| should_nudge, | ||
| reset_nudge_backoff, | ||
| nudge_unity_focus, | ||
| _is_available, | ||
| ) | ||
|
|
||
|
|
||
| class TestShouldNudge: | ||
| """Tests for should_nudge() decision logic.""" | ||
|
|
||
| def test_returns_false_when_not_running(self): | ||
| assert should_nudge(status="succeeded", editor_is_focused=False, last_update_unix_ms=0, current_time_ms=99999) is False | ||
|
|
||
| def test_returns_false_when_focused(self): | ||
| assert should_nudge(status="running", editor_is_focused=True, last_update_unix_ms=0, current_time_ms=99999) is False | ||
|
|
||
| def test_returns_true_when_stalled_and_unfocused(self): | ||
| now_ms = int(time.time() * 1000) | ||
| stale_ms = now_ms - 5000 # 5s ago | ||
| assert should_nudge(status="running", editor_is_focused=False, last_update_unix_ms=stale_ms, current_time_ms=now_ms) is True | ||
|
|
||
| def test_returns_false_when_recently_updated(self): | ||
| now_ms = int(time.time() * 1000) | ||
| recent_ms = now_ms - 1000 # 1s ago (within 3s threshold) | ||
| assert should_nudge(status="running", editor_is_focused=False, last_update_unix_ms=recent_ms, current_time_ms=now_ms) is False | ||
|
|
||
| def test_returns_true_when_no_updates_yet(self): | ||
| """No last_update_unix_ms means tests might be stuck at start.""" | ||
| assert should_nudge(status="running", editor_is_focused=False, last_update_unix_ms=None) is True | ||
|
|
||
| def test_custom_stall_threshold(self): | ||
| now_ms = int(time.time() * 1000) | ||
| stale_ms = now_ms - 2000 # 2s ago | ||
| # Default threshold (3s) — not stale yet | ||
| assert should_nudge(status="running", editor_is_focused=False, last_update_unix_ms=stale_ms, current_time_ms=now_ms) is False | ||
| # Custom threshold (1s) — stale | ||
| assert should_nudge(status="running", editor_is_focused=False, last_update_unix_ms=stale_ms, current_time_ms=now_ms, stall_threshold_ms=1000) is True | ||
|
|
||
| def test_returns_false_for_failed_status(self): | ||
| assert should_nudge(status="failed", editor_is_focused=False, last_update_unix_ms=0, current_time_ms=99999) is False | ||
|
|
||
| def test_returns_false_for_cancelled_status(self): | ||
| assert should_nudge(status="cancelled", editor_is_focused=False, last_update_unix_ms=0, current_time_ms=99999) is False | ||
|
|
||
|
|
||
| class TestResetNudgeBackoff: | ||
| """Tests for reset_nudge_backoff() state management.""" | ||
|
|
||
| def test_resets_consecutive_nudges(self): | ||
| import utils.focus_nudge as fn | ||
| fn._consecutive_nudges = 5 | ||
| reset_nudge_backoff() | ||
| assert fn._consecutive_nudges == 0 | ||
|
|
||
| def test_updates_last_progress_time(self): | ||
| import utils.focus_nudge as fn | ||
| old_time = fn._last_progress_time | ||
| reset_nudge_backoff() | ||
| assert fn._last_progress_time >= old_time | ||
|
|
||
|
|
||
| class TestNudgeUnityFocus: | ||
| """Tests for nudge_unity_focus() gating logic.""" | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_skips_when_not_available(self): | ||
| with patch("utils.focus_nudge._is_available", return_value=False): | ||
| result = await nudge_unity_focus(force=True) | ||
| assert result is False | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_skips_when_unity_already_focused(self): | ||
| from utils.focus_nudge import _FrontmostAppInfo | ||
| with patch("utils.focus_nudge._is_available", return_value=True), \ | ||
| patch("utils.focus_nudge._get_frontmost_app", return_value=_FrontmostAppInfo(name="Unity")): | ||
| result = await nudge_unity_focus(force=True) | ||
| assert result is False | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_skips_when_frontmost_app_unknown(self): | ||
| with patch("utils.focus_nudge._is_available", return_value=True), \ | ||
| patch("utils.focus_nudge._get_frontmost_app", return_value=None): | ||
| result = await nudge_unity_focus(force=True) | ||
| assert result is False | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_rate_limited_by_backoff(self): | ||
| import utils.focus_nudge as fn | ||
| from utils.focus_nudge import _FrontmostAppInfo | ||
| # Simulate a very recent nudge | ||
| fn._last_nudge_time = time.monotonic() | ||
| fn._consecutive_nudges = 0 | ||
| with patch("utils.focus_nudge._is_available", return_value=True), \ | ||
| patch("utils.focus_nudge._get_frontmost_app", return_value=_FrontmostAppInfo(name="Terminal")): | ||
| result = await nudge_unity_focus(force=False) | ||
| assert result is False |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
progress = data.get("progress", {})returnsNonewhen the key is present with valuenull.dict.get(key, default)only substitutesdefaultwhen the key is absent; an explicitnull/Nonevalue is returned as-is. SinceTestJobProgress | None = Nonemeans Unity can legally send"progress": null(e.g., before the first test starts), line 328 will raiseAttributeError: 'NoneType' object has no attribute 'get'in that case.The identical pattern in the pre-existing
wait_timeoutpath (line 285) has the same bug and should be fixed there too.🐛 Proposed fix (new path at line 327, and matching fix at line 285)
Apply the same one-line change to the pre-existing
wait_timeoutpath at line 285.🤖 Prompt for AI Agents