Skip to content

Commit 954adb9

Browse files
feedback
1 parent 5cb936f commit 954adb9

2 files changed

Lines changed: 73 additions & 39 deletions

File tree

Dockerfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@ FROM ${WOLFI_BASE} AS builder
88
# of Factor's VM); the rest fetch + bootstrap the source tree.
99
RUN apk add --no-cache bash build-base curl git wget
1010

11+
# Factor 0.101
12+
ARG FACTOR_COMMIT="a56e6390e81340be6573cb790311c0a980a5f369"
13+
1114
WORKDIR /opt
1215
RUN git clone https://github.com/factor/factor.git && \
1316
cd factor && \
14-
git checkout a56e6390e81340be6573cb790311c0a980a5f369
17+
git checkout ${FACTOR_COMMIT}
1518
WORKDIR /opt/factor
1619
RUN ./build.sh update
1720

bin/run.sh

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,37 @@ fi
2828
slug="$1"
2929
solution_dir=$(realpath "${2%/}")
3030
output_dir=$(realpath "${3%/}")
31-
mkdir -p "$output_dir"
31+
mkdir -p "${output_dir}"
3232
results_file="${output_dir}/results.json"
3333
canonical_root="/opt/test-runner/tests/${slug}"
3434
test_file="${solution_dir}/${slug}/${slug}-tests.factor"
3535

3636
echo "${slug}: testing..."
3737

38-
if [[ ! -f "$test_file" ]]; then
38+
if [[ ! -f "${test_file}" ]]; then
3939
jq -n --arg msg "test file not found: ${slug}-tests.factor" \
40-
'{version: 3, status: "error", message: $msg}' >"$results_file"
40+
'{version: 3, status: "error", message: $msg}' > "${results_file}"
4141
exit 0
4242
fi
4343

4444
# Copy the fixture to a fresh temp dir so the rewrite below does not mutate
4545
# the source.
4646
tmp_dir=$(mktemp -d -t "factor-runner-${slug}-XXXXX")
47-
trap 'rm -rf "$tmp_dir"' EXIT
48-
cp -r "${solution_dir}/." "$tmp_dir"
47+
trap 'rm -rf "${tmp_dir}"' EXIT
48+
cp -r "${solution_dir}/." "${tmp_dir}"
4949
stripped_tests="${tmp_dir}/${slug}/${slug}-tests.factor"
50-
awk '!/^STOP-HERE$/' "$stripped_tests" > "${stripped_tests}.new"
51-
mv "${stripped_tests}.new" "$stripped_tests"
50+
awk '!/^STOP-HERE$/' "${stripped_tests}" > "${stripped_tests}.new"
51+
mv "${stripped_tests}.new" "${stripped_tests}"
5252

5353
# Run Factor; capture combined stdout/stderr.
5454
set +e
55-
raw_output=$(cd "$tmp_dir" && factor -roots=. -run=exercism-tools "$slug" 2>&1)
55+
raw_output=$(cd "${tmp_dir}" && factor -roots=. -run=exercism-tools "${slug}" 2>&1)
5656
set -e
5757
# Normalize the tmp path to the canonical Docker path.
58-
raw_output=${raw_output//$tmp_dir/$canonical_root}
58+
raw_output=${raw_output//${tmp_dir}/${canonical_root}}
5959

6060
# Awk parser shared by all stages: JSON-escape a single string field.
61-
read -r -d '' AWK_JSON <<'AWK' || true
61+
read -r -d '' awk_json << 'AWK' || true
6262
function json_str(s, r) {
6363
r = s
6464
gsub(/\\/, "\\\\", r)
@@ -75,7 +75,7 @@ AWK
7575
# 1. Extract source-test records (one JSON object per line, NDJSON):
7676
# {"line_no":N,"task_id":N|null,"test_code":"..."}
7777
# Reads the post-strip file so line numbers match what Factor reports.
78-
src_tests=$(awk "$AWK_JSON"'
78+
src_tests=$(awk "${awk_json}"'
7979
BEGIN { task = "null" }
8080
/^[[:space:]]*TASK:[[:space:]]+[0-9]+/ {
8181
match($0, /TASK:[[:space:]]+[0-9]+/)
@@ -95,19 +95,21 @@ src_tests=$(awk "$AWK_JSON"'
9595
# 2. Parse Factor stdout into NDJSON segments and failures:
9696
# segments: {"type":"segment","idx":N,"failed":bool,"output":"..."}
9797
# failures: {"type":"failure","line_no":N,"message":"..."}
98-
parsed=$(printf '%s\n' "$raw_output" | awk "$AWK_JSON"'
98+
parsed=$(printf '%s\n' "${raw_output}" | awk "${awk_json}"'
9999
function close_segment( out, i) {
100100
if (idx == 0) return
101101
out = ""
102102
for (i = 1; i <= seg_n; i++) out = out (i > 1 ? "\n" : "") seg[i]
103-
sub(/^\n+/, "", out); sub(/\n+$/, "", out)
103+
sub(/^\n+/, "", out)
104+
sub(/\n+$/, "", out)
104105
printf "{\"type\":\"segment\",\"idx\":%d,\"failed\":%s,\"output\":%s}\n",
105106
idx, (seg_failed ? "true" : "false"), json_str(out)
106107
}
107108
function close_failure( body, i) {
108109
body = ""
109110
for (i = 1; i <= fail_n; i++) body = body (i > 1 ? "\n" : "") fail[i]
110-
sub(/^\n+/, "", body); sub(/\n+$/, "", body)
111+
sub(/^\n+/, "", body)
112+
sub(/\n+$/, "", body)
111113
printf "{\"type\":\"failure\",\"line_no\":%d,\"message\":%s}\n",
112114
fail_line, json_str(body)
113115
}
@@ -122,65 +124,94 @@ parsed=$(printf '%s\n' "$raw_output" | awk "$AWK_JSON"'
122124
# must-infer → "Must Infer:"
123125
# must-infer-as → "Must Infer As:"
124126
BEGIN {
125-
state = "inline"; idx = 0
127+
state = "inline"
128+
idx = 0
126129
header_re = "^(Unit Test|Unit Test~|Unit Test V~|Long Unit Test|Must Fail|Must Fail With|Must Not Fail|Must Infer|Must Infer As): "
127130
}
128131
state == "inline" && $0 ~ header_re {
129132
close_segment()
130-
idx++; seg_failed = 0; seg_n = 0; delete seg
133+
idx++
134+
seg_failed = 0
135+
seg_n = 0
136+
delete seg
131137
next
132138
}
133139
state == "inline" && $0 == "###FAIL_BEGIN###" {
134-
close_segment(); idx = 0
135-
state = "fail_loc"; next
140+
close_segment()
141+
idx = 0
142+
state = "fail_loc"
143+
next
136144
}
137145
state == "inline" && $0 == "--> test failed!" {
138-
seg_failed = 1; next
146+
seg_failed = 1
147+
next
139148
}
140149
state == "inline" {
141-
if (idx > 0) { seg_n++; seg[seg_n] = $0 }
150+
if (idx > 0) {
151+
seg_n++
152+
seg[seg_n] = $0
153+
}
142154
next
143155
}
144156
state == "fail_loc" {
145157
if (match($0, /:[[:space:]]*[0-9]+[[:space:]]*$/)) {
146-
n = substr($0, RSTART, RLENGTH); gsub(/[^0-9]/, "", n)
158+
n = substr($0, RSTART, RLENGTH)
159+
gsub(/[^0-9]/, "", n)
147160
fail_line = n + 0
148-
} else { fail_line = 0 }
149-
fail_n = 0; delete fail
150-
state = "fail_body"; next
161+
} else {
162+
fail_line = 0
163+
}
164+
fail_n = 0
165+
delete fail
166+
state = "fail_body"
167+
next
151168
}
152169
state == "fail_body" && $0 == "###FAIL_END###" {
153170
close_failure()
154-
state = "fail_between"; next
171+
state = "fail_between"
172+
next
155173
}
156174
state == "fail_body" {
157-
fail_n++; fail[fail_n] = $0; next
175+
fail_n++
176+
fail[fail_n] = $0
177+
next
158178
}
159179
state == "fail_between" && $0 == "###FAIL_BEGIN###" {
160-
state = "fail_loc"; next
180+
state = "fail_loc"
181+
next
161182
}
162183
END { close_segment() }
163184
')
164185

165-
segments=$(printf '%s\n' "$parsed" | awk '/"type":"segment"/' || true)
166-
failures=$(printf '%s\n' "$parsed" | awk '/"type":"failure"/' || true)
186+
segments=$(printf '%s\n' "${parsed}" | awk '/"type":"segment"/' || true)
187+
failures=$(printf '%s\n' "${parsed}" | awk '/"type":"failure"/' || true)
167188

168189
# 3. If no segments emitted, surface a top-level error from the raw output.
169-
if [[ -z "$segments" ]]; then
170-
cleaned=$(printf '%s\n' "$raw_output" | awk '/^\([UO]\) /{exit} {print}' \
171-
| awk 'NF { print; blank = 0; next } !blank { print; blank = 1 }')
172-
if [[ -z "$cleaned" ]]; then cleaned="No tests were executed"; fi
173-
jq -n --arg msg "$cleaned" '{version:3, status:"error", message:$msg}' >"$results_file"
190+
if [[ -z "${segments}" ]]; then
191+
cleaned=$(printf '%s\n' "${raw_output}" | awk '/^\([UO]\) /{exit} {print}' \
192+
| awk '
193+
NF {
194+
print
195+
blank = 0
196+
next
197+
}
198+
!blank {
199+
print
200+
blank = 1
201+
}
202+
')
203+
[[ -z "${cleaned}" ]] && cleaned="No tests were executed"
204+
jq -n --arg msg "${cleaned}" '{version:3, status:"error", message:$msg}' > "${results_file}"
174205
exit 0
175206
fi
176207

177208
# 4. Compose the v3 JSON.
178209
# --slurpfile would require files; instead pass NDJSON via --argjson after
179210
# converting each line. We use jq -s on a pipeline of NDJSON inputs.
180211
jq -n \
181-
--argjson srcs "$(printf '%s\n' "$src_tests" | jq -s '.')" \
182-
--argjson segs "$(printf '%s\n' "$segments" | jq -s '.')" \
183-
--argjson fails "$(printf '%s\n' "$failures" | jq -s '.')" \
212+
--argjson srcs "$(printf '%s\n' "${src_tests}" | jq -s '.')" \
213+
--argjson segs "$(printf '%s\n' "${segments}" | jq -s '.')" \
214+
--argjson fails "$(printf '%s\n' "${failures}" | jq -s '.')" \
184215
'
185216
($fails | map({(.line_no|tostring): .message}) | add // {}) as $fail_by_line
186217
| $segs | sort_by(.idx)
@@ -209,6 +240,6 @@ jq -n \
209240
)
210241
| (if all(.status == "pass") then "pass" else "fail" end) as $top
211242
| {version: 3, status: $top, tests: .}
212-
' >"$results_file"
243+
' > "${results_file}"
213244

214245
echo "${slug}: done"

0 commit comments

Comments
 (0)