.dotfiles

Dotfiles Test Suite

A focused, signal-driven test suite for validating dotfiles functionality.

Quick Start

# Run all tests
./test/runner.zsh

# Run with debug output
./test/runner.zsh --debug

# Run in CI mode
CI=1 ./test/runner.zsh

# Run quick tests only
./test/runner.zsh --quick

Test Philosophy

This test suite focuses on meaningful signal rather than exhaustive coverage. Each test should answer a specific question about the health of the dotfiles setup:

  1. Does it work? - Core functionality tests
  2. Is it fast? - Performance regression tests
  3. Is it correct? - Configuration validation
  4. Will it deploy? - Integration tests

Test Categories

Core Validation (Must Pass)

Integration Tests (Should Pass)

Performance Tests (Nice to Pass)

Test Artifacts

Test results are logged to standard output with color-coded status:

Architecture

test/
├── runner.zsh          # Main test runner (Zsh)
├── security_test.zsh   # Security vulnerability scanning
├── e2e/                # Docker-based end-to-end tests
├── examples/           # Reference/example tests
├── functional/         # Functional tests (< 30s)
├── integration/        # Integration tests (< 60s)
├── lib/                # Test framework and helpers
├── performance/        # Performance regression tests
├── smoke/              # Quick validation tests (smoke + regression)
├── stress/             # Edge case and extreme condition tests
├── unit/               # Unit tests (< 5s)
│   ├── config/         # App configuration tests (kitty, wezterm, tmux, etc.)
│   ├── lib/            # Library function tests
│   ├── nvim/           # Neovim configuration tests
│   ├── scripts/        # Script tests (including git)
│   └── setup/          # Setup script tests
└── README.md           # This file

Key Features

Exit Codes

Failure propagation: the runner executes each test file through a wrapper that installs an EXIT trap escalating to exit 1 whenever $FAILED > 0. Test files therefore do NOT need to manage their own exit codes (an unconditional exit 0 at the end of a file no longer masks failed assertions). Inside run_test_functions, a test_* function returning non-zero is counted as a failure even if it never calls fail().

Gotcha: assert_x || assert_y chains count a failure for every missed alternative (each assert calls fail() internally before || moves on). Put the alternative that actually matches first, or use a plain if grep with an explicit pass/fail/skip.

Safety: test files that execute install.sh, symlinks.sh, uninstall.sh, or switch-theme.sh MUST sandbox HOME (and XDG_CONFIG_HOME where read) into $TEST_TMP_DIR first - never point them at the real home directory. symlinks.sh deletes and recreates ~/.config/nvim on real runs.

Adding New Tests

Create a new test file in the appropriate category:

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

# Source test framework
source "$TEST_DIR/lib/test_helpers.zsh"

describe "My Feature"

test_case "Feature should work"
if [[ condition ]]; then
    pass
else
    fail "Expected X but got Y"
fi

Make it executable and run via runner.zsh.

Design Principles

  1. Signal over Coverage - Test what matters, not everything
  2. Fast Feedback - All tests should complete quickly
  3. Clear Failures - Error messages should be actionable
  4. No Dependencies - Use only Python stdlib
  5. Reproducible - Tests should be deterministic

Test Coverage

The suite includes: