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