Skip to content

Commit 1a629f1

Browse files
rikedypIsaacG
andauthored
Set up test runner (#7)
* use Dyalog docker image * set up runner #3 * actually run a test * update dyalogscript file permissions * modify docker setup * write results to /tmp directory * reset docker run scripts to match production * cleanup * remove redundant WORKDIR command * set WORKDIR in Dockerfile * updated to use Link, which works read-only even if SALT init fails * No need for CDShy * Assert injected by test framework * ensure testns expunged for debugging * CD from Dyalog to solution dir to run tests * Using link, no longer need 2⎕FIX * get test code from source * fix expected results for success test * loop over tests * exit 0 is default * add tests * fix empty-file test directory for docker * use UNIX LF line endings for test files * Update bin/run-tests.sh Co-authored-by: Isaac Good <[email protected]> * Update bin/run-tests.sh Co-authored-by: Isaac Good <[email protected]> * Update bin/run.sh Co-authored-by: Isaac Good <[email protected]> * don't capture output, remove optional comments * swap error one ←→ error single * overall status error if all tests error * test run slug is test_dir_name --------- Co-authored-by: Isaac Good <[email protected]>
1 parent 2869986 commit 1a629f1

37 files changed

+274
-68
lines changed

APLSource/Assert.aplf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Assert←{ 0∊⍵: ⎕SIGNAL ⊂('EN' 8)('EM' 'ASSERTION ERROR')('Message' ⍺) ⋄ shy←0}

APLSource/CD.aplf

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
old←force CD new;OS;is64;t;get;lib;chdir;⎕ML ⍝ Current/Change Directory
2+
⍝ empty "new": only report; non-empty "new": change dir
3+
⍝ warns about tied files with relative tie names if force=0, skips check if force=1
4+
⎕ML←1
5+
OS←3↑⊃# ⎕WG'APLVersion'
6+
:If 'Win'≡OS ⍝ Under Windows we do the association here because we need
7+
t←'A*'[1+80=⎕DR''] ⍝ the programs to GET the current directory
8+
'get'⎕NA'U Kernel32|GetCurrentDirectory',t,' U >0T' ⍝ Associate Get function
9+
old←2⊃get 260 260 ⍝ in all cases we return the current dir
10+
'chdir'⎕NA'U Kernel32|SetCurrentDirectory',t,' <0T' ⍝ Associate Set function
11+
:Else
12+
old←⊃⎕SH'pwd'
13+
:EndIf
14+
15+
:If ~0∊⍴new ⍝ change directory
16+
17+
:If 'Win'≡OS
18+
⍝ chdir returns a single int: 1=ok
19+
('Unable to change directory: ',new,': Invalid directory')⎕SIGNAL(chdir⊂new)↓22
20+
:Else
21+
:Select OS
22+
:Case 'AIX'
23+
is64←32={z←⍵ ⋄ ⎕SIZE'z'}⍬
24+
lib←'I libc.a(shr',(is64/'_64'),'.o)|chdir <0T1[]'
25+
:Case 'Lin'
26+
⍝ Extract real name of libc that we are actually using
27+
t←⊃⎕SH'echo $PPID'
28+
t←⎕SH'ldd /proc/',t,'/exe'
29+
lib←⊃('^\s*libc\.so\b.*=>\s*(\S*)'⎕S'\1')t
30+
lib←'I ',lib,'|chdir <0T1[]'
31+
:Case 'Mac'
32+
lib←'I /usr/lib/libc.dylib|chdir <0T1[]'
33+
⍝ chdir returns a single integer: 0=ok
34+
:EndSelect
35+
⎕NA lib
36+
('Unable to change directory: ',new,': Invalid directory')⎕SIGNAL(0≠chdir⊂new)/22
37+
:EndIf
38+
:EndIf

APLSource/DoTest.aplf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
DoTest←{
2+
⍝ Execute test function ⍵
3+
⍝ In namespace ⍺
4+
⍺←⎕THIS
5+
_←(⍕⍺)⎕NS'Assert' ⍝ Inject Assert function into test namespace
6+
nl←⎕UCS 13
7+
Format←{1↓∊nl∘,¨⊆⍵}
8+
0::'error'(Format ⎕DMX.DM)
9+
8::'fail'(Format ⎕DMX.Message)
10+
1=⍺⍎⍵:'pass' ''
11+
'fail' 'Test failed.'
12+
}

APLSource/Run.aplf

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Run←{
2+
⍝ ⍵: Directory of test suite to run
3+
⍝ ←: Namespace of test results (can be output with (1⎕JSON ns)⎕NPUT filename )
4+
5+
r←⍎'result'⎕NS''⊣⎕EX'result'
6+
r.version←2
7+
0::r⊣r.status←'error'⊣r.message←'Could not load test suite ',⍵
8+
settings.overwrite←1⊣settings←⎕NS''
9+
link←settings ⎕SE.Link.Import'testns'⍵⊣⎕EX'testns'
10+
tests←'Test_'testns.⎕NL ¯3
11+
0∊⍴tests:r⊣r.status←'error'⊣r.message←'No test files found in directory ',⍵
12+
r.tests←⎕NS¨''⍨¨tests
13+
r.tests.name←tests
14+
r.tests.test_code←testns.⎕VR¨tests
15+
r.tests.(status message)←testns∘DoTest¨tests
16+
status←'fail' 'error' 'pass'
17+
r.status←status⊃⍨1+0 1 2+.×∧/status∘.≡r.tests.status
18+
19+
r
20+
}

APLSource/Version.aplf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
v←Version
2+
v←'0.1.0'

Dockerfile

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
FROM alpine:3.18
2-
3-
# install packages required to run the tests
4-
RUN apk add --no-cache jq coreutils
1+
FROM dyalog/dyalog:19.0
52

3+
COPY . /opt/test-runner
64
WORKDIR /opt/test-runner
7-
COPY . .
85
ENTRYPOINT ["/opt/test-runner/bin/run.sh"]

README.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,21 @@
22

33
The Docker image to automatically run tests on Dyalog APL solutions submitted to [Exercism].
44

5-
## Getting started
5+
## Developing the Runner
6+
The runner consists of functions in the **APLSource** directory. It is developed using Link.
67

7-
Build the test runner, conforming to the [Test Runner interface specification](https://github.com/exercism/docs/blob/main/building/tooling/test-runners/interface.md).
8-
Update the files to match your track's needs. At the very least, you'll need to update `bin/run.sh`, `Dockerfile` and the test solutions in the `tests` directory
8+
In Dyalog:
99

10-
- Tip: look for `TODO:` comments to point you towards code that need updating
11-
- Tip: look for `OPTIONAL:` comments to point you towards code that _could_ be useful
12-
- Tip: if it proves impossible for the Docker image to work on a read-only filesystem, remove the `--read-only` flag from the `bin/run-in-docker.sh` and `bin/run-tests-in-docker.sh` files.
13-
We don't yet enforce a read-only file system in production, but we might in the future!
10+
```apl
11+
]LINK.Create # APLSource
12+
```
13+
14+
The `Run` function is the interface that simply returns JSON text of the results object. The script **bin/dylaog-apl-runner.apls** gets parameters from the command line and outputs the result of the `Run` function to the file specified.
15+
16+
## Design
17+
This implementation of the version 2 test runner interface allows multiple tests: one APL function per test. This is one **.aplf** file per function, which allows natural development and debugging using [Link](https://dyalog.github.io/link).
18+
19+
The tests make use of an `Assert` function which takes a left argument that is the message to be displayed to the user in case the test case fails. In this way, a single test function can test multiple aspects of an exercise and usefully report failures back to the user.
1420

1521
## Run the test runner
1622

bin/dyalog-apl-runner.apls

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/dyalogscript DYALOG_INITSESSION=1
2+
⍝ Outputs JSON text to file describing the results of the test run
3+
4+
⍝ Import test runner
5+
⎕SE.Link.Import 'Runner' 'APLSource'
6+
7+
⍝ Get from envrionment rather than arguments so that we do not have to check count or order of parameters
8+
(slug soln outdir outfile)←{2⎕NQ#'GetEnvironment' ⍵}¨'SLUG' 'SOLN' 'OUTDIR' 'OUTFILE'
9+
10+
home ← 0 Runner.CD soln
11+
12+
results←Runner.Run soln ⍝ Why slug needed if solution expected to be in solution folder?
13+
output←1(⎕JSON⎕OPT'Compact'0)results ⍝ Readable (non-compact) JSON result
14+
output (⎕NPUT⎕OPT'NEOL'2) outfile 1 ⍝ Output using OS line endings (NEOL 2)
15+
16+
0 Runner.CD home
17+
18+
⎕←'Test results:'
19+
⎕←results.status

bin/run-tests.sh

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,22 @@
1212
# ./bin/run-tests.sh
1313

1414
exit_code=0
15+
echo "current directory ${PWD}"
1516

1617
# Iterate over all test directories
1718
for test_dir in tests/*; do
1819
test_dir_name=$(basename "${test_dir}")
1920
test_dir_path=$(realpath "${test_dir}")
20-
21-
bin/run.sh "${test_dir_name}" "${test_dir_path}" "${test_dir_path}"
22-
23-
# OPTIONAL: Normalize the results file
24-
# If the results.json file contains information that changes between
25-
# different test runs (e.g. timing information or paths), you should normalize
26-
# the results file to allow the diff comparison below to work as expected
21+
test_out_path="/tmp/${test_dir_name}"
22+
# Let Dyalog create the files to avoid its stdout (⎕←) length limit
23+
bin/run.sh "$test_dir_name" "${test_dir_path}" "${test_out_path}"
2724

2825
file="results.json"
2926
expected_file="expected_${file}"
27+
3028
echo "${test_dir_name}: comparing ${file} to ${expected_file}"
3129

32-
if ! diff "${test_dir_path}/${file}" "${test_dir_path}/${expected_file}"; then
30+
if ! diff "${test_out_path}/${file}" "${test_dir_path}/${expected_file}"; then
3331
exit_code=1
3432
fi
3533
done

bin/run.sh

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,6 @@ results_file="${output_dir}/results.json"
2929
# Create the output directory if it doesn't exist
3030
mkdir -p "${output_dir}"
3131

32-
echo "${slug}: testing..."
33-
34-
# Run the tests for the provided implementation file and redirect stdout and
35-
# stderr to capture it
36-
test_output=$(false)
37-
# TODO: substitute "false" with the actual command to run the test:
38-
# test_output=$(command_to_run_tests 2>&1)
39-
40-
# Write the results.json file based on the exit code of the command that was
41-
# just executed that tested the implementation file
42-
if [ $? -eq 0 ]; then
43-
jq -n '{version: 1, status: "pass"}' > ${results_file}
44-
else
45-
# OPTIONAL: Sanitize the output
46-
# In some cases, the test output might be overly verbose, in which case stripping
47-
# the unneeded information can be very helpful to the student
48-
# sanitized_test_output=$(printf "${test_output}" | sed -n '/Test results:/,$p')
49-
50-
# OPTIONAL: Manually add colors to the output to help scanning the output for errors
51-
# If the test output does not contain colors to help identify failing (or passing)
52-
# tests, it can be helpful to manually add colors to the output
53-
# colorized_test_output=$(echo "${test_output}" \
54-
# | GREP_COLOR='01;31' grep --color=always -E -e '^(ERROR:.*|.*failed)$|$' \
55-
# | GREP_COLOR='01;32' grep --color=always -E -e '^.*passed$|$')
56-
57-
jq -n --arg output "${test_output}" '{version: 1, status: "fail", message: $output}' > ${results_file}
58-
fi
32+
echo "Testing ${slug}..."
5933

60-
echo "${slug}: done"
34+
SLUG="$slug" SOLN="$solution_dir" OUTDIR="$output_dir" OUTFILE="$results_file" ./bin/dyalog-apl-runner.apls

0 commit comments

Comments
 (0)