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