]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: fix parsing of error in struct field.
[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"; d:Boolean
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                 five ::= 5
967                 four ::= 4
968                 x1:[five]number
969                 x2:[four]number
970                 x1 = x2
971
972 ###### output: type_err3
973         .tmp.code:8:12: error: expected number but variable 'c' is string
974         .tmp.code:7:8: info: this is where 'c' was set to string
975         .tmp.code:8:12: error: Arithmetic returns number but string expected
976         .tmp.code:7:8: info: variable 'c' was set as string here.
977         .tmp.code:9:24: error: Boolean operation found where string expected
978         .tmp.code:7:8: info: variable 'c' was set as string here.
979         .tmp.code:10:12: error: Comparison returns Boolean but string expected
980         .tmp.code:7:8: info: variable 'c' was set as string here.
981         .tmp.code:11:21: error: Concat returns string but number expected
982         .tmp.code:12:8: error: string cannot be indexed
983         .tmp.code:12:8: error: string cannot be indexed
984         .tmp.code:21:13: error: expected number found string
985         .tmp.code:17:16: error: expected number, found string
986         .tmp.code:24:8: error: cannot assign value of type [5]number
987         .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
988         .tmp.code:23:36: info: this is where 'a3' was set to [10]number
989         .tmp.code:25:8: error: cannot assign value of type [5]number
990         .tmp.code:23:8: info: variable 'a1' was set as [5]number here.
991         .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
992         .tmp.code:23:51: info: this is where 'a4' was set to [5]string
993         .tmp.code:26:8: error: cannot assign value of type [5]number
994         .tmp.code:23:8: info: variable 'a1' was set as [5]number here.
995         .tmp.code:27:16: error: expected number found string
996         .tmp.code:28:16: error: expected string found Boolean
997         .tmp.code:29:12: error: have number but need string
998         .tmp.code:7:8: info: variable 'c' was set as string here.
999         .tmp.code:32:8: error: variable used but not declared: foo
1000         .tmp.code:32:8: error: field reference on none is not supported
1001         .tmp.code:32:16: error: expected none found number
1002         .tmp.code:33:14: error: field reference on string is not supported
1003         .tmp.code:34:14: error: cannot find requested field in foo
1004         .tmp.code:35:17: error: have string but need number
1005         .tmp.code:38:29: error: expected number found Boolean
1006         .tmp.code:41:23: error: have number but need string
1007         .tmp.code:41:29: error: string cannot provide length
1008         .tmp.code:43:21: error: Can only convert string to number, not Boolean
1009         .tmp.code:43:8: info: variable 'x' was set as Boolean here.
1010         .tmp.code:49:13: error: expected [five]number but variable 'x2' is [four]number
1011         .tmp.code:48:8: info: this is where 'x2' was set to [four]number
1012         .tmp.code:49:8: error: cannot assign value of type [five]number
1013         .tmp.code:47:8: info: variable 'x1' was set as [five]number here.
1014         oceani: type error in program - not running.
1015
1016 ###### test: type_err4
1017         func main()
1018                 a:=1; b=2; c::=3
1019                 print a, b, c
1020
1021 ###### output: type_err4
1022         .tmp.code:3:14: error: variable used but not declared: b
1023         .tmp.code:3:16: error: expected none found number
1024         .tmp.code:3:14: info: variable 'b' was set as none here.
1025         oceani: type error in program - not running.
1026
1027 ###### test: type_err5
1028         struct foo
1029                 bar:baz
1030                 a:number
1031         struct baz
1032                 bat:foo
1033                 b:string
1034         struct foo
1035                 c:number
1036                 x:[5]:string
1037
1038 ###### output: type_err5
1039         .tmp.code:8:7: error: type already declared: foo
1040         .tmp.code:2:7: info: this is location of declartion: foo
1041         .tmp.code:10:13: Syntax error in struct field: :
1042         .tmp.code:5:7: error: type has recursive definition: baz
1043         .tmp.code:2:7: error: type has recursive definition: foo
1044
1045 ###### test: type_err6
1046
1047         func main()
1048                 a:= "hello"
1049                 if ?a:
1050                         print "no"
1051                 print a ?? "there"
1052
1053 ###### output: type_err6
1054         .tmp.code:4:12: error: '?' requires a testable value, not string
1055         .tmp.code:6:14: error: "??" requires a testable value, not string
1056         oceani: type error in program - not running.
1057
1058
1059 ###### test list
1060         oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
1061
1062 ###### test: type_err_const
1063         const
1064                 foo :: number = 45
1065                 bar ::= "string" + 56
1066         const
1067                 bar ::= "baz"
1068         func main()
1069                 foo := 4
1070                 print foo, bar
1071
1072         // trigger duplicate-main error
1073         func main()
1074                 foo := 6
1075                 print bar, foo
1076
1077 ###### output: type_err_const
1078         .tmp.code:6:8: error: name already declared: bar
1079         .tmp.code:4:8: info: this is where 'bar' was first declared
1080         .tmp.code:8:8: error: variable 'foo' redeclared
1081         .tmp.code:3:8: info: this is where 'foo' was first declared
1082         .tmp.code:12:5: error: function 'main' redeclared
1083         .tmp.code:7:5: info: this is where 'main' was first declared
1084         .tmp.code:13:8: error: variable 'foo' redeclared
1085         .tmp.code:3:8: info: this is where 'foo' was first declared
1086         .tmp.code:4:16: error: expected number found string
1087
1088 ###### test: type_err_const1
1089         const
1090                 foo : number = 45
1091                 bar := "string"
1092         func main()
1093                 foo := 4
1094                 print foo, bar
1095
1096 ###### output: type_err_const1
1097         .tmp.code:3:12: Syntax error in constant: :
1098         .tmp.code:4:12: Syntax error in constant: :
1099
1100 ###### test: type_err_const2
1101         const
1102                 four ::= two + two
1103                 two ::= four / 2
1104         
1105 ###### output: type_err_const2
1106         .tmp.code:3:8: error: const four cannot be resolved.
1107         .tmp.code:4:8: error: const two cannot be resolved.
1108
1109 ###### test: missing_program
1110         const
1111                 foo::="bar"
1112
1113 ###### output: missing_program
1114         oceani: no main function found.
1115
1116 ###### test: bad_main
1117         func main(foo:string)
1118                 print foo
1119
1120 ###### output: bad_main
1121         .tmp.code:2:10: error: expected []string but variable 'foo' is string
1122         .tmp.code:2:10: info: this is where 'foo' was set to string
1123         oceani: main has wrong type.
1124
1125 Test for type errors with functions
1126
1127 ###### test list
1128         oceani_failing_tests += func_err_args func_err_redeclare
1129
1130 ###### test: func_err_args
1131
1132         func test1(a:number; b:string; c:[3]Boolean)
1133                 print a, b, c[1]
1134
1135         func test2(a:number; b:string; c:[3]Boolean)
1136                 print a, b, c[1]
1137
1138         func test3()
1139                 # use undefined names
1140                 print a, z
1141
1142         func main()
1143                 truth:[3]Boolean
1144                 truth[1] = True
1145                 test1(1,"hello")
1146                 test1("hello",1)
1147                 test1(1, "two", truth)
1148                 test1(1, 2, truth)
1149                 test1(1, "lo", truth, 4)
1150                 print test(), test1(1,2,3)
1151                 if test1 == test2:
1152                         pass
1153
1154         func test4(a:number):string
1155                 use a * a
1156
1157         func test5(a:number):string
1158                 print a
1159
1160         struct foo
1161                 a: number
1162                 b:string = "hello"
1163
1164         func test6(a:number):foo
1165                 b:foo
1166                 b.a = a
1167                 use b
1168
1169         func test7(a:@number)
1170                 test7(45)
1171                 test7("45")
1172
1173 ###### output: func_err_args
1174         .tmp.code:40:14: error: cannot pass rval when reference expected
1175         .tmp.code:41:14: error: expected @number found string
1176         .tmp.code:34:5: error: function cannot return value of type foo
1177         .tmp.code:28:8: error: expected string, found none
1178         .tmp.code:25:8: error: expected string, found number
1179         .tmp.code:15:14: error: insufficient arguments to function.
1180         .tmp.code:16:14: error: expected number found string
1181         .tmp.code:16:22: error: expected string found number
1182         .tmp.code:16:14: error: insufficient arguments to function.
1183         .tmp.code:18:17: error: expected string found number
1184         .tmp.code:19:14: error: too many arguments to function.
1185         .tmp.code:20:14: error: attempt to call a non-function.
1186         .tmp.code:20:32: error: expected string found number
1187         .tmp.code:20:28: error: insufficient arguments to function.
1188         .tmp.code:21:20: error: expected "func test1" but variable 'test2' is "func test2"
1189         .tmp.code:5:5: info: this is where 'test2' was set to "func test2"
1190         .tmp.code:10:14: error: variable used but not declared: a
1191         .tmp.code:10:17: error: variable used but not declared: z
1192         oceani: type error in program - not running.
1193
1194 ###### test: func_err_redeclare
1195
1196         func test1(a:number; b:string; c:[3]Boolean)
1197                 print a, b, c[1]
1198
1199         func test1
1200         do
1201                 pass
1202
1203         func test1
1204                 b:Boolean
1205         do
1206                 pass
1207
1208 ###### output: func_err_redeclare
1209         .tmp.code:5:5: error: function 'test1' redeclared
1210         .tmp.code:2:5: info: this is where 'test1' was first declared
1211         .tmp.code:9:5: error: function 'test1' redeclared
1212         .tmp.code:2:5: info: this is where 'test1' was first declared
1213
1214 Test for errors with references
1215
1216 ###### test list
1217         oceani_failing_tests += ref_err1 ref_err2
1218
1219 ###### test: ref_err1
1220         func main()
1221                 ref:@number
1222                 @foo = ref
1223                 ref = @old()
1224                 if ref == @null:
1225                         print "null"
1226
1227 ###### output: ref_err1
1228         .tmp.code:4:9: error: only "@free" makes sense here: foo
1229         .tmp.code:5:15: error: Only reference function is "@new()": old
1230         .tmp.code:6:19: error: Only reference value is "@nil": null
1231
1232 ###### test: ref_err2
1233         func main()
1234                 ref:@number
1235                 ref2:@string
1236                 num:number = @new()
1237                 print num@
1238                 if num == @nil or ref == ref2 or ref == 2 or ref.foo:
1239                         @free = num
1240                 ref = 1
1241
1242 ###### output: ref_err2
1243         .tmp.code:5:22: error: @new() can only be used with references, not number
1244         .tmp.code:5:8: info: variable 'num' was set as number here.
1245         .tmp.code:6:14: error: Cannot dereference number
1246         .tmp.code:7:19: error: @nil can only be used with reference, not number
1247         .tmp.code:7:33: error: expected @number but variable 'ref2' is @string
1248         .tmp.code:4:8: info: this is where 'ref2' was set to @string
1249         .tmp.code:7:48: error: expected @number found number
1250         .tmp.code:7:53: error: field reference on number is not supported
1251         .tmp.code:7:56: error: have none but need Boolean
1252         .tmp.code:8:17: error: @free can only be assigned a reference, not number
1253         .tmp.code:8:17: error: @free can only be assigned a reference, not number
1254         .tmp.code:9:8: error: Cannot assign an rval to a reference.
1255         oceani: type error in program - not running.
1256
1257 ## Test erroneous command line args
1258
1259 To improve coverage, we want to test correct handling of strange command
1260 line arguments.  These tests won't use code, so the exiting test types
1261 won't work.  So we need to be able to explicitly give the command line,
1262 and the expected output, and have that tested and the coverage assessed.
1263 Rather than having to spell out the whole command name, just give "cmd",
1264 and discard that.  Requiring but discarding the command make an empty
1265 command list possible.
1266
1267 ###### test code
1268         @for t in $(oceani_special_tests); do \
1269             echo -n "Test $$t ... ";\
1270             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1271             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1272             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1273             if ! cmp -s .tmp.want .tmp.have; then \
1274                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1275             echo  "passed"; \
1276             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1277         done || true
1278 ###### valgrind test code
1279         @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1280             echo -n "Valgrind $$t.. "; \
1281             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1282             if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1283                [ $$? -eq 42 ]; then \
1284                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1285             if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1286                echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1287             if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1288                echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1289             echo " passed"; \
1290         done
1291
1292 ###### test list
1293         oceani_special_tests += "cmd"
1294         oceani_special_tests += "cmd,-zyx"
1295         oceani_special_tests += "cmd,nofile"
1296         oceani_special_tests += "cmd,/dev/null"
1297         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1298
1299 ###### output: cmd
1300         oceani: no input file given
1301
1302 ###### output: cmd,-zyx
1303         ./oceani: invalid option -- 'z'
1304         Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1305
1306 ###### output: cmd,nofile
1307         oceani: cannot open nofile
1308
1309 ###### output: cmd,/dev/null
1310         oceani: could not find any code in /dev/null
1311
1312 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1313         oceani: cannot find section toast:nothing