]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: allow global constants to be used before declared.
[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                 active:Boolean = True
589
590         struct baz { a:number; b:Boolean; }
591
592         func main
593         do
594                 info:[4]foo
595
596                 for i:=0; then i=i+1; while i < 4:
597                         switch i
598                         case 2: nm:= "peter"
599                         case 0: nm:= "bob"
600                         case 1: nm:= "jane"
601                         else    nm:= "janine"
602
603                         info[i].name = nm
604                         info[i].size[0] = i*i
605                         if nm != "jane":
606                                 info[i].active = False
607
608                 for i:=0; then i=i+1; while i < 4:
609                         print info[i].name, info[i].active, info[i].size[0]
610
611 ###### output: structs
612
613         bob False 0
614         jane True 1
615         peter False 4
616         janine False 9
617
618 ## Functions
619
620 Test functions.  They don't return anything, so we need to get them to print
621
622 ###### test list
623         oceani_tests += functions func_ret_type
624
625 ###### test: functions
626
627         func test1
628                 t: Boolean
629         do
630                 if t:
631                         print "true"
632
633         func noarg
634         do
635                 pass
636
637         func twoarg
638                 a:number
639                 b:string
640         do
641                 while a > 0:
642                         print b
643                         a = a - 1
644
645         func test(n:number; s:string)
646                 if n >= 1:
647                         print n,s,
648                         test(n-1, "."++s)
649                 else
650                         print "done"
651
652         func to_polar
653                 x:number; y:number
654         return
655                 rho:number
656                 theta:number
657         do
658                 rho = x + y
659                 theta = x - y
660
661         func main()
662                 for i:=0; then i = i + 1; while i < 5:
663                         test(i, " ")
664                 angular := to_polar(32, 23)
665                 print angular.rho, angular.theta
666
667 ###### output: functions
668         done
669         1  done
670         2  1 . done
671         3  2 . 1 .. done
672         4  3 . 2 .. 1 ... done
673         55 9
674
675 ###### test: func_ret_type
676
677         func double(n:number):number
678                 use n+n
679
680         func answer
681                 prefix:string
682                 suffix:string
683         return string
684         do
685                 use prefix ++ suffix
686
687         func noarg_returns
688         return Boolean
689         do
690                 use 22/7 == 3.14159
691
692         func main()
693                 for j:=10; then j = j - 3; while j > -5:
694                         print answer("dou","ble"), j, "is", double(j)
695
696 ###### output: func_ret_type
697         double 10 is 20
698         double 7 is 14
699         double 4 is 8
700         double 1 is 2
701         double -2 is -4
702
703 ## Test code with syntax errors
704
705 Syntax errors aren't handled well yet - the result is almost always a
706 single message about the first error.  So this section will be fairly
707 thin until we add proper parsing recovery in the face of common errors.
708
709 A special case of syntax errors is token errors, when a token is only
710 accepted because the parser doesn't know quite enough to reject it.
711 There are handled better as they are quite local, so a single test
712 program can trigger most of the possible errors.
713
714 To handle erronous code, we need a different set of tests, as we need to
715 capture `stderr`. The same test code will be used for type errors too.
716 As error messages contain the line number, and we don't want changes to
717 this file to change the reported numbers, we copy the code into a
718 separate file first, then run from there.
719
720 ###### test code
721         @for t in $(oceani_failing_tests); do \
722             echo -n "Test $$t ... "; \
723             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
724             echo '``````' > .tmp.code; \
725             ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
726             ./oceani .tmp.code > .tmp.have 2>&1; \
727             if ! cmp -s .tmp.want .tmp.have; then \
728                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
729             echo  "passed"; \
730             ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
731         done || true
732
733 ###### combine test lists
734         oceani_valg_tests += $(oceani_failing_tests)
735
736 ###### test list
737         oceani_failing_tests := syn1
738         oceani_failing_tests += tokerr
739
740 ###### test: syn1
741
742         func main()
743                 if then else while do
744
745 ###### output: syn1
746         .tmp.code:3:11: Syntax error in statement: then
747
748 ###### test: tokerr
749         func main()
750                 a := 1i  // imaginary numbers aren't understood
751                 b:[2i]number // array sizes are handled separately
752                 c:[3.14159]Boolean // array sizes must be integers
753                 d:[1_000_000_000_000]number // they mustn't be huge
754                 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
755
756                 multi := """
757                 This is a multiline string
758                 With an unsupportable suffix
759                 """Aa
760
761                 xx:unknown = 24
762                 yy:[unknowable]number
763                 zzsize := 4
764                 zz:[zzsize]string // size must be constant, use ::=
765
766                 // These numbers should be bad in all contexts: FIXME
767                 aa:[00123]number
768
769 ###### output: tokerr
770         .tmp.code:3:13: error: unsupported number suffix: 1i
771         .tmp.code:4:11: error: unsupported number suffix: 2i
772         .tmp.code:5:11: error: array size must be an integer: 3.14159
773         .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
774         .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
775         .tmp.code:9:17: error: unsupported string suffix: """
776                 This is a multiline string
777                 With an unsupportable suffix
778                 """Aa
779         .tmp.code:14:11: error: undefined type: unknown
780         .tmp.code:15:12: error: name undeclared: unknowable
781         .tmp.code:17:12: error: array size must be a constant: zzsize
782         .tmp.code:20:12: error: unrecognised number: 00123
783
784 ## Tests for type errors
785
786 Type error don't cause parsing to abort, so we can fit many in the
787 one test program.  Some type errors are found during the parse, others
788 during type analysis which doesn't run if parsing failed.  So we cannot
789 fit everything in one.
790
791 These programs were generated by looking for the
792 various places that `type_err()` are called.
793
794 ###### test list
795         oceani_failing_tests += type_err1 type_err2 type_err3 type_err4
796
797 ###### test: type_err1
798
799         func main()
800                 print "hello" ++ 5, 5 ++ "hello"
801
802                 b ::= 3
803                 b = b + 1
804
805                 if 3 * 4 and not True: print "Weird"
806
807 ###### output: type_err1
808         .tmp.code:3:25: error: expected string found number
809         .tmp.code:3:28: error: expected string found number
810         .tmp.code:6:8: error: Cannot assign to a constant: b
811         .tmp.code:5:8: info: name was defined as a constant here
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:8:11: error: Arithmetic returns number but Boolean expected
815         oceani: type error in program - not running.
816
817 ###### test: type_err2
818
819         func main()
820                 a := 1
821                 a := 2
822                 a ::= 3
823                 a:number = 4
824                 a ::number = 5
825                 c:
826
827 ###### output: type_err2
828         .tmp.code:4:8: error: variable 'a' redeclared
829         .tmp.code:3:8: info: this is where 'a' was first declared
830         .tmp.code:5:8: error: variable 'a' redeclared
831         .tmp.code:3:8: info: this is where 'a' was first declared
832         .tmp.code:6:8: error: variable 'a' redeclared
833         .tmp.code:3:8: info: this is where 'a' was first declared
834         .tmp.code:7:8: error: variable 'a' redeclared
835         .tmp.code:3:8: info: this is where 'a' was first declared
836         .tmp.code:8:8: Variable declared with no type or value: c
837
838 ###### test: type_err3
839
840         struct foo
841                 a: number
842                 b:string = "hello"
843
844         func main()
845                 c := "hello"
846                 c = c + 1
847                 c = "hello" ++ (True and False)
848                 c = 4 < 5
849                 print 45 + ( "Hello" ++ "there")
850                 c[5] = 1
851
852                 while
853                         use 1
854                         use True
855                         use "Hello"
856                 do
857                         print
858                 case 1: print "one"
859                 case "Hello": print "Hello"
860
861                 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
862                 a1 = a2
863                 a1 = a3
864                 a1 = a4
865                 a1[2] = "hello"
866                 a4[1] = True
867                 c = a2[3]
868
869                 bar:foo
870                 foo.c = 43
871                 print c.foo
872                 print bar.c
873                 print bar.b + 42
874
875                 // trigger 'labels not permitted' error message
876                 while 1 if True else False:
877                         print
878                 case 2: print "two"
879
880 ###### output: type_err3
881         .tmp.code:8:12: error: expected number but variable 'c' is string
882         .tmp.code:7:8: info: this is where 'c' was set to string
883         .tmp.code:8:12: error: Arithmetic returns number but string expected
884         .tmp.code:7:8: info: variable 'c' was set as string here.
885         .tmp.code:9:24: error: Boolean operation found where string expected
886         .tmp.code:10:12: error: Comparison returns Boolean but string expected
887         .tmp.code:7:8: info: variable 'c' was set as string here.
888         .tmp.code:11:21: error: Concat returns string but number expected
889         .tmp.code:12:8: error: string cannot be indexed
890         .tmp.code:12:8: error: string cannot be indexed
891         .tmp.code:21:13: error: expected number found string
892         .tmp.code:17:16: error: expected number, found string
893         .tmp.code:24:8: error: cannot assign value of type [5]number
894         .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
895         .tmp.code:23:36: info: this is where 'a3' was set to [10]number
896         .tmp.code:25:8: error: cannot assign value of type [5]number
897         .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
898         .tmp.code:23:51: info: this is where 'a4' was set to [5]string
899         .tmp.code:26:8: error: cannot assign value of type [5]number
900         .tmp.code:27:16: error: expected number found string
901         .tmp.code:28:16: error: expected string found Boolean
902         .tmp.code:29:12: error: have number but need string
903         .tmp.code:7:8: info: variable 'c' was set as string here.
904         .tmp.code:32:8: error: variable used but not declared: foo
905         .tmp.code:32:8: error: field reference attempted on none, not a struct
906         .tmp.code:32:16: error: expected none found number
907         .tmp.code:33:14: error: field reference attempted on string, not a struct
908         .tmp.code:34:14: error: cannot find requested field in foo
909         .tmp.code:35:17: error: have string but need number
910         .tmp.code:38:29: error: expected number (labels not permitted) found Boolean
911         oceani: type error in program - not running.
912
913 ###### test: type_err4
914         func main()
915                 a:=1; b=2; c::=3
916                 print a, b, c
917
918 ###### output: type_err4
919         .tmp.code:3:14: error: variable used but not declared: b
920         .tmp.code:3:16: error: expected none found number
921         .tmp.code:3:14: info: variable 'b' was set as none here.
922         oceani: type error in program - not running.
923
924 ###### test list
925         oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
926
927 ###### test: type_err_const
928         const
929                 foo :: number = 45
930                 bar ::= "string" + 56
931         const
932                 bar ::= "baz"
933         func main()
934                 foo := 4
935                 print foo, bar
936
937         // trigger duplicate-main error
938         func main()
939                 foo := 6
940                 print bar, foo
941
942 ###### output: type_err_const
943         .tmp.code:6:8: error: name already declared: bar
944         .tmp.code:4:8: info: this is where 'bar' was first declared
945         .tmp.code:8:8: error: variable 'foo' redeclared
946         .tmp.code:3:8: info: this is where 'foo' was first declared
947         .tmp.code:12:5: error: function 'main' redeclared
948         .tmp.code:7:5: info: this is where 'main' was first declared
949         .tmp.code:13:8: error: variable 'foo' redeclared
950         .tmp.code:3:8: info: this is where 'foo' was first declared
951         .tmp.code:4:16: error: expected number found string
952
953 ###### test: type_err_const1
954         const
955                 foo : number = 45
956                 bar := "string"
957         func main()
958                 foo := 4
959                 print foo, bar
960
961 ###### output: type_err_const1
962         .tmp.code:3:12: Syntax error in constant: :
963         .tmp.code:4:12: Syntax error in constant: :
964
965 ###### test: type_err_const2
966         const
967                 four ::= two + two
968                 two ::= four / 2
969         
970 ###### output: type_err_const2
971         .tmp.code:3:8: error: const four cannot be resolved.
972         .tmp.code:4:8: error: const two cannot be resolved.
973
974 ###### test: missing_program
975         const
976                 foo::="bar"
977
978 ###### output: missing_program
979         oceani: no main function found.
980
981 ###### test: bad_main
982         func main(foo:string)
983                 print foo
984
985 ###### output: bad_main
986         .tmp.code:??:??: error: expected []string but variable 'foo' is string
987         .tmp.code:??:??: info: this is where 'NOTVAR' was set to string
988         oceani: main has wrong type.
989
990 Test for type errors with functions
991
992 ###### test list
993         oceani_failing_tests += func_err_args func_err_redeclare
994
995 ###### test: func_err_args
996
997         func test1(a:number; b:string; c:[3]Boolean)
998                 print a, b, c[1]
999
1000         func test2(a:number; b:string; c:[3]Boolean)
1001                 print a, b, c[1]
1002
1003         func test3()
1004                 # use undefined names
1005                 print a, z
1006
1007         func main()
1008                 truth:[3]Boolean
1009                 truth[1] = True
1010                 test1(1,"hello")
1011                 test1("hello",1)
1012                 test1(1, "two", truth)
1013                 test1(1, 2, truth)
1014                 test1(1, "lo", truth, 4)
1015                 print test(), test1(1,2,3)
1016                 if test1 == test2:
1017                         pass
1018
1019         func test4(a:number):string
1020                 use a * a
1021
1022         func test5(a:number):string
1023                 print a
1024
1025         struct foo
1026                 a: number
1027                 b:string = "hello"
1028
1029         func test6(a:number):foo
1030                 b:foo
1031                 b.a = a
1032                 use b
1033
1034 ###### output: func_err_args
1035         .tmp.code:34:5: error: function cannot return value of type foo
1036         .tmp.code:28:14: error: expected string, found none
1037         .tmp.code:25:8: error: expected string, found number
1038         .tmp.code:15:14: error: insufficient arguments to function.
1039         .tmp.code:16:14: error: expected number found string
1040         .tmp.code:16:22: error: expected string found number
1041         .tmp.code:16:14: error: insufficient arguments to function.
1042         .tmp.code:18:17: error: expected string found number
1043         .tmp.code:19:14: error: too many arguments to function.
1044         .tmp.code:20:14: error: attempt to call a non-function.
1045         .tmp.code:20:32: error: expected string found number
1046         .tmp.code:20:28: error: insufficient arguments to function.
1047         .tmp.code:21:20: error: expected *invalid*type* but variable 'test2' is *invalid*type*
1048         .tmp.code:??:??: info: this is where 'NOTVAR' was set to *invalid*type*
1049         .tmp.code:10:14: error: variable used but not declared: a
1050         .tmp.code:10:17: error: variable used but not declared: z
1051         oceani: type error in program - not running.
1052
1053 ###### test: func_err_redeclare
1054
1055         func test1(a:number; b:string; c:[3]Boolean)
1056                 print a, b, c[1]
1057
1058         func test1
1059         do
1060                 pass
1061
1062         func test1
1063                 b:Boolean
1064         do
1065                 pass
1066
1067 ###### output: func_err_redeclare
1068         .tmp.code:5:5: error: function 'test1' redeclared
1069         .tmp.code:2:5: info: this is where 'test1' was first declared
1070         .tmp.code:9:5: error: function 'test1' redeclared
1071         .tmp.code:2:5: info: this is where 'test1' was first declared
1072
1073 ## Test erroneous command line args
1074
1075 To improve coverage, we want to test correct handling of strange command
1076 line arguments.  These tests won't use code, so the exiting test types
1077 won't work.  So we need to be able to explicitly give the command line,
1078 and the expected output, and have that tested and the coverage assessed.
1079 Rather than having to spell out the whole command name, just give "cmd",
1080 and discard that.  Requiring but discarding the command make an empty
1081 command list possible.
1082
1083 ###### test code
1084         @for t in $(oceani_special_tests); do \
1085             echo -n "Test $$t ... ";\
1086             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1087             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1088             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1089             if ! cmp -s .tmp.want .tmp.have; then \
1090                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1091             echo  "passed"; \
1092             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1093         done || true
1094 ###### valgrind test code
1095         @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1096             echo -n "Valgrind $$t.. "; \
1097             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1098             if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1099                [ $$? -eq 42 ]; then \
1100                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1101             if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1102                echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1103             if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1104                echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1105             echo " passed"; \
1106         done
1107
1108 ###### test list
1109         oceani_special_tests += "cmd"
1110         oceani_special_tests += "cmd,-zyx"
1111         oceani_special_tests += "cmd,nofile"
1112         oceani_special_tests += "cmd,/dev/null"
1113         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1114
1115 ###### output: cmd
1116         oceani: no input file given
1117
1118 ###### output: cmd,-zyx
1119         ./oceani: invalid option -- 'z'
1120         Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1121
1122 ###### output: cmd,nofile
1123         oceani: cannot open nofile
1124
1125 ###### output: cmd,/dev/null
1126         oceani: could not find any code in /dev/null
1127
1128 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1129         oceani: cannot find section toast:nothing