]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: mark code that doesn't need testing.
[ocean] / csrc / oceani-tests.mdc
1 # Ocean Interpreter test code
2
3 Regular testing is, of course, important for developing any software.
4 The Ocean interpreted is no exception.  This document allows easy
5 testing by providing:
6
7 - a collection of test program
8 - the expected output of these programs when run with various arguments
9 - some "Makefile" code to tie it all together.
10
11 Three different sorts of tests are run.  As soon as any fail, the whole
12 test stops.
13
14 1/ Each program is run and the output is compared against the expected
15     output
16 2/ Each program is then run under valgrind, and an error is reported
17     if valgrind detects an error, or if it reports and lost or unfreed
18     memory.
19 3/ Each program is parsed and printed, then the result is parsed and printed.
20     The two results must match.
21 4/ Each program is run with a version of `oceani` with test-coverage
22     recording enabled.  Once all programs have successfully run and
23     all the coverage data is available, we check that all lines have
24     been tested at least once. A few exceptions are allowed such as
25     lines that call `abort()`.  If any non-exceptional lines have not
26     been run, this final test fails.
27     Until the tests suite is (more) completed, we only throw and error
28     if fewer than 75% of the lines have been tested.
29
30 Each test has a name, which used to identify the section in this file, and optionally some
31 arguments separated from the name by commas.  For each test, there is a section named
32 "output:" followed by the name-and-arguments.
33
34 ###### File: oceani-tests.mk
35
36         oceani_tests :=
37         ## test list
38
39         tests:: oceani_test_suite
40         oceani_test_suite: oceani coverage_oceani
41                 @./parsergen --report --LR1 --tag Parser oceani.mdc | grep " - no conflicts" > /dev/null || \
42                     { echo "Grammar container conflicts, please review" ; exit 1; }
43                 @rm -rf coverage; mkdir -p coverage
44                 @cp *.gcno coverage
45                 @for T in $(oceani_tests); do \
46                     echo -n "Test $$T ... "; \
47                     i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
48                     ./md2c oceani-tests.mdc "output: $$T" | grep -v '^#' > .tmp.want; \
49                     ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} > .tmp.have; \
50                     if ! cmp -s .tmp.want .tmp.have; then \
51                        echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
52                     echo -n "passed ... "; \
53                     if ! valgrind --error-exitcode=1 --log-file=.tmp.valg ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} \
54                          > /dev/null 2>&1 ; then \
55                        echo "valgrind FAILED"; cat .tmp.valg; exit 1; fi ; \
56                     if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
57                        echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
58                     if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
59                        echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
60                     echo -n "valgrind passed ... "; \
61                     echo '``````' > .tmp.code1; echo '``````' > .tmp.code2 ;\
62                     ./oceani --noexec --print --section "test: $$t" oceani-tests.mdc >> .tmp.code1; \
63                     ./oceani --noexec --print .tmp.code1 >> .tmp.code2 ;\
64                     if ! cmp -s .tmp.code1 .tmp.code2; then \
65                        echo "Printing Failed"; diff -u .tmp.code1 .tmp.code2; exit1 ; fi ; \
66                     echo "Printing passed"; \
67                     ./coverage_oceani --print --section "test: $$t" oceani-tests.mdc $${1+"$$@"} > /dev/null ; \
68                     ./coverage_oceani -tpbn --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1; \
69                 done
70
71                 ## test code
72
73                 @gcov -o coverage oceani.mdc > /dev/null 2> /dev/null
74                 @mv *.gcov coverage ; [ -f .gcov ] && mv .gcov coverage
75                 @ awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
76                     END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
77                          if (ran < (ran + skip) *0.93) exit(1) }' \
78                         coverage/oceani.mdc.gcov
79                 @rm -f .tmp*
80
81         coverage_oceani: oceani.c
82                 $(CC) $(CFLAGS) --coverage -fprofile-dir=coverage -o coverage_oceani oceani.c $(LDLIBS)
83
84 ## Values and variables
85
86 The first test stores values in variables and performs various
87 calculations on them.
88
89 ###### test list
90        oceani_tests += "valvar"
91
92
93 ###### test: valvar
94
95         program:
96                 a := 23; b:=12 ; b1 := -b
97                 print a, b, a+b, a-b, a*b, a/b, a%b
98                 print a<b, a<=b, a>b, a>=b, a<a, a==b, a==a
99                 print +a, +b, +b1, -a, -b, -b1
100                 x := True; y := False
101                 print x and y, x or y, x and x, y or y, x and not x, x < y
102
103                 c ::= "This is a string"
104                 d ::= " field theory"
105                 print c, d, c++d
106
107                 aconst :: string = "unchanging"
108
109 ###### output: valvar
110
111         23 12 35 11 276 1.91667 11
112         False False True True False False True
113         23 12 12 -23 -12 12
114         False True True False False False
115         This is a string  field theory This is a string field theory
116
117 Next we change the value of variables
118
119 ###### test list
120        oceani_tests += "setvar"
121
122 ###### test: setvar
123
124         program:
125                 a := 4
126                 a = a * a
127                 a = (a + a) * (a + a)
128                 a = a * a * a
129                 print a, a/a
130
131 ###### output: setvar
132         1.07374e+09 1
133
134 ## Conditions and Loops
135
136 Now we need to test if/else and some different loops
137
138 ###### test list
139        oceani_tests += cond_loop
140
141 ###### test: cond_loop
142
143         program:
144                 a := 4
145                 if a < 5:
146                         print "Success"
147                 else:
148                         print "Failure"
149                 for b:=1; then b=b+b; while b < 100:
150                         print '', b,
151                 print
152                 // Newtons method for square root of 2
153                 target ::= 2
154                 guess := target
155                 for:
156                         count: number = 0
157                 while:
158                         current := guess * guess
159                         use +(current - target) > 0.000000001
160                 do:
161                         guess = (guess + (target / guess) ) / 2
162                         print count, guess
163                         count = count + 1
164                 print "error is ", target - guess * guess
165
166                 for j:=0; then j = j+3 ; while j < 10:
167                         if j != 0 and then 20 / j > 3:
168                                 print "20 /", j," =", 20 / j
169                         else:
170                                 print "I won't calculate 20 /", j
171                 pi ::= 3.1415926535897
172                 if 355/113 == pi or else +(pi - 355/113) < 0.001:
173                         print "Close enough"
174                 print "lower" if 355/113 < pi else "higher"
175
176 ###### output: cond_loop
177         Success
178          1 2 4 8 16 32 64
179         0 1.5
180         1 1.41667
181         2 1.41422
182         3 1.41421
183         error is  -4.51095e-12
184         I won't calculate 20 / 0
185         20 / 3  = 6.66667
186         20 / 6  = 3.33333
187         I won't calculate 20 / 9
188         Close enough
189         higher
190
191 ## Say Hello
192
193 The demonstration code presented in the interpreted is suitable for the test suite.
194 Here I break it into two parts, keeping the array code separate.
195
196 ###### test list
197         oceani_tests += "sayhello,55,33"
198         oceani_tests += "sayhello,12,60"
199
200 ###### test: sayhello
201
202         program A B:
203                 print "Hello World, what lovely oceans you have!"
204                 /* When a variable is defined in both branches of an 'if',
205                  * and used afterwards, the variables are merged.
206                  */
207                 if A > B:
208                         bigger := "yes"
209                 else:
210                         bigger := "no"
211                 print "Is", A, "bigger than", B,"? ", bigger
212                 /* If a variable is not used after the 'if', no
213                  * merge happens, so types can be different
214                  */
215                 if A > B * 2:
216                         double:string = "yes"
217                         print A, "is more than twice", B, "?", double
218                 else:
219                         double := B*2
220                         print "double", B, "is", double
221
222                 a : number
223                 a = A;
224                 b:number = B
225                 if a > 0 and b > 0:
226                         while a != b:
227                                 if a < b:
228                                         b = b - a
229                                 else:
230                                         a = a - b
231                         print "GCD of", A, "and", B,"is", a
232                 else if a <= 0:
233                         print a, "is not positive, cannot calculate GCD"
234                 else:
235                         print b, "is not positive, cannot calculate GCD"
236
237                 for:
238                         togo := 10
239                         f1 := 1; f2 := 1
240                         print "Fibonacci:", f1,f2,
241                 then togo = togo - 1
242                 while togo > 0:
243                         f3 := f1 + f2
244                         print "", f3,
245                         f1 = f2
246                         f2 = f3
247                 print ""
248
249                 /* Binary search... */
250                 for:
251                         lo:= 0; hi := 100
252                         target := 77
253                 while:
254                         mid := (lo + hi) / 2
255                         if mid == target:
256                                 use Found
257                         if mid < target:
258                                 lo = mid
259                         else:
260                                 hi = mid
261                         if hi - lo < 1:
262                                 use GiveUp
263                         use True
264                 do: pass
265                 case Found:
266                         print "Yay, I found", target
267                 case GiveUp:
268                         print "Closest I found was", mid
269
270 ###### output: sayhello,55,33
271         Hello World, what lovely oceans you have!
272         Is 55 bigger than 33 ?  yes
273         double 33 is 66
274         GCD of 55 and 33 is 11
275         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
276         Closest I found was 77.3438
277
278 ###### output: sayhello,12,60
279         Hello World, what lovely oceans you have!
280         Is 12 bigger than 60 ?  no
281         double 60 is 120
282         GCD of 12 and 60 is 12
283         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
284         Closest I found was 77.3438
285
286 ###### test list
287         oceani_tests += "insert_sort"
288 ###### test: insert_sort
289         program:
290                 size::=55
291                 list:[size]number
292                 list[0] = 1234
293                 for i:=1; then i = i + 1; while i < size:
294                         n := list[i-1] * list[i-1]
295                         list[i] = (n / 100) % 10000
296
297                 print "Before sort:"
298                 for i:=0; then i = i + 1; while i < size:
299                         print "list[",i,"]=",list[i]
300
301                 for i := 1; then i=i+1; while i < size:
302                         for j:=i-1; then j=j-1; while j >= 0:
303                                 if list[j] > list[j+1]:
304                                         t:= list[j]
305                                         list[j] = list[j+1]
306                                         list[j+1] = t
307                 print "After sort:"
308                 for i:=0; then i = i + 1; while i < size:
309                         print "list[",i,"]=",list[i]
310
311 ###### output: insert_sort
312         Before sort:
313         list[ 0 ]= 1234
314         list[ 1 ]= 5227
315         list[ 2 ]= 3215
316         list[ 3 ]= 3362
317         list[ 4 ]= 3030
318         list[ 5 ]= 1809
319         list[ 6 ]= 2724
320         list[ 7 ]= 4201
321         list[ 8 ]= 6484
322         list[ 9 ]= 422
323         list[ 10 ]= 1780
324         list[ 11 ]= 1684
325         list[ 12 ]= 8358
326         list[ 13 ]= 8561
327         list[ 14 ]= 2907
328         list[ 15 ]= 4506
329         list[ 16 ]= 3040
330         list[ 17 ]= 2416
331         list[ 18 ]= 8370
332         list[ 19 ]= 569
333         list[ 20 ]= 3237
334         list[ 21 ]= 4781
335         list[ 22 ]= 8579
336         list[ 23 ]= 5992
337         list[ 24 ]= 9040
338         list[ 25 ]= 7216
339         list[ 26 ]= 706
340         list[ 27 ]= 4984
341         list[ 28 ]= 8402
342         list[ 29 ]= 5936
343         list[ 30 ]= 2360
344         list[ 31 ]= 5696
345         list[ 32 ]= 4444
346         list[ 33 ]= 7491
347         list[ 34 ]= 1150
348         list[ 35 ]= 3225
349         list[ 36 ]= 4006
350         list[ 37 ]= 480
351         list[ 38 ]= 2304
352         list[ 39 ]= 3084
353         list[ 40 ]= 5110
354         list[ 41 ]= 1121
355         list[ 42 ]= 2566
356         list[ 43 ]= 5843
357         list[ 44 ]= 1406
358         list[ 45 ]= 9768
359         list[ 46 ]= 4138
360         list[ 47 ]= 1230
361         list[ 48 ]= 5129
362         list[ 49 ]= 3066
363         list[ 50 ]= 4003
364         list[ 51 ]= 240
365         list[ 52 ]= 576
366         list[ 53 ]= 3317
367         list[ 54 ]= 24
368         After sort:
369         list[ 0 ]= 24
370         list[ 1 ]= 240
371         list[ 2 ]= 422
372         list[ 3 ]= 480
373         list[ 4 ]= 569
374         list[ 5 ]= 576
375         list[ 6 ]= 706
376         list[ 7 ]= 1121
377         list[ 8 ]= 1150
378         list[ 9 ]= 1230
379         list[ 10 ]= 1234
380         list[ 11 ]= 1406
381         list[ 12 ]= 1684
382         list[ 13 ]= 1780
383         list[ 14 ]= 1809
384         list[ 15 ]= 2304
385         list[ 16 ]= 2360
386         list[ 17 ]= 2416
387         list[ 18 ]= 2566
388         list[ 19 ]= 2724
389         list[ 20 ]= 2907
390         list[ 21 ]= 3030
391         list[ 22 ]= 3040
392         list[ 23 ]= 3066
393         list[ 24 ]= 3084
394         list[ 25 ]= 3215
395         list[ 26 ]= 3225
396         list[ 27 ]= 3237
397         list[ 28 ]= 3317
398         list[ 29 ]= 3362
399         list[ 30 ]= 4003
400         list[ 31 ]= 4006
401         list[ 32 ]= 4138
402         list[ 33 ]= 4201
403         list[ 34 ]= 4444
404         list[ 35 ]= 4506
405         list[ 36 ]= 4781
406         list[ 37 ]= 4984
407         list[ 38 ]= 5110
408         list[ 39 ]= 5129
409         list[ 40 ]= 5227
410         list[ 41 ]= 5696
411         list[ 42 ]= 5843
412         list[ 43 ]= 5936
413         list[ 44 ]= 5992
414         list[ 45 ]= 6484
415         list[ 46 ]= 7216
416         list[ 47 ]= 7491
417         list[ 48 ]= 8358
418         list[ 49 ]= 8370
419         list[ 50 ]= 8402
420         list[ 51 ]= 8561
421         list[ 52 ]= 8579
422         list[ 53 ]= 9040
423         list[ 54 ]= 9768
424
425 ## Arrays
426
427 We already have some array tests, but this is where we put other
428 ad-hoc things array related.
429
430 ###### test list
431         oceani_tests += arrays
432
433 ###### test: arrays
434
435         program:
436                 bools:[5]Boolean
437                 strings:[4]string
438
439                 bools[3] = strings[1] == "Hello"
440                 bools[1] = strings[2] <= "there"
441
442                 for i:=0; then i=i+1; while i<5:
443                         print '', bools[i],
444                 print
445
446 ###### output: arrays
447          False True False False False
448
449 ## Test code with syntax errors
450
451 Syntax errors aren't handled well yet - the result is almost always a
452 single message about the first error.  So this section will be fairly
453 thin until we add proper parsing recovery in the face of common errors.
454
455 A special case of syntax errors is token errors, when a token is only
456 accepted because the parser doesn't know quite enough to reject it.
457 There are handled better as they are quite local, so a single test
458 program can trigger most of the possible errors.
459
460 To handle erronous code, we need a different set of tests, as we need to
461 capture `stderr`. The same test code will be used for type errors too.
462 As error messages contain the line number, and we don't want changes to
463 this file to change the reported numbers, we copy the code into a
464 separate file first, then run from there.
465
466 ###### test code
467         @for t in $(oceani_failing_tests); do \
468             echo -n "Test $$t ... "; \
469             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
470             echo '``````' > .tmp.code; \
471             ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
472             ./oceani .tmp.code > .tmp.have 2>&1; \
473             if ! cmp -s .tmp.want .tmp.have; then \
474                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
475             echo  "passed"; \
476             ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
477         done || true
478
479 ###### test list
480         oceani_failing_tests := syn1
481         oceani_failing_tests += tokerr
482
483 ###### test: syn1
484
485         program:
486                 if then else while do
487
488 ###### output: syn1
489         .tmp.code:3:11: error: unhandled parse error: then
490
491 ###### test: tokerr
492         program:
493                 a := 1i  // imaginary numbers aren't understood
494                 b:[2i]number // array sizes are handled separately
495                 c:[3.14159]Boolean // array sizes must be integers
496                 d:[1_000_000_000_000]number // they mustn't be huge
497                 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
498
499                 multi := """
500                 This is a multiline string
501                 With an unsupportable suffix
502                 """Aa
503
504                 xx:unknown = 24
505                 yy:[unknowable]number
506                 zzsize := 4
507                 zz:[zzsize]string // size must be constant, use ::=
508
509                 // These numbers should be bad in all contexts: FIXME
510                 aa:[00123]number
511
512 ###### output: tokerr
513         .tmp.code:3:13: error: unsupported number suffix: 1i
514         .tmp.code:4:11: error: unsupported number suffix: 2i
515         .tmp.code:5:11: error: array size must be an integer: 3.14159
516         .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
517         .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
518         .tmp.code:9:17: error: unsupported string suffix: """
519                 This is a multiline string
520                 With an unsupportable suffix
521                 """Aa
522         .tmp.code:14:11: error: undefined type: unknown
523         .tmp.code:15:12: error: name undeclared: unknowable
524         .tmp.code:17:12: error: array size must be a constant: zzsize
525         .tmp.code:20:12: error: unrecognised number: 00123
526
527 ## Tests for type errors
528
529 Type error don't cause parsing to abort, so we can fit many in the
530 one test program.  Some type errors are found during the parse, others
531 during type analysis which doesn't run if parsing failed.  So we cannot
532 fit everything in one.
533
534 These programs were generated by looking for the
535 various places that `type_err()` are called.
536
537 ###### test list
538         oceani_failing_tests += type_err1 type_err2 type_err3 type_err4
539
540 ###### test: type_err1
541
542         program:
543                 print "hello" ++ 5, 5 ++ "hello"
544
545                 b ::= 3
546                 b = b + 1
547
548                 if 3 * 4 and not True: print "Weird"
549
550 ###### output: type_err1
551         .tmp.code:3:25: error: expected string found number
552         .tmp.code:3:28: error: expected string found number
553         .tmp.code:6:8: error: Cannot assign to a constant: b
554         .tmp.code:5:8: info: name was defined as a constant here
555         .tmp.code:6:8: error: Cannot assign to a constant: b
556         .tmp.code:5:8: info: name was defined as a constant here
557         .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
558         oceani: type error in program - not running.
559
560 ###### test: type_err2
561
562         program:
563                 a := 1
564                 a := 2
565                 a ::= 3
566                 a:number = 4
567                 a ::number = 5
568                 c:
569
570 ###### output: type_err2
571         .tmp.code:4:8: error: variable 'a' redeclared
572         .tmp.code:3:8: info: this is where 'a' was first declared
573         .tmp.code:5:8: error: variable 'a' redeclared
574         .tmp.code:3:8: info: this is where 'a' was first declared
575         .tmp.code:6:8: error: variable 'a' redeclared
576         .tmp.code:3:8: info: this is where 'a' was first declared
577         .tmp.code:7:8: error: variable 'a' redeclared
578         .tmp.code:3:8: info: this is where 'a' was first declared
579         .tmp.code:8:8: Variable declared with no type or value: c
580
581 ###### test: type_err3
582
583         program:
584                 c := "hello"
585                 c = c + 1
586                 c = "hello" ++ (True and False)
587                 c = 4 < 5
588                 print 45 + ( "Hello" ++ "there")
589                 c[5] = 1
590
591                 while:
592                         use 1
593                         use True
594                         use "Hello"
595                 do:
596                         print
597                 case 1: print "one"
598                 case "Hello": print "Hello"
599
600                 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
601                 a1 = a2
602                 a1 = a3
603                 a1 = a4
604                 a1[2] = "hello"
605                 a4[1] = True
606                 c = a2[3]
607
608 ###### output: type_err3
609         .tmp.code:4:12: error: expected number but variable 'c' is string
610         .tmp.code:3:8: info: this is where 'c' was set to string
611         .tmp.code:4:12: error: Arithmetic returns number but string expected
612         .tmp.code:3:8: info: variable 'c' was set as string here.
613         .tmp.code:5:24: error: Boolean operation found where string expected
614         .tmp.code:6:12: error: Comparison returns Boolean but string expected
615         .tmp.code:3:8: info: variable 'c' was set as string here.
616         .tmp.code:7:21: error: Concat returns string but number expected
617         .tmp.code:8:8: error: string cannot be indexed
618         .tmp.code:8:8: error: string cannot be indexed
619         .tmp.code:17:13: error: expected number found string
620         .tmp.code:13:16: error: expected number, found string
621         .tmp.code:20:8: error: cannot assign value of type [5]number
622         .tmp.code:21:13: error: expected [5]number but variable 'a3' is [10]number
623         .tmp.code:19:36: info: this is where 'a3' was set to [10]number
624         .tmp.code:21:8: error: cannot assign value of type [5]number
625         .tmp.code:22:13: error: expected [5]number but variable 'a4' is [5]string
626         .tmp.code:19:51: info: this is where 'a4' was set to [5]string
627         .tmp.code:22:8: error: cannot assign value of type [5]number
628         .tmp.code:23:16: error: expected number found string
629         .tmp.code:24:16: error: expected string found Boolean
630         .tmp.code:25:12: error: have number but need string
631         .tmp.code:3:8: info: variable 'c' was set as string here.
632         oceani: type error in program - not running.
633
634 ###### test: type_err4
635         program:
636                 a:=1; b=2; c::=3
637                 print a, b, c
638
639 ###### output: type_err4
640         .tmp.code:3:14: error: expected *unknown*type* (labels not permitted) but variable 'b' is label
641         .tmp.code:3:14: info: this is where 'b' was set to label
642         .tmp.code:3:16: error: expected label found number
643         .tmp.code:3:14: info: variable 'b' was set as label here.
644         .tmp.code:4:17: error: expected *unknown*type* (labels not permitted) but variable 'b' is label
645         .tmp.code:3:14: info: this is where 'b' was set to label
646         oceani: type error in program - not running.
647
648 ## Test erroneous command line args
649
650 To improve coverage, we want to test correct handling of strange command
651 line arguments.  These tests won't use code, so the exiting test types
652 won't work.  So we need to be able to explicitly give the command line,
653 and the expected output, and have that tested and the coverage assessed.
654 Rather than having to spell out the whole command name, just give "cmd",
655 and discard that.  Requiring but discarding the command make an empty
656 command list possible.
657
658 ###### test code
659         @for t in $(oceani_special_tests); do \
660             echo -n "Test $$t ... ";\
661             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
662             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
663             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
664             if ! cmp -s .tmp.want .tmp.have; then \
665                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
666             echo  "passed"; \
667             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
668         done || true
669
670
671 ###### test list
672         oceani_special_tests += "cmd"
673         oceani_special_tests += "cmd,-zyx"
674         oceani_special_tests += "cmd,nofile"
675         oceani_special_tests += "cmd,/dev/null"
676         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
677
678 ###### output: cmd
679         oceani: no input file given
680
681 ###### output: cmd,-zyx
682         ./oceani: invalid option -- 'z'
683         Usage: oceani --trace --print --noexec --brackets--section=SectionName prog.ocn
684
685 ###### output: cmd,nofile
686         oceani: cannot open nofile
687
688 ###### output: cmd,/dev/null
689         oceani: could not find any code in /dev/null
690
691 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
692         oceani: cannot find section toast:nothing
693