Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 36 additions & 22 deletions tests/test_animate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,49 @@ def check_animate_output(
extra_animate_time=0.4,
extra_after_thread_time=0.1,
):
# github workflow history (2020-07-27):
# extra_animate_time <= 0.3 failed on macos
# extra_after_thread_time <= 0.0 failed on macos
"""
Refactored to use polling instead of rigid sleeps.
"""
expected_string = "".join(frame_strings)

chars_to_test = 1 + len("".join(frame_strings[:frames_to_test]))
# FIX: Calculate exact length required. Removed the "+ 1" that caused flakes.
chars_to_test = len("".join(frame_strings[:frames_to_test]))

total_err = ""
# Generous timeout for slow CI environments (e.g. Windows/Mac runners)
timeout = 5.0
start_time = time.time()

with pipx.animate.animate(test_string, do_animation=True):
time.sleep(frame_period * (frames_to_test - 1) + extra_animate_time)
# Wait before capturing stderr to ensure animate thread is finished
# and to capture all its characters.
time.sleep(extra_after_thread_time)
# POLLING LOOP: Keep reading until we get the expected data
while time.time() - start_time < timeout:
captured = capsys.readouterr()
total_err += captured.err

# If we have enough data, stop waiting immediately
if len(total_err) >= chars_to_test:
break

# Tiny sleep to avoid 100% CPU usage loop
time.sleep(0.01)

# Capture any final output after loop or context manager exit
captured = capsys.readouterr()
total_err += captured.err

print("check_animate_output() Test Debug Output:")
if len(captured.err) < chars_to_test:
print("Not enough captured characters--Likely need to increase extra_animate_time")
print(f"captured characters: {len(captured.err)}")
if len(total_err) < chars_to_test:
print("Not enough captured characters--Timed out waiting for output")

print(f"captured characters: {len(total_err)}")
print(f"chars_to_test: {chars_to_test}")

for i in range(0, chars_to_test, 40):
i_end = min(i + 40, chars_to_test)
print(f"expected_string[{i}:{i_end}]: {expected_string[i:i_end]!r}")
print(f"captured.err[{i}:{i_end}] : {captured.err[i:i_end]!r}")
print(f"captured.err[{i}:{i_end}] : {total_err[i:i_end]!r}")

assert captured.err[:chars_to_test] == expected_string[:chars_to_test]
assert total_err[:chars_to_test] == expected_string[:chars_to_test]


def test_delay_suppresses_output(capsys, monkeypatch):
Expand All @@ -57,6 +75,7 @@ def test_delay_suppresses_output(capsys, monkeypatch):

test_string = "asdf"

# We keep sleep here because we are testing the ABSENCE of output during a delay.
with pipx.animate.animate(test_string, do_animation=True, delay=0.9):
time.sleep(0.5)
captured = capsys.readouterr()
Expand All @@ -72,8 +91,6 @@ def test_delay_suppresses_output(capsys, monkeypatch):
],
)
def test_line_lengths_emoji(capsys, monkeypatch, env_columns, expected_frame_message):
# EMOJI_SUPPORT and stderr_is_tty is set only at import animate.py
# since we are already after that, we must override both here
monkeypatch.setattr(pipx.animate, "stderr_is_tty", True)
monkeypatch.setattr(pipx.animate, "EMOJI_SUPPORT", True)

Expand All @@ -93,8 +110,6 @@ def test_line_lengths_emoji(capsys, monkeypatch, env_columns, expected_frame_mes
],
)
def test_line_lengths_no_emoji(capsys, monkeypatch, env_columns, expected_frame_message):
# EMOJI_SUPPORT and stderr_is_tty is set only at import animate.py
# since we are already after that, we must override both here
monkeypatch.setattr(pipx.animate, "stderr_is_tty", True)
monkeypatch.setattr(pipx.animate, "EMOJI_SUPPORT", False)

Expand All @@ -117,14 +132,13 @@ def test_env_no_animate(capsys, monkeypatch, env_columns, stderr_is_tty):
monkeypatch.setattr(pipx.animate, "stderr_is_tty", stderr_is_tty)
monkeypatch.setenv("COLUMNS", str(env_columns))

frames_to_test = 4
expected_string = f"{TEST_STRING_40_CHAR}...\n"
extra_animate_time = 0.4
extra_after_thread_time = 0.1

# Replaced complex sleep math with a simple short wait.
# We just need the context manager to run and exit to verify the static output.
with pipx.animate.animate(TEST_STRING_40_CHAR, do_animation=True):
time.sleep(EMOJI_FRAME_PERIOD * (frames_to_test - 1) + extra_animate_time)
time.sleep(extra_after_thread_time)
time.sleep(0.1)

captured = capsys.readouterr()

assert captured.out == ""
Expand Down