]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: move var_block_close() calls to the code sections that close the block
[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                 @ awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
82                     END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
83                          if (ran < (ran + skip) *0.956) exit(1) }' \
84                         coverage/oceani.mdc.gcov
85                 @rm -f .tmp*
86
87                 @[ -n "$$SKIP_VALGRIND" ] || for T in $(oceani_tests); do \
88                     echo -n "Valgrind $$T.. "; \
89                     i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
90                     if ! valgrind --error-exitcode=1 --log-file=.tmp.valg ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} \
91                          > /dev/null 2>&1 ; then \
92                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
93                     if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
94                        echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
95                     if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
96                        echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
97                     echo " passed"; \
98                 done
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
114                 argv:[argc::]string
115         do
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: 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                 if a > 0 and b > 0:
335                         while a != b:
336                                 if a < b:
337                                         b = b - a
338                                 else
339                                         a = a - b
340                         print "GCD of", A, "and", B,"is", a
341                 else if a <= 0:
342                         print a, "is not positive, cannot calculate GCD"
343                 else
344                         print b, "is not positive, cannot calculate GCD"
345
346                 for
347                         togo := 10
348                         f1 := 1; f2 := 1
349                         print "Fibonacci:", f1,f2,
350                 then togo = togo - 1
351                 while togo > 0:
352                         f3 := f1 + f2
353                         print "", f3,
354                         f1 = f2
355                         f2 = f3
356                 print ""
357
358                 if bbool:
359                         print astr ++ " was the str"
360                 else
361                         print "I found the str over " ++ astr
362
363                 /* Binary search... */
364                 for
365                         lo:= 0; hi := 100
366                         target := 77
367                 while
368                         mid := (lo + hi) / 2
369                         if mid == target:
370                                 use Found
371                         if mid < target:
372                                 lo = mid
373                         else
374                                 hi = mid
375                         if hi - lo < 1:
376                                 lo = mid
377                                 use GiveUp
378                         use True
379                 do pass
380                 case Found:
381                         print "Yay, I found", target
382                 case GiveUp:
383                         print "Closest I found was", lo
384
385 ###### output: sayhello,55,33,hello,True
386         Hello World, what lovely oceans you have!
387         Is 55 bigger than 33 ?  yes
388         double 33 is 66
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         GCD of 12 and 60 is 12
399         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
400         I found the str over there
401         Closest I found was 77.3438
402
403 ###### test list
404         oceani_tests += "insert_sort"
405 ###### test: insert_sort
406         func main()
407                 size::=55
408                 list:[size]number
409                 list[0] = 1_234
410                 for i:=1; then i = i + 1; while i < size:
411                         n := list[i-1] * list[i-1]
412                         list[i] = (n / 100) % 10000
413
414                 print "Before sort:"
415                 for i:=0; then i = i + 1; while i < size:
416                         print "list[",i,"]=",list[i]
417
418                 for i := 1; then i=i+1; while i < size:
419                         for j:=i-1; then j=j-1; while j >= 0:
420                                 if list[j] > list[j+1]:
421                                         t:= list[j]
422                                         list[j] = list[j+1]
423                                         list[j+1] = t
424                 print "After sort:"
425                 for i:=0; then i = i + 1; while i < size:
426                         print "list[",i,"]=",list[i]
427
428 ###### output: insert_sort
429         Before sort:
430         list[ 0 ]= 1234
431         list[ 1 ]= 5227
432         list[ 2 ]= 3215
433         list[ 3 ]= 3362
434         list[ 4 ]= 3030
435         list[ 5 ]= 1809
436         list[ 6 ]= 2724
437         list[ 7 ]= 4201
438         list[ 8 ]= 6484
439         list[ 9 ]= 422
440         list[ 10 ]= 1780
441         list[ 11 ]= 1684
442         list[ 12 ]= 8358
443         list[ 13 ]= 8561
444         list[ 14 ]= 2907
445         list[ 15 ]= 4506
446         list[ 16 ]= 3040
447         list[ 17 ]= 2416
448         list[ 18 ]= 8370
449         list[ 19 ]= 569
450         list[ 20 ]= 3237
451         list[ 21 ]= 4781
452         list[ 22 ]= 8579
453         list[ 23 ]= 5992
454         list[ 24 ]= 9040
455         list[ 25 ]= 7216
456         list[ 26 ]= 706
457         list[ 27 ]= 4984
458         list[ 28 ]= 8402
459         list[ 29 ]= 5936
460         list[ 30 ]= 2360
461         list[ 31 ]= 5696
462         list[ 32 ]= 4444
463         list[ 33 ]= 7491
464         list[ 34 ]= 1150
465         list[ 35 ]= 3225
466         list[ 36 ]= 4006
467         list[ 37 ]= 480
468         list[ 38 ]= 2304
469         list[ 39 ]= 3084
470         list[ 40 ]= 5110
471         list[ 41 ]= 1121
472         list[ 42 ]= 2566
473         list[ 43 ]= 5843
474         list[ 44 ]= 1406
475         list[ 45 ]= 9768
476         list[ 46 ]= 4138
477         list[ 47 ]= 1230
478         list[ 48 ]= 5129
479         list[ 49 ]= 3066
480         list[ 50 ]= 4003
481         list[ 51 ]= 240
482         list[ 52 ]= 576
483         list[ 53 ]= 3317
484         list[ 54 ]= 24
485         After sort:
486         list[ 0 ]= 24
487         list[ 1 ]= 240
488         list[ 2 ]= 422
489         list[ 3 ]= 480
490         list[ 4 ]= 569
491         list[ 5 ]= 576
492         list[ 6 ]= 706
493         list[ 7 ]= 1121
494         list[ 8 ]= 1150
495         list[ 9 ]= 1230
496         list[ 10 ]= 1234
497         list[ 11 ]= 1406
498         list[ 12 ]= 1684
499         list[ 13 ]= 1780
500         list[ 14 ]= 1809
501         list[ 15 ]= 2304
502         list[ 16 ]= 2360
503         list[ 17 ]= 2416
504         list[ 18 ]= 2566
505         list[ 19 ]= 2724
506         list[ 20 ]= 2907
507         list[ 21 ]= 3030
508         list[ 22 ]= 3040
509         list[ 23 ]= 3066
510         list[ 24 ]= 3084
511         list[ 25 ]= 3215
512         list[ 26 ]= 3225
513         list[ 27 ]= 3237
514         list[ 28 ]= 3317
515         list[ 29 ]= 3362
516         list[ 30 ]= 4003
517         list[ 31 ]= 4006
518         list[ 32 ]= 4138
519         list[ 33 ]= 4201
520         list[ 34 ]= 4444
521         list[ 35 ]= 4506
522         list[ 36 ]= 4781
523         list[ 37 ]= 4984
524         list[ 38 ]= 5110
525         list[ 39 ]= 5129
526         list[ 40 ]= 5227
527         list[ 41 ]= 5696
528         list[ 42 ]= 5843
529         list[ 43 ]= 5936
530         list[ 44 ]= 5992
531         list[ 45 ]= 6484
532         list[ 46 ]= 7216
533         list[ 47 ]= 7491
534         list[ 48 ]= 8358
535         list[ 49 ]= 8370
536         list[ 50 ]= 8402
537         list[ 51 ]= 8561
538         list[ 52 ]= 8579
539         list[ 53 ]= 9040
540         list[ 54 ]= 9768
541
542 ## Arrays
543
544 We already have some array tests, but this is where we put other
545 ad-hoc things array related.
546
547 ###### test list
548         oceani_tests += arrays
549
550 ###### test: arrays
551
552         func main()
553                 bools:[5]Boolean
554                 strings:[4]string
555
556                 bools[3] = strings[1] == "Hello"
557                 bools[1] = strings[2] <= "there"
558
559                 for i:=0; then i=i+1; while i<5:
560                         j ::= i
561                         ra:[j]number
562                         ra[i-1] = i*i
563                         ra[6] = 42 // mustn't crash
564                         print '', bools[i], ra[j-1],
565                 print
566
567 ###### output: arrays
568          False 0 True 1 False 4 False 9 False 16
569
570 ## Structures
571
572 Time to test if structure declarations and accesses work correctly.
573
574 ###### test list
575         oceani_tests += structs
576
577 ###### test: structs
578
579         const three ::= 3
580         struct foo
581                 size:[three]number
582                 name:string
583                 active:Boolean = True
584
585         struct baz { a:number; b:Boolean; }
586
587         func main
588         do
589                 info:[4]foo
590
591                 for i:=0; then i=i+1; while i < 4:
592                         switch i
593                         case 2: nm:= "peter"
594                         case 0: nm:= "bob"
595                         case 1: nm:= "jane"
596                         else    nm:= "janine"
597
598                         info[i].name = nm
599                         info[i].size[0] = i*i
600                         if nm != "jane":
601                                 info[i].active = False
602
603                 for i:=0; then i=i+1; while i < 4:
604                         print info[i].name, info[i].active, info[i].size[0]
605
606 ###### output: structs
607
608         bob False 0
609         jane True 1
610         peter False 4
611         janine False 9
612
613 ## Test code with syntax errors
614
615 Syntax errors aren't handled well yet - the result is almost always a
616 single message about the first error.  So this section will be fairly
617 thin until we add proper parsing recovery in the face of common errors.
618
619 A special case of syntax errors is token errors, when a token is only
620 accepted because the parser doesn't know quite enough to reject it.
621 There are handled better as they are quite local, so a single test
622 program can trigger most of the possible errors.
623
624 To handle erronous code, we need a different set of tests, as we need to
625 capture `stderr`. The same test code will be used for type errors too.
626 As error messages contain the line number, and we don't want changes to
627 this file to change the reported numbers, we copy the code into a
628 separate file first, then run from there.
629
630 ###### test code
631         @for t in $(oceani_failing_tests); do \
632             echo -n "Test $$t ... "; \
633             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
634             echo '``````' > .tmp.code; \
635             ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
636             ./oceani .tmp.code > .tmp.have 2>&1; \
637             if ! cmp -s .tmp.want .tmp.have; then \
638                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
639             echo  "passed"; \
640             ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
641         done || true
642
643 ###### test list
644         oceani_failing_tests := syn1
645         oceani_failing_tests += tokerr
646
647 ###### test: syn1
648
649         func main()
650                 if then else while do
651
652 ###### output: syn1
653         .tmp.code:3:11: Syntax error in statement: then
654
655 ###### test: tokerr
656         func main()
657                 a := 1i  // imaginary numbers aren't understood
658                 b:[2i]number // array sizes are handled separately
659                 c:[3.14159]Boolean // array sizes must be integers
660                 d:[1_000_000_000_000]number // they mustn't be huge
661                 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
662
663                 multi := """
664                 This is a multiline string
665                 With an unsupportable suffix
666                 """Aa
667
668                 xx:unknown = 24
669                 yy:[unknowable]number
670                 zzsize := 4
671                 zz:[zzsize]string // size must be constant, use ::=
672
673                 // These numbers should be bad in all contexts: FIXME
674                 aa:[00123]number
675
676 ###### output: tokerr
677         .tmp.code:3:13: error: unsupported number suffix: 1i
678         .tmp.code:4:11: error: unsupported number suffix: 2i
679         .tmp.code:5:11: error: array size must be an integer: 3.14159
680         .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
681         .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
682         .tmp.code:9:17: error: unsupported string suffix: """
683                 This is a multiline string
684                 With an unsupportable suffix
685                 """Aa
686         .tmp.code:14:11: error: undefined type: unknown
687         .tmp.code:15:12: error: name undeclared: unknowable
688         .tmp.code:17:12: error: array size must be a constant: zzsize
689         .tmp.code:20:12: error: unrecognised number: 00123
690
691 ## Tests for type errors
692
693 Type error don't cause parsing to abort, so we can fit many in the
694 one test program.  Some type errors are found during the parse, others
695 during type analysis which doesn't run if parsing failed.  So we cannot
696 fit everything in one.
697
698 These programs were generated by looking for the
699 various places that `type_err()` are called.
700
701 ###### test list
702         oceani_failing_tests += type_err1 type_err2 type_err3 type_err4
703
704 ###### test: type_err1
705
706         func main()
707                 print "hello" ++ 5, 5 ++ "hello"
708
709                 b ::= 3
710                 b = b + 1
711
712                 if 3 * 4 and not True: print "Weird"
713
714 ###### output: type_err1
715         .tmp.code:3:25: error: expected string found number
716         .tmp.code:3:28: error: expected string found number
717         .tmp.code:6:8: error: Cannot assign to a constant: b
718         .tmp.code:5:8: info: name was defined as a constant here
719         .tmp.code:6:8: error: Cannot assign to a constant: b
720         .tmp.code:5:8: info: name was defined as a constant here
721         .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
722         oceani: type error in program - not running.
723
724 ###### test: type_err2
725
726         func main()
727                 a := 1
728                 a := 2
729                 a ::= 3
730                 a:number = 4
731                 a ::number = 5
732                 c:
733
734 ###### output: type_err2
735         .tmp.code:4:8: error: variable 'a' redeclared
736         .tmp.code:3:8: info: this is where 'a' was first declared
737         .tmp.code:5:8: error: variable 'a' redeclared
738         .tmp.code:3:8: info: this is where 'a' was first declared
739         .tmp.code:6:8: error: variable 'a' redeclared
740         .tmp.code:3:8: info: this is where 'a' was first declared
741         .tmp.code:7:8: error: variable 'a' redeclared
742         .tmp.code:3:8: info: this is where 'a' was first declared
743         .tmp.code:8:8: Variable declared with no type or value: c
744
745 ###### test: type_err3
746
747         struct foo
748                 a: number
749                 b:string = "hello"
750
751         func main()
752                 c := "hello"
753                 c = c + 1
754                 c = "hello" ++ (True and False)
755                 c = 4 < 5
756                 print 45 + ( "Hello" ++ "there")
757                 c[5] = 1
758
759                 while
760                         use 1
761                         use True
762                         use "Hello"
763                 do
764                         print
765                 case 1: print "one"
766                 case "Hello": print "Hello"
767
768                 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
769                 a1 = a2
770                 a1 = a3
771                 a1 = a4
772                 a1[2] = "hello"
773                 a4[1] = True
774                 c = a2[3]
775
776                 bar:foo
777                 foo.c = 43
778                 print c.foo
779                 print bar.c
780                 print bar.b + 42
781                 
782
783                 // trigger 'labels not permitted' error message
784                 while 1 if True else False:
785                         print
786                 case 2: print "two"
787
788 ###### output: type_err3
789         .tmp.code:8:12: error: expected number but variable 'c' is string
790         .tmp.code:7:8: info: this is where 'c' was set to string
791         .tmp.code:8:12: error: Arithmetic returns number but string expected
792         .tmp.code:7:8: info: variable 'c' was set as string here.
793         .tmp.code:9:24: error: Boolean operation found where string expected
794         .tmp.code:10:12: error: Comparison returns Boolean but string expected
795         .tmp.code:7:8: info: variable 'c' was set as string here.
796         .tmp.code:11:21: error: Concat returns string but number expected
797         .tmp.code:12:8: error: string cannot be indexed
798         .tmp.code:12:8: error: string cannot be indexed
799         .tmp.code:21:13: error: expected number found string
800         .tmp.code:17:16: error: expected number, found string
801         .tmp.code:24:8: error: cannot assign value of type [5]number
802         .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
803         .tmp.code:23:36: info: this is where 'a3' was set to [10]number
804         .tmp.code:25:8: error: cannot assign value of type [5]number
805         .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
806         .tmp.code:23:51: info: this is where 'a4' was set to [5]string
807         .tmp.code:26:8: error: cannot assign value of type [5]number
808         .tmp.code:27:16: error: expected number found string
809         .tmp.code:28:16: error: expected string found Boolean
810         .tmp.code:29:12: error: have number but need string
811         .tmp.code:7:8: info: variable 'c' was set as string here.
812         .tmp.code:32:8: error: variable used but not declared: foo
813         .tmp.code:32:8: error: field reference attempted on none, not a struct
814         .tmp.code:32:16: error: expected none found number
815         .tmp.code:33:14: error: field reference attempted on string, not a struct
816         .tmp.code:34:14: error: cannot find requested field in foo
817         .tmp.code:35:17: error: have string but need number
818         .tmp.code:39:29: error: expected number (labels not permitted) found Boolean
819         oceani: type error in program - not running.
820
821 ###### test: type_err4
822         func main()
823                 a:=1; b=2; c::=3
824                 print a, b, c
825
826 ###### output: type_err4
827         .tmp.code:3:14: error: variable used but not declared: b
828         .tmp.code:3:16: error: expected none found number
829         .tmp.code:3:14: info: variable 'b' was set as none here.
830         oceani: type error in program - not running.
831
832 ###### test list
833         oceani_failing_tests += type_err_const type_err_const1 missing_program
834
835 ###### test: type_err_const
836         const
837                 foo :: number = 45
838                 bar ::= "string" + 56
839         const
840                 bar ::= "baz"
841         func main()
842                 foo := 4
843                 print foo, bar
844
845         // trigger duplicate-main error
846         func main()
847                 foo := 6
848                 print bar, foo
849
850 ###### output: type_err_const
851         .tmp.code:4:16: error: expected number found string
852         .tmp.code:6:8: error: name already declared: bar
853         .tmp.code:4:8: info: this is where 'bar' was first declared
854         .tmp.code:8:8: error: variable 'foo' redeclared
855         .tmp.code:3:8: info: this is where 'foo' was first declared
856         .tmp.code:13:8: error: variable 'foo' redeclared
857         .tmp.code:3:8: info: this is where 'foo' was first declared
858         .tmp.code:13:8: "main" defined a second time
859
860 ###### test: type_err_const1
861         const
862                 foo : number = 45
863                 bar := "string"
864         func main()
865                 foo := 4
866                 print foo, bar
867
868 ###### output: type_err_const1
869         .tmp.code:3:12: Syntax error in constant: :
870         .tmp.code:4:12: Syntax error in constant: :
871
872 ###### test: missing_program
873         const
874                 foo::="bar"
875
876 ###### output: missing_program
877         oceani: no main function found.
878
879 ## Test erroneous command line args
880
881 To improve coverage, we want to test correct handling of strange command
882 line arguments.  These tests won't use code, so the exiting test types
883 won't work.  So we need to be able to explicitly give the command line,
884 and the expected output, and have that tested and the coverage assessed.
885 Rather than having to spell out the whole command name, just give "cmd",
886 and discard that.  Requiring but discarding the command make an empty
887 command list possible.
888
889 ###### test code
890         @for t in $(oceani_special_tests); do \
891             echo -n "Test $$t ... ";\
892             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
893             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
894             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
895             if ! cmp -s .tmp.want .tmp.have; then \
896                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
897             echo  "passed"; \
898             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
899         done || true
900
901 ###### test list
902         oceani_special_tests += "cmd"
903         oceani_special_tests += "cmd,-zyx"
904         oceani_special_tests += "cmd,nofile"
905         oceani_special_tests += "cmd,/dev/null"
906         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
907
908 ###### output: cmd
909         oceani: no input file given
910
911 ###### output: cmd,-zyx
912         ./oceani: invalid option -- 'z'
913         Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
914
915 ###### output: cmd,nofile
916         oceani: cannot open nofile
917
918 ###### output: cmd,/dev/null
919         oceani: could not find any code in /dev/null
920
921 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
922         oceani: cannot find section toast:nothing