1 # Ocean Interpreter test code
3 Regular testing is, of course, important for developing any software.
4 The Ocean interpreted is no exception. This document allows easy
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.
11 Three different sorts of tests are run. As soon as any fail, the whole
14 1/ Each program is run and the output is compared against the expected
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
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.
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.
34 ###### File: oceani-tests.mk
38 oceani_valg_tests := $(oceani_tests)
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; }
47 @rm -rf coverage; mkdir -p 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.. "; \
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; \
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; \
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
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; \
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
103 coverage_oceani: oceani.c
104 $(CC) $(CFLAGS) --coverage -fprofile-dir=coverage -o coverage_oceani oceani.c $(LDLIBS)
106 ## Values and variables
108 The first test stores values in variables and performs various
109 calculations on them.
112 oceani_tests += "valvar"
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
124 c ::= "This is a string"
125 d ::= " field theory"
128 aconst :: string = "unchanging"
137 ###### output: valvar
139 23 12 35 11 276 1.916666667 11
140 False False True True False False True
142 False True True False False False
143 This is a string field theory This is a string field theory
146 Next we change the value of variables
149 oceani_tests += "setvar"
156 a = (a + a) * (a + a)
160 ###### output: setvar
166 oceani_tests += "consts"
172 four ::= 2 + 2 ; five ::= 10/2
173 const pie ::= "I like Pie";
174 cake ::= "The cake is"
178 print "Hello World, what lovely oceans you have!"
179 print "are there", five, "?"
180 print pi, pie, "but", cake, "Tau is", tau
182 ###### output: consts
183 Hello World, what lovely oceans you have!
185 3.141592653 I like Pie but The cake is a lie Tau is 6.283185306
187 Test merging of variables from multiple cases
190 oceani_tests += varmerge
192 ###### test: varmerge
195 for i:=0; then i=i+1; while i < 5:
198 case 1: scratch:=42; num:="one"
205 for i:=0; then i=i+1; while i < 5:
212 // re-declare a CondScope variable
217 ###### output: varmerge
218 zero , one , two , three , many ,
221 ## Conditions and Loops
223 Now we need to test if/else and some different loops
226 oceani_tests += cond_loop
228 ###### test: cond_loop
236 for b:=1; then b=b+b; while b < 100:
239 // Newtons method for square root of 2
245 current := guess * guess
246 use +(current - target) > 0.000000001
248 guess = (guess + (target / guess) ) / 2
251 print "error is ", target - guess * guess
253 for j:=0; then j = j+3 ; while j < 10:
254 if j != 0 and then 20 / j > 3:
255 print "20 /", j," =", 20 / j
257 print "I won't calculate 20 /", j
258 pi ::= 3.1415926535897
259 if 355/113 == pi or else +(pi - 355/113) < 0.001:
261 print "lower" if 355/113 < pi else "higher"
263 if pi > 3 then print "pi exceeds three"; else print "need more pie"
264 if (pi < 3) { print "not enough pi" } else { print "pi sufficient" }
265 for { i := 0; sum := 0 }
271 print "sum 1..10 is", sum
280 ###### output: cond_loop
287 error is -4.510950445e-12
288 I won't calculate 20 / 0
291 I won't calculate 20 / 9
301 The demonstration code presented in the interpreted is suitable for the test suite.
302 Here I break it into two parts, keeping the array code separate.
305 oceani_tests += "sayhello,55,33,hello,True"
306 oceani_tests += "sayhello,12,60,there,False"
308 ###### test: sayhello
310 func main(av:[ac::number]string)
311 A := $av[1]; B := $av[2]
313 bbool := av[ac-1] == "True"
314 print "Hello World, what lovely oceans you have!"
315 /* When a variable is defined in both branches of an 'if',
316 * and used afterwards, the variables are merged.
322 print "Is", A, "bigger than", B,"? ", bigger
323 /* If a variable is not used after the 'if', no
324 * merge happens, so types can be different
327 double:string = "yes"
328 print A, "is more than twice", B, "?", double
331 print "double", B, "is", double
336 print "still", bigger // check for regression in scoping
343 print "GCD of", A, "and", B,"is", a
345 print a, "is not positive, cannot calculate GCD"
347 print b, "is not positive, cannot calculate GCD"
352 print "Fibonacci:", f1,f2,
362 print astr ++ " was the str"
364 print "I found the str over " ++ astr
366 /* Binary search... */
384 print "Yay, I found", target
386 print "Closest I found was", lo
388 ###### output: sayhello,55,33,hello,True
389 Hello World, what lovely oceans you have!
390 Is 55 bigger than 33 ? yes
393 GCD of 55 and 33 is 11
394 Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
396 Closest I found was 77.34375
398 ###### output: sayhello,12,60,there,False
399 Hello World, what lovely oceans you have!
400 Is 12 bigger than 60 ? no
403 GCD of 12 and 60 is 12
404 Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
405 I found the str over there
406 Closest I found was 77.34375
409 oceani_tests += "insert_sort"
410 ###### test: insert_sort
415 for i:=1; then i = i + 1; while i < size:
416 n := list[i-1] * list[i-1]
417 list[i] = (n / 100) % 10000
420 for i:=0; then i = i + 1; while i < size:
421 print "list[",i,"]=",list[i]
423 for i := 1; then i=i+1; while i < size:
424 for j:=i-1; then j=j-1; while j >= 0:
425 if list[j] > list[j+1]:
430 for i:=0; then i = i + 1; while i < size:
431 print "list[",i,"]=",list[i]
433 ###### output: insert_sort
549 We already have some array tests, but this is where we put other
550 ad-hoc things array related.
553 oceani_tests += arrays
561 bools[3] = strings[1] == "Hello"
562 bools[1] = strings[2] <= "there"
564 for i:=0; then i=i+1; while i<5:
568 ra[6] = 42 // mustn't crash
569 print '', bools[i], ra[j-1],
572 ###### output: arrays
573 False 0 True 1 False 4 False 9 False 16
577 Time to test if structure declarations and accesses work correctly.
580 oceani_tests += structs
589 active:Boolean = True
591 struct baz { a:number; b:Boolean; }
597 for i:=0; then i=i+1; while i < 4:
605 info[i].size[0] = i*i
607 info[i].active = False
609 for i:=0; then i=i+1; while i < 4:
610 print info[i].name, info[i].active, info[i].size[0]
611 info[0].thing.b = True
613 ###### output: structs
622 Test functions. They don't return anything, so we need to get them to print
625 oceani_tests += functions func_ret_type
627 ###### test: functions
647 func test(n:number; s:string)
664 for i:=0; then i = i + 1; while i < 5:
666 angular := to_polar(32, 23)
667 print angular.rho, angular.theta
669 ###### output: functions
674 4 3 . 2 .. 1 ... done
677 ###### test: func_ret_type
679 func double(n:number):number
695 for j:=10; then j = j - 3; while j > -5:
696 print answer("dou","ble"), j, "is", double(j)
698 ###### output: func_ret_type
705 ## Test code with syntax errors
707 Syntax errors aren't handled well yet - the result is almost always a
708 single message about the first error. So this section will be fairly
709 thin until we add proper parsing recovery in the face of common errors.
711 A special case of syntax errors is token errors, when a token is only
712 accepted because the parser doesn't know quite enough to reject it.
713 There are handled better as they are quite local, so a single test
714 program can trigger most of the possible errors.
716 To handle erronous code, we need a different set of tests, as we need to
717 capture `stderr`. The same test code will be used for type errors too.
718 As error messages contain the line number, and we don't want changes to
719 this file to change the reported numbers, we copy the code into a
720 separate file first, then run from there.
723 @for t in $(oceani_failing_tests); do \
724 echo -n "Test $$t ... "; \
725 ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
726 echo '``````' > .tmp.code; \
727 ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
728 ./oceani .tmp.code > .tmp.have 2>&1; \
729 if ! cmp -s .tmp.want .tmp.have; then \
730 echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
732 ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
735 ###### combine test lists
736 oceani_valg_tests += $(oceani_failing_tests)
739 oceani_failing_tests := syn1
740 oceani_failing_tests += tokerr
745 if then else while do
748 .tmp.code:3:11: Syntax error in statement: then
752 a := 1i // imaginary numbers aren't understood
753 b:[2i]number // array sizes are handled separately
754 c:[3.14159]Boolean // array sizes must be integers
755 d:[1_000_000_000_000]number // they mustn't be huge
756 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
759 This is a multiline string
760 With an unsupportable suffix
764 yy:[unknowable]number
766 zz:[zzsize]string // size must be constant, use ::=
768 // These numbers should be bad in all contexts: FIXME
771 ###### output: tokerr
772 .tmp.code:3:13: error: unsupported number suffix: 1i
773 .tmp.code:4:11: error: unsupported number suffix: 2i
774 .tmp.code:5:11: error: array size must be an integer: 3.14159
775 .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
776 .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
777 .tmp.code:9:17: error: unsupported string suffix: """
778 This is a multiline string
779 With an unsupportable suffix
781 .tmp.code:15:12: error: name undeclared: unknowable
782 .tmp.code:17:12: error: array size must be a constant: zzsize
783 .tmp.code:20:12: error: unrecognised number: 00123
784 .tmp.code:14:11: error: type used but not declared: unknown
786 ## Tests for type errors
788 Type error don't cause parsing to abort, so we can fit many in the
789 one test program. Some type errors are found during the parse, others
790 during type analysis which doesn't run if parsing failed. So we cannot
791 fit everything in one.
793 These programs were generated by looking for the
794 various places that `type_err()` are called.
797 oceani_failing_tests += type_err1 type_err2 type_err3 type_err4 type_err5
799 ###### test: type_err1
802 print "hello" ++ 5, 5 ++ "hello"
807 if 3 * 4 and not True: print "Weird"
809 ###### output: type_err1
810 .tmp.code:3:25: error: expected string found number
811 .tmp.code:3:28: error: expected string found number
812 .tmp.code:6:8: error: Cannot assign to a constant: b
813 .tmp.code:5:8: info: name was defined as a constant here
814 .tmp.code:6:8: error: Cannot assign to a constant: b
815 .tmp.code:5:8: info: name was defined as a constant here
816 .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
817 oceani: type error in program - not running.
819 ###### test: type_err2
829 ###### output: type_err2
830 .tmp.code:4:8: error: variable 'a' redeclared
831 .tmp.code:3:8: info: this is where 'a' was first declared
832 .tmp.code:5:8: error: variable 'a' redeclared
833 .tmp.code:3:8: info: this is where 'a' was first declared
834 .tmp.code:6:8: error: variable 'a' redeclared
835 .tmp.code:3:8: info: this is where 'a' was first declared
836 .tmp.code:7:8: error: variable 'a' redeclared
837 .tmp.code:3:8: info: this is where 'a' was first declared
838 .tmp.code:8:8: Variable declared with no type or value: c
840 ###### test: type_err3
849 c = "hello" ++ (True and False)
851 print 45 + ( "Hello" ++ "there")
861 case "Hello": print "Hello"
863 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
877 // trigger 'labels not permitted' error message
878 while 1 if True else False:
882 ###### output: type_err3
883 .tmp.code:8:12: error: expected number but variable 'c' is string
884 .tmp.code:7:8: info: this is where 'c' was set to string
885 .tmp.code:8:12: error: Arithmetic returns number but string expected
886 .tmp.code:7:8: info: variable 'c' was set as string here.
887 .tmp.code:9:24: error: Boolean operation found where string expected
888 .tmp.code:10:12: error: Comparison returns Boolean but string expected
889 .tmp.code:7:8: info: variable 'c' was set as string here.
890 .tmp.code:11:21: error: Concat returns string but number expected
891 .tmp.code:12:8: error: string cannot be indexed
892 .tmp.code:12:8: error: string cannot be indexed
893 .tmp.code:21:13: error: expected number found string
894 .tmp.code:17:16: error: expected number, found string
895 .tmp.code:24:8: error: cannot assign value of type [5]number
896 .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
897 .tmp.code:23:36: info: this is where 'a3' was set to [10]number
898 .tmp.code:25:8: error: cannot assign value of type [5]number
899 .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
900 .tmp.code:23:51: info: this is where 'a4' was set to [5]string
901 .tmp.code:26:8: error: cannot assign value of type [5]number
902 .tmp.code:27:16: error: expected number found string
903 .tmp.code:28:16: error: expected string found Boolean
904 .tmp.code:29:12: error: have number but need string
905 .tmp.code:7:8: info: variable 'c' was set as string here.
906 .tmp.code:32:8: error: variable used but not declared: foo
907 .tmp.code:32:8: error: field reference attempted on none, not a struct
908 .tmp.code:32:16: error: expected none found number
909 .tmp.code:33:14: error: field reference attempted on string, not a struct
910 .tmp.code:34:14: error: cannot find requested field in foo
911 .tmp.code:35:17: error: have string but need number
912 .tmp.code:38:29: error: expected number (labels not permitted) found Boolean
913 oceani: type error in program - not running.
915 ###### test: type_err4
920 ###### output: type_err4
921 .tmp.code:3:14: error: variable used but not declared: b
922 .tmp.code:3:16: error: expected none found number
923 .tmp.code:3:14: info: variable 'b' was set as none here.
924 oceani: type error in program - not running.
926 ###### test: type_err5
936 ###### output: type_err5
937 .tmp.code:8:7: error: type already declared: foo
938 .tmp.code:2:7: info: this is location of declartion: foo
939 .tmp.code:2:7: error: type has recursive definition: foo
940 .tmp.code:5:7: error: type has recursive definition: baz
943 oceani_failing_tests += type_err_const type_err_const1 type_err_const2 missing_program bad_main
945 ###### test: type_err_const
948 bar ::= "string" + 56
955 // trigger duplicate-main error
960 ###### output: type_err_const
961 .tmp.code:6:8: error: name already declared: bar
962 .tmp.code:4:8: info: this is where 'bar' was first declared
963 .tmp.code:8:8: error: variable 'foo' redeclared
964 .tmp.code:3:8: info: this is where 'foo' was first declared
965 .tmp.code:12:5: error: function 'main' redeclared
966 .tmp.code:7:5: info: this is where 'main' was first declared
967 .tmp.code:13:8: error: variable 'foo' redeclared
968 .tmp.code:3:8: info: this is where 'foo' was first declared
969 .tmp.code:4:16: error: expected number found string
971 ###### test: type_err_const1
979 ###### output: type_err_const1
980 .tmp.code:3:12: Syntax error in constant: :
981 .tmp.code:4:12: Syntax error in constant: :
983 ###### test: type_err_const2
988 ###### output: type_err_const2
989 .tmp.code:3:8: error: const four cannot be resolved.
990 .tmp.code:4:8: error: const two cannot be resolved.
992 ###### test: missing_program
996 ###### output: missing_program
997 oceani: no main function found.
999 ###### test: bad_main
1000 func main(foo:string)
1003 ###### output: bad_main
1004 .tmp.code:??:??: error: expected []string but variable 'foo' is string
1005 .tmp.code:??:??: info: this is where 'NOTVAR' was set to string
1006 oceani: main has wrong type.
1008 Test for type errors with functions
1011 oceani_failing_tests += func_err_args func_err_redeclare
1013 ###### test: func_err_args
1015 func test1(a:number; b:string; c:[3]Boolean)
1018 func test2(a:number; b:string; c:[3]Boolean)
1022 # use undefined names
1030 test1(1, "two", truth)
1032 test1(1, "lo", truth, 4)
1033 print test(), test1(1,2,3)
1037 func test4(a:number):string
1040 func test5(a:number):string
1047 func test6(a:number):foo
1052 ###### output: func_err_args
1053 .tmp.code:34:5: error: function cannot return value of type foo
1054 .tmp.code:28:14: error: expected string, found none
1055 .tmp.code:25:8: error: expected string, found number
1056 .tmp.code:15:14: error: insufficient arguments to function.
1057 .tmp.code:16:14: error: expected number found string
1058 .tmp.code:16:22: error: expected string found number
1059 .tmp.code:16:14: error: insufficient arguments to function.
1060 .tmp.code:18:17: error: expected string found number
1061 .tmp.code:19:14: error: too many arguments to function.
1062 .tmp.code:20:14: error: attempt to call a non-function.
1063 .tmp.code:20:32: error: expected string found number
1064 .tmp.code:20:28: error: insufficient arguments to function.
1065 .tmp.code:21:20: error: expected *invalid*type* but variable 'test2' is *invalid*type*
1066 .tmp.code:??:??: info: this is where 'NOTVAR' was set to *invalid*type*
1067 .tmp.code:10:14: error: variable used but not declared: a
1068 .tmp.code:10:17: error: variable used but not declared: z
1069 oceani: type error in program - not running.
1071 ###### test: func_err_redeclare
1073 func test1(a:number; b:string; c:[3]Boolean)
1085 ###### output: func_err_redeclare
1086 .tmp.code:5:5: error: function 'test1' redeclared
1087 .tmp.code:2:5: info: this is where 'test1' was first declared
1088 .tmp.code:9:5: error: function 'test1' redeclared
1089 .tmp.code:2:5: info: this is where 'test1' was first declared
1091 ## Test erroneous command line args
1093 To improve coverage, we want to test correct handling of strange command
1094 line arguments. These tests won't use code, so the exiting test types
1095 won't work. So we need to be able to explicitly give the command line,
1096 and the expected output, and have that tested and the coverage assessed.
1097 Rather than having to spell out the whole command name, just give "cmd",
1098 and discard that. Requiring but discarding the command make an empty
1099 command list possible.
1102 @for t in $(oceani_special_tests); do \
1103 echo -n "Test $$t ... ";\
1104 i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1105 ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1106 ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1107 if ! cmp -s .tmp.want .tmp.have; then \
1108 echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1110 ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1112 ###### valgrind test code
1113 @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1114 echo -n "Valgrind $$t.. "; \
1115 i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1116 if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1117 [ $$? -eq 42 ]; then \
1118 echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1119 if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1120 echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1121 if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1122 echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1127 oceani_special_tests += "cmd"
1128 oceani_special_tests += "cmd,-zyx"
1129 oceani_special_tests += "cmd,nofile"
1130 oceani_special_tests += "cmd,/dev/null"
1131 oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1134 oceani: no input file given
1136 ###### output: cmd,-zyx
1137 ./oceani: invalid option -- 'z'
1138 Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1140 ###### output: cmd,nofile
1141 oceani: cannot open nofile
1143 ###### output: cmd,/dev/null
1144 oceani: could not find any code in /dev/null
1146 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1147 oceani: cannot find section toast:nothing