Skip to content
Merged
Show file tree
Hide file tree
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
21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Tests

on:
push:
pull_request:

jobs:
test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
cache: npm
- name: Install zsh
run: sudo apt-get update && sudo apt-get install -y zsh
- run: npm ci
- run: npm test
7 changes: 7 additions & 0 deletions configs/core/perf.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

zmodload zsh/datetime 2>/dev/null

if (( ! ${+EPOCHREALTIME} )); then
# zsh/datetime unavailable; define no-op stubs so _stage/_zsh_report calls still succeed
_stage() { :; }
_zsh_report() { :; }
return
fi

typeset -ga _zsh_stage_names
typeset -ga _zsh_stage_durations
typeset -g _zsh_report_called
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"append": "node src/cli.js append",
"verify": "node src/cli.js verify",
"clean": "node src/cli.js clean",
"test": "vitest run",
"test": "zsh tests/perf.zsh && vitest run",
"test:watch": "vitest"
},
"keywords": ["dotfiles", "macos", "setup", "zsh", "cli"],
Expand Down
56 changes: 55 additions & 1 deletion tests/configs.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, test, expect } from "vitest";
import { describe, test, expect, beforeEach } from "vitest";
import { readFileSync, existsSync } from "node:fs";
import { join } from "node:path";
import { execSync } from "node:child_process";
Expand Down Expand Up @@ -157,3 +157,57 @@ describe("Static config templates", () => {
}
});
});

describe("perf.zsh EPOCHREALTIME guard", () => {
let perf;

beforeEach(() => {
perf = readFileSync(join(CONFIGS_DIR, "core", "perf.zsh"), "utf-8");
});

test("checks EPOCHREALTIME availability after zmodload attempt", () => {
expect(perf).toContain("${+EPOCHREALTIME}");
});

test("guard is placed after zmodload and before typeset declarations", () => {
const zmodloadIdx = perf.indexOf("zmodload zsh/datetime");
const guardIdx = perf.indexOf("${+EPOCHREALTIME}");
const typesetIdx = perf.indexOf("typeset -ga _zsh_stage_names");

expect(zmodloadIdx).toBeGreaterThan(-1);
expect(guardIdx).toBeGreaterThan(zmodloadIdx);
expect(guardIdx).toBeLessThan(typesetIdx);
});

test("defines no-op _stage stub in the fallback block", () => {
expect(perf).toMatch(/_stage\(\) \{ :; \}/);
});

test("defines no-op _zsh_report stub in the fallback block", () => {
expect(perf).toMatch(/_zsh_report\(\) \{ :; \}/);
});

test("uses return to skip the normal-path setup in the fallback block", () => {
// 'return' must appear inside the guard block (after the guard, before typeset declarations)
const guardIdx = perf.indexOf("${+EPOCHREALTIME}");
const typesetIdx = perf.indexOf("typeset -ga _zsh_stage_names");

// Use a regex search from the guard position to allow varying indentation
const afterGuard = perf.slice(guardIdx, typesetIdx);
expect(afterGuard).toMatch(/^\s*return\s*$/m);
});

test("normal path defines full _record_stage_duration function", () => {
expect(perf).toMatch(/_record_stage_duration\(\) \{/);
});

test("normal path _stage records stage names, not just a stub", () => {
// The full _stage definition assigns to _zsh_current_stage
expect(perf).toContain('_zsh_current_stage="$1"');
});

test("normal path _zsh_report outputs a timing table", () => {
expect(perf).toContain("┌──────────────────────────┐");
expect(perf).toContain("total");
});
});
153 changes: 153 additions & 0 deletions tests/perf.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#!/usr/bin/env zsh
# =============================================================================
# Runtime behavioral tests for configs/core/perf.zsh
#
# Tests the EPOCHREALTIME guard: verifies that sourcing perf.zsh produces
# functional no-op stubs when zsh/datetime is unavailable, and full timing
# instrumentation when it is available.
#
# Usage:
# zsh tests/perf.zsh
#
# Requires zsh. The "normal path" tests additionally require the zsh/datetime
# module (present on macOS and most Linux distributions with a full zsh build).
# =============================================================================

typeset -i _pass=0 _fail=0

PERF_ZSH="${${(%):-%x}:A:h}/../configs/core/perf.zsh"
# ${(%):-%x} – expands to the path of the current script
# :A – resolves symlinks to an absolute path
# :h – strips the last component (filename), leaving the directory

_ok() { print " ✓ $1"; (( _pass++ )); }
_fail() { print " ✗ $1\n expected: '$2'\n actual: '$3'"; (( _fail++ )); }

_assert_eq() {
local desc="$1" expected="$2" actual="$3"
[[ "$expected" == "$actual" ]] && _ok "$desc" || _fail "$desc" "$expected" "$actual"
}

_assert_ne() {
local desc="$1" unexpected="$2" actual="$3"
[[ "$unexpected" != "$actual" ]] && _ok "$desc" || _fail "$desc" "(not $unexpected)" "$actual"
}

_assert_contains() {
local desc="$1" needle="$2" haystack="$3"
[[ "$haystack" == *"$needle"* ]] && _ok "$desc" || _fail "$desc" "*$needle*" "$haystack"
}

# Each test case runs in a dedicated zsh subprocess for complete state isolation.
# Results (PASS/FAIL lines) are printed to stdout and the subprocess exits
# non-zero on assertion failure so the parent can tally counts.

_run() {
local label="$1"
local script="$2"
local out rc
out=$(zsh -c "$script" 2>&1)
rc=$?
if (( rc == 0 )); then
_ok "$label"
else
_fail "$label" "exit 0" "exit $rc ($out)"
fi
}

# =============================================================================
# 1. Fallback path: EPOCHREALTIME unavailable (zsh/datetime not loaded)
# =============================================================================

# Common preamble embedded into every fallback subprocess: override zmodload so
# the zsh/datetime module is never loaded and EPOCHREALTIME stays unset.
_FALLBACK_SETUP="
function zmodload { :; }
unset EPOCHREALTIME 2>/dev/null
source '$PERF_ZSH'
"

print "\nperf.zsh – fallback path (EPOCHREALTIME unavailable):"

_run "_stage is a no-op (does not set _zsh_current_stage)" "
$_FALLBACK_SETUP
_stage 'test-stage'
[[ -z \"\${_zsh_current_stage:-}\" ]]
"

_run "_zsh_report is a no-op (produces no output)" "
$_FALLBACK_SETUP
out=\$(_zsh_report 2>&1)
[[ -z \"\$out\" ]]
"

_run "_zsh_stage_names array is not populated after _stage call" "
$_FALLBACK_SETUP
_stage 'x'
_stage 'y'
[[ -z \"\${_zsh_stage_names[*]:-}\" ]]
"

_run "repeated _stage calls do not raise errors" "
$_FALLBACK_SETUP
_stage 'a'; _stage 'b'; _stage 'c'
true
"

_run "repeated _zsh_report calls do not raise errors" "
$_FALLBACK_SETUP
_zsh_report; _zsh_report
true
"

# =============================================================================
# 2. Normal path: EPOCHREALTIME available (zsh/datetime loaded successfully)
# =============================================================================
print "\nperf.zsh – normal path (EPOCHREALTIME available):"

if ! zsh -c "zmodload zsh/datetime 2>/dev/null && (( \${+EPOCHREALTIME} ))" >/dev/null 2>&1; then
print " ⚠ zsh/datetime not available in this environment; skipping normal-path tests"
else
_run "_stage sets _zsh_current_stage" "
source '$PERF_ZSH'
_stage 'env'
[[ \"\$_zsh_current_stage\" == 'env' ]]
"

_run "_stage records the previous stage name when transitioning" "
source '$PERF_ZSH'
_stage 'env'
_stage 'tools'
[[ \"\${_zsh_stage_names[1]}\" == 'env' ]]
"

_run "_stage records a non-zero duration for the previous stage" "
source '$PERF_ZSH'
_stage 'env'
sleep 0.01
_stage 'tools'
(( \${_zsh_stage_durations[1]:-0} > 0 ))
"

_run "_zsh_report outputs a timing table containing 'total'" "
source '$PERF_ZSH'
_stage 'env'
_stage 'tools'
out=\$(_zsh_report 2>&1)
[[ \"\$out\" == *'total'* ]]
"

_run "_zsh_report is idempotent (second call produces no extra output)" "
source '$PERF_ZSH'
_stage 'env'
_zsh_report >/dev/null 2>&1
second=\$(_zsh_report 2>&1)
[[ -z \"\$second\" ]]
"
fi

# =============================================================================
# Summary
# =============================================================================
print "\nResults: $_pass passed, $_fail failed."
(( _fail == 0 ))