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