Skip to content

Commit

Permalink
✅Fixed pytest option and codes
Browse files Browse the repository at this point in the history
  • Loading branch information
7rikazhexde committed Aug 1, 2024
1 parent f2d0225 commit b340ee2
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 14 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,4 @@ scripts_are_modules = true

[tool.pytest.ini_options]
testpaths = ["tests",]
addopts = "--capture=no"
99 changes: 97 additions & 2 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

from video_grid_merge import __main__ as main

sys.stdin = io.StringIO()


@pytest.fixture
def mock_terminal(monkeypatch: MonkeyPatch) -> Tuple[Any, Any, Any]:
Expand Down Expand Up @@ -113,16 +115,109 @@ def mock_register(func: Callable[[], None]) -> None:

monkeypatch.setattr("atexit.register", mock_register)

# モジュールを再インポートして atexit.register の呼び出しをトリガー
# Reimport the module and trigger a call to atexit.register
import importlib

importlib.reload(main)

# atexit.register が reset_terminal で呼び出されたことを確認
# Confirm that atexit.register was called with reset_terminal
assert len(called_functions) == 1
assert called_functions[0] == main.reset_terminal


def test_reset_terminal_no_original_settings(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(main, "original_terminal_settings", None)
main.reset_terminal() # Verify that no errors occur


def test_reset_terminal_with_error(monkeypatch: MonkeyPatch) -> None:
def mock_tcsetattr(*args: Any) -> None:
raise termios.error("Mock error")

monkeypatch.setattr(termios, "tcsetattr", mock_tcsetattr)
monkeypatch.setattr(main, "original_terminal_settings", object())
main.reset_terminal() # Verify that no errors occur


def test_safe_input_no_original_settings(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(main, "original_terminal_settings", None)
monkeypatch.setattr("builtins.input", lambda _: "test")
result = main.safe_input("Prompt: ")
assert result == "test"


def test_safe_input_with_error(monkeypatch: MonkeyPatch) -> None:
def mock_tcflush(*args: Any) -> None:
raise termios.error("Mock error")

monkeypatch.setattr(termios, "tcflush", mock_tcflush)
monkeypatch.setattr(main, "original_terminal_settings", object())
monkeypatch.setattr("builtins.input", lambda _: "test")
result = main.safe_input("Prompt: ")
assert result == "test"


@pytest.mark.parametrize("version", ["v1", "v2", "invalid"])
def test_main_all_versions(
version: str,
mock_file_operations: Any,
capsys: pytest.CaptureFixture[str],
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(main, "ffmpeg_cmd_version", version)
if version == "invalid":
with pytest.raises(ValueError, match="Invalid ffmpeg_cmd_version: invalid"):
main.main()
else:
main.main()
captured = capsys.readouterr()
assert "Video Grid Merge Start" in captured.out
assert "Video Grid Merge End And Output Success" in captured.out
assert f"Executing command: ffmpeg_command_{version}" in captured.out


def test_main_with_non_square_number_of_videos(
mock_file_operations: Any, monkeypatch: pytest.MonkeyPatch
) -> None:
def mock_get_video_files(folder: str) -> List[str]:
return [
"video1.mp4",
"video2.mp4",
"video3.mp4",
] # 3 videos (not square number)

monkeypatch.setattr(main, "get_video_files", mock_get_video_files)

with pytest.raises(SystemExit):
main.main()


def test_original_terminal_settings_error(monkeypatch: MonkeyPatch) -> None:
def mock_tcgetattr(*args: Any) -> None:
raise termios.error("Mock termios error")

monkeypatch.setattr(termios, "tcgetattr", mock_tcgetattr)

# Reimport the module to trigger the exception in the global scope
import importlib

importlib.reload(main)

assert main.original_terminal_settings is None


def test_reset_terminal_with_none_settings(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(main, "original_terminal_settings", None)
main.reset_terminal() # This should not raise an exception


def test_safe_input_with_none_settings(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr(main, "original_terminal_settings", None)
monkeypatch.setattr(builtins, "input", lambda _: "test input")
result = main.safe_input("Prompt: ")
assert result == "test input"


def test_get_video_files(tmp_path: Any) -> None:
(tmp_path / "video1.mp4").touch()
(tmp_path / "video2.mov").touch()
Expand Down
60 changes: 48 additions & 12 deletions video_grid_merge/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import atexit
import io
import math
import os
import subprocess
Expand All @@ -21,8 +22,31 @@
ffmpeg_loglevel = "error"
ffmpeg_cmd_version = "v1"

# Save original terminal settings
original_terminal_settings = termios.tcgetattr(sys.stdin)
original_terminal_settings = None


def init_terminal_settings() -> None:
"""
Initialize and save the original terminal settings.
This function attempts to save the current terminal settings. If successful,
these settings can be used later to reset the terminal to its original state.
Global Variables:
original_terminal_settings (termios.tcgetattr): Stores the original terminal settings.
Raises:
termios.error: If there's an error getting the terminal attributes.
io.UnsupportedOperation: If the operation is not supported (e.g., in a non-interactive environment).
"""
global original_terminal_settings
try:
original_terminal_settings = termios.tcgetattr(sys.stdin)
except (termios.error, io.UnsupportedOperation):
original_terminal_settings = None


init_terminal_settings()


def reset_terminal() -> None:
Expand All @@ -37,12 +61,6 @@ def reset_terminal() -> None:
1. Uses termios.tcsetattr to apply the original settings.
2. Applies the settings immediately but waits for output to drain first.
Parameters:
None
Returns:
None
Side Effects:
- Modifies the current terminal settings.
- May affect how the terminal handles input and output after this call.
Expand All @@ -58,25 +76,43 @@ def reset_terminal() -> None:
Raises:
termios.error: If there's an error setting the terminal attributes.
"""
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_terminal_settings)
if original_terminal_settings is not None:
try:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, original_terminal_settings)
except termios.error:
# Silently pass if we're in a non-interactive environment
pass


# Reset terminal settings upon program exit
# Ensure terminal settings are reset when the program exits
atexit.register(reset_terminal)


def safe_input(prompt: str) -> str:
"""
Safely get input from user after resetting the input buffer.
This function clears the input buffer before prompting for user input,
which can help prevent unwanted input from being processed. The buffer
is only cleared in an interactive environment.
Args:
prompt (str): The prompt to display to the user.
Returns:
str: The user's input.
Notes:
- The input buffer is only cleared if original_terminal_settings is not None,
indicating we're in an interactive environment.
- If clearing the buffer fails, the function will still attempt to get user input.
"""
# Clear input buffer
termios.tcflush(sys.stdin, termios.TCIFLUSH)
if original_terminal_settings is not None:
try:
termios.tcflush(sys.stdin, termios.TCIFLUSH)
except termios.error:
# If flushing fails, continue to input anyway
pass

return input(prompt)

Expand Down

0 comments on commit b340ee2

Please sign in to comment.