Skip to content

Commit 9a70573

Browse files
improve test code extraction
1 parent ca7aae1 commit 9a70573

6 files changed

Lines changed: 269 additions & 17 deletions

File tree

bin/run.sh

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,57 @@ 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+
# A test runs from its first code line through the line that ends in a
79+
# test word (e.g. `unit-test`); accumulate the whole span so `test_code`
80+
# holds the full multi-line source, not just the closing `] unit-test`.
81+
# `line_no` stays the closing line, matching what Factor reports for
82+
# failures. Blank, comment, and TASK lines reset the buffer so they are
83+
# never folded into the next test.
7884
src_tests=$(awk "${awk_json}"'
79-
BEGIN { task = "null" }
85+
function reset() {
86+
n = 0
87+
delete buf
88+
}
89+
function flush( i, code) {
90+
code = ""
91+
for (i = 1; i <= n; i++) code = code (i > 1 ? "\n" : "") buf[i]
92+
printf "{\"line_no\":%d,\"task_id\":%s,\"test_code\":%s}\n", NR, task, json_str(code)
93+
reset()
94+
}
95+
BEGIN {
96+
task = "null"
97+
n = 0
98+
}
8099
/^[[:space:]]*TASK:[[:space:]]+[0-9]+/ {
81100
match($0, /TASK:[[:space:]]+[0-9]+/)
82101
s = substr($0, RSTART, RLENGTH)
83102
sub(/^TASK:[[:space:]]+/, "", s)
84103
task = s
104+
reset()
85105
next
86106
}
87-
/(unit-test|unit-test~|unit-test-v~|long-unit-test|must-fail-with|must-fail|must-not-fail|must-infer|must-infer-as)[[:space:]]*$/ {
107+
/^[[:space:]]*$/ {
108+
reset()
109+
next
110+
}
111+
/^[[:space:]]*!/ {
112+
reset()
113+
next
114+
}
115+
# A `"label" description` line names the test that follows; it is its own
116+
# statement, not part of the test form, so treat it as a boundary.
117+
/^[[:space:]]*"[^"]*"[[:space:]]+description[[:space:]]*$/ {
118+
reset()
119+
next
120+
}
121+
{
88122
line = $0
89-
sub(/^[[:space:]]+/, "", line)
90123
sub(/[[:space:]]+$/, "", line)
91-
printf "{\"line_no\":%d,\"task_id\":%s,\"test_code\":%s}\n", NR, task, json_str(line)
124+
n++
125+
buf[n] = line
126+
}
127+
/(unit-test|unit-test~|unit-test-v~|long-unit-test|must-fail-with|must-fail|must-not-fail|must-infer|must-infer-as)[[:space:]]*$/ {
128+
flush()
92129
}
93130
' "${tmp_dir}/${slug}/${slug}-tests.factor")
94131

@@ -139,6 +176,17 @@ parsed=$(printf '%s\n' "${raw_output}" | awk "${awk_json}"'
139176
delete seg
140177
cur_name = pending_name
141178
pending_name = ""
179+
# Factor pretty-prints a long test form across lines: the header
180+
# line is just "Unit Test: {" and the form body follows, indented,
181+
# up to a closing "}" at column 0. Those continuation lines are an
182+
# echo of the test, not program output, so skip them.
183+
rest = $0
184+
sub(header_re, "", rest)
185+
if (rest == "{") state = "header_cont"
186+
next
187+
}
188+
state == "header_cont" {
189+
if ($0 ~ /^}[[:space:]]*$/) state = "inline"
142190
next
143191
}
144192
# A generated "###DESC### <description>" line labels the test that

tests/concept-concurrency/expected_results.json

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@
4747
{
4848
"name": "Test 8",
4949
"status": "pass",
50-
"test_code": "] unit-test",
51-
"task_id": 3,
52-
"output": " { 5 0 }\n [\n <crane> dup 5 swap hoist-crate\n tonnage>> <crane> tonnage>>\n ]\n}"
50+
"test_code": "{ 5 0 } [\n <crane> dup 5 swap hoist-crate tonnage>>\n <crane> tonnage>>\n] unit-test",
51+
"task_id": 3
5352
},
5453
{
5554
"name": "Test 9",
@@ -60,9 +59,8 @@
6059
{
6160
"name": "Test 10",
6261
"status": "pass",
63-
"test_code": "] unit-test",
64-
"task_id": 4,
65-
"output": " { 52 }\n [\n <crane> dup 35 swap hoist-crate dup 17 swap hoist-crate\n tonnage>>\n ]\n}"
62+
"test_code": "{ 52 }\n[\n <crane>\n dup 35 swap hoist-crate\n dup 17 swap hoist-crate\n tonnage>>\n] unit-test",
63+
"task_id": 4
6664
},
6765
{
6866
"name": "Test 11",
@@ -79,22 +77,20 @@
7977
{
8078
"name": "Test 13",
8179
"status": "pass",
82-
"test_code": "] unit-test",
83-
"task_id": 6,
84-
"output": " { 32 }\n [\n <crane> { { 5 5 } { 10 } { 3 4 5 } } over load-cargo\n crane-tonnage\n ]\n}"
80+
"test_code": "{ 32 } [\n <crane>\n { { 5 5 } { 10 } { 3 4 5 } } over load-cargo\n crane-tonnage\n] unit-test",
81+
"task_id": 6
8582
},
8683
{
8784
"name": "Test 14",
8885
"status": "pass",
89-
"test_code": "] unit-test",
86+
"test_code": "{ 0 } [\n <crane>\n { } over load-cargo\n crane-tonnage\n] unit-test",
9087
"task_id": 6
9188
},
9289
{
9390
"name": "Test 15",
9491
"status": "pass",
95-
"test_code": "] unit-test",
96-
"task_id": 6,
97-
"output": " { 5050 }\n [\n <crane> 100 <iota> [ 1 + 1array ] map over load-cargo\n crane-tonnage\n ]\n}"
92+
"test_code": "{ 5050 } [\n <crane>\n 100 <iota> [ 1 + 1array ] map\n over load-cargo\n crane-tonnage\n] unit-test",
93+
"task_id": 6
9894
}
9995
]
10096
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
USING: concept-quotations exercism-tools kernel math math.order
2+
random random.mersenne-twister sequences sets sorting tools.test ;
3+
IN: concept-quotations.tests
4+
5+
TASK: 1 roll-die
6+
{ 6 } [ 4 <mersenne-twister> [ 6 roll-die ] with-random ] unit-test
7+
8+
STOP-HERE
9+
10+
{ 2 } [ 5 <mersenne-twister> [ 6 roll-die ] with-random ] unit-test
11+
12+
! Every roll of a six-sided die lands in 1..6.
13+
{ t } [
14+
1 <mersenne-twister> [ 200 [ 6 roll-die ] replicate ] with-random
15+
[ 1 6 between? ] all?
16+
] unit-test
17+
18+
TASK: 2 pick-prize
19+
{ "e" } [
20+
4 <mersenne-twister>
21+
[ { "a" "b" "c" "d" "e" } pick-prize ] with-random
22+
] unit-test
23+
24+
{ "b" } [
25+
5 <mersenne-twister>
26+
[ { "a" "b" "c" "d" "e" } pick-prize ] with-random
27+
] unit-test
28+
29+
TASK: 3 shuffle-deck
30+
{ { 5 1 3 4 2 } } [
31+
42 <mersenne-twister> [ { 1 2 3 4 5 } shuffle-deck ] with-random
32+
] unit-test
33+
34+
{ { 2 4 3 5 1 } } [
35+
7 <mersenne-twister> [ { 1 2 3 4 5 } shuffle-deck ] with-random
36+
] unit-test
37+
38+
! Shuffling keeps exactly the same cards, only reordered.
39+
{ t } [
40+
13 <mersenne-twister> [ { 3 1 4 1 5 9 } shuffle-deck ] with-random
41+
natural-sort { 1 1 3 4 5 9 } =
42+
] unit-test
43+
44+
TASK: 4 deal-hand
45+
{ { 30 40 20 } } [
46+
42 <mersenne-twister> [ { 10 20 30 40 50 } 3 deal-hand ] with-random
47+
] unit-test
48+
49+
{ { 30 50 10 } } [
50+
7 <mersenne-twister> [ { 10 20 30 40 50 } 3 deal-hand ] with-random
51+
] unit-test
52+
53+
! A dealt hand is distinct cards drawn from the deck.
54+
{ t } [
55+
7 <mersenne-twister> [ { 10 20 30 40 50 } 3 deal-hand ] with-random
56+
[ [ { 10 20 30 40 50 } member? ] all? ]
57+
[ [ length ] [ members length ] bi = ] bi and
58+
] unit-test
59+
60+
TASK: 5 play-seeded
61+
! play-seeded installs the seeded generator and returns the result.
62+
{ 6 } [ 4 [ 6 roll-die ] play-seeded ] unit-test
63+
64+
! The same seed reproduces the same game.
65+
{ t } [
66+
99 [ { 10 20 30 40 50 } 3 deal-hand ] play-seeded
67+
99 [ { 10 20 30 40 50 } 3 deal-hand ] play-seeded
68+
=
69+
] unit-test
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
USING: kernel locals math random random.mersenne-twister ;
2+
IN: concept-quotations
3+
4+
:: roll-die ( sides -- n )
5+
sides random 1 + ;
6+
7+
:: pick-prize ( prizes -- prize )
8+
prizes random ;
9+
10+
:: shuffle-deck ( deck -- deck' )
11+
deck randomize ;
12+
13+
:: deal-hand ( deck n -- hand )
14+
deck n sample ;
15+
16+
:: play-seeded ( seed quot -- )
17+
seed 1 + <mersenne-twister> quot with-random ; inline
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
USING: accessors command-line continuations debugger io kernel
2+
lexer namespaces sequences source-files.errors.debugger
3+
system tools.test vocabs vocabs.loader ;
4+
IN: exercism-tools
5+
6+
SYNTAX: STOP-HERE
7+
lexer get [ text>> length ] keep line<< ;
8+
9+
SYNTAX: TASK:
10+
lexer get next-line ;
11+
12+
! Label the test that follows with its description. The marker lets the
13+
! wrapper strip this line from captured output and attach it to the next
14+
! test as a name, rather than leaving it in the previous test's output.
15+
: description ( str -- )
16+
"###DESC### " write print ;
17+
18+
! Print one failure block in a stable, parser-friendly form. Bracketed by
19+
! markers so a wrapper can split the stream reliably and avoid Factor's
20+
! noisy callstack output (which is interleaved with subsequent failures).
21+
:: print-failure ( failure -- )
22+
"###FAIL_BEGIN###" print
23+
failure error-location print
24+
failure error>> [ error. ] [ 2drop ] recover
25+
"###FAIL_END###" print
26+
flush ;
27+
28+
: print-failures ( -- )
29+
test-failures get [ print-failure ] each ;
30+
31+
: run-exercism-tests ( -- )
32+
command-line get first
33+
[ require ] [ test ] bi
34+
test-failures get empty?
35+
[ 0 exit ] [ print-failures 1 exit ] if ;
36+
37+
MAIN: run-exercism-tests
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"version": 3,
3+
"status": "fail",
4+
"tests": [
5+
{
6+
"name": "Test 1",
7+
"status": "pass",
8+
"test_code": "{ 6 } [ 4 <mersenne-twister> [ 6 roll-die ] with-random ] unit-test",
9+
"task_id": 1
10+
},
11+
{
12+
"name": "Test 2",
13+
"status": "pass",
14+
"test_code": "{ 2 } [ 5 <mersenne-twister> [ 6 roll-die ] with-random ] unit-test",
15+
"task_id": 1
16+
},
17+
{
18+
"name": "Test 3",
19+
"status": "pass",
20+
"test_code": "{ t } [\n 1 <mersenne-twister> [ 200 [ 6 roll-die ] replicate ] with-random\n [ 1 6 between? ] all?\n] unit-test",
21+
"task_id": 1
22+
},
23+
{
24+
"name": "Test 4",
25+
"status": "pass",
26+
"test_code": "{ \"e\" } [\n 4 <mersenne-twister>\n [ { \"a\" \"b\" \"c\" \"d\" \"e\" } pick-prize ] with-random\n] unit-test",
27+
"task_id": 2
28+
},
29+
{
30+
"name": "Test 5",
31+
"status": "pass",
32+
"test_code": "{ \"b\" } [\n 5 <mersenne-twister>\n [ { \"a\" \"b\" \"c\" \"d\" \"e\" } pick-prize ] with-random\n] unit-test",
33+
"task_id": 2
34+
},
35+
{
36+
"name": "Test 6",
37+
"status": "pass",
38+
"test_code": "{ { 5 1 3 4 2 } } [\n 42 <mersenne-twister> [ { 1 2 3 4 5 } shuffle-deck ] with-random\n] unit-test",
39+
"task_id": 3
40+
},
41+
{
42+
"name": "Test 7",
43+
"status": "pass",
44+
"test_code": "{ { 2 4 3 5 1 } } [\n 7 <mersenne-twister> [ { 1 2 3 4 5 } shuffle-deck ] with-random\n] unit-test",
45+
"task_id": 3
46+
},
47+
{
48+
"name": "Test 8",
49+
"status": "pass",
50+
"test_code": "{ t } [\n 13 <mersenne-twister> [ { 3 1 4 1 5 9 } shuffle-deck ] with-random\n natural-sort { 1 1 3 4 5 9 } =\n] unit-test",
51+
"task_id": 3
52+
},
53+
{
54+
"name": "Test 9",
55+
"status": "pass",
56+
"test_code": "{ { 30 40 20 } } [\n 42 <mersenne-twister> [ { 10 20 30 40 50 } 3 deal-hand ] with-random\n] unit-test",
57+
"task_id": 4
58+
},
59+
{
60+
"name": "Test 10",
61+
"status": "pass",
62+
"test_code": "{ { 30 50 10 } } [\n 7 <mersenne-twister> [ { 10 20 30 40 50 } 3 deal-hand ] with-random\n] unit-test",
63+
"task_id": 4
64+
},
65+
{
66+
"name": "Test 11",
67+
"status": "pass",
68+
"test_code": "{ t } [\n 7 <mersenne-twister> [ { 10 20 30 40 50 } 3 deal-hand ] with-random\n [ [ { 10 20 30 40 50 } member? ] all? ]\n [ [ length ] [ members length ] bi = ] bi and\n] unit-test",
69+
"task_id": 4
70+
},
71+
{
72+
"name": "Test 12",
73+
"status": "fail",
74+
"test_code": "{ 6 } [ 4 [ 6 roll-die ] play-seeded ] unit-test",
75+
"task_id": 5,
76+
"message": "=== Expected:\n6\n=== Got:\n2"
77+
},
78+
{
79+
"name": "Test 13",
80+
"status": "pass",
81+
"test_code": "{ t } [\n 99 [ { 10 20 30 40 50 } 3 deal-hand ] play-seeded\n 99 [ { 10 20 30 40 50 } 3 deal-hand ] play-seeded\n =\n] unit-test",
82+
"task_id": 5
83+
}
84+
]
85+
}

0 commit comments

Comments
 (0)