]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: allow types to be used before declaration
[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         oceani_valg_tests := $(oceani_tests)
39         ## combine test lists
40
41         tests:: oceani_test_suite
42         oceani_test_suite: oceani coverage_oceani
43                 @echo -n Checking grammar ...
44                 @./parsergen --report --LALR --tag Parser oceani.mdc | grep " - no conflicts" > /dev/null || \
45                     { echo "Grammar contains conflicts, please review" ; exit 1; }
46                 @echo ok
47                 @rm -rf coverage; mkdir -p coverage
48                 @cp *.gcno coverage
49                 @for T in $(oceani_tests); do \
50                     echo -n "Test $$T.. "; \
51                     i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
52                     ./md2c oceani-tests.mdc "output: $$T" | grep -v '^#' > .tmp.want; \
53                     ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} > .tmp.have; \
54                     if ! cmp -s .tmp.want .tmp.have; then \
55                        echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
56                     echo -n "printing.. "; \
57                     echo '``````' > .tmp.code1; echo '``````' > .tmp.code2 ;\
58                     ./oceani --noexec --print --section "test: $$t" oceani-tests.mdc >> .tmp.code1; \
59                     ./oceani --noexec --print .tmp.code1 >> .tmp.code2 || exit 1;\
60                     if ! cmp -s .tmp.code1 .tmp.code2; then \
61                        echo " Failed"; diff -u .tmp.code1 .tmp.code2; exit 1 ; fi ; \
62                     echo -n "extra-newlines.. "; \
63                     sed -e 'i\
64                     ' .tmp.code1 > .tmp.code1a; \
65                     echo '``````' > .tmp.code2a ;\
66                     ./oceani --noexec --print .tmp.code1a >> .tmp.code2a || exit 1;\
67                     if ! cmp -s .tmp.code1 .tmp.code2a; then \
68                        echo " Failed"; diff -u .tmp.code1 .tmp.code2a; exit 1; fi ; \
69                     echo -n "exec-after-print.. "; \
70                     ./oceani .tmp.code1 $${1+"$$@"} > .tmp.have ; \
71                     if ! cmp -s .tmp.want .tmp.have; then \
72                        echo " FAILED"; diff -u .tmp.want .tmp.have; exit 1;fi; \
73                     echo " all passed"; \
74                     ./coverage_oceani --print --section "test: $$t" oceani-tests.mdc $${1+"$$@"} > /dev/null ; \
75                     ./coverage_oceani -tpbn --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1; \
76                 done
77
78                 ## test code
79
80                 @for i in coverage/#*.gcda; do mv $$i coverage/$${i##*#}; done
81                 @gcov -o coverage oceani.mdc > /dev/null 2> /dev/null
82                 @mv *.gcov coverage ; [ -f .gcov ] && mv .gcov coverage || true
83                 @rm -f .tmp*
84
85                 @[ -n "$$SKIP_VALGRIND" ] || for T in $(oceani_valg_tests); do \
86                     echo -n "Valgrind $$T.. "; \
87                     i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
88                     if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} \
89                          > /dev/null 2>&1 ; [ $$? -eq 42 ] ; then \
90                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
91                     if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
92                        echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
93                     if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
94                        echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
95                     echo " passed"; \
96                 done
97                 ## valgrind test code
98                 @[ -n "$$SKIP_COVERAGE_CHECK" ] || awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
99                     END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
100                          if (ran < (ran + skip) *0.972) exit(1) }' \
101                         coverage/oceani.mdc.gcov
102
103         coverage_oceani: oceani.c
104                 $(CC) $(CFLAGS) --coverage -fprofile-dir=coverage -o coverage_oceani oceani.c $(LDLIBS)
105
106 ## Values and variables
107
108 The first test stores values in variables and performs various
109 calculations on them.
110
111 ###### test list
112        oceani_tests += "valvar"
113
114 ###### test: valvar
115
116         func main(argv:[argc::]string)
117                 a := 23; b:=12 ; b1 := -b
118                 print a, b, a+b, a-b, a*b, a/b, a%b
119                 print a<b, a<=b, a>b, a>=b, a<a, a==b, a==a
120                 print +a, +b, +b1, -a, -b, -b1
121                 x := True; y := False
122                 print x and y, x or y, x and x, y or y, x and not x, x < y
123
124                 c ::= "This is a string"
125                 d ::= " field theory"
126                 print c, d, c++d
127
128                 aconst :: string = "unchanging"
129
130                 // Check wrapping
131                 print
132                   a + b
133                   + (a*2)
134                   + b1
135                   + b
136
137 ###### output: valvar
138
139         23 12 35 11 276 1.916666667 11
140         False False True True False False True
141         23 12 12 -23 -12 12
142         False True True False False False
143         This is a string  field theory This is a string field theory
144         81
145
146 Next we change the value of variables
147
148 ###### test list
149        oceani_tests += "setvar"
150
151 ###### test: setvar
152
153         func main()
154                 a := 4
155                 a = a * a
156                 a = (a + a) * (a + a)
157                 a = a * a * a
158                 print a, a/a
159
160 ###### output: setvar
161         1073741824 1
162
163 Now some contants
164
165 ###### test list
166         oceani_tests += "consts"
167
168 ###### test: consts
169         const
170                 tau ::= pi * 2
171                 pi ::= 3.141 592 653
172                 four ::= 2 + 2 ; five ::= 10/2
173         const pie ::= "I like Pie";
174                 cake ::= "The cake is"
175                   ++ " a lie"
176
177         func main()
178                 print "Hello World, what lovely oceans you have!"
179                 print "are there", five, "?"
180                 print pi, pie, "but", cake, "Tau is", tau
181
182 ###### output: consts
183         Hello World, what lovely oceans you have!
184         are there 5 ?
185         3.141592653 I like Pie but The cake is a lie Tau is 6.283185306
186
187 Test merging of variables from multiple cases
188
189 ###### test list
190         oceani_tests += varmerge
191
192 ###### test: varmerge
193
194         func main()
195                 for i:=0; then i=i+1; while i < 5:
196                         switch i:
197                                 case 0: num:="zero"
198                                 case 1: scratch:=42; num:="one"
199                                 case 2: num:="two"
200                                 case 3: num:="three"
201                                 else    num:="many"
202                         print num,", ",
203                 print
204
205                 for i:=0; then i=i+1; while i < 5:
206                         switch i:
207                                 case 0: num:="zero"
208                                 case 1: num:="one"
209                                 case 2: num:="two"
210                                 case 3: num:="three"
211                                 else    num:="many"
212                         // re-declare a CondScope variable
213                         num := i*i
214                         print num,", ",
215                 print
216
217 ###### output: varmerge
218         zero , one , two , three , many , 
219         0 , 1 , 4 , 9 , 16 , 
220
221 ## Conditions and Loops
222
223 Now we need to test if/else and some different loops
224
225 ###### test list
226        oceani_tests += cond_loop
227
228 ###### test: cond_loop
229
230         func main()
231                 a := 4
232                 if a < 5:
233                         print "Success"
234                 else
235                         print "Failure"
236                 for b:=1; then b=b+b; while b < 100:
237                         print '', b,
238                 print
239                 // Newtons method for square root of 2
240                 target ::= 2
241                 guess := target
242                 for
243                         count: number = 0
244                 while
245                         current := guess * guess
246                         use +(current - target) > 0.000000001
247                 do
248                         guess = (guess + (target / guess) ) / 2
249                         print count, guess
250                         count = count + 1
251                 print "error is ", target - guess * guess
252
253                 for j:=0; then j = j+3 ; while j < 10:
254                         if j != 0 and then 20 / j > 3:
255                                 print "20 /", j," =", 20 / j
256                         else
257                                 print "I won't calculate 20 /", j
258                 pi ::= 3.1415926535897
259                 if 355/113 == pi or else +(pi - 355/113) < 0.001:
260                         print "Close enough"
261                 print "lower" if 355/113 < pi else "higher"
262
263                 if pi > 3 then print "pi exceeds three"; else print "need more pie"
264                 if (pi < 3) { print "not enough pi" } else { print "pi sufficient" }
265                 for { i := 0; sum := 0 }
266                 then { i = i+1 }
267                 while i <= 10:
268                         sum = sum + i
269                 else
270                         pass
271                         print "sum 1..10 is", sum
272                 if
273                         PI1 := 22/7
274                         use PI1 < pi
275                 then
276                         print "Smaller"
277                 else
278                         print 'larger'
279
280 ###### output: cond_loop
281         Success
282          1 2 4 8 16 32 64
283         0 1.5
284         1 1.416666667
285         2 1.414215686
286         3 1.414213562
287         error is  -4.510950445e-12
288         I won't calculate 20 / 0
289         20 / 3  = 6.666666667
290         20 / 6  = 3.333333333
291         I won't calculate 20 / 9
292         Close enough
293         higher
294         pi exceeds three
295         pi sufficient
296         sum 1..10 is 55
297         larger
298
299 ## Say Hello
300
301 The demonstration code presented in the interpreted is suitable for the test suite.
302 Here I break it into two parts, keeping the array code separate.
303
304 ###### test list
305         oceani_tests += "sayhello,55,33,hello,True"
306         oceani_tests += "sayhello,12,60,there,False"
307
308 ###### test: sayhello
309
310         func main(av:[ac::number]string)
311                 A := $av[1]; B := $av[2]
312                 astr := av[3]
313                 bbool := av[ac-1] == "True"
314                 print "Hello World, what lovely oceans you have!"
315                 /* When a variable is defined in both branches of an 'if',
316                  * and used afterwards, the variables are merged.
317                  */
318                 if A > B:
319                         bigger := "yes"
320                 else
321                         bigger := "no"
322                 print "Is", A, "bigger than", B,"? ", bigger
323                 /* If a variable is not used after the 'if', no
324                  * merge happens, so types can be different
325                  */
326                 if A > B * 2:
327                         double:string = "yes"
328                         print A, "is more than twice", B, "?", double
329                 else
330                         double := B*2
331                         print "double", B, "is", double
332
333                 a : number
334                 a = A;
335                 b:number = B
336                 print "still", bigger // check for regression in scoping
337                 if a > 0 and b > 0:
338                         while a != b:
339                                 if a < b:
340                                         b = b - a
341                                 else
342                                         a = a - b
343                         print "GCD of", A, "and", B,"is", a
344                 else if a <= 0:
345                         print a, "is not positive, cannot calculate GCD"
346                 else
347                         print b, "is not positive, cannot calculate GCD"
348
349                 for
350                         togo := 10
351                         f1 := 1; f2 := 1
352                         print "Fibonacci:", f1,f2,
353                 then togo = togo - 1
354                 while togo > 0:
355                         f3 := f1 + f2
356                         print "", f3,
357                         f1 = f2
358                         f2 = f3
359                 print ""
360
361                 if bbool:
362                         print astr ++ " was the str"
363                 else
364                         print "I found the str over " ++ astr
365
366                 /* Binary search... */
367                 for
368                         lo:= 0; hi := 100
369                         target := 77
370                 while
371                         mid := (lo + hi) / 2
372                         if mid == target:
373                                 use Found
374                         if mid < target:
375                                 lo = mid
376                         else
377                                 hi = mid
378                         if hi - lo < 1:
379                                 lo = mid
380                                 use GiveUp
381                         use True
382                 do pass
383                 case Found:
384                         print "Yay, I found", target
385                 case GiveUp:
386                         print "Closest I found was", lo
387
388 ###### output: sayhello,55,33,hello,True
389         Hello World, what lovely oceans you have!
390         Is 55 bigger than 33 ?  yes
391         double 33 is 66
392         still yes
393         GCD of 55 and 33 is 11
394         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
395         hello was the str
396         Closest I found was 77.34375
397
398 ###### output: sayhello,12,60,there,False
399         Hello World, what lovely oceans you have!
400         Is 12 bigger than 60 ?  no
401         double 60 is 120
402         still no
403         GCD of 12 and 60 is 12
404         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
405         I found the str over there
406         Closest I found was 77.34375
407
408 ###### test list
409         oceani_tests += "insert_sort"
410 ###### test: insert_sort
411         func main()
412                 size::=55
413                 list:[size]number
414                 list[0] = 1_234
415                 for i:=1; then i = i + 1; while i < size:
416                         n := list[i-1] * list[i-1]
417                         list[i] = (n / 100) % 10000
418
419                 print "Before sort:"
420                 for i:=0; then i = i + 1; while i < size:
421                         print "list[",i,"]=",list[i]
422
423                 for i := 1; then i=i+1; while i < size:
424                         for j:=i-1; then j=j-1; while j >= 0:
425                                 if list[j] > list[j+1]:
426                                         t:= list[j]
427                                         list[j] = list[j+1]
428                                         list[j+1] = t
429                 print "After sort:"
430                 for i:=0; then i = i + 1; while i < size:
431                         print "list[",i,"]=",list[i]
432
433 ###### output: insert_sort
434         Before sort:
435         list[ 0 ]= 1234
436         list[ 1 ]= 5227
437         list[ 2 ]= 3215
438         list[ 3 ]= 3362
439         list[ 4 ]= 3030
440         list[ 5 ]= 1809
441         list[ 6 ]= 2724
442         list[ 7 ]= 4201
443         list[ 8 ]= 6484
444         list[ 9 ]= 422
445         list[ 10 ]= 1780
446         list[ 11 ]= 1684
447         list[ 12 ]= 8358
448         list[ 13 ]= 8561
449         list[ 14 ]= 2907
450         list[ 15 ]= 4506
451         list[ 16 ]= 3040
452         list[ 17 ]= 2416
453         list[ 18 ]= 8370
454         list[ 19 ]= 569
455         list[ 20 ]= 3237
456         list[ 21 ]= 4781
457         list[ 22 ]= 8579
458         list[ 23 ]= 5992
459         list[ 24 ]= 9040
460         list[ 25 ]= 7216
461         list[ 26 ]= 706
462         list[ 27 ]= 4984
463         list[ 28 ]= 8402
464         list[ 29 ]= 5936
465         list[ 30 ]= 2360
466         list[ 31 ]= 5696
467         list[ 32 ]= 4444
468         list[ 33 ]= 7491
469         list[ 34 ]= 1150
470         list[ 35 ]= 3225
471         list[ 36 ]= 4006
472         list[ 37 ]= 480
473         list[ 38 ]= 2304
474         list[ 39 ]= 3084
475         list[ 40 ]= 5110
476         list[ 41 ]= 1121
477         list[ 42 ]= 2566
478         list[ 43 ]= 5843
479         list[ 44 ]= 1406
480         list[ 45 ]= 9768
481         list[ 46 ]= 4138
482         list[ 47 ]= 1230
483         list[ 48 ]= 5129
484         list[ 49 ]= 3066
485         list[ 50 ]= 4003
486         list[ 51 ]= 240
487         list[ 52 ]= 576
488         list[ 53 ]= 3317
489         list[ 54 ]= 24
490         After sort:
491         list[ 0 ]= 24
492         list[ 1 ]= 240
493         list[ 2 ]= 422
494         list[ 3 ]= 480
495         list[ 4 ]= 569
496         list[ 5 ]= 576
497         list[ 6 ]= 706
498         list[ 7 ]= 1121
499         list[ 8 ]= 1150
500         list[ 9 ]= 1230
501         list[ 10 ]= 1234
502         list[ 11 ]= 1406
503         list[ 12 ]= 1684
504         list[ 13 ]= 1780
505         list[ 14 ]= 1809
506         list[ 15 ]= 2304
507         list[ 16 ]= 2360
508         list[ 17 ]= 2416
509         list[ 18 ]= 2566
510         list[ 19 ]= 2724
511         list[ 20 ]= 2907
512         list[ 21 ]= 3030
513         list[ 22 ]= 3040
514         list[ 23 ]= 3066
515         list[ 24 ]= 3084
516         list[ 25 ]= 3215
517         list[ 26 ]= 3225
518         list[ 27 ]= 3237
519         list[ 28 ]= 3317
520         list[ 29 ]= 3362
521         list[ 30 ]= 4003
522         list[ 31 ]= 4006
523         list[ 32 ]= 4138
524         list[ 33 ]= 4201
525         list[ 34 ]= 4444
526         list[ 35 ]= 4506
527         list[ 36 ]= 4781
528         list[ 37 ]= 4984
529         list[ 38 ]= 5110
530         list[ 39 ]= 5129
531         list[ 40 ]= 5227
532         list[ 41 ]= 5696
533         list[ 42 ]= 5843
534         list[ 43 ]= 5936
535         list[ 44 ]= 5992
536         list[ 45 ]= 6484
537         list[ 46 ]= 7216
538         list[ 47 ]= 7491
539         list[ 48 ]= 8358
540         list[ 49 ]= 8370
541         list[ 50 ]= 8402
542         list[ 51 ]= 8561
543         list[ 52 ]= 8579
544         list[ 53 ]= 9040
545         list[ 54 ]= 9768
546
547 ## Arrays
548
549 We already have some array tests, but this is where we put other
550 ad-hoc things array related.
551
552 ###### test list
553         oceani_tests += arrays
554
555 ###### test: arrays
556
557         func main()
558                 bools:[5]Boolean
559                 strings:[4]string
560
561                 bools[3] = strings[1] == "Hello"
562                 bools[1] = strings[2] <= "there"
563
564                 for i:=0; then i=i+1; while i<5:
565                         j ::= i
566                         ra:[j]number
567                         ra[i-1] = i*i
568                         ra[6] = 42 // mustn't crash
569                         print '', bools[i], ra[j-1],
570                 print
571
572 ###### output: arrays
573          False 0 True 1 False 4 False 9 False 16
574
575 ## Structures
576
577 Time to test if structure declarations and accesses work correctly.
578
579 ###### test list
580         oceani_tests += structs
581
582 ###### test: structs
583
584         const three ::= 3
585         struct foo
586                 size:[three]number
587                 name:string
588                 thing:baz
589                 active:Boolean = True
590
591         struct baz { a:number; b:Boolean; }
592
593         func main
594         do
595                 info:[4]foo
596
597                 for i:=0; then i=i+1; while i < 4:
598                         switch i
599                         case 2: nm:= "peter"
600                         case 0: nm:= "bob"
601                         case 1: nm:= "jane"
602                         else    nm:= "janine"
603
604                         info[i].name = nm
605                         info[i].size[0] = i*i
606                         if nm != "jane":
607                                 info[i].active = False
608
609                 for i:=0; then i=i+1; while i < 4:
610                         print info[i].name, info[i].active, info[i].size[0]
611                 info[0].thing.b = True
612
613 ###### output: structs
614
615         bob False 0
616         jane True 1
617         peter False 4
618         janine False 9
619
620 ## Functions
621
622 Test functions.  They don't return anything, so we need to get them to print
623
624 ###### test list
625         oceani_tests += functions func_ret_type
626
627 ###### test: functions
628
629         func test1
630                 t: Boolean
631         do
632                 if t:
633                         print "true"
634
635         func noarg
636         do
637                 pass
638
639         func twoarg
640                 a:number
641                 b:string
642         do
643                 while a > 0:
644                         print b
645                         a = a - 1
646
647         func test(n:number; s:string)
648                 if n >= 1:
649                         print n,s,
650                         test(n-1, "."++s)
651                 else
652                         print "done"
653
654         func to_polar
655                 x:number; y:number
656         return
657                 rho:number
658                 theta:number
659         do
660                 rho = x + y
661                 theta = x - y
662
663         func main()
664                 for i:=0; then i = i + 1; while i < 5:
665                         test(i, " ")
666                 angular := to_polar(32, 23)
667                 print angular.rho, angular.theta
668
669 ###### output: functions
670         done
671         1  done
672         2  1 . done
673         3  2 . 1 .. done
674         4  3 . 2 .. 1 ... done
675         55 9
676
677 ###### test: func_ret_type
678
679         func double(n:number):number
680                 use n+n
681
682         func answer
683                 prefix:string
684                 suffix:string
685         return string
686         do
687                 use prefix ++ suffix
688
689         func noarg_returns
690         return Boolean
691         do
692                 use 22/7 == 3.14159
693
694         func main()
695                 for j:=10; then j = j - 3; while j > -5:
696                         print answer("dou","ble"), j, "is", double(j)
697
698 ###### output: func_ret_type
699         double 10 is 20
700         double 7 is 14
701         double 4 is 8
702         double 1 is 2
703         double -2 is -4
704
705 ## Test code with syntax errors
706
707 Syntax errors aren't handled well yet - the result is almost always a
708 single message about the first error.  So this section will be fairly
709 thin until we add proper parsing recovery in the face of common errors.
710
711 A special case of syntax errors is token errors, when a token is only
712 accepted because the parser doesn't know quite enough to reject it.
713 There are handled better as they are quite local, so a single test
714 program can trigger most of the possible errors.
715
716 To handle erronous code, we need a different set of tests, as we need to
717 capture `stderr`. The same test code will be used for type errors too.
718 As error messages contain the line number, and we don't want changes to
719 this file to change the reported numbers, we copy the code into a
720 separate file first, then run from there.
721
722 ###### test code
723         @for t in $(oceani_failing_tests); do \
724             echo -n "Test $$t ... "; \
725             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
726             echo '``````' > .tmp.code; \
727             ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
728             ./oceani .tmp.code > .tmp.have 2>&1; \
729             if ! cmp -s .tmp.want .tmp.have; then \
730                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
731             echo  "passed"; \
732             ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
733         done || true
734
735 ###### combine test lists
736         oceani_valg_tests += $(oceani_failing_tests)
737
738 ###### test list
739         oceani_failing_tests := syn1
740         oceani_failing_tests += tokerr
741
742 ###### test: syn1
743
744         func main()
745                 if then else while do
746
747 ###### output: syn1
748         .tmp.code:3:11: Syntax error in statement: then
749
750 ###### test: tokerr
751         func main()
752                 a := 1i  // imaginary numbers aren't understood
753                 b:[2i]number // array sizes are handled separately
754                 c:[3.14159]Boolean // array sizes must be integers
755                 d:[1_000_000_000_000]number // they mustn't be huge
756                 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
757
758                 multi := """
759                 This is a multiline string
760                 With an unsupportable suffix
761                 """Aa
762
763                 xx:unknown = 24
764                 yy:[unknowable]number
765                 zzsize := 4
766                 zz:[zzsize]string // size must be constant, use ::=
767
768                 // These numbers should be bad in all contexts: FIXME
769                 aa:[00123]number
770
771 ###### output: tokerr
772         .tmp.code:3:13: error: unsupported number suffix: 1i
773         .tmp.code:4:11: error: unsupported number suffix: 2i
774         .tmp.code:5:11: error: array size must be an integer: 3.14159
775         .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
776         .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
777         .tmp.code:9:17: error: unsupported string suffix: """
778                 This is a multiline string
779                 With an unsupportable suffix
780                 """Aa
781         .tmp.code:15:12: error: name undeclared: unknowable
782         .tmp.code:17:12: error: array size must be a constant: zzsize
783         .tmp.code:20:12: error: unrecognised number: 00123
784         .tmp.code:14:11: error: type used but not declared: unknown
785
786 ## Tests for type errors
787
788 Type error don't cause parsing to abort, so we can fit many in the
789 one test program.  Some type errors are found during the parse, others
790 during type analysis which doesn't run if parsing failed.  So we cannot
791 fit everything in one.
792
793 These programs were generated by looking for the
794 various places that `type_err()` are called.
795
796 ###### test list
797         oceani_failing_tests += type_err1 type_err2 type_err3 type_err4 type_err5
798
799 ###### test: type_err1
800
801         func main()
802                 print "hello" ++ 5, 5 ++ "hello"
803
804                 b ::= 3
805                 b = b + 1
806
807                 if 3 * 4 and not True: print "Weird"
808
809 ###### output: type_err1
810         .tmp.code:3:25: error: expected string found number
811         .tmp.code:3:28: error: expected string found number
812         .tmp.code:6:8: error: Cannot assign to a constant: b
813         .tmp.code:5:8: info: name was defined as a constant here
814         .tmp.code:6:8: error: Cannot assign to a constant: b
815         .tmp.code:5:8: info: name was defined as a constant here
816         .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
817         oceani: type error in program - not running.
818
819 ###### test: type_err2
820
821         func main()
822                 a := 1
823                 a := 2
824                 a ::= 3
825                 a:number = 4
826                 a ::number = 5
827                 c:
828
829 ###### output: type_err2
830         .tmp.code:4:8: error: variable 'a' redeclared
831         .tmp.code:3:8: info: this is where 'a' was first declared
832         .tmp.code:5:8: error: variable 'a' redeclared
833         .tmp.code:3:8: info: this is where 'a' was first declared
834         .tmp.code:6:8: error: variable 'a' redeclared
835         .tmp.code:3:8: info: this is where 'a' was first declared
836         .tmp.code:7:8: error: variable 'a' redeclared
837         .tmp.code:3:8: info: this is where 'a' was first declared
838         .tmp.code:8:8: Variable declared with no type or value: c
839
840 ###### test: type_err3
841
842         struct foo
843                 a: number
844                 b:string = "hello"
845
846         func main()
847                 c := "hello"
848                 c = c + 1
849                 c = "hello" ++ (True and False)
850                 c = 4 < 5
851                 print 45 + ( "Hello" ++ "there")
852                 c[5] = 1
853
854                 while
855                         use 1
856                         use True
857                         use "Hello"
858                 do
859                         print
860                 case 1: print "one"
861                 case "Hello": print "Hello"
862
863                 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
864                 a1 = a2
865                 a1 = a3
866                 a1 = a4
867                 a1[2] = "hello"
868                 a4[1] = True
869                 c = a2[3]
870
871                 bar:foo
872                 foo.c = 43
873                 print c.foo
874                 print bar.c
875                 print bar.b + 42
876
877                 // trigger 'labels not permitted' error message
878                 while 1 if True else False:
879                         print
880                 case 2: print "two"
881
882 ###### output: type_err3
883         .tmp.code:8:12: error: expected number but variable 'c' is string
884         .tmp.code:7:8: info: this is where 'c' was set to string
885         .tmp.code:8:12: error: Arithmetic returns number but string expected
886         .tmp.code:7:8: info: variable 'c' was set as string here.
887         .tmp.code:9:24: error: Boolean operation found where string expected
888         .tmp.code:10:12: error: Comparison returns Boolean but string expected
889         .tmp.code:7:8: info: variable 'c' was set as string here.
890         .tmp.code:11:21: error: Concat returns string but number expected
891         .tmp.code:12:8: error: string cannot be indexed
892         .tmp.code:12:8: error: string cannot be indexed
893         .tmp.code:21:13: error: expected number found string
894         .tmp.code:17:16: error: expected number, found string
895         .tmp.code:24:8: error: cannot assign value of type [5]number
896         .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
897         .tmp.code:23:36: info: this is where 'a3' was set to [10]number
898         .tmp.code:25:8: error: cannot assign value of type [5]number
899         .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
900         .tmp.code:23:51: info: this is where 'a4' was set to [5]string
901         .tmp.code:26:8: error: cannot assign value of type [5]number
902         .tmp.code:27:16: error: expected number found string
903         .tmp.code:28:16: error: expected string found Boolean
904         .tmp.code:29:12: error: have number but need string
905         .tmp.code:7:8: info: variable 'c' was set as string here.
906         .tmp.code:32:8: error: variable used but not declared: foo
907         .tmp.code:32:8: error: field reference attempted on none, not a struct
908         .tmp.code:32:16: error: expected none found number
909         .tmp.code:33:14: error: field reference attempted on string, not a struct
910         .tmp.code:34:14: error: cannot find requested field in foo
911         .tmp.code:35:17: error: have string but need number
912         .tmp.code:38:29: error: expected number (labels not permitted) found Boolean
913         oceani: type error in program - not running.
914
915 ###### test: type_err4
916         func main()
917                 a:=1; b=2; c::=3
918                 print a, b, c
919
920 ###### output: type_err4
921         .tmp.code:3:14: error: variable used but not declared: b
922         .tmp.code:3:16: error: expected none found number
923         .tmp.code:3:14: info: variable 'b' was set as none here.
924         oceani: type error in program - not running.
925
926 ###### test: type_err5
927         struct foo
928                 bar:baz
929                 a:number
930         struct baz
931                 bat:foo
932                 b:string
933         struct foo
934                 c:number
935
936 ###### output: type_err5
937         .tmp.code:8:7: error: type already declared: foo
938         .tmp.code:2:7: info: this is location of declartion: foo
939         .tmp.code:2:7: error: type has recursive definition: foo
940         .tmp.code:5:7: error: type has recursive definition: baz
941
942 ###### test list
943         oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
944
945 ###### test: type_err_const
946         const
947                 foo :: number = 45
948                 bar ::= "string" + 56
949         const
950                 bar ::= "baz"
951         func main()
952                 foo := 4
953                 print foo, bar
954
955         // trigger duplicate-main error
956         func main()
957                 foo := 6
958                 print bar, foo
959
960 ###### output: type_err_const
961         .tmp.code:6:8: error: name already declared: bar
962         .tmp.code:4:8: info: this is where 'bar' was first declared
963         .tmp.code:8:8: error: variable 'foo' redeclared
964         .tmp.code:3:8: info: this is where 'foo' was first declared
965         .tmp.code:12:5: error: function 'main' redeclared
966         .tmp.code:7:5: info: this is where 'main' was first declared
967         .tmp.code:13:8: error: variable 'foo' redeclared
968         .tmp.code:3:8: info: this is where 'foo' was first declared
969         .tmp.code:4:16: error: expected number found string
970
971 ###### test: type_err_const1
972         const
973                 foo : number = 45
974                 bar := "string"
975         func main()
976                 foo := 4
977                 print foo, bar
978
979 ###### output: type_err_const1
980         .tmp.code:3:12: Syntax error in constant: :
981         .tmp.code:4:12: Syntax error in constant: :
982
983 ###### test: type_err_const2
984         const
985                 four ::= two + two
986                 two ::= four / 2
987         
988 ###### output: type_err_const2
989         .tmp.code:3:8: error: const four cannot be resolved.
990         .tmp.code:4:8: error: const two cannot be resolved.
991
992 ###### test: missing_program
993         const
994                 foo::="bar"
995
996 ###### output: missing_program
997         oceani: no main function found.
998
999 ###### test: bad_main
1000         func main(foo:string)
1001                 print foo
1002
1003 ###### output: bad_main
1004         .tmp.code:??:??: error: expected []string but variable 'foo' is string
1005         .tmp.code:??:??: info: this is where 'NOTVAR' was set to string
1006         oceani: main has wrong type.
1007
1008 Test for type errors with functions
1009
1010 ###### test list
1011         oceani_failing_tests += func_err_args func_err_redeclare
1012
1013 ###### test: func_err_args
1014
1015         func test1(a:number; b:string; c:[3]Boolean)
1016                 print a, b, c[1]
1017
1018         func test2(a:number; b:string; c:[3]Boolean)
1019                 print a, b, c[1]
1020
1021         func test3()
1022                 # use undefined names
1023                 print a, z
1024
1025         func main()
1026                 truth:[3]Boolean
1027                 truth[1] = True
1028                 test1(1,"hello")
1029                 test1("hello",1)
1030                 test1(1, "two", truth)
1031                 test1(1, 2, truth)
1032                 test1(1, "lo", truth, 4)
1033                 print test(), test1(1,2,3)
1034                 if test1 == test2:
1035                         pass
1036
1037         func test4(a:number):string
1038                 use a * a
1039
1040         func test5(a:number):string
1041                 print a
1042
1043         struct foo
1044                 a: number
1045                 b:string = "hello"
1046
1047         func test6(a:number):foo
1048                 b:foo
1049                 b.a = a
1050                 use b
1051
1052 ###### output: func_err_args
1053         .tmp.code:34:5: error: function cannot return value of type foo
1054         .tmp.code:28:14: error: expected string, found none
1055         .tmp.code:25:8: error: expected string, found number
1056         .tmp.code:15:14: error: insufficient arguments to function.
1057         .tmp.code:16:14: error: expected number found string
1058         .tmp.code:16:22: error: expected string found number
1059         .tmp.code:16:14: error: insufficient arguments to function.
1060         .tmp.code:18:17: error: expected string found number
1061         .tmp.code:19:14: error: too many arguments to function.
1062         .tmp.code:20:14: error: attempt to call a non-function.
1063         .tmp.code:20:32: error: expected string found number
1064         .tmp.code:20:28: error: insufficient arguments to function.
1065         .tmp.code:21:20: error: expected *invalid*type* but variable 'test2' is *invalid*type*
1066         .tmp.code:??:??: info: this is where 'NOTVAR' was set to *invalid*type*
1067         .tmp.code:10:14: error: variable used but not declared: a
1068         .tmp.code:10:17: error: variable used but not declared: z
1069         oceani: type error in program - not running.
1070
1071 ###### test: func_err_redeclare
1072
1073         func test1(a:number; b:string; c:[3]Boolean)
1074                 print a, b, c[1]
1075
1076         func test1
1077         do
1078                 pass
1079
1080         func test1
1081                 b:Boolean
1082         do
1083                 pass
1084
1085 ###### output: func_err_redeclare
1086         .tmp.code:5:5: error: function 'test1' redeclared
1087         .tmp.code:2:5: info: this is where 'test1' was first declared
1088         .tmp.code:9:5: error: function 'test1' redeclared
1089         .tmp.code:2:5: info: this is where 'test1' was first declared
1090
1091 ## Test erroneous command line args
1092
1093 To improve coverage, we want to test correct handling of strange command
1094 line arguments.  These tests won't use code, so the exiting test types
1095 won't work.  So we need to be able to explicitly give the command line,
1096 and the expected output, and have that tested and the coverage assessed.
1097 Rather than having to spell out the whole command name, just give "cmd",
1098 and discard that.  Requiring but discarding the command make an empty
1099 command list possible.
1100
1101 ###### test code
1102         @for t in $(oceani_special_tests); do \
1103             echo -n "Test $$t ... ";\
1104             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1105             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1106             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1107             if ! cmp -s .tmp.want .tmp.have; then \
1108                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1109             echo  "passed"; \
1110             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1111         done || true
1112 ###### valgrind test code
1113         @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1114             echo -n "Valgrind $$t.. "; \
1115             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1116             if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1117                [ $$? -eq 42 ]; then \
1118                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1119             if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1120                echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1121             if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1122                echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1123             echo " passed"; \
1124         done
1125
1126 ###### test list
1127         oceani_special_tests += "cmd"
1128         oceani_special_tests += "cmd,-zyx"
1129         oceani_special_tests += "cmd,nofile"
1130         oceani_special_tests += "cmd,/dev/null"
1131         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1132
1133 ###### output: cmd
1134         oceani: no input file given
1135
1136 ###### output: cmd,-zyx
1137         ./oceani: invalid option -- 'z'
1138         Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1139
1140 ###### output: cmd,nofile
1141         oceani: cannot open nofile
1142
1143 ###### output: cmd,/dev/null
1144         oceani: could not find any code in /dev/null
1145
1146 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1147         oceani: cannot find section toast:nothing