{ok} ← {opts←'q'} ##.test script        ⍝ Run test script: no news => good news.

Namespace #.scripts contains character-vector test scripts,  which  exercise the
code in this workspace. In general, test's argument is a vector or matrix of the
names of scripts to be executed. For example:

      test'display'     ⍝ test display function.

      test'hex' 'dec'   ⍝ test functions: hex and dec.

      test ⎕nl 4        ⍝ test all operators.

As a convenience, right argument '' is interpreted as (scripts.⎕nl 2),  a matrix
of all scripts, except for those starting with '_'.   Such names may be used for
auxiliary sub-scripts, which are not intended to be run in isolation.

During test's evaluation of each script, differences between actual and expected
results  are displayed in the session; the shy result is a boolean vector indic-
ating which scripts succeeded without fault.

If  a  difference  is detected, the script name, and expected and actual results
are shown in the form:

    name expected → actual

Blanks  in  expected and actual results are replaced with '·' characters to show
where differences may be attributed to spacing. A common example of this is with
nested arrays, where the display form often contains trailing blanks.

      ⍕⍳1 2             ⍝ nested array.
 1 1  1 2

      ' ·'subs ⍕⍳1 2    ⍝ note trailing blank in output:
·1·1··1·2·

In  this case, expected trailing blanks must be present in the script, otherwise
differences will be reported.

Optional left argument [opts] determines:

    'q' quietly execute each script (default),
    '⎕' show timestamp and script name.
    'v' verbose: display the script as it is executed.
    'e' edit script(s).
    'l' list script names, for example: cols'l'test''.
    's' stop on difference and display the actual result in an edit window.
    '∘' break execution if difference detected.

Option 's' is useful while developing scripts.  When a difference  is  detected,
execution is paused and the actual result presented in an edit  window.  If this
new value is acceptable,  copy it to the clipboard;  close the window  and  then
paste it into the script edit-window at the appropriate place.  On  closing  the
script-window, execution will proceed until the  end  of  the  script or another
difference is detected.

Option '∘' causes [test] to suspend when there's a difference. In the suspension
local variables "exp" and "act" contain nested character vectors of expected and
actual results, respectively.

Left argument [opts] is set in variable "Alpha", which is  visible  from  within
the script.  This is useful for passing options to a recursive call on test. For
example, a script may contain the line:

    Alpha test '_sub'       ⍝ pass (eg) verbose option to auxiliary script

Note that the items of left argument vector are unrestricted and so may  contain
values that are meaningful to specific scripts.  An  example  is the test script
for  [turtle],  where  an  upper-case 'V' indicates the test is to be run inter-
actively.

More than one option may be given:

    'eq' test 'gcd'         ⍝ edit then (quietly) test "gcd"
                              ¯          ¯
Shared sub-scripts
------------------
It is convenient for related functions, such as  [alget] and [alset], to share a
common test script in which the tests for the functions may interact.   However,
when  testing  multiple scripts, such common scripts would  be executed multiple
times.

    ┌─────┐
    │alget├─→──┐                ⍝ test script "alget"
    └─────┘    │    ┌──────┐
               ├─→──┤alists│    ⍝ common sub-script "alists"
    ┌─────┐    │    └──────┘
    │alset├─→──┘                ⍝ test script "alset"
    └─────┘

This repetition would increase the elapsed time of the testing for  no  benefit.
To avoid this, [test] maintains a been-here-before list in variable ⍙⍙⍙, located
locally in test's initial stack frame.  Each application of [test] checks to see
whether its script has already been executed and, if so, refrains from repeating
the test.

    0=⎕NC'⍙⍙⍙':⍺{⍙⍙⍙←⍬ ⋄ ⍎'⍺ test ⍵'}⍵  ⍝ initialised been-here-before list: ⍙⍙⍙
    ,..
    (⊂⍵)∊⍙⍙⍙:ok←1 ⋄ ⍙⍙⍙,←⊂⍵             ⍝ been-here-before: skip else continue

Note, however, that some shared sub-scripts are _designed_ to be repeated.  Test
scripts for the Binary Search Tree  functions:  [avl],  [redblack],  [sbst]  and
[splay] each define their own differing versions of  auxiliary  functions [put],
[rem] and [chk] before calling common test _BST, to test them.   In this case we
want:

    test'avl' 'redblack'

to call _BST for both tests.  Fortunately, there is a way to code this  require-
ment. Test-scripts run in a "sandbox" copy of the host namespace, created by the
[test] function:

    tmp←⎕NS ⎕NL 3 4 9                       ⍝ tmp space for evaluation.

so  that  names  created by one test do not interfere with those that follow. In
particular, any script containing the line:

    Alpha test '_sub'               ⍝ apply [test] in tmp space
                                                      ¯¯¯
will call the copy of test running in sandbox namespace tmp.  Local variable ⍙⍙⍙
will not then be in scope and so the sub-script will be executed.  On the  other
hand, script line:

    Alpha ##.test '_sub'            ⍝ apply [test] in calling space
          ¯¯                                          ¯¯¯¯¯¯¯
will call [test] in its _originating_ namespace and so ⍙⍙⍙ will be in scope.

(muse: this distinction is related to that between lexical and dynamic scoping.)

See ##.scripts.avl and ##.scripts.alset for examples.

Control Structures
------------------
Scripts may contain :If, :Return and :ReturnIf control structures for condition-
al inclusion of test lines:

    :If 80=⎕dr' '
        ... unicode-specific tests
    :Else
        ... classic-specific tests
    :EndIf

    :If ⎕FR=1287            ⍝ decimal floating point:
        :Return             ⍝   skip rest of script
    :EndIf

    and its shorter equivalent, which skips the rest of the script if true:

    :ReturnIf ⎕FR=1287      ⍝ skip rest of script if decimal floating point

Lines starting with ']' are passed on to the user-command processor.

    ]boxing on              ⍝ test user-command
Was OFF

Note  that  variables  and (even multi-line) functions defined in the script are
local  to  that  script.  An example of a script might be the following newline-
delimited character vector:

          display scripts.sample
┌→────────────────────────────────────────┐
│      ⍝ Sample Script                    │
│                                         │
│      cvec ← 1↓'                         │
│mult-line                                │
│character                                │
│vector'                 ⍝ Temp variable  │
│                                         │
│      dup←{             ⍝ Temp function  │
│          ⍵ ⍵                            │
│      }                                  │
│                                         │
│      dup cvec          ⍝ ]boxing is on  │
│┌→────────┬─────────┐                    │
││mult-line│mult-line│                    │
││character│character│                    │
││vector   │vector   │                    │
│└────────→┴────────→┘                    │
│                                         │
│:If 'v'∊Alpha           ⍝ verbose mode:  │
│        'Sqrt ¯1:',¯1*÷2                 │
│    Sqrt ¯1: 0J1                         │
│:Else                                    │
│        ¯1*÷2                            │
│    0J1                                  │
│:EndIf                                   │
└─────────────────────────────────────────┘

To include additional tests in a script just execute the new test case in the
session and,  if the result is correct, copy and paste the input line and its
result into the script. Note however, the following restrictions:

Bugs:

[1] Some of the scripts will report differences unless [AutoFormat functions] is
    switched _off_, using the Session menu:

    ┌──···Options···───────────────
    │ ┌──···Configure···─────────────
    │ │ ┌──···Trace/Edit···────────────
    │ │ │   ···
    │ │ │    [ ] AutoFormat functions
    │ │ │     ↑
    │ │ │     └── Uncheck box.

    Or set environment variable AUTOFORMAT=0 in the calling line (or shell).

[2] Test is currently confused by script lines that contain:

    Embedded assignments:   ·   ·   ·   ·   ·   2+a←3
    Diamonds:   ·   ·   ·   ·   ·   ·   ·   ·   2+3 ⋄ 4+5
    System commands ·   ·   ·   ·   ·   ·   ·   )erase a
    Quad output ·   ·   ·   ·   ·   ·   ·   ·   ⎕←...
    Assigned result of raw dfn application: ·   a←{...}0
    Assigned functions derived from dfns:   ·   f←{...}¨

    (
        The  latter  two  problems occur because the lines are misinterpreted as
        simple dfn definitions. The test function could be more sophisticated in
        distinguishing  such  cases, but it is easy to avoid the error by coding
        it as: "a← {...}0" or "f← {...}¨" (with a blank after the ←), which con-
        founds the part of the code that is looking for dfns to fix.
    )

[3] It would be nice to have :ElseIf, :Select/:Case[List] keywords.

Technical notes:

[test] creates a temporary namespace in which to execute scripts, and into which
all  root space functions and operators (#.⎕nl 3 4) are copied. Within the temp-
orary space, the script is processed as follows:

    Removing comments and ignoring blank lines:

    If the next line contains an unclosed quoted vector
        form a multi-line character vector with the following line.

    Otherwise, if the next line starts with a ':'
        process the :If/:Else/:End[If] or :Return[If] control structure.

    Otherwise, if the next line contains a dfn definition:
        identify and ⎕fx the single- or multiple-line dfn.

    Otherwise, if the next line is an assign ('←' at outer level):
        execute the assignment in the tmp space.

    Otherwise, if the next line starts with a ']'
        process it as a user command.

    Otherwise:
        execute the expression and compare the result with following lines from
        the script.

Script dependencies
-------------------
Each script contains a special ⍝∇ comment line, which identifies external depen-
dencies.  This is so that it and its dependent names may be copied into a clear
WS to be tested in isolation.

    ⍝∇ name ...

Examples:

      test'hex'                 ⍝ test hex function (no news is good news).

      test'hex' 'dec'           ⍝ test two scripts.

      test scripts.⎕nl 2        ⍝ test all scripts.

      (∧/test'')/'ok'           ⍝ test all scripts.
ok
      test time''               ⍝ time the test.
19.30

    'l' test 'a' ⎕nl 4          ⍝ scripts for a-operators.
┌───┬───┬─────┬──────┬──┬───┐
│acc│and│ascan│ascana│at│avl│
└───┴───┴─────┴──────┴──┴───┘

    'q' test'gcd' 'osc'         ⍝ quiet: report only differences.

    'e' test'gcd' 'osc'         ⍝ edit script(s).

    'v' test'gcd' 'osc'         ⍝ verbose: show test running.
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ scripts.gcd
⍝ Greatest Common Divisor:

      105 gcd 330
15

      factors (3×5×7) gcd 5×7×11
5 7

      lcm←{⍺×⍵÷⍺ gcd ⍵}                     ⍝ lowest common multiple

      factors (3×5×7) lcm 5×7×11
3 5 7 11
⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝ scripts.osc
⍝ Oscillate - probably returns 1:

      osc¨1 to 40
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

      {2|⍵:1+3×⍵ ⋄ ⍵÷2}traj 27
27 82 41 124 62 31 94 47 142 71 214 107 322 161 484 242 121 364 182 91 274 137 4

      12 206 103 310 155 466 233 700 350 175 526 263 790 395 1186 593 1780 890 4

      45 1336 668 334 167 502 251 754 377 1132 566 283 850 425 1276 638 319 958

      479 1438 719 2158 1079 3238 1619 4858 2429 7288 3644 1822 911 2734 1367 41

      02 2051 6154 3077 9232 4616 2308 1154 577 1732 866 433 1300 650 325 976 48

      8 244 122 61 184 92 46 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1

See also: time

Back to: contents

Back to: Workspaces