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