Skip to main content

Testing Guide

This guide covers the gummyworm testing infrastructure, including how to run tests, write new tests, and understand the test architecture.

Quick Start

# Run all tests
./tests/run_all.sh

# Run only unit tests (fast, no ImageMagick required)
./tests/run_all.sh --unit

# Run only integration tests
./tests/run_all.sh --integration

# Run with verbose output
./tests/run_all.sh --verbose

# Run a single test file
./tests/test_utils.sh

Test Architecture

tests/
├── test_runner.sh # Shared test framework & assertions
├── run_all.sh # Master test runner
├── test_utils.sh # Unit tests for lib/utils.sh
├── test_palettes.sh # Unit tests for lib/palettes.sh
├── test_image.sh # Unit tests for lib/image.sh
├── test_export.sh # Unit tests for lib/export.sh & lib/converter.sh
├── test_integration.sh # End-to-end CLI tests
├── test_basic.sh # Legacy integration tests (deprecated)
└── fixtures/ # Test data
├── test.palette # Sample custom palette
├── README.md # Fixtures documentation
└── expected/ # Expected outputs for regression tests

Test Categories

CategoryFilesImageMagickPurpose
Unittest_*.sh (except integration)OptionalTest individual functions in isolation
Integrationtest_integration.shRequiredTest complete CLI workflows

Test Runner Framework

All test files source test_runner.sh which provides:

Assertions

# Equality checks
assert_equals "expected" "actual" "message"
assert_not_equals "unexpected" "actual" "message"

# Boolean conditions
assert_true 'command_to_test' "message"
assert_false 'command_that_should_fail' "message"

# String checks
assert_contains "$haystack" "needle" "message"
assert_not_contains "$haystack" "needle" "message"
assert_matches "$string" "regex_pattern" "message"

# File checks
assert_file_exists "/path/to/file" "message"
assert_file_not_exists "/path/to/file" "message"
assert_dir_exists "/path/to/dir" "message"
assert_file_not_empty "/path/to/file" "message"

# Exit codes
assert_exit_code 0 "command_to_run" "message"
assert_exit_code 1 "command_that_should_fail" "message"

# Output checks
assert_output "expected output" "command" "message"
assert_output_contains "substring" "command" "message"

Temp File Management

# Create temp files (automatically cleaned up)
local tmpfile=$(make_temp_file ".txt")
local tmpdir=$(make_temp_dir)

# Files are cleaned up automatically after tests

Mock Functions

# Mock ImageMagick commands for unit tests
mock_identify 640 480 "PNG" # Returns these dimensions
mock_convert # Creates empty files

# Restore after testing
restore_mocks

Test Discovery

Tests are automatically discovered by function name prefix:

test_my_feature_works() {
# Test code here
assert_equals "expected" "actual"
}

# Discovered and run automatically
run_discovered_tests "My Test Suite"

Writing Tests

Unit Test Example

#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/test_runner.sh"

# Load gummyworm libraries
load_gummyworm_libs

# Test functions must start with test_
test_my_function_basic() {
local result
result=$(my_function "input")
assert_equals "expected" "$result" "basic case works"
}

test_my_function_edge_case() {
assert_false 'my_function ""' "empty input handled"
}

# Run all test_* functions
run_discovered_tests "my_function Unit Tests"

Integration Test Example

#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/test_runner.sh"

# Setup creates test images
setup() {
if ! has_imagemagick; then
echo "Warning: ImageMagick not available"
return
fi

TEST_IMG=$(make_temp_file ".png")
create_test_image "$TEST_IMG" 50 50
}

# Helper to skip when ImageMagick unavailable
require_imagemagick() {
if ! has_imagemagick; then
skip_test "ImageMagick not available"
return 1
fi
return 0
}

test_cli_basic_conversion() {
require_imagemagick || return 0
assert_exit_code 0 "$GUMMYWORM -q -w 20 '$TEST_IMG'"
}

run_discovered_tests "CLI Integration Tests"

Testing Best Practices

  1. Name tests descriptively: test_is_positive_int_rejects_negative
  2. Test one thing per function: Keep tests focused
  3. Use meaningful assertion messages: Help debug failures
  4. Clean up resources: Use make_temp_file for automatic cleanup
  5. Handle missing dependencies: Use skip_test gracefully
  6. Mock external commands: Use mocks for unit test isolation

Running Specific Tests

# Run individual test file
./tests/test_utils.sh

# Run with bash debugging
bash -x ./tests/test_utils.sh

# Run specific test function (not directly supported, but you can add)
TEST_FILTER="is_positive_int" ./tests/test_utils.sh

CI Integration

The test suite is designed for CI environments:

# GitHub Actions example
- name: Run unit tests
run: ./tests/run_all.sh --unit

- name: Run integration tests
run: |
brew install imagemagick # or apt-get
./tests/run_all.sh --integration

Exit Codes

  • 0: All tests passed
  • 1: One or more tests failed

Test Coverage

Current Coverage by Module

ModuleUnit TestsIntegration TestsCoverage
lib/utils.sh-High
lib/palettes.shHigh
lib/image.shMedium
lib/converter.shMedium
lib/export.shHigh
lib/cli.sh-Medium
bin/gummyworm-High

Adding Coverage

To improve coverage:

  1. Identify untested functions in lib/ files
  2. Add unit tests for pure functions
  3. Add integration tests for CLI behaviors
  4. Add regression tests with fixtures for output formats

Fixtures

Test fixtures are stored in tests/fixtures/:

fixtures/
├── test.palette # Custom palette for testing
├── expected/ # Expected outputs for regression
│ └── .gitkeep
└── README.md

Adding Expected Outputs

# Generate expected output
./gummyworm -w 20 -f html tests/fixtures/test_image.png > tests/fixtures/expected/output.html

# Use in regression test
test_html_output_unchanged() {
local output=$(make_temp_file ".html")
"$GUMMYWORM" -w 20 -f html -o "$output" "$FIXTURE_IMG"

# Compare with expected
diff -q "$output" "$FIXTURES_DIR/expected/output.html"
}

Troubleshooting

Tests fail with "command not found"

Ensure scripts are executable:

chmod +x tests/*.sh

ImageMagick tests skipped

Install ImageMagick:

# macOS
brew install imagemagick

# Ubuntu/Debian
sudo apt-get install imagemagick

# Fedora
sudo dnf install ImageMagick

Python-dependent tests fail

Some unicode tests require Python 3:

python3 --version  # Verify installation

Test hangs

Check for infinite loops in tested functions, or use timeout:

timeout 60 ./tests/test_utils.sh