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