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