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