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