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