1414# Global variables
1515IMAGE_NAME=" dotfiles-test"
1616REPORT_FILE=" ${REPORT_FILE:- test_report.txt} "
17- CURRENT_CONTAINER=" "
1817REPO_DIR=" $( git rev-parse --show-toplevel) "
18+ REPORT_LOCK=" ${REPORT_FILE} .lock"
1919
2020# Build the test image
2121build_image () {
2222 printf " Building test image with %s...\n" " $CONTAINER_RUNTIME "
2323 " $CONTAINER_RUNTIME " build -t " $IMAGE_NAME " -f " $REPO_DIR /Containerfile" " $REPO_DIR "
2424}
2525
26- # Start a new test container
27- start_container () {
28- CURRENT_CONTAINER=$( " $CONTAINER_RUNTIME " run -d \
29- -v " $REPO_DIR :/dotfiles:ro" \
30- " $IMAGE_NAME " \
31- sleep infinity)
32- printf " Started container: %s\n" " $CURRENT_CONTAINER "
33- }
34-
35- # Stop and remove the current container
36- cleanup_container () {
37- if [ -n " $CURRENT_CONTAINER " ]; then
38- printf " Cleaning up container: %s\n" " $CURRENT_CONTAINER "
39- " $CONTAINER_RUNTIME " rm -f " $CURRENT_CONTAINER " > /dev/null 2>&1 || true
40- CURRENT_CONTAINER=" "
41- fi
42- }
43-
44- # Assert function: run a command in the container and report the result
45- # Usage: assert <description> <command>
46- assert () {
26+ # Run a single test case in its own container
27+ # Usage: run_test_case <description> <command>
28+ run_test_case () {
4729 description=" $1 "
4830 shift
4931 command=" $* "
5032
51- printf " \n[TEST] %s\n" " $description " | tee -a " $REPORT_FILE "
52- printf " Command: %s\n" " $command " | tee -a " $REPORT_FILE "
33+ # Start a container for this test
34+ container=$( " $CONTAINER_RUNTIME " run -d \
35+ -v " $REPO_DIR :/dotfiles:ro" \
36+ " $IMAGE_NAME " \
37+ sleep infinity 2>&1 )
5338
5439 # Run the command in the container and capture output and exit code
55- if output=$( " $CONTAINER_RUNTIME " exec " $CURRENT_CONTAINER " sh -c " $command " 2>&1 ) ; then
40+ if output=$( " $CONTAINER_RUNTIME " exec " $container " sh -c " $command " 2>&1 ) ; then
5641 exit_code=0
5742 else
5843 exit_code=$?
5944 fi
6045
61- # Write output to report
62- if [ -n " $output " ]; then
63- printf " Output:\n%s\n" " $output " | tee -a " $REPORT_FILE "
64- fi
46+ # Clean up the container
47+ " $CONTAINER_RUNTIME " rm -f " $container " > /dev/null 2>&1 || true
48+
49+ # Write results to report (with locking for parallel safety)
50+ (
51+ # Simple file-based locking mechanism
52+ while ! mkdir " $REPORT_LOCK " 2> /dev/null; do
53+ sleep 0.1
54+ done
55+ trap ' rmdir "$REPORT_LOCK" 2>/dev/null || true' EXIT
56+
57+ {
58+ printf " \n[TEST] %s\n" " $description "
59+ printf " Command: %s\n" " $command "
60+
61+ if [ -n " $output " ]; then
62+ printf " Output:\n%s\n" " $output "
63+ fi
64+
65+ if [ $exit_code -eq 0 ]; then
66+ printf " [PASS] %s\n" " $description "
67+ else
68+ printf " [FAIL] %s (exit code: %d)\n" " $description " " $exit_code "
69+ fi
70+ } >> " $REPORT_FILE "
71+ )
6572
66- # Check result and write to report
73+ # Also print to stdout
6774 if [ $exit_code -eq 0 ]; then
68- printf " [PASS] %s\n" " $description " | tee -a " $REPORT_FILE "
69- return 0
75+ printf " [PASS] %s\n" " $description "
7076 else
71- printf " [FAIL] %s (exit code: %d)\n" " $description " " $exit_code " | tee -a " $REPORT_FILE "
72- return 1
77+ printf " [FAIL] %s (exit code: %d)\n" " $description " " $exit_code "
7378 fi
79+
80+ return $exit_code
81+ }
82+
83+ # Assert function: wrapper for run_test_case for compatibility
84+ # Usage: assert <description> <command>
85+ assert () {
86+ run_test_case " $@ "
7487}
7588
7689# Run common tests that apply to all profiles
@@ -80,42 +93,82 @@ run_common_tests() {
8093
8194 printf " \n--- Common Tests ---\n" | tee -a " $REPORT_FILE "
8295
96+ # Collect test commands to run in parallel
97+ test_pids=" "
98+
8399 # Test 1: Check for syntax errors in install script
84100 if [ -f " $profile_dir /install.sh" ]; then
85- assert " Install script has no syntax errors" \
86- " sh -n /dotfiles/profiles/$profile_name /install.sh"
101+ run_test_case " Install script has no syntax errors" \
102+ " sh -n /dotfiles/profiles/$profile_name /install.sh" &
103+ test_pids=" $test_pids $! "
87104 fi
88105
89106 # Test 2: Check for syntax errors in uninstall script
90107 if [ -f " $profile_dir /uninstall.sh" ]; then
91- assert " Uninstall script has no syntax errors" \
92- " sh -n /dotfiles/profiles/$profile_name /uninstall.sh"
108+ run_test_case " Uninstall script has no syntax errors" \
109+ " sh -n /dotfiles/profiles/$profile_name /uninstall.sh" &
110+ test_pids=" $test_pids $! "
93111 fi
94112
113+ # Wait for syntax checks to complete before running install tests
114+ for pid in $test_pids ; do
115+ wait " $pid " || true
116+ done
117+ test_pids=" "
118+
95119 # Test 3: Install script exits successfully
96120 if [ -f " $profile_dir /install.sh" ]; then
97- assert " Install script exits successfully" \
98- " cd /dotfiles/profiles/$profile_name && sh install.sh"
121+ run_test_case " Install script exits successfully" \
122+ " cd /dotfiles/profiles/$profile_name && sh install.sh" &
123+ test_pids=" $test_pids $! "
99124 fi
100125
126+ # Wait for first install to complete
127+ for pid in $test_pids ; do
128+ wait " $pid " || true
129+ done
130+ test_pids=" "
131+
101132 # Test 4: Install script is idempotent (runs twice successfully)
102133 if [ -f " $profile_dir /install.sh" ]; then
103- assert " Install script exits successfully on second run (idempotent)" \
104- " cd /dotfiles/profiles/$profile_name && sh install.sh"
134+ run_test_case " Install script exits successfully on second run (idempotent)" \
135+ " cd /dotfiles/profiles/$profile_name && sh install.sh" &
136+ test_pids=" $test_pids $! "
105137 fi
106138
107- # Test 5: Uninstall script exits successfully
139+ # Test 5: Uninstall script exits successfully (can run in parallel with idempotent test)
108140 if [ -f " $profile_dir /uninstall.sh" ]; then
109- assert " Uninstall script exits successfully" \
110- " cd /dotfiles/profiles/$profile_name && sh uninstall.sh"
141+ run_test_case " Uninstall script exits successfully" \
142+ " cd /dotfiles/profiles/$profile_name && sh uninstall.sh" &
143+ test_pids=" $test_pids $! "
111144 fi
112145
113- # Test 6: Install then uninstall works
146+ # Wait for those to complete
147+ for pid in $test_pids ; do
148+ wait " $pid " || true
149+ done
150+ test_pids=" "
151+
152+ # Test 6: Install then uninstall works (sequential in same container handled by new approach)
114153 if [ -f " $profile_dir /install.sh" ] && [ -f " $profile_dir /uninstall.sh" ]; then
115- assert " Install script exits successfully (before uninstall test)" \
116- " cd /dotfiles/profiles/$profile_name && sh install.sh"
117- assert " Uninstall script exits successfully after install" \
118- " cd /dotfiles/profiles/$profile_name && sh uninstall.sh"
154+ run_test_case " Install script exits successfully (before uninstall test)" \
155+ " cd /dotfiles/profiles/$profile_name && sh install.sh" &
156+ test_pids=" $test_pids $! "
157+
158+ # Wait for install to complete
159+ for pid in $test_pids ; do
160+ wait " $pid " || true
161+ done
162+ test_pids=" "
163+
164+ run_test_case " Uninstall script exits successfully after install" \
165+ " cd /dotfiles/profiles/$profile_name && sh uninstall.sh" &
166+ test_pids=" $test_pids $! "
167+
168+ # Wait for uninstall to complete
169+ for pid in $test_pids ; do
170+ wait " $pid " || true
171+ done
119172 fi
120173}
121174
@@ -129,12 +182,11 @@ run_profile_tests() {
129182 return 1
130183 fi
131184
132- printf " \n========================================\n" | tee -a " $REPORT_FILE "
133- printf " Testing profile: %s\n" " $profile_name " | tee -a " $REPORT_FILE "
134- printf " ========================================\n" | tee -a " $REPORT_FILE "
135-
136- # Start a fresh container for this profile
137- start_container
185+ {
186+ printf " \n========================================\n"
187+ printf " Testing profile: %s\n" " $profile_name "
188+ printf " ========================================\n"
189+ } | tee -a " $REPORT_FILE "
138190
139191 # Run common tests for all profiles
140192 run_common_tests " $profile_name "
@@ -146,9 +198,6 @@ run_profile_tests() {
146198 # shellcheck disable=SC1090
147199 . " $profile_dir /tests.sh"
148200 fi
149-
150- # Clean up after tests
151- cleanup_container
152201}
153202
154203# Main test runner
@@ -186,12 +235,6 @@ case "${1:-}" in
186235build_image)
187236 build_image
188237 ;;
189- start_container)
190- start_container
191- ;;
192- cleanup_container)
193- cleanup_container
194- ;;
195238run_tests)
196239 shift
197240 run_tests " $@ "
@@ -202,8 +245,6 @@ $(basename "$0") <command>
202245
203246Available commands:
204247 build_image Build the test container image
205- start_container Start a test container
206- cleanup_container Clean up the current container
207248 run_tests [profile] Run tests for all profiles or a specific profile
208249 help Show this help message"
209250
0 commit comments