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