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