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