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