diff --git a/Makefile b/Makefile index 52d29a2..02587b6 100644 --- a/Makefile +++ b/Makefile @@ -17,11 +17,13 @@ ALL_LIBS := $(LIBS) $(SDL_LIBS) $(FFMPEG_LIBS) -lm BUILD_DIR ?= build TARGET = $(BUILD_DIR)/gsplash +DUMMY_TARGET = $(BUILD_DIR)/dummy_game SRC = src/gsplash.c +DUMMY_SRC = src/dummy_game.c .PHONY: all clean install uninstall check -all: $(TARGET) +all: $(TARGET) $(DUMMY_TARGET) $(BUILD_DIR): mkdir -p $(BUILD_DIR) @@ -29,11 +31,16 @@ $(BUILD_DIR): $(TARGET): $(BUILD_DIR) $(SRC) $(CC) $(ALL_CFLAGS) $(LDFLAGS) $(SRC) -o $@ $(ALL_LIBS) +$(DUMMY_TARGET): $(BUILD_DIR) $(DUMMY_SRC) + $(CC) $(CFLAGS) $(SDL_CFLAGS) $(LDFLAGS) $(DUMMY_SRC) -o $@ $(SDL_LIBS) + # Lightweight smoke test (headless via SDL_VIDEODRIVER=dummy) -check: $(TARGET) +check: $(TARGET) $(DUMMY_TARGET) @echo "Running smoke test (headless)..." SDL_VIDEODRIVER=dummy $(TARGET) nonexistent.png /bin/true || true - @echo "Smoke test finished" + @echo "Running CLI test suite..." + ./tests/test_cli.sh + @echo "All tests finished successfully" install: $(TARGET) install -d "$(DESTDIR)$(bindir)" @@ -43,4 +50,4 @@ uninstall: rm -f "$(DESTDIR)$(bindir)/gsplash" clean: - rm -f $(TARGET) + rm -f $(TARGET) $(DUMMY_TARGET) diff --git a/README.md b/README.md index 83627a2..5ae3edb 100644 --- a/README.md +++ b/README.md @@ -39,14 +39,38 @@ sudo make install DESTDIR=/some/staging/path make install ``` -## Smoke test (headless) +## Testing -Run a lightweight headless smoke test that uses the dummy video driver so it doesn't require a display: +Gsplash includes several testing utilities to ensure proper functionality without requiring a heavy game binary. + +### Automated Testing + +Run the automated test suite (which includes a headless smoke test and CLI argument validation) using: ```bash make check ``` +### Interactive Visual Testing + +To physically test the splash screen rendering modes (`stretch`, `center`, `crop`) with a real image, use the interactive test script. It launches `gsplash` with each mode and prompts you to confirm if it displayed correctly: + +```bash +./tests/test_interactive.sh path/to/your/image.png +``` +*(Tip: You can place your test images in the `tests/assets/` directory).* + +### Dummy Game Utility + +For manual testing, a `dummy_game` binary is built alongside `gsplash`. It mimics a real game by sleeping to simulate startup time, creating an SDL window to trigger `gsplash`'s focus loss detection and exiting cleanly. + +```bash +./build/gsplash path/to/image.png ./build/dummy_game + +# Test with a custom 10 second simulated game load time +./build/gsplash path/to/image.png ./build/dummy_game 10 +``` + ## Usage ```bash diff --git a/src/dummy_game.c b/src/dummy_game.c new file mode 100644 index 0000000..90b3584 --- /dev/null +++ b/src/dummy_game.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + int delay_seconds = 5; + if (argc > 1) { + int parsed = atoi(argv[1]); + if (parsed > 0) { + delay_seconds = parsed; + } + } + + printf("[dummy_game] Simulating engine startup for %d seconds...\n", delay_seconds); + for (int i = 0; i < delay_seconds; i++) { + sleep(1); + printf("[dummy_game] Loading... %d/%d\n", i + 1, delay_seconds); + } + + printf("[dummy_game] Creating window to trigger focus-loss on splash screen...\n"); + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "[dummy_game] SDL Init Failed: %s\n", SDL_GetError()); + return 1; + } + + SDL_Window *window = SDL_CreateWindow( + "Dummy Game Window", + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + 800, 600, + SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE + ); + + if (!window) { + fprintf(stderr, "[dummy_game] Window Creation Failed: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (renderer) { + SDL_SetRenderDrawColor(renderer, 0, 120, 255, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + } + + printf("[dummy_game] Window created. Waiting 5 seconds before exiting...\n"); + + SDL_Event event; + int running = 1; + Uint32 start_time = SDL_GetTicks(); + + while (running && (SDL_GetTicks() - start_time < 5000)) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + running = 0; + } + } + SDL_Delay(50); + } + + printf("[dummy_game] Exiting normally.\n"); + if (renderer) SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/src/gsplash.c b/src/gsplash.c index 42d4644..831dacf 100644 --- a/src/gsplash.c +++ b/src/gsplash.c @@ -317,8 +317,14 @@ int main(int argc, char *argv[]) render_mode = parse_render_mode(argv[1] + 7); arg_index += 1; } - else if (argc >= 4 && strcmp(argv[1], "-m") == 0) + else if (argc >= 2 && strcmp(argv[1], "-m") == 0) { + if (argc < 4) + { + log_error("Usage: %s [--mode=stretch|center|crop] [args...]", argv[0]); + log_error(" %s -m stretch|center|crop [args...]", argv[0]); + return 1; + } render_mode = parse_render_mode(argv[2]); arg_index += 2; } @@ -333,7 +339,7 @@ int main(int argc, char *argv[]) const char *image_path = argv[arg_index]; const char *game_path = argv[arg_index + 1]; - log_info("Starting splash: image='%s', game='%s'", image_path, game_path); + log_info("Starting splash: image='%s', game='%s', mode=%d", image_path, game_path, render_mode); // Initialize SDL2 Video subsystems if (SDL_Init(SDL_INIT_VIDEO) < 0) diff --git a/tests/assets/sample.jpg b/tests/assets/sample.jpg new file mode 100644 index 0000000..83d068c Binary files /dev/null and b/tests/assets/sample.jpg differ diff --git a/tests/test_cli.sh b/tests/test_cli.sh new file mode 100755 index 0000000..e4f0a0d --- /dev/null +++ b/tests/test_cli.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# A test suite for testing CLI argument permutations of gsplash + +GSPLASH="./build/gsplash" +GAME="/bin/true" +IMAGE="nonexistent.png" + +if [ ! -f "$GSPLASH" ]; then + echo "Please run 'make' first to build gsplash." + exit 1 +fi + +export SDL_VIDEODRIVER=dummy + +pass=0 +fail=0 + +run_test() { + local expected_status=$1 + shift + local expected_log="$1" + shift + local desc="$1" + shift + + printf "Test: %-45s " "$desc" + + # Run the command and capture output + local output + output=$($GSPLASH "$@" 2>&1) + local status=$? + + local passed=true + local error_msg="" + + if [ $status -ne $expected_status ]; then + passed=false + error_msg="Expected status $expected_status, got $status" + elif [ -n "$expected_log" ]; then + if ! echo "$output" | grep -q "$expected_log"; then + passed=false + error_msg="Expected log missing: $expected_log" + fi + fi + + if $passed; then + echo "PASS" + pass=$((pass+1)) + else + echo "FAIL ($error_msg)" + fail=$((fail+1)) + fi +} + +echo "=== Running CLI Argument Tests ===" + +# Valid combinations (0=STRETCH, 1=CENTER, 2=CROP) +run_test 0 "mode=1" "Basic invocation" $IMAGE $GAME +run_test 0 "mode=0" "Long mode: stretch" --mode=stretch $IMAGE $GAME +run_test 0 "mode=1" "Long mode: center" --mode=center $IMAGE $GAME +run_test 0 "mode=2" "Long mode: crop" --mode=crop $IMAGE $GAME +run_test 0 "mode=0" "Long mode: fallback to stretch" --mode=invalid $IMAGE $GAME +run_test 0 "mode=0" "Short mode: stretch" -m stretch $IMAGE $GAME +run_test 0 "mode=1" "Short mode: center" -m center $IMAGE $GAME +run_test 0 "mode=2" "Short mode: crop" -m crop $IMAGE $GAME +run_test 0 "mode=0" "Short mode: fallback to stretch" -m whatever $IMAGE $GAME +run_test 0 "mode=1" "With game arguments" $IMAGE $GAME arg1 arg2 +run_test 0 "mode=2" "Mode and game arguments" -m crop $IMAGE $GAME arg1 arg2 + +# Invalid combinations (Usage errors, so we just check for status 1, no specific log needed) +run_test 1 "" "No arguments" +run_test 1 "" "Only image" $IMAGE +run_test 1 "" "Missing arg for short mode (-m only)" -m $IMAGE +run_test 1 "" "Missing game for short mode" -m stretch $IMAGE + +echo "=================================================" +echo "Tests completed: $pass passed, $fail failed." + +if [ $fail -gt 0 ]; then + exit 1 +fi +exit 0 diff --git a/tests/test_interactive.sh b/tests/test_interactive.sh new file mode 100755 index 0000000..f6b0c09 --- /dev/null +++ b/tests/test_interactive.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +GSPLASH="./build/gsplash" +DUMMY="./build/dummy_game" + +if [ ! -f "$GSPLASH" ] || [ ! -f "$DUMMY" ]; then + echo "Please run 'make' first to build gsplash and dummy_game." + exit 1 +fi + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +IMAGE="$1" +if [ ! -f "$IMAGE" ]; then + echo "Error: File '$IMAGE' not found." + exit 1 +fi + +echo "=== Interactive gsplash Visual Test ===" +echo "This will physically display the splash screen using different modes." +echo "You will be asked to confirm if it looked correct after each test." +echo "" + +pass=0 +fail=0 + +run_interactive_test() { + local mode="$1" + + echo "------------------------------------------------" + echo "Testing mode: $mode" + echo "Launching in 2 seconds (it will stay open for 3 seconds)..." + sleep 2 + + # Run gsplash with a 3-second dummy game + $GSPLASH --mode="$mode" "$IMAGE" "$DUMMY" 3 + + # Prompt the user interactively + while true; do + read -p "Did it display correctly for mode '$mode'? [y/N] " response + case "$response" in + [yY][eE][sS]|[yY]) + echo "=> PASS recorded." + pass=$((pass+1)) + break + ;; + [nN][oO]|[nN]|"") + echo "=> FAIL recorded." + fail=$((fail+1)) + break + ;; + *) + echo "Please answer y or n." + ;; + esac + done +} + +run_interactive_test "stretch" +run_interactive_test "center" +run_interactive_test "crop" + +echo "================================================" +echo "Interactive Testing Complete!" +echo "Passed: $pass" +echo "Failed: $fail"