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