Skip to content

Commit 39950b6

Browse files
Refactor tests to run each test case in its own container with parallel execution
Co-authored-by: HectorCastelli <3715874+HectorCastelli@users.noreply.github.com>
1 parent 01a904b commit 39950b6

File tree

2 files changed

+109
-67
lines changed

2 files changed

+109
-67
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
.target/
33
# Test reports
44
test_report.txt
5+
test_report.txt.lock

scripts/tests.sh

Lines changed: 108 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -14,63 +14,76 @@ fi
1414
# Global variables
1515
IMAGE_NAME="dotfiles-test"
1616
REPORT_FILE="${REPORT_FILE:-test_report.txt}"
17-
CURRENT_CONTAINER=""
1817
REPO_DIR="$(git rev-parse --show-toplevel)"
18+
REPORT_LOCK="${REPORT_FILE}.lock"
1919

2020
# Build the test image
2121
build_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
186235
build_image)
187236
build_image
188237
;;
189-
start_container)
190-
start_container
191-
;;
192-
cleanup_container)
193-
cleanup_container
194-
;;
195238
run_tests)
196239
shift
197240
run_tests "$@"
@@ -202,8 +245,6 @@ $(basename "$0") <command>
202245
203246
Available 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

Comments
 (0)