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
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ RUN rm -rf .git build vm src misc Factor.app \
GNUmakefile Nmakefile LICENSE.txt README.md \
build.sh build.cmd unmaintained

# Keep only the extra/ source that sequences.extras needs to compile on demand:
# itself plus extra/assocs.extras and extra/shuffle.
# Keep only the extra/ source needed to compile sequences.extras and
# assocs.extras on demand: those two plus extra/shuffle.
RUN find extra -mindepth 1 -maxdepth 1 \
! -name sequences ! -name assocs ! -name shuffle \
-exec rm -rf {} + && \
Expand Down
29 changes: 22 additions & 7 deletions bin/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ tmp_dir=$(mktemp -d -t "factor-runner-${slug}-XXXXX")
trap 'rm -rf "${tmp_dir}"' EXIT
cp -r "${solution_dir}/." "${tmp_dir}"
stripped_tests="${tmp_dir}/${slug}/${slug}-tests.factor"
awk '!/^STOP-HERE$/' "${stripped_tests}" > "${stripped_tests}.new"
# Blank each STOP-HERE line rather than deleting it: a blank line is a no-op
# to Factor, and keeping the line count intact means the line numbers Factor
# reports for failures still match the file the student submitted.
awk '{ if ($0 ~ /^STOP-HERE$/) print ""; else print }' \
"${stripped_tests}" > "${stripped_tests}.new"
mv "${stripped_tests}.new" "${stripped_tests}"

# Run Factor; capture combined stdout/stderr.
Expand Down Expand Up @@ -131,7 +135,7 @@ src_tests=$(awk "${awk_json}"'

# 2. Parse Factor stdout into NDJSON segments and failures:
# segments: {"type":"segment","idx":N,"failed":bool,"name":"...","output":"..."}
# failures: {"type":"failure","line_no":N,"message":"..."}
# failures: {"type":"failure","line_no":N,"location":"...","message":"..."}
parsed=$(printf '%s\n' "${raw_output}" | awk "${awk_json}"'
function close_segment( out, i) {
if (idx == 0) return
Expand All @@ -147,8 +151,8 @@ parsed=$(printf '%s\n' "${raw_output}" | awk "${awk_json}"'
for (i = 1; i <= fail_n; i++) body = body (i > 1 ? "\n" : "") fail[i]
sub(/^\n+/, "", body)
sub(/\n+$/, "", body)
printf "{\"type\":\"failure\",\"line_no\":%d,\"message\":%s}\n",
fail_line, json_str(body)
printf "{\"type\":\"failure\",\"line_no\":%d,\"location\":%s,\"message\":%s}\n",
fail_line, json_str(fail_loc), json_str(body)
}
# Factor renders each test-word name into a title, e.g.:
# unit-test → "Unit Test:"
Expand Down Expand Up @@ -221,8 +225,14 @@ parsed=$(printf '%s\n' "${raw_output}" | awk "${awk_json}"'
n = substr($0, RSTART, RLENGTH)
gsub(/[^0-9]/, "", n)
fail_line = n + 0
# exercism-tools prints "<path>: <line#>"; collapse it to the
# conventional "<path>:<line#>" so the student can paste it
# into an editor. The path is already relative to the solution
# root, so it is safe to show as-is.
fail_loc = substr($0, 1, RSTART - 1) ":" fail_line
} else {
fail_line = 0
fail_loc = ""
}
fail_n = 0
delete fail
Expand Down Expand Up @@ -276,15 +286,16 @@ jq -n \
--argjson segs "$(printf '%s\n' "${segments}" | jq -s '.')" \
--argjson fails "$(printf '%s\n' "${failures}" | jq -s '.')" \
'
($fails | map({(.line_no|tostring): .message}) | add // {}) as $fail_by_line
($fails | map({(.line_no|tostring): .}) | add // {}) as $fail_by_line
| $segs | sort_by(.idx)
| to_entries
| map(
.value as $seg
| (.key) as $i
| ($srcs[$i] // null) as $src
| ($src.line_no | tostring) as $ln
| ($fail_by_line[$ln] // null) as $msg
| ($fail_by_line[$ln] // null) as $fail
| ($fail.message // null) as $msg
| (
if $seg.failed then
if ($msg // "" | startswith("=== Expected:")) then "fail"
Expand All @@ -300,7 +311,11 @@ jq -n \
test_code: ($src.test_code // ""),
}
+ (if $src.task_id then {task_id: $src.task_id} else {} end)
+ (if $seg.failed then {message: ($msg // "test failed")} else {} end)
+ (if $seg.failed then
{message: (if $msg == null then "test failed"
elif ($fail.location // "") == "" then $msg
else $fail.location + "\n" + $msg end)}
else {} end)
+ (if $seg.output != "" then {output: ($seg.output[0:500])} else {} end)
)
| (if all(.status == "pass") then "pass" else "fail" end) as $top
Expand Down
16 changes: 7 additions & 9 deletions tests/all-fail/exercism-tools/exercism-tools.factor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
USING: accessors command-line continuations debugger io kernel
lexer namespaces sequences source-files.errors.debugger
system tools.test vocabs vocabs.loader ;
lexer namespaces prettyprint.config sequences
source-files.errors.debugger system tools.test vocabs
vocabs.loader ;
IN: exercism-tools

SYNTAX: STOP-HERE
Expand All @@ -9,26 +10,23 @@ SYNTAX: STOP-HERE
SYNTAX: TASK:
lexer get next-line ;

! Label the test that follows with its description. The marker lets the
! wrapper strip this line from captured output and attach it to the next
! test as a name, rather than leaving it in the previous test's output.
! Label the test that follows with its description.
: description ( str -- )
"###DESC### " write print ;

! Print one failure block in a stable, parser-friendly form. Bracketed by
! markers so a wrapper can split the stream reliably and avoid Factor's
! noisy callstack output (which is interleaved with subsequent failures).
! Print one failure block in a stable, parser-friendly form.
:: print-failure ( failure -- )
"###FAIL_BEGIN###" print
failure error-location print
failure error>> [ error. ] [ 2drop ] recover
[ failure error>> [ error. ] [ 2drop ] recover ] without-limits
"###FAIL_END###" print
flush ;

: print-failures ( -- )
test-failures get [ print-failure ] each ;

: run-exercism-tests ( -- )
vocab-roots [ "." prefix ] change-global
command-line get first
[ require ] [ test ] bi
test-failures get empty?
Expand Down
4 changes: 2 additions & 2 deletions tests/all-fail/expected_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
"name": "greet returns hello",
"status": "fail",
"test_code": "{ \"hello\" } [ greet ] unit-test",
"message": "=== Expected:\n\"hello\"\n=== Got:\n\"wrong\""
"message": "all-fail/all-fail-tests.factor:5\n=== Expected:\n\"hello\"\n=== Got:\n\"wrong\""
},
{
"name": "greet returns world",
"status": "fail",
"test_code": "{ \"world\" } [ greet ] unit-test",
"message": "=== Expected:\n\"world\"\n=== Got:\n\"wrong\""
"message": "all-fail/all-fail-tests.factor:10\n=== Expected:\n\"world\"\n=== Got:\n\"wrong\""
}
]
}
16 changes: 7 additions & 9 deletions tests/concept-concurrency/exercism-tools/exercism-tools.factor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
USING: accessors command-line continuations debugger io kernel
lexer namespaces sequences source-files.errors.debugger
system tools.test vocabs vocabs.loader ;
lexer namespaces prettyprint.config sequences
source-files.errors.debugger system tools.test vocabs
vocabs.loader ;
IN: exercism-tools

SYNTAX: STOP-HERE
Expand All @@ -9,26 +10,23 @@ SYNTAX: STOP-HERE
SYNTAX: TASK:
lexer get next-line ;

! Label the test that follows with its description. The marker lets the
! wrapper strip this line from captured output and attach it to the next
! test as a name, rather than leaving it in the previous test's output.
! Label the test that follows with its description.
: description ( str -- )
"###DESC### " write print ;

! Print one failure block in a stable, parser-friendly form. Bracketed by
! markers so a wrapper can split the stream reliably and avoid Factor's
! noisy callstack output (which is interleaved with subsequent failures).
! Print one failure block in a stable, parser-friendly form.
:: print-failure ( failure -- )
"###FAIL_BEGIN###" print
failure error-location print
failure error>> [ error. ] [ 2drop ] recover
[ failure error>> [ error. ] [ 2drop ] recover ] without-limits
"###FAIL_END###" print
flush ;

: print-failures ( -- )
test-failures get [ print-failure ] each ;

: run-exercism-tests ( -- )
vocab-roots [ "." prefix ] change-global
command-line get first
[ require ] [ test ] bi
test-failures get empty?
Expand Down
16 changes: 7 additions & 9 deletions tests/concept-multiline/exercism-tools/exercism-tools.factor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
USING: accessors command-line continuations debugger io kernel
lexer namespaces sequences source-files.errors.debugger
system tools.test vocabs vocabs.loader ;
lexer namespaces prettyprint.config sequences
source-files.errors.debugger system tools.test vocabs
vocabs.loader ;
IN: exercism-tools

SYNTAX: STOP-HERE
Expand All @@ -9,26 +10,23 @@ SYNTAX: STOP-HERE
SYNTAX: TASK:
lexer get next-line ;

! Label the test that follows with its description. The marker lets the
! wrapper strip this line from captured output and attach it to the next
! test as a name, rather than leaving it in the previous test's output.
! Label the test that follows with its description.
: description ( str -- )
"###DESC### " write print ;

! Print one failure block in a stable, parser-friendly form. Bracketed by
! markers so a wrapper can split the stream reliably and avoid Factor's
! noisy callstack output (which is interleaved with subsequent failures).
! Print one failure block in a stable, parser-friendly form.
:: print-failure ( failure -- )
"###FAIL_BEGIN###" print
failure error-location print
failure error>> [ error. ] [ 2drop ] recover
[ failure error>> [ error. ] [ 2drop ] recover ] without-limits
"###FAIL_END###" print
flush ;

: print-failures ( -- )
test-failures get [ print-failure ] each ;

: run-exercism-tests ( -- )
vocab-roots [ "." prefix ] change-global
command-line get first
[ require ] [ test ] bi
test-failures get empty?
Expand Down
2 changes: 1 addition & 1 deletion tests/concept-multiline/expected_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"status": "fail",
"test_code": "{ 6 } [ 4 [ 6 roll-die ] play-seeded ] unit-test",
"task_id": 5,
"message": "=== Expected:\n6\n=== Got:\n2"
"message": "concept-multiline/concept-multiline-tests.factor:62\n=== Expected:\n6\n=== Got:\n2"
},
{
"name": "Test 13",
Expand Down
16 changes: 7 additions & 9 deletions tests/concept-not-parsing/exercism-tools/exercism-tools.factor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
USING: accessors command-line continuations debugger io kernel
lexer namespaces sequences source-files.errors.debugger
system tools.test vocabs vocabs.loader ;
lexer namespaces prettyprint.config sequences
source-files.errors.debugger system tools.test vocabs
vocabs.loader ;
IN: exercism-tools

SYNTAX: STOP-HERE
Expand All @@ -9,26 +10,23 @@ SYNTAX: STOP-HERE
SYNTAX: TASK:
lexer get next-line ;

! Label the test that follows with its description. The marker lets the
! wrapper strip this line from captured output and attach it to the next
! test as a name, rather than leaving it in the previous test's output.
! Label the test that follows with its description.
: description ( str -- )
"###DESC### " write print ;

! Print one failure block in a stable, parser-friendly form. Bracketed by
! markers so a wrapper can split the stream reliably and avoid Factor's
! noisy callstack output (which is interleaved with subsequent failures).
! Print one failure block in a stable, parser-friendly form.
:: print-failure ( failure -- )
"###FAIL_BEGIN###" print
failure error-location print
failure error>> [ error. ] [ 2drop ] recover
[ failure error>> [ error. ] [ 2drop ] recover ] without-limits
"###FAIL_END###" print
flush ;

: print-failures ( -- )
test-failures get [ print-failure ] each ;

: run-exercism-tests ( -- )
vocab-roots [ "." prefix ] change-global
command-line get first
[ require ] [ test ] bi
test-failures get empty?
Expand Down
2 changes: 1 addition & 1 deletion tests/concept-not-parsing/expected_results.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": 3,
"status": "error",
"message": "/opt/test-runner/tests/concept-not-parsing/concept-not-parsing/concept-not-parsing.factor\n\n6: : cube ( n -- n^3 ) ?!?! ;\n ^\nNo word named '?!?!' found in current vocabulary search path"
"message": "concept-not-parsing/concept-not-parsing.factor\n\n6: : cube ( n -- n^3 ) ?!?! ;\n ^\nNo word named '?!?!' found in current vocabulary search path"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
USING: accessors command-line continuations debugger io kernel
lexer namespaces sequences source-files.errors.debugger
system tools.test vocabs vocabs.loader ;
lexer namespaces prettyprint.config sequences
source-files.errors.debugger system tools.test vocabs
vocabs.loader ;
IN: exercism-tools

SYNTAX: STOP-HERE
Expand All @@ -9,26 +10,23 @@ SYNTAX: STOP-HERE
SYNTAX: TASK:
lexer get next-line ;

! Label the test that follows with its description. The marker lets the
! wrapper strip this line from captured output and attach it to the next
! test as a name, rather than leaving it in the previous test's output.
! Label the test that follows with its description.
: description ( str -- )
"###DESC### " write print ;

! Print one failure block in a stable, parser-friendly form. Bracketed by
! markers so a wrapper can split the stream reliably and avoid Factor's
! noisy callstack output (which is interleaved with subsequent failures).
! Print one failure block in a stable, parser-friendly form.
:: print-failure ( failure -- )
"###FAIL_BEGIN###" print
failure error-location print
failure error>> [ error. ] [ 2drop ] recover
[ failure error>> [ error. ] [ 2drop ] recover ] without-limits
"###FAIL_END###" print
flush ;

: print-failures ( -- )
test-failures get [ print-failure ] each ;

: run-exercism-tests ( -- )
vocab-roots [ "." prefix ] change-global
command-line get first
[ require ] [ test ] bi
test-failures get empty?
Expand Down
14 changes: 7 additions & 7 deletions tests/concept-partial-fail/expected_results.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,50 +21,50 @@
"status": "fail",
"test_code": "{ 27 } [ \"cubing 3\" print 3 cube ] unit-test",
"task_id": 2,
"message": "=== Expected:\n27\n=== Got:\n12",
"message": "concept-partial-fail/concept-partial-fail-tests.factor:12\n=== Expected:\n27\n=== Got:\n12",
"output": "cubing 3"
},
{
"name": "Test 4",
"status": "fail",
"test_code": "{ 64 } [ 4 cube ] unit-test",
"task_id": 2,
"message": "=== Expected:\n64\n=== Got:\n20"
"message": "concept-partial-fail/concept-partial-fail-tests.factor:13\n=== Expected:\n64\n=== Got:\n20"
},
{
"name": "Test 5",
"status": "error",
"test_code": "{ { } } [ { } but-first ] unit-test",
"task_id": 3,
"message": "Cannot create slice: from > to\nfrom 1\nto 0\nseq { }"
"message": "concept-partial-fail/concept-partial-fail-tests.factor:16\nCannot create slice: from > to\nfrom 1\nto 0\nseq { }"
},
{
"name": "Test 6",
"status": "error",
"test_code": "{ 0 } [ 4 0 / ] unit-test",
"task_id": 4,
"message": "Division by zero\nx 4"
"message": "concept-partial-fail/concept-partial-fail-tests.factor:19\nDivision by zero\nx 4"
},
{
"name": "Test 7",
"status": "error",
"test_code": "{ 0 } [ 10 { 1 2 3 } nth ] unit-test",
"task_id": 5,
"message": "Sequence index out of bounds\nindex 10\nseq { 1 2 3 }"
"message": "concept-partial-fail/concept-partial-fail-tests.factor:22\nSequence index out of bounds\nindex 10\nseq { 1 2 3 }"
},
{
"name": "Test 8",
"status": "error",
"test_code": "{ \"text\" } [ 5 label ] unit-test",
"task_id": 6,
"message": "Generic word label does not define a method for the fixnum class.\nDispatching on object: 5"
"message": "concept-partial-fail/concept-partial-fail-tests.factor:25\nGeneric word label does not define a method for the fixnum class.\nDispatching on object: 5"
},
{
"name": "Test 9",
"status": "error",
"test_code": "{ 0 } [ 5 [ drop ] call( x -- x ) ] unit-test",
"task_id": 7,
"message": "Quotation's stack effect does not match call site\nquot [ drop ]\ncall-site ( x -- x )"
"message": "concept-partial-fail/concept-partial-fail-tests.factor:28\nQuotation's stack effect does not match call site\nquot [ drop ]\ncall-site ( x -- x )"
}
]
}
Loading
Loading