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