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