]> ocean-lang.org Git - ocean/blob - csrc/oceani-tests.mdc
oceani: add a 'length' operator
[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         oceani_valg_tests := $(oceani_tests)
39         ## combine test lists
40
41         tests:: oceani_test_suite
42         oceani_test_suite: oceani coverage_oceani
43                 @echo -n Checking grammar ...
44                 @./parsergen --report --LALR --tag Parser oceani.mdc | grep " - no conflicts" > /dev/null || \
45                     { echo "Grammar contains conflicts, please review" ; exit 1; }
46                 @echo ok
47                 @rm -rf coverage; mkdir -p coverage
48                 @cp *.gcno coverage
49                 @for T in $(oceani_tests); do \
50                     echo -n "Test $$T.. "; \
51                     i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
52                     ./md2c oceani-tests.mdc "output: $$T" | grep -v '^#' > .tmp.want; \
53                     ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} > .tmp.have; \
54                     if ! cmp -s .tmp.want .tmp.have; then \
55                        echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
56                     echo -n "printing.. "; \
57                     echo '``````' > .tmp.code1; echo '``````' > .tmp.code2 ;\
58                     ./oceani --noexec --print --section "test: $$t" oceani-tests.mdc >> .tmp.code1; \
59                     ./oceani --noexec --print .tmp.code1 >> .tmp.code2 || exit 1;\
60                     if ! cmp -s .tmp.code1 .tmp.code2; then \
61                        echo " Failed"; diff -u .tmp.code1 .tmp.code2; exit 1 ; fi ; \
62                     echo -n "extra-newlines.. "; \
63                     sed -e 'i\
64                     ' .tmp.code1 > .tmp.code1a; \
65                     echo '``````' > .tmp.code2a ;\
66                     ./oceani --noexec --print .tmp.code1a >> .tmp.code2a || exit 1;\
67                     if ! cmp -s .tmp.code1 .tmp.code2a; then \
68                        echo " Failed"; diff -u .tmp.code1 .tmp.code2a; exit 1; fi ; \
69                     echo -n "exec-after-print.. "; \
70                     ./oceani .tmp.code1 $${1+"$$@"} > .tmp.have ; \
71                     if ! cmp -s .tmp.want .tmp.have; then \
72                        echo " FAILED"; diff -u .tmp.want .tmp.have; exit 1;fi; \
73                     echo " all passed"; \
74                     ./coverage_oceani --print --section "test: $$t" oceani-tests.mdc $${1+"$$@"} > /dev/null ; \
75                     ./coverage_oceani -tpbn --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1; \
76                 done
77
78                 ## test code
79
80                 @for i in coverage/#*.gcda; do mv $$i coverage/$${i##*#}; done
81                 @gcov -o coverage oceani.mdc > /dev/null 2> /dev/null
82                 @mv *.gcov coverage ; [ -f .gcov ] && mv .gcov coverage || true
83                 @rm -f .tmp*
84
85                 @[ -n "$$SKIP_VALGRIND" ] || for T in $(oceani_valg_tests); do \
86                     echo -n "Valgrind $$T.. "; \
87                     i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
88                     if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} \
89                          > /dev/null 2>&1 ; [ $$? -eq 42 ] ; then \
90                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
91                     if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
92                        echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
93                     if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
94                        echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
95                     echo " passed"; \
96                 done
97                 ## valgrind test code
98                 @[ -n "$$SKIP_COVERAGE_CHECK" ] || awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
99                     END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
100                          if (ran < (ran + skip) *0.972) exit(1) }' \
101                         coverage/oceani.mdc.gcov
102
103         coverage_oceani: oceani.c
104                 $(CC) $(CFLAGS) --coverage -fprofile-dir=coverage -o coverage_oceani oceani.c $(LDLIBS)
105
106 ## Values and variables
107
108 The first test stores values in variables and performs various
109 calculations on them.
110
111 ###### test list
112        oceani_tests += "valvar"
113
114 ###### test: valvar
115
116         func main(argv:[argc::]string)
117                 a := 23; b:=12 ; b1 := -b
118                 print a, b, a+b, a-b, a*b, a/b, a%b
119                 print a<b, a<=b, a>b, a>=b, a<a, a==b, a==a
120                 print +a, +b, +b1, -a, -b, -b1
121                 x := True; y := False
122                 print x and y, x or y, x and x, y or y, x and not x, x < y
123
124                 c ::= "This is a string"
125                 d ::= " field theory"
126                 print c, d, c++d
127
128                 aconst :: string = "unchanging"
129
130                 // Check wrapping
131                 print
132                   a + b
133                   + (a*2)
134                   + b1
135                   + b
136
137                 z:Boolean= True
138                 w:Boolean = False
139                 if ?z:
140                         print "w??z", w??z
141                         print "z??w", z??w
142                         print "z??False", z??False, "w??False", w??False
143                 if ?w:
144                         print "Weird?"
145
146 ###### output: valvar
147
148         23 12 35 11 276 1.916666667 11
149         False False True True False False True
150         23 12 12 -23 -12 12
151         False True True False False False
152         This is a string  field theory This is a string field theory
153         81
154         w??z True
155         z??w True
156         z??False True w??False False
157
158 Next we change the value of variables
159
160 ###### test list
161        oceani_tests += "setvar"
162
163 ###### test: setvar
164
165         func main()
166                 a := 4
167                 a = a * a
168                 a = (a + a) * (a + a)
169                 a = a * a * a
170                 print a, a/a
171
172 ###### output: setvar
173         1073741824 1
174
175 Now some contants
176
177 ###### test list
178         oceani_tests += "consts"
179
180 ###### test: consts
181         const
182                 tau ::= pi * 2
183                 pi ::= 3.141 592 653
184                 four ::= 2 + 2 ; five ::= 10/2
185         const pie ::= "I like Pie";
186                 cake ::= "The cake is"
187                   ++ " a lie"
188
189         func main()
190                 print "Hello World, what lovely oceans you have!"
191                 print "are there", five, "?"
192                 print pi, pie, "but", cake, "Tau is", tau
193
194 ###### output: consts
195         Hello World, what lovely oceans you have!
196         are there 5 ?
197         3.141592653 I like Pie but The cake is a lie Tau is 6.283185306
198
199 Test merging of variables from multiple cases
200
201 ###### test list
202         oceani_tests += varmerge
203
204 ###### test: varmerge
205
206         func main()
207                 for i:=0; then i=i+1; while i < 5:
208                         switch i:
209                                 case 0: num:="zero"
210                                 case 1: scratch:=42; num:="one"
211                                 case 2: num:="two"
212                                 case 3: num:="three"
213                                 else    num:="many"
214                         print num,", ",
215                 print
216
217                 for i:=0; then i=i+1; while i < 5:
218                         switch i:
219                                 case 0: num:="zero"
220                                 case 1: num:="one"
221                                 case 2: num:="two"
222                                 case 3: num:="three"
223                                 else    num:="many"
224                         // re-declare a CondScope variable
225                         num := i*i
226                         print num,", ",
227                 print
228
229 ###### output: varmerge
230         zero , one , two , three , many , 
231         0 , 1 , 4 , 9 , 16 , 
232
233 ## Conditions and Loops
234
235 Now we need to test if/else and some different loops
236
237 ###### test list
238        oceani_tests += cond_loop
239
240 ###### test: cond_loop
241
242         func main()
243                 a := 4
244                 if a < 5:
245                         print "Success"
246                 else
247                         print "Failure"
248                 for b:=1; then b=b+b; while b < 100:
249                         print '', b,
250                 print
251                 // Newtons method for square root of 2
252                 target ::= 2
253                 guess := target
254                 for
255                         count: number = 0
256                 while
257                         current := guess * guess
258                         use +(current - target) > 0.000000001
259                 do
260                         guess = (guess + (target / guess) ) / 2
261                         print count, guess
262                         count = count + 1
263                 print "error is ", target - guess * guess
264
265                 for j:=0; then j = j+3 ; while j < 10:
266                         if j != 0 and then 20 / j > 3:
267                                 print "20 /", j," =", 20 / j
268                         else
269                                 print "I won't calculate 20 /", j
270                 pi ::= 3.1415926535897
271                 if 355/113 == pi or else +(pi - 355/113) < 0.001:
272                         print "Close enough"
273                 print "lower" if 355/113 < pi else "higher"
274
275                 if pi > 3 then print "pi exceeds three"; else print "need more pie"
276                 if (pi < 3) { print "not enough pi" } else { print "pi sufficient" }
277                 for { i := 0; sum := 0 }
278                 then { i = i+1 }
279                 while i <= 10:
280                         sum = sum + i
281                 else
282                         pass
283                         print "sum 1..10 is", sum
284                 if
285                         PI1 := 22/7
286                         use PI1 < pi
287                 then
288                         print "Smaller"
289                 else
290                         print 'larger'
291
292 ###### output: cond_loop
293         Success
294          1 2 4 8 16 32 64
295         0 1.5
296         1 1.416666667
297         2 1.414215686
298         3 1.414213562
299         error is  -4.510950445e-12
300         I won't calculate 20 / 0
301         20 / 3  = 6.666666667
302         20 / 6  = 3.333333333
303         I won't calculate 20 / 9
304         Close enough
305         higher
306         pi exceeds three
307         pi sufficient
308         sum 1..10 is 55
309         larger
310
311 ## Say Hello
312
313 The demonstration code presented in the interpreted is suitable for the test suite.
314 Here I break it into two parts, keeping the array code separate.
315
316 ###### test list
317         oceani_tests += "sayhello,55,33,hello,True"
318         oceani_tests += "sayhello,12,60,there,False"
319
320 ###### test: sayhello
321
322         func main(av:[ac::number]string)
323                 A := $av[1]; B := $av[2]
324                 astr := av[3]
325                 l := av[]
326                 bbool := av[l-1] == "True"
327                 print "Hello World, what lovely oceans you have!"
328                 /* When a variable is defined in both branches of an 'if',
329                  * and used afterwards, the variables are merged.
330                  */
331                 if A > B:
332                         bigger := "yes"
333                 else
334                         bigger := "no"
335                 print "Is", A, "bigger than", B,"? ", bigger
336                 /* If a variable is not used after the 'if', no
337                  * merge happens, so types can be different
338                  */
339                 if A > B * 2:
340                         double:string = "yes"
341                         print A, "is more than twice", B, "?", double
342                 else
343                         double := B*2
344                         print "double", B, "is", double
345
346                 a : number
347                 a = A;
348                 b:number = B
349                 print "still", bigger // check for regression in scoping
350                 if a > 0 and b > 0:
351                         while a != b:
352                                 if a < b:
353                                         b = b - a
354                                 else
355                                         a = a - b
356                         print "GCD of", A, "and", B,"is", a
357                 else if a <= 0:
358                         print a, "is not positive, cannot calculate GCD"
359                 else
360                         print b, "is not positive, cannot calculate GCD"
361
362                 for
363                         togo := 10
364                         f1 := 1; f2 := 1
365                         print "Fibonacci:", f1,f2,
366                 then togo = togo - 1
367                 while togo > 0:
368                         f3 := f1 + f2
369                         print "", f3,
370                         f1 = f2
371                         f2 = f3
372                 print ""
373
374                 if bbool:
375                         print astr ++ " was the str"
376                 else
377                         print "I found the str over " ++ astr
378
379                 /* Binary search... */
380                 for
381                         lo:= 0; hi := 100
382                         target := 77
383                 while
384                         mid := (lo + hi) / 2
385                         if mid == target:
386                                 use .Found
387                         if mid < target:
388                                 lo = mid
389                         else
390                                 hi = mid
391                         if hi - lo < 1:
392                                 lo = mid
393                                 use .GiveUp
394                         use True
395                 do pass
396                 case .Found:
397                         print "Yay, I found", target
398                 case .GiveUp:
399                         print "Closest I found was", lo
400
401 ###### output: sayhello,55,33,hello,True
402         Hello World, what lovely oceans you have!
403         Is 55 bigger than 33 ?  yes
404         double 33 is 66
405         still yes
406         GCD of 55 and 33 is 11
407         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
408         hello was the str
409         Closest I found was 77.34375
410
411 ###### output: sayhello,12,60,there,False
412         Hello World, what lovely oceans you have!
413         Is 12 bigger than 60 ?  no
414         double 60 is 120
415         still no
416         GCD of 12 and 60 is 12
417         Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
418         I found the str over there
419         Closest I found was 77.34375
420
421 ###### test list
422         oceani_tests += "insert_sort"
423 ###### test: insert_sort
424         func main()
425                 size::=55
426                 list:[size]number
427                 list[0] = 1_234
428                 for i:=1; then i = i + 1; while i < size:
429                         n := list[i-1] * list[i-1]
430                         list[i] = (n / 100) % 10000
431
432                 print "Before sort:"
433                 for i:=0; then i = i + 1; while i < size:
434                         print "list[",i,"]=",list[i]
435
436                 for i := 1; then i=i+1; while i < size:
437                         for j:=i-1; then j=j-1; while j >= 0:
438                                 if list[j] > list[j+1]:
439                                         t:= list[j]
440                                         list[j] = list[j+1]
441                                         list[j+1] = t
442                 print "After sort:"
443                 for i:=0; then i = i + 1; while i < size:
444                         print "list[",i,"]=",list[i]
445
446 ###### output: insert_sort
447         Before sort:
448         list[ 0 ]= 1234
449         list[ 1 ]= 5227
450         list[ 2 ]= 3215
451         list[ 3 ]= 3362
452         list[ 4 ]= 3030
453         list[ 5 ]= 1809
454         list[ 6 ]= 2724
455         list[ 7 ]= 4201
456         list[ 8 ]= 6484
457         list[ 9 ]= 422
458         list[ 10 ]= 1780
459         list[ 11 ]= 1684
460         list[ 12 ]= 8358
461         list[ 13 ]= 8561
462         list[ 14 ]= 2907
463         list[ 15 ]= 4506
464         list[ 16 ]= 3040
465         list[ 17 ]= 2416
466         list[ 18 ]= 8370
467         list[ 19 ]= 569
468         list[ 20 ]= 3237
469         list[ 21 ]= 4781
470         list[ 22 ]= 8579
471         list[ 23 ]= 5992
472         list[ 24 ]= 9040
473         list[ 25 ]= 7216
474         list[ 26 ]= 706
475         list[ 27 ]= 4984
476         list[ 28 ]= 8402
477         list[ 29 ]= 5936
478         list[ 30 ]= 2360
479         list[ 31 ]= 5696
480         list[ 32 ]= 4444
481         list[ 33 ]= 7491
482         list[ 34 ]= 1150
483         list[ 35 ]= 3225
484         list[ 36 ]= 4006
485         list[ 37 ]= 480
486         list[ 38 ]= 2304
487         list[ 39 ]= 3084
488         list[ 40 ]= 5110
489         list[ 41 ]= 1121
490         list[ 42 ]= 2566
491         list[ 43 ]= 5843
492         list[ 44 ]= 1406
493         list[ 45 ]= 9768
494         list[ 46 ]= 4138
495         list[ 47 ]= 1230
496         list[ 48 ]= 5129
497         list[ 49 ]= 3066
498         list[ 50 ]= 4003
499         list[ 51 ]= 240
500         list[ 52 ]= 576
501         list[ 53 ]= 3317
502         list[ 54 ]= 24
503         After sort:
504         list[ 0 ]= 24
505         list[ 1 ]= 240
506         list[ 2 ]= 422
507         list[ 3 ]= 480
508         list[ 4 ]= 569
509         list[ 5 ]= 576
510         list[ 6 ]= 706
511         list[ 7 ]= 1121
512         list[ 8 ]= 1150
513         list[ 9 ]= 1230
514         list[ 10 ]= 1234
515         list[ 11 ]= 1406
516         list[ 12 ]= 1684
517         list[ 13 ]= 1780
518         list[ 14 ]= 1809
519         list[ 15 ]= 2304
520         list[ 16 ]= 2360
521         list[ 17 ]= 2416
522         list[ 18 ]= 2566
523         list[ 19 ]= 2724
524         list[ 20 ]= 2907
525         list[ 21 ]= 3030
526         list[ 22 ]= 3040
527         list[ 23 ]= 3066
528         list[ 24 ]= 3084
529         list[ 25 ]= 3215
530         list[ 26 ]= 3225
531         list[ 27 ]= 3237
532         list[ 28 ]= 3317
533         list[ 29 ]= 3362
534         list[ 30 ]= 4003
535         list[ 31 ]= 4006
536         list[ 32 ]= 4138
537         list[ 33 ]= 4201
538         list[ 34 ]= 4444
539         list[ 35 ]= 4506
540         list[ 36 ]= 4781
541         list[ 37 ]= 4984
542         list[ 38 ]= 5110
543         list[ 39 ]= 5129
544         list[ 40 ]= 5227
545         list[ 41 ]= 5696
546         list[ 42 ]= 5843
547         list[ 43 ]= 5936
548         list[ 44 ]= 5992
549         list[ 45 ]= 6484
550         list[ 46 ]= 7216
551         list[ 47 ]= 7491
552         list[ 48 ]= 8358
553         list[ 49 ]= 8370
554         list[ 50 ]= 8402
555         list[ 51 ]= 8561
556         list[ 52 ]= 8579
557         list[ 53 ]= 9040
558         list[ 54 ]= 9768
559
560 ## Arrays
561
562 We already have some array tests, but this is where we put other
563 ad-hoc things array related.
564
565 ###### test list
566         oceani_tests += arrays
567
568 ###### test: arrays
569
570         func main()
571                 bools:[5]Boolean
572                 strings:[4]string
573
574                 bools[3] = strings[1] == "Hello"
575                 bools[1] = strings[2] <= "there"
576
577                 for i:=0; then i=i+1; while i<5:
578                         j ::= i
579                         ra:[j]number
580                         ra[i-1] = i*i
581                         ra[6] = 42 // mustn't crash
582                         print '', bools[i], ra[j-1],
583                 print
584
585 ###### output: arrays
586          False 0 True 1 False 4 False 9 False 16
587
588 ## Structures
589
590 Time to test if structure declarations and accesses work correctly.
591
592 ###### test list
593         oceani_tests += structs
594
595 ###### test: structs
596
597         const three ::= 3
598         struct foo
599                 size:[three]number
600                 name:string
601                 thing:baz
602                 active:Boolean = True
603
604         struct baz { a:number; b:Boolean; }
605
606         func main
607         do
608                 info:[4]foo
609
610                 for i:=0; then i=i+1; while i < 4:
611                         switch i
612                         case 2: nm:= "peter"
613                         case 0: nm:= "bob"
614                         case 1: nm:= "jane"
615                         else    nm:= "janine"
616
617                         info[i].name = nm
618                         info[i].size[0] = i*i
619                         if nm != "jane":
620                                 info[i].active = False
621
622                 for i:=0; then i=i+1; while i < 4:
623                         print info[i].name, info[i].active, info[i].size[0]
624                 info[0].thing.b = True
625
626 ###### output: structs
627
628         bob False 0
629         jane True 1
630         peter False 4
631         janine False 9
632
633 ## Functions
634
635 Test functions.  They don't return anything, so we need to get them to print
636
637 ###### test list
638         oceani_tests += functions func_ret_type
639
640 ###### test: functions
641
642         func test1
643                 t: Boolean
644         do
645                 if t:
646                         print "true"
647
648         func noarg
649         do
650                 pass
651
652         func twoarg
653                 a:number
654                 b:string
655         do
656                 while a > 0:
657                         print b
658                         a = a - 1
659
660         func test(n:number; s:string)
661                 if n >= 1:
662                         print n,s,
663                         test(n-1, "."++s)
664                 else
665                         print "done"
666
667         func to_polar
668                 x:number; y:number
669         return
670                 rho:number
671                 theta:number
672         do
673                 rho = x + y
674                 theta = x - y
675
676         func main()
677                 for i:=0; then i = i + 1; while i < 5:
678                         test(i, " ")
679                 angular := to_polar(32, 23)
680                 print angular.rho, angular.theta
681
682 ###### output: functions
683         done
684         1  done
685         2  1 . done
686         3  2 . 1 .. done
687         4  3 . 2 .. 1 ... done
688         55 9
689
690 ###### test: func_ret_type
691
692         func double(n:number):number
693                 use n+n
694
695         func answer
696                 prefix:string
697                 suffix:string
698         return string
699         do
700                 use prefix ++ suffix
701
702         func noarg_returns
703         return Boolean
704         do
705                 use 22/7 == 3.14159
706
707         func main()
708                 for j:=10; then j = j - 3; while j > -5:
709                         print answer("dou","ble"), j, "is", double(j)
710
711 ###### output: func_ret_type
712         double 10 is 20
713         double 7 is 14
714         double 4 is 8
715         double 1 is 2
716         double -2 is -4
717
718 ## References
719
720 A simple linked list example
721
722 ###### test list
723         oceani_tests += "linked_list,one,two,three,four"
724
725 ###### test: linked_list
726
727         struct linkage
728                 next: @node
729         struct node
730                 list: linkage
731                 this: string
732
733         func insert(list:@linkage; new:string)
734                 p:=list
735                 while ?p.next and then p.next.this < new:
736                         p = p.next.list
737                 t:@node = @new()
738                 t.list.next = p.next
739                 t.this = new
740                 p.next = t;
741
742         func printlist(list:@linkage)
743                 while ?list.next:
744                         print list@.next.this
745                         list = list@.next.list
746
747         func freelist(list:@linkage)
748                 if list.next != @nil:
749                         lp:@linkage = list.next.list
750                         freelist(lp)
751                         @free = list.next
752
753         func main(argv:[ac::]string)
754                 list : linkage
755
756                 insert(list, "@start");
757                 insert(list, "~end")
758                 for i:=1; then i=i+1; while i < argv[]:
759                         insert(list, argv[i])
760                 insert(list, "Hello!")
761                 printlist(list)
762                 freelist(list)
763
764 ###### output: linked_list,one,two,three,four
765         @start
766         Hello!
767         four
768         one
769         three
770         two
771         ~end
772
773 ## Test code with syntax errors
774
775 Syntax errors aren't handled well yet - the result is almost always a
776 single message about the first error.  So this section will be fairly
777 thin until we add proper parsing recovery in the face of common errors.
778
779 A special case of syntax errors is token errors, when a token is only
780 accepted because the parser doesn't know quite enough to reject it.
781 There are handled better as they are quite local, so a single test
782 program can trigger most of the possible errors.
783
784 To handle erronous code, we need a different set of tests, as we need to
785 capture `stderr`. The same test code will be used for type errors too.
786 As error messages contain the line number, and we don't want changes to
787 this file to change the reported numbers, we copy the code into a
788 separate file first, then run from there.
789
790 ###### test code
791         @for t in $(oceani_failing_tests); do \
792             echo -n "Test $$t ... "; \
793             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
794             echo '``````' > .tmp.code; \
795             ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
796             ./oceani .tmp.code > .tmp.have 2>&1; \
797             if ! cmp -s .tmp.want .tmp.have; then \
798                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
799             echo  "passed"; \
800             ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
801         done || true
802
803 ###### combine test lists
804         oceani_valg_tests += $(oceani_failing_tests)
805
806 ###### test list
807         oceani_failing_tests := syn1
808         oceani_failing_tests += tokerr
809
810 ###### test: syn1
811
812         func main()
813                 if then else while do
814
815 ###### output: syn1
816         .tmp.code:3:11: Syntax error in statement: then
817
818 ###### test: tokerr
819         func main()
820                 a := 1i  // imaginary numbers aren't understood
821                 b:[2i]number // array sizes are handled separately
822                 c:[3.14159]Boolean // array sizes must be integers
823                 d:[1_000_000_000_000]number // they mustn't be huge
824                 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
825
826                 multi := """
827                 This is a multiline string
828                 With an unsupportable suffix
829                 """Aa
830
831                 xx:unknown = 24
832                 yy:[unknowable]number
833                 zzsize := 4
834                 zz:[zzsize]string // size must be constant, use ::=
835
836                 // These numbers should be bad in all contexts: FIXME
837                 aa:[00123]number
838
839 ###### output: tokerr
840         .tmp.code:3:13: error: unsupported number suffix: 1i
841         .tmp.code:4:11: error: unsupported number suffix: 2i
842         .tmp.code:5:11: error: array size must be an integer: 3.14159
843         .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
844         .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
845         .tmp.code:9:17: error: unsupported string suffix: """
846                 This is a multiline string
847                 With an unsupportable suffix
848                 """Aa
849         .tmp.code:15:12: error: name undeclared: unknowable
850         .tmp.code:17:12: error: array size must be a constant: zzsize
851         .tmp.code:20:12: error: unrecognised number: 00123
852         .tmp.code:14:11: error: type used but not declared: unknown
853
854 ## Tests for type errors
855
856 Type error don't cause parsing to abort, so we can fit many in the
857 one test program.  Some type errors are found during the parse, others
858 during type analysis which doesn't run if parsing failed.  So we cannot
859 fit everything in one.
860
861 These programs were generated by looking for the
862 various places that `type_err()` are called.
863
864 ###### test list
865         oceani_failing_tests += type_err1 type_err2 type_err3 type_err4 type_err5 type_err6
866
867 ###### test: type_err1
868
869         func main()
870                 print "hello" ++ 5, 5 ++ "hello"
871
872                 b ::= 3
873                 b = b + 1
874
875                 if 3 * 4 and not True: print "Weird"
876                 d:number = .fred
877                 (d + b) = 12
878
879 ###### output: type_err1
880         .tmp.code:3:25: error: expected string found number
881         .tmp.code:3:28: error: expected string found number
882         .tmp.code:6:8: error: Cannot assign to a constant: b
883         .tmp.code:5:8: info: name was defined as a constant here
884         .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
885         .tmp.code:9:20: error: expected number found label
886         .tmp.code:9:8: info: variable 'd' was set as number here.
887         .tmp.code:10:8: error: cannot assign to an rval
888         oceani: type error in program - not running.
889
890 ###### test: type_err2
891
892         func main()
893                 a := 1
894                 a := 2
895                 a ::= 3
896                 a:number = 4
897                 a ::number = 5
898                 c:
899
900 ###### output: type_err2
901         .tmp.code:4:8: error: variable 'a' redeclared
902         .tmp.code:3:8: info: this is where 'a' was first declared
903         .tmp.code:5:8: error: variable 'a' redeclared
904         .tmp.code:3:8: info: this is where 'a' was first declared
905         .tmp.code:6:8: error: variable 'a' redeclared
906         .tmp.code:3:8: info: this is where 'a' was first declared
907         .tmp.code:7:8: error: variable 'a' redeclared
908         .tmp.code:3:8: info: this is where 'a' was first declared
909         .tmp.code:8:8: Variable declared with no type or value: c
910
911 ###### test: type_err3
912
913         struct foo
914                 a: number
915                 b:string = "hello"
916
917         func main()
918                 c := "hello"
919                 c = c + 1
920                 c = "hello" ++ (True and False)
921                 c = 4 < 5
922                 print 45 + ( "Hello" ++ "there")
923                 c[5] = 1
924
925                 while
926                         use 1
927                         use True
928                         use "Hello"
929                 do
930                         print
931                 case 1: print "one"
932                 case "Hello": print "Hello"
933
934                 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
935                 a1 = a2
936                 a1 = a3
937                 a1 = a4
938                 a1[2] = "hello"
939                 a4[1] = True
940                 c = a2[3]
941
942                 bar:foo
943                 foo.c = 43
944                 print c.foo
945                 print bar.c
946                 print bar.b + 42
947
948                 // trigger 'labels not permitted' error message
949                 while 1 if True else False:
950                         print
951                 case 2: print "two"
952                 print "one" ++ a4[], c[]
953
954 ###### output: type_err3
955         .tmp.code:8:12: error: expected number but variable 'c' is string
956         .tmp.code:7:8: info: this is where 'c' was set to string
957         .tmp.code:8:12: error: Arithmetic returns number but string expected
958         .tmp.code:7:8: info: variable 'c' was set as string here.
959         .tmp.code:9:24: error: Boolean operation found where string expected
960         .tmp.code:7:8: info: variable 'c' was set as string here.
961         .tmp.code:10:12: error: Comparison returns Boolean but string expected
962         .tmp.code:7:8: info: variable 'c' was set as string here.
963         .tmp.code:11:21: error: Concat returns string but number expected
964         .tmp.code:12:8: error: string cannot be indexed
965         .tmp.code:12:8: error: string cannot be indexed
966         .tmp.code:21:13: error: expected number found string
967         .tmp.code:17:16: error: expected number, found string
968         .tmp.code:24:8: error: cannot assign value of type [5]number
969         .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
970         .tmp.code:23:36: info: this is where 'a3' was set to [10]number
971         .tmp.code:25:8: error: cannot assign value of type [5]number
972         .tmp.code:23:8: info: variable 'a1' was set as [5]number here.
973         .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
974         .tmp.code:23:51: info: this is where 'a4' was set to [5]string
975         .tmp.code:26:8: error: cannot assign value of type [5]number
976         .tmp.code:23:8: info: variable 'a1' was set as [5]number here.
977         .tmp.code:27:16: error: expected number found string
978         .tmp.code:28:16: error: expected string found Boolean
979         .tmp.code:29:12: error: have number but need string
980         .tmp.code:7:8: info: variable 'c' was set as string here.
981         .tmp.code:32:8: error: variable used but not declared: foo
982         .tmp.code:32:8: error: field reference on none is not supported
983         .tmp.code:32:16: error: expected none found number
984         .tmp.code:33:14: error: field reference on string is not supported
985         .tmp.code:34:14: error: cannot find requested field in foo
986         .tmp.code:35:17: error: have string but need number
987         .tmp.code:38:29: error: expected number found Boolean
988         .tmp.code:41:23: error: have number but need string
989         .tmp.code:41:29: error: string cannot provide length
990         oceani: type error in program - not running.
991
992 ###### test: type_err4
993         func main()
994                 a:=1; b=2; c::=3
995                 print a, b, c
996
997 ###### output: type_err4
998         .tmp.code:3:14: error: variable used but not declared: b
999         .tmp.code:3:16: error: expected none found number
1000         .tmp.code:3:14: info: variable 'b' was set as none here.
1001         oceani: type error in program - not running.
1002
1003 ###### test: type_err5
1004         struct foo
1005                 bar:baz
1006                 a:number
1007         struct baz
1008                 bat:foo
1009                 b:string
1010         struct foo
1011                 c:number
1012
1013 ###### output: type_err5
1014         .tmp.code:8:7: error: type already declared: foo
1015         .tmp.code:2:7: info: this is location of declartion: foo
1016         .tmp.code:5:7: error: type has recursive definition: baz
1017         .tmp.code:2:7: error: type has recursive definition: foo
1018
1019 ###### test: type_err6
1020
1021         func main()
1022                 a:= "hello"
1023                 if ?a:
1024                         print "no"
1025                 print a ?? "there"
1026
1027 ###### output: type_err6
1028         .tmp.code:4:12: error: '?' requires a testable value, not string
1029         .tmp.code:6:14: error: "??" requires a testable value, not string
1030         oceani: type error in program - not running.
1031
1032
1033 ###### test list
1034         oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
1035
1036 ###### test: type_err_const
1037         const
1038                 foo :: number = 45
1039                 bar ::= "string" + 56
1040         const
1041                 bar ::= "baz"
1042         func main()
1043                 foo := 4
1044                 print foo, bar
1045
1046         // trigger duplicate-main error
1047         func main()
1048                 foo := 6
1049                 print bar, foo
1050
1051 ###### output: type_err_const
1052         .tmp.code:6:8: error: name already declared: bar
1053         .tmp.code:4:8: info: this is where 'bar' was first declared
1054         .tmp.code:8:8: error: variable 'foo' redeclared
1055         .tmp.code:3:8: info: this is where 'foo' was first declared
1056         .tmp.code:12:5: error: function 'main' redeclared
1057         .tmp.code:7:5: info: this is where 'main' was first declared
1058         .tmp.code:13:8: error: variable 'foo' redeclared
1059         .tmp.code:3:8: info: this is where 'foo' was first declared
1060         .tmp.code:4:16: error: expected number found string
1061
1062 ###### test: type_err_const1
1063         const
1064                 foo : number = 45
1065                 bar := "string"
1066         func main()
1067                 foo := 4
1068                 print foo, bar
1069
1070 ###### output: type_err_const1
1071         .tmp.code:3:12: Syntax error in constant: :
1072         .tmp.code:4:12: Syntax error in constant: :
1073
1074 ###### test: type_err_const2
1075         const
1076                 four ::= two + two
1077                 two ::= four / 2
1078         
1079 ###### output: type_err_const2
1080         .tmp.code:3:8: error: const four cannot be resolved.
1081         .tmp.code:4:8: error: const two cannot be resolved.
1082
1083 ###### test: missing_program
1084         const
1085                 foo::="bar"
1086
1087 ###### output: missing_program
1088         oceani: no main function found.
1089
1090 ###### test: bad_main
1091         func main(foo:string)
1092                 print foo
1093
1094 ###### output: bad_main
1095         .tmp.code:2:10: error: expected []string but variable 'foo' is string
1096         .tmp.code:2:10: info: this is where 'foo' was set to string
1097         oceani: main has wrong type.
1098
1099 Test for type errors with functions
1100
1101 ###### test list
1102         oceani_failing_tests += func_err_args func_err_redeclare
1103
1104 ###### test: func_err_args
1105
1106         func test1(a:number; b:string; c:[3]Boolean)
1107                 print a, b, c[1]
1108
1109         func test2(a:number; b:string; c:[3]Boolean)
1110                 print a, b, c[1]
1111
1112         func test3()
1113                 # use undefined names
1114                 print a, z
1115
1116         func main()
1117                 truth:[3]Boolean
1118                 truth[1] = True
1119                 test1(1,"hello")
1120                 test1("hello",1)
1121                 test1(1, "two", truth)
1122                 test1(1, 2, truth)
1123                 test1(1, "lo", truth, 4)
1124                 print test(), test1(1,2,3)
1125                 if test1 == test2:
1126                         pass
1127
1128         func test4(a:number):string
1129                 use a * a
1130
1131         func test5(a:number):string
1132                 print a
1133
1134         struct foo
1135                 a: number
1136                 b:string = "hello"
1137
1138         func test6(a:number):foo
1139                 b:foo
1140                 b.a = a
1141                 use b
1142
1143         func test7(a:@number)
1144                 test7(45)
1145                 test7("45")
1146
1147 ###### output: func_err_args
1148         .tmp.code:40:14: error: cannot pass rval when reference expected
1149         .tmp.code:41:14: error: expected @number found string
1150         .tmp.code:34:5: error: function cannot return value of type foo
1151         .tmp.code:28:8: error: expected string, found none
1152         .tmp.code:25:8: error: expected string, found number
1153         .tmp.code:15:14: error: insufficient arguments to function.
1154         .tmp.code:16:14: error: expected number found string
1155         .tmp.code:16:22: error: expected string found number
1156         .tmp.code:16:14: error: insufficient arguments to function.
1157         .tmp.code:18:17: error: expected string found number
1158         .tmp.code:19:14: error: too many arguments to function.
1159         .tmp.code:20:14: error: attempt to call a non-function.
1160         .tmp.code:20:32: error: expected string found number
1161         .tmp.code:20:28: error: insufficient arguments to function.
1162         .tmp.code:21:20: error: expected "func test1" but variable 'test2' is "func test2"
1163         .tmp.code:5:5: info: this is where 'test2' was set to "func test2"
1164         .tmp.code:10:14: error: variable used but not declared: a
1165         .tmp.code:10:17: error: variable used but not declared: z
1166         oceani: type error in program - not running.
1167
1168 ###### test: func_err_redeclare
1169
1170         func test1(a:number; b:string; c:[3]Boolean)
1171                 print a, b, c[1]
1172
1173         func test1
1174         do
1175                 pass
1176
1177         func test1
1178                 b:Boolean
1179         do
1180                 pass
1181
1182 ###### output: func_err_redeclare
1183         .tmp.code:5:5: error: function 'test1' redeclared
1184         .tmp.code:2:5: info: this is where 'test1' was first declared
1185         .tmp.code:9:5: error: function 'test1' redeclared
1186         .tmp.code:2:5: info: this is where 'test1' was first declared
1187
1188 Test for errors with references
1189
1190 ###### test list
1191         oceani_failing_tests += ref_err1 ref_err2
1192
1193 ###### test: ref_err1
1194         func main()
1195                 ref:@number
1196                 @foo = ref
1197                 ref = @old()
1198                 if ref == @null:
1199                         print "null"
1200
1201 ###### output: ref_err1
1202         .tmp.code:4:9: error: only "@free" makes sense here: foo
1203         .tmp.code:5:15: error: Only reference function is "@new()": old
1204         .tmp.code:6:19: error: Only reference value is "@nil": null
1205
1206 ###### test: ref_err2
1207         func main()
1208                 ref:@number
1209                 ref2:@string
1210                 num:number = @new()
1211                 print num@
1212                 if num == @nil or ref == ref2 or ref == 2 or ref.foo:
1213                         @free = num
1214                 ref = 1
1215
1216 ###### output: ref_err2
1217         .tmp.code:5:22: error: @new() can only be used with references, not number
1218         .tmp.code:5:8: info: variable 'num' was set as number here.
1219         .tmp.code:6:14: error: Cannot dereference number
1220         .tmp.code:7:19: error: @nil can only be used with reference, not number
1221         .tmp.code:7:33: error: expected @number but variable 'ref2' is @string
1222         .tmp.code:4:8: info: this is where 'ref2' was set to @string
1223         .tmp.code:7:48: error: expected @number found number
1224         .tmp.code:7:53: error: field reference on number is not supported
1225         .tmp.code:7:56: error: have none but need Boolean
1226         .tmp.code:8:17: error: @free can only be assigned a reference, not number
1227         .tmp.code:8:17: error: @free can only be assigned a reference, not number
1228         .tmp.code:9:8: error: Cannot assign an rval to a reference.
1229         oceani: type error in program - not running.
1230
1231 ## Test erroneous command line args
1232
1233 To improve coverage, we want to test correct handling of strange command
1234 line arguments.  These tests won't use code, so the exiting test types
1235 won't work.  So we need to be able to explicitly give the command line,
1236 and the expected output, and have that tested and the coverage assessed.
1237 Rather than having to spell out the whole command name, just give "cmd",
1238 and discard that.  Requiring but discarding the command make an empty
1239 command list possible.
1240
1241 ###### test code
1242         @for t in $(oceani_special_tests); do \
1243             echo -n "Test $$t ... ";\
1244             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1245             ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1246             ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1247             if ! cmp -s .tmp.want .tmp.have; then \
1248                echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1249             echo  "passed"; \
1250             ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1251         done || true
1252 ###### valgrind test code
1253         @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1254             echo -n "Valgrind $$t.. "; \
1255             i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1256             if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1257                [ $$? -eq 42 ]; then \
1258                        echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1259             if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1260                echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1261             if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1262                echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1263             echo " passed"; \
1264         done
1265
1266 ###### test list
1267         oceani_special_tests += "cmd"
1268         oceani_special_tests += "cmd,-zyx"
1269         oceani_special_tests += "cmd,nofile"
1270         oceani_special_tests += "cmd,/dev/null"
1271         oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1272
1273 ###### output: cmd
1274         oceani: no input file given
1275
1276 ###### output: cmd,-zyx
1277         ./oceani: invalid option -- 'z'
1278         Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1279
1280 ###### output: cmd,nofile
1281         oceani: cannot open nofile
1282
1283 ###### output: cmd,/dev/null
1284         oceani: could not find any code in /dev/null
1285
1286 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1287         oceani: cannot find section toast:nothing