]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: exercise more parsing options for blocks.
[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         // exercise the parsing options
706         func t1 (a:number) {
707                 print "t1"
708         }
709         func t2 (a:string) {print "string"}
710         func t3() print "t3"
711
712 ###### output: functions
713         done
714         1  done
715         2  1 . done
716         3  2 . 1 .. done
717         4  3 . 2 .. 1 ... done
718         55 9
719
720 ###### test: func_ret_type
721
722         func double(n:number):number
723                 use n+n
724
725         func answer
726                 prefix:string
727                 suffix:string
728         return string
729         do
730                 use prefix ++ suffix
731
732         func noarg_returns
733         return Boolean
734         do
735                 use 22/7 == 3.14159
736
737         func main()
738                 for j:=10; then j = j - 3; while j > -5:
739                         print answer("dou","ble"), j, "is", double(j)
740
741 ###### output: func_ret_type
742         double 10 is 20
743         double 7 is 14
744         double 4 is 8
745         double 1 is 2
746         double -2 is -4
747
748 ## References
749
750 A simple linked list example
751
752 ###### test list
753         oceani_tests += "linked_list,one,two,three,four"
754
755 ###### test: linked_list
756
757         struct linkage
758                 next: @node
759         struct node
760                 list: linkage
761                 this: string
762
763         func insert(list:@linkage; new:string)
764                 p:=list
765                 while ?p.next and then p.next.this < new:
766                         p = p.next.list
767                 t:@node = @new()
768                 t.list.next = p.next
769                 t.this = new
770                 p.next = t;
771
772         func printlist(list:@linkage)
773                 while ?list.next:
774                         print list@.next.this
775                         list = list@.next.list
776
777         func freelist(list:@linkage)
778                 if list.next != @nil:
779                         lp:@linkage = list.next.list
780                         freelist(lp)
781                         @free = list.next
782
783         func main(argv:[]string)
784                 list : linkage
785
786                 insert(list, "@start");
787                 insert(list, "~end")
788                 for i:=1; then i=i+1; while i < argv[]:
789                         insert(list, argv[i])
790                 insert(list, "Hello!")
791                 printlist(list)
792                 freelist(list)
793
794 ###### output: linked_list,one,two,three,four
795         @start
796         Hello!
797         four
798         one
799         three
800         two
801         ~end
802
803 ## Test code with syntax errors
804
805 Syntax errors aren't handled well yet - the result is almost always a
806 single message about the first error.  So this section will be fairly
807 thin until we add proper parsing recovery in the face of common errors.
808
809 A special case of syntax errors is token errors, when a token is only
810 accepted because the parser doesn't know quite enough to reject it.
811 There are handled better as they are quite local, so a single test
812 program can trigger most of the possible errors.
813
814 To handle erronous code, we need a different set of tests, as we need to
815 capture `stderr`. The same test code will be used for type errors too.
816 As error messages contain the line number, and we don't want changes to
817 this file to change the reported numbers, we copy the code into a
818 separate file first, then run from there.
819
820 ###### test code
821         @for t in $(oceani_failing_tests); do \
822             echo -n "Test $$t ... "; \
823             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
824             echo '``````' > .tmp.code; \
825             ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
826             ./oceani .tmp.code > .tmp.have 2>&1; \
827             if ! cmp -s .tmp.want .tmp.have; then \
828                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
829             echo  "passed"; \
830             ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
831         done || true
832
833 ###### combine test lists
834         oceani_valg_tests += $(oceani_failing_tests)
835
836 ###### test list
837         oceani_failing_tests := syn1
838         oceani_failing_tests += tokerr
839
840 ###### test: syn1
841
842         func main()
843                 if then else while do
844
845 ###### output: syn1
846         .tmp.code:3:11: Syntax error in statement: then
847
848 ###### test: tokerr
849         func main()
850                 a := 1i  // imaginary numbers aren't understood
851                 b:[2i]number // array sizes are handled separately
852                 c:[3.14159]Boolean // array sizes must be integers
853                 d:[1_000_000_000_000]number // they mustn't be huge
854                 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
855
856                 multi := """
857                 This is a multiline string
858                 With an unsupportable suffix
859                 """Aa
860
861                 xx:unknown = 24
862                 yy:[unknowable]number
863                 zzsize := 4
864                 zz:[zzsize]string // size must be constant, use ::=
865
866                 // These numbers should be bad in all contexts: FIXME
867                 aa:[00123]number
868
869 ###### output: tokerr
870         .tmp.code:3:13: error: unsupported number suffix: 1i
871         .tmp.code:4:11: error: unsupported number suffix: 2i
872         .tmp.code:5:11: error: array size must be an integer: 3.14159
873         .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
874         .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
875         .tmp.code:9:17: error: unsupported string suffix: """
876                 This is a multiline string
877                 With an unsupportable suffix
878                 """Aa
879         .tmp.code:15:12: error: name undeclared: unknowable
880         .tmp.code:17:12: error: array size must be a constant: zzsize
881         .tmp.code:20:12: error: unrecognised number: 00123
882         .tmp.code:14:11: error: type used but not declared: unknown
883
884 ## Tests for type errors
885
886 Type error don't cause parsing to abort, so we can fit many in the
887 one test program.  Some type errors are found during the parse, others
888 during type analysis which doesn't run if parsing failed.  So we cannot
889 fit everything in one.
890
891 These programs were generated by looking for the
892 various places that `type_err()` are called.
893
894 ###### test list
895         oceani_failing_tests += type_err1 type_err2 type_err3 type_err4 type_err5 type_err6
896
897 ###### test: type_err1
898
899         func main()
900                 print "hello" ++ 5, 5 ++ "hello"
901
902                 b ::= 3
903                 b = b + 1
904
905                 if 3 * 4 and not True: print "Weird"
906                 d:number = .fred
907                 (d + b) = 12
908
909 ###### output: type_err1
910         .tmp.code:3:25: error: expected string found number
911         .tmp.code:3:28: error: expected string found number
912         .tmp.code:6:8: error: Cannot assign to a constant: b
913         .tmp.code:5:8: info: name was defined as a constant here
914         .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
915         .tmp.code:9:20: error: expected number found label
916         .tmp.code:9:8: info: variable 'd' was set as number here.
917         .tmp.code:10:8: error: cannot assign to an rval
918         oceani: type error in program - not running.
919
920 ###### test: type_err2
921
922         func main()
923                 a := 1
924                 a := 2
925                 a ::= 3
926                 a:number = 4
927                 a ::number = 5
928                 c:
929                 d:number = 02
930
931 ###### output: type_err2
932         .tmp.code:4:8: error: variable 'a' redeclared
933         .tmp.code:3:8: info: this is where 'a' was first declared
934         .tmp.code:5:8: error: variable 'a' redeclared
935         .tmp.code:3:8: info: this is where 'a' was first declared
936         .tmp.code:6:8: error: variable 'a' redeclared
937         .tmp.code:3:8: info: this is where 'a' was first declared
938         .tmp.code:7:8: error: variable 'a' redeclared
939         .tmp.code:3:8: info: this is where 'a' was first declared
940         .tmp.code:8:8: Variable declared with no type or value: c
941         .tmp.code:9:19: error: unsupported number format: 02
942
943 ###### test: type_err3
944
945         struct foo
946                 a: number
947                 b:string = "hello"; d:Boolean
948
949         func main()
950                 c := "hello"
951                 c = c + 1
952                 c = "hello" ++ (True and False)
953                 c = 4 < 5
954                 print 45 + ( "Hello" ++ "there")
955                 c[5] = 1
956
957                 while
958                         use 1
959                         use True
960                         use "Hello"
961                 do
962                         print
963                 case 1: print "one"
964                 case "Hello": print "Hello"
965
966                 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
967                 a1 = a2
968                 a1 = a3
969                 a1 = a4
970                 a1[2] = "hello"
971                 a4[1] = True
972                 c = a2[3]
973
974                 bar:foo
975                 foo.c = 43
976                 print c.foo
977                 print bar.c
978                 print bar.b + 42
979
980                 // trigger 'labels not permitted' error message
981                 while 1 if True else False:
982                         print
983                 case 2: print "two"
984                 print "one" ++ a4[], c[]
985
986                 x:Boolean = $"42"
987
988                 five ::= 5
989                 four ::= 4
990                 x1:[five]number
991                 x2:[four]number
992                 x1 = x2
993
994 ###### output: type_err3
995         .tmp.code:8:12: error: expected number but variable 'c' is string
996         .tmp.code:7:8: info: this is where 'c' was set to string
997         .tmp.code:8:12: error: Arithmetic returns number but string expected
998         .tmp.code:7:8: info: variable 'c' was set as string here.
999         .tmp.code:9:24: error: Boolean operation found where string expected
1000         .tmp.code:7:8: info: variable 'c' was set as string here.
1001         .tmp.code:10:12: error: Comparison returns Boolean but string expected
1002         .tmp.code:7:8: info: variable 'c' was set as string here.
1003         .tmp.code:11:21: error: Concat returns string but number expected
1004         .tmp.code:12:8: error: string cannot be indexed
1005         .tmp.code:12:8: error: string cannot be indexed
1006         .tmp.code:21:13: error: expected number found string
1007         .tmp.code:17:16: error: expected number, found string
1008         .tmp.code:24:8: error: cannot assign value of type [5]number
1009         .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
1010         .tmp.code:23:36: info: this is where 'a3' was set to [10]number
1011         .tmp.code:25:8: error: cannot assign value of type [5]number
1012         .tmp.code:23:8: info: variable 'a1' was set as [5]number here.
1013         .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
1014         .tmp.code:23:51: info: this is where 'a4' was set to [5]string
1015         .tmp.code:26:8: error: cannot assign value of type [5]number
1016         .tmp.code:23:8: info: variable 'a1' was set as [5]number here.
1017         .tmp.code:27:16: error: expected number found string
1018         .tmp.code:28:16: error: expected string found Boolean
1019         .tmp.code:29:12: error: have number but need string
1020         .tmp.code:7:8: info: variable 'c' was set as string here.
1021         .tmp.code:32:8: error: variable used but not declared: foo
1022         .tmp.code:32:8: error: field reference on none is not supported
1023         .tmp.code:32:16: error: expected none found number
1024         .tmp.code:33:14: error: field reference on string is not supported
1025         .tmp.code:34:14: error: cannot find requested field in foo
1026         .tmp.code:35:17: error: have string but need number
1027         .tmp.code:38:29: error: expected number found Boolean
1028         .tmp.code:41:23: error: have number but need string
1029         .tmp.code:41:29: error: string cannot provide length
1030         .tmp.code:43:21: error: Can only convert string to number, not Boolean
1031         .tmp.code:43:8: info: variable 'x' was set as Boolean here.
1032         .tmp.code:49:13: error: expected [five]number but variable 'x2' is [four]number
1033         .tmp.code:48:8: info: this is where 'x2' was set to [four]number
1034         .tmp.code:49:8: error: cannot assign value of type [five]number
1035         .tmp.code:47:8: info: variable 'x1' was set as [five]number here.
1036         oceani: type error in program - not running.
1037
1038 ###### test: type_err4
1039         func main()
1040                 a:=1; b=2; c::=3
1041                 print a, b, c
1042
1043 ###### output: type_err4
1044         .tmp.code:3:14: error: variable used but not declared: b
1045         .tmp.code:3:16: error: expected none found number
1046         .tmp.code:3:14: info: variable 'b' was set as none here.
1047         oceani: type error in program - not running.
1048
1049 ###### test: type_err5
1050         struct foo
1051                 bar:baz
1052                 a:number
1053         struct baz
1054                 bat:foo
1055                 b:string
1056         struct foo
1057                 c:number
1058                 x:[5]:string
1059
1060 ###### output: type_err5
1061         .tmp.code:8:7: error: type already declared: foo
1062         .tmp.code:2:7: info: this is location of declartion: foo
1063         .tmp.code:10:13: Syntax error in struct field: :
1064         .tmp.code:5:7: error: type has recursive definition: baz
1065         .tmp.code:2:7: error: type has recursive definition: foo
1066
1067 ###### test: type_err6
1068
1069         func main()
1070                 a:= "hello"
1071                 if ?a:
1072                         print "no"
1073                 print a ?? "there"
1074
1075 ###### output: type_err6
1076         .tmp.code:4:12: error: '?' requires a testable value, not string
1077         .tmp.code:6:14: error: "??" requires a testable value, not string
1078         oceani: type error in program - not running.
1079
1080
1081 ###### test list
1082         oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
1083
1084 ###### test: type_err_const
1085         const
1086                 foo :: number = 45
1087                 bar ::= "string" + 56
1088         const
1089                 bar ::= "baz"
1090         func main()
1091                 foo := 4
1092                 print foo, bar
1093
1094         // trigger duplicate-main error
1095         func main()
1096                 foo := 6
1097                 print bar, foo
1098
1099 ###### output: type_err_const
1100         .tmp.code:6:8: error: name already declared: bar
1101         .tmp.code:4:8: info: this is where 'bar' was first declared
1102         .tmp.code:8:8: error: variable 'foo' redeclared
1103         .tmp.code:3:8: info: this is where 'foo' was first declared
1104         .tmp.code:12:5: error: function 'main' redeclared
1105         .tmp.code:7:5: info: this is where 'main' was first declared
1106         .tmp.code:13:8: error: variable 'foo' redeclared
1107         .tmp.code:3:8: info: this is where 'foo' was first declared
1108         .tmp.code:4:16: error: expected number found string
1109
1110 ###### test: type_err_const1
1111         const
1112                 foo : number = 45
1113                 bar := "string"
1114         func main()
1115                 foo := 4
1116                 print foo, bar
1117
1118 ###### output: type_err_const1
1119         .tmp.code:3:12: Syntax error in constant: :
1120         .tmp.code:4:12: Syntax error in constant: :
1121
1122 ###### test: type_err_const2
1123         const
1124                 four ::= two + two
1125                 two ::= four / 2
1126         
1127 ###### output: type_err_const2
1128         .tmp.code:3:8: error: const four cannot be resolved.
1129         .tmp.code:4:8: error: const two cannot be resolved.
1130
1131 ###### test: missing_program
1132         const
1133                 foo::="bar"
1134
1135 ###### output: missing_program
1136         oceani: no main function found.
1137
1138 ###### test: bad_main
1139         func main(foo:string)
1140                 print foo
1141
1142 ###### output: bad_main
1143         .tmp.code:2:10: error: expected []string but variable 'foo' is string
1144         .tmp.code:2:10: info: this is where 'foo' was set to string
1145         oceani: main has wrong type.
1146
1147 Test for type errors with functions
1148
1149 ###### test list
1150         oceani_failing_tests += func_err_args func_err_redeclare
1151
1152 ###### test: func_err_args
1153
1154         func test1(a:number; b:string; c:[3]Boolean)
1155                 print a, b, c[1]
1156
1157         func test2(a:number; b:string; c:[3]Boolean)
1158                 print a, b, c[1]
1159
1160         func test3()
1161                 # use undefined names
1162                 print a, z
1163
1164         func main()
1165                 truth:[3]Boolean
1166                 truth[1] = True
1167                 test1(1,"hello")
1168                 test1("hello",1)
1169                 test1(1, "two", truth)
1170                 test1(1, 2, truth)
1171                 test1(1, "lo", truth, 4)
1172                 print test(), test1(1,2,3)
1173                 if test1 == test2:
1174                         pass
1175
1176         func test4(a:number):string
1177                 use a * a
1178
1179         func test5(a:number):string
1180                 print a
1181
1182         struct foo
1183                 a: number
1184                 b:string = "hello"
1185
1186         func test6(a:number):foo
1187                 b:foo
1188                 b.a = a
1189                 use b
1190
1191         func test7(a:@number)
1192                 test7(45)
1193                 test7("45")
1194
1195 ###### output: func_err_args
1196         .tmp.code:40:14: error: cannot pass rval when reference expected
1197         .tmp.code:41:14: error: expected @number found string
1198         .tmp.code:34:5: error: function cannot return value of type foo
1199         .tmp.code:28:8: error: expected string, found none
1200         .tmp.code:25:8: error: expected string, found number
1201         .tmp.code:15:14: error: insufficient arguments to function.
1202         .tmp.code:16:14: error: expected number found string
1203         .tmp.code:16:22: error: expected string found number
1204         .tmp.code:16:14: error: insufficient arguments to function.
1205         .tmp.code:18:17: error: expected string found number
1206         .tmp.code:19:14: error: too many arguments to function.
1207         .tmp.code:20:14: error: attempt to call a non-function.
1208         .tmp.code:20:32: error: expected string found number
1209         .tmp.code:20:28: error: insufficient arguments to function.
1210         .tmp.code:21:20: error: expected "func test1" but variable 'test2' is "func test2"
1211         .tmp.code:5:5: info: this is where 'test2' was set to "func test2"
1212         .tmp.code:10:14: error: variable used but not declared: a
1213         .tmp.code:10:17: error: variable used but not declared: z
1214         oceani: type error in program - not running.
1215
1216 ###### test: func_err_redeclare
1217
1218         func test1(a:number; b:string; c:[3]Boolean)
1219                 print a, b, c[1]
1220
1221         func test1
1222         do
1223                 pass
1224
1225         func test1
1226                 b:Boolean
1227         do
1228                 pass
1229
1230 ###### output: func_err_redeclare
1231         .tmp.code:5:5: error: function 'test1' redeclared
1232         .tmp.code:2:5: info: this is where 'test1' was first declared
1233         .tmp.code:9:5: error: function 'test1' redeclared
1234         .tmp.code:2:5: info: this is where 'test1' was first declared
1235
1236 Test for errors with references
1237
1238 ###### test list
1239         oceani_failing_tests += ref_err1 ref_err2
1240
1241 ###### test: ref_err1
1242         func main()
1243                 ref:@number
1244                 @foo = ref
1245                 ref = @old()
1246                 if ref == @null:
1247                         print "null"
1248
1249 ###### output: ref_err1
1250         .tmp.code:4:9: error: only "@free" makes sense here: foo
1251         .tmp.code:5:15: error: Only reference function is "@new()": old
1252         .tmp.code:6:19: error: Only reference value is "@nil": null
1253
1254 ###### test: ref_err2
1255         func main()
1256                 ref:@number
1257                 ref2:@string
1258                 num:number = @new()
1259                 print num@
1260                 if num == @nil or ref == ref2 or ref == 2 or ref.foo:
1261                         @free = num
1262                 ref = 1
1263
1264 ###### output: ref_err2
1265         .tmp.code:5:22: error: @new() can only be used with references, not number
1266         .tmp.code:5:8: info: variable 'num' was set as number here.
1267         .tmp.code:6:14: error: Cannot dereference number
1268         .tmp.code:7:19: error: @nil can only be used with reference, not number
1269         .tmp.code:7:33: error: expected @number but variable 'ref2' is @string
1270         .tmp.code:4:8: info: this is where 'ref2' was set to @string
1271         .tmp.code:7:48: error: expected @number found number
1272         .tmp.code:7:53: error: field reference on number is not supported
1273         .tmp.code:7:56: error: have none but need Boolean
1274         .tmp.code:8:17: error: @free can only be assigned a reference, not number
1275         .tmp.code:8:17: error: @free can only be assigned a reference, not number
1276         .tmp.code:9:8: error: Cannot assign an rval to a reference.
1277         oceani: type error in program - not running.
1278
1279 ## Test erroneous command line args
1280
1281 To improve coverage, we want to test correct handling of strange command
1282 line arguments.  These tests won't use code, so the exiting test types
1283 won't work.  So we need to be able to explicitly give the command line,
1284 and the expected output, and have that tested and the coverage assessed.
1285 Rather than having to spell out the whole command name, just give "cmd",
1286 and discard that.  Requiring but discarding the command make an empty
1287 command list possible.
1288
1289 ###### test code
1290         @for t in $(oceani_special_tests); do \
1291             echo -n "Test $$t ... ";\
1292             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1293             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1294             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1295             if ! cmp -s .tmp.want .tmp.have; then \
1296                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1297             echo  "passed"; \
1298             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1299         done || true
1300 ###### valgrind test code
1301         @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1302             echo -n "Valgrind $$t.. "; \
1303             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1304             if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1305                [ $$? -eq 42 ]; then \
1306                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1307             if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1308                echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1309             if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1310                echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1311             echo " passed"; \
1312         done
1313
1314 ###### test list
1315         oceani_special_tests += "cmd"
1316         oceani_special_tests += "cmd,-zyx"
1317         oceani_special_tests += "cmd,nofile"
1318         oceani_special_tests += "cmd,/dev/null"
1319         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1320
1321 ###### output: cmd
1322         oceani: no input file given
1323
1324 ###### output: cmd,-zyx
1325         ./oceani: invalid option -- 'z'
1326         Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1327
1328 ###### output: cmd,nofile
1329         oceani: cannot open nofile
1330
1331 ###### output: cmd,/dev/null
1332         oceani: could not find any code in /dev/null
1333
1334 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1335         oceani: cannot find section toast:nothing