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