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"
171 four ::= 2 + 2 ; five ::= 10/2
172 const pie ::= "I like Pie";
173 cake ::= "The cake is"
177 print "Hello World, what lovely oceans you have!"
178 print "are there", five, "?"
179 print pi, pie, "but", cake
181 ###### output: consts
182 Hello World, what lovely oceans you have!
184 3.141592653 I like Pie but The cake is a lie
186 Test merging of variables from multiple cases
189 oceani_tests += varmerge
191 ###### test: varmerge
194 for i:=0; then i=i+1; while i < 5:
197 case 1: scratch:=42; num:="one"
204 for i:=0; then i=i+1; while i < 5:
211 // re-declare a CondScope variable
216 ###### output: varmerge
217 zero , one , two , three , many ,
220 ## Conditions and Loops
222 Now we need to test if/else and some different loops
225 oceani_tests += cond_loop
227 ###### test: cond_loop
235 for b:=1; then b=b+b; while b < 100:
238 // Newtons method for square root of 2
244 current := guess * guess
245 use +(current - target) > 0.000000001
247 guess = (guess + (target / guess) ) / 2
250 print "error is ", target - guess * guess
252 for j:=0; then j = j+3 ; while j < 10:
253 if j != 0 and then 20 / j > 3:
254 print "20 /", j," =", 20 / j
256 print "I won't calculate 20 /", j
257 pi ::= 3.1415926535897
258 if 355/113 == pi or else +(pi - 355/113) < 0.001:
260 print "lower" if 355/113 < pi else "higher"
262 if pi > 3 then print "pi exceeds three"; else print "need more pie"
263 if (pi < 3) { print "not enough pi" } else { print "pi sufficient" }
264 for { i := 0; sum := 0 }
270 print "sum 1..10 is", sum
279 ###### output: cond_loop
286 error is -4.510950445e-12
287 I won't calculate 20 / 0
290 I won't calculate 20 / 9
300 The demonstration code presented in the interpreted is suitable for the test suite.
301 Here I break it into two parts, keeping the array code separate.
304 oceani_tests += "sayhello,55,33,hello,True"
305 oceani_tests += "sayhello,12,60,there,False"
307 ###### test: sayhello
309 func main(av:[ac::number]string)
310 A := $av[1]; B := $av[2]
312 bbool := av[ac-1] == "True"
313 print "Hello World, what lovely oceans you have!"
314 /* When a variable is defined in both branches of an 'if',
315 * and used afterwards, the variables are merged.
321 print "Is", A, "bigger than", B,"? ", bigger
322 /* If a variable is not used after the 'if', no
323 * merge happens, so types can be different
326 double:string = "yes"
327 print A, "is more than twice", B, "?", double
330 print "double", B, "is", double
335 print "still", bigger // check for regression in scoping
342 print "GCD of", A, "and", B,"is", a
344 print a, "is not positive, cannot calculate GCD"
346 print b, "is not positive, cannot calculate GCD"
351 print "Fibonacci:", f1,f2,
361 print astr ++ " was the str"
363 print "I found the str over " ++ astr
365 /* Binary search... */
383 print "Yay, I found", target
385 print "Closest I found was", lo
387 ###### output: sayhello,55,33,hello,True
388 Hello World, what lovely oceans you have!
389 Is 55 bigger than 33 ? yes
392 GCD of 55 and 33 is 11
393 Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
395 Closest I found was 77.34375
397 ###### output: sayhello,12,60,there,False
398 Hello World, what lovely oceans you have!
399 Is 12 bigger than 60 ? no
402 GCD of 12 and 60 is 12
403 Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
404 I found the str over there
405 Closest I found was 77.34375
408 oceani_tests += "insert_sort"
409 ###### test: insert_sort
414 for i:=1; then i = i + 1; while i < size:
415 n := list[i-1] * list[i-1]
416 list[i] = (n / 100) % 10000
419 for i:=0; then i = i + 1; while i < size:
420 print "list[",i,"]=",list[i]
422 for i := 1; then i=i+1; while i < size:
423 for j:=i-1; then j=j-1; while j >= 0:
424 if list[j] > list[j+1]:
429 for i:=0; then i = i + 1; while i < size:
430 print "list[",i,"]=",list[i]
432 ###### output: insert_sort
548 We already have some array tests, but this is where we put other
549 ad-hoc things array related.
552 oceani_tests += arrays
560 bools[3] = strings[1] == "Hello"
561 bools[1] = strings[2] <= "there"
563 for i:=0; then i=i+1; while i<5:
567 ra[6] = 42 // mustn't crash
568 print '', bools[i], ra[j-1],
571 ###### output: arrays
572 False 0 True 1 False 4 False 9 False 16
576 Time to test if structure declarations and accesses work correctly.
579 oceani_tests += structs
587 active:Boolean = True
589 struct baz { a:number; b:Boolean; }
595 for i:=0; then i=i+1; while i < 4:
603 info[i].size[0] = i*i
605 info[i].active = False
607 for i:=0; then i=i+1; while i < 4:
608 print info[i].name, info[i].active, info[i].size[0]
610 ###### output: structs
619 Test functions. They don't return anything, so we need to get them to print
622 oceani_tests += functions func_ret_type
624 ###### test: functions
644 func test(n:number; s:string)
661 for i:=0; then i = i + 1; while i < 5:
663 angular := to_polar(32, 23)
664 print angular.rho, angular.theta
666 ###### output: functions
671 4 3 . 2 .. 1 ... done
674 ###### test: func_ret_type
676 func double(n:number):number
692 for j:=10; then j = j - 3; while j > -5:
693 print answer("dou","ble"), j, "is", double(j)
695 ###### output: func_ret_type
702 ## Test code with syntax errors
704 Syntax errors aren't handled well yet - the result is almost always a
705 single message about the first error. So this section will be fairly
706 thin until we add proper parsing recovery in the face of common errors.
708 A special case of syntax errors is token errors, when a token is only
709 accepted because the parser doesn't know quite enough to reject it.
710 There are handled better as they are quite local, so a single test
711 program can trigger most of the possible errors.
713 To handle erronous code, we need a different set of tests, as we need to
714 capture `stderr`. The same test code will be used for type errors too.
715 As error messages contain the line number, and we don't want changes to
716 this file to change the reported numbers, we copy the code into a
717 separate file first, then run from there.
720 @for t in $(oceani_failing_tests); do \
721 echo -n "Test $$t ... "; \
722 ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
723 echo '``````' > .tmp.code; \
724 ./md2c oceani-tests.mdc "test: $$t" | grep -v '^#' >> .tmp.code; \
725 ./oceani .tmp.code > .tmp.have 2>&1; \
726 if ! cmp -s .tmp.want .tmp.have; then \
727 echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
729 ./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
732 ###### combine test lists
733 oceani_valg_tests += $(oceani_failing_tests)
736 oceani_failing_tests := syn1
737 oceani_failing_tests += tokerr
742 if then else while do
745 .tmp.code:3:11: Syntax error in statement: then
749 a := 1i // imaginary numbers aren't understood
750 b:[2i]number // array sizes are handled separately
751 c:[3.14159]Boolean // array sizes must be integers
752 d:[1_000_000_000_000]number // they mustn't be huge
753 patn: string = "foo[ ,_]*bar"re // regexp strings are just a dream
756 This is a multiline string
757 With an unsupportable suffix
761 yy:[unknowable]number
763 zz:[zzsize]string // size must be constant, use ::=
765 // These numbers should be bad in all contexts: FIXME
768 ###### output: tokerr
769 .tmp.code:3:13: error: unsupported number suffix: 1i
770 .tmp.code:4:11: error: unsupported number suffix: 2i
771 .tmp.code:5:11: error: array size must be an integer: 3.14159
772 .tmp.code:6:11: error: array size is too large: 1_000_000_000_000
773 .tmp.code:7:23: error: unsupported string suffix: "foo[ ,_]*bar"re
774 .tmp.code:9:17: error: unsupported string suffix: """
775 This is a multiline string
776 With an unsupportable suffix
778 .tmp.code:14:11: error: undefined type: unknown
779 .tmp.code:15:12: error: name undeclared: unknowable
780 .tmp.code:17:12: error: array size must be a constant: zzsize
781 .tmp.code:20:12: error: unrecognised number: 00123
783 ## Tests for type errors
785 Type error don't cause parsing to abort, so we can fit many in the
786 one test program. Some type errors are found during the parse, others
787 during type analysis which doesn't run if parsing failed. So we cannot
788 fit everything in one.
790 These programs were generated by looking for the
791 various places that `type_err()` are called.
794 oceani_failing_tests += type_err1 type_err2 type_err3 type_err4
796 ###### test: type_err1
799 print "hello" ++ 5, 5 ++ "hello"
804 if 3 * 4 and not True: print "Weird"
806 ###### output: type_err1
807 .tmp.code:3:25: error: expected string found number
808 .tmp.code:3:28: error: expected string found number
809 .tmp.code:6:8: error: Cannot assign to a constant: b
810 .tmp.code:5:8: info: name was defined as a constant here
811 .tmp.code:6:8: error: Cannot assign to a constant: b
812 .tmp.code:5:8: info: name was defined as a constant here
813 .tmp.code:8:11: error: Arithmetic returns number but Boolean expected
814 oceani: type error in program - not running.
816 ###### test: type_err2
826 ###### output: type_err2
827 .tmp.code:4:8: error: variable 'a' redeclared
828 .tmp.code:3:8: info: this is where 'a' was first declared
829 .tmp.code:5:8: error: variable 'a' redeclared
830 .tmp.code:3:8: info: this is where 'a' was first declared
831 .tmp.code:6:8: error: variable 'a' redeclared
832 .tmp.code:3:8: info: this is where 'a' was first declared
833 .tmp.code:7:8: error: variable 'a' redeclared
834 .tmp.code:3:8: info: this is where 'a' was first declared
835 .tmp.code:8:8: Variable declared with no type or value: c
837 ###### test: type_err3
846 c = "hello" ++ (True and False)
848 print 45 + ( "Hello" ++ "there")
858 case "Hello": print "Hello"
860 a1:[5]number; a2:[5]number; a3:[10]number; a4:[5]string
874 // trigger 'labels not permitted' error message
875 while 1 if True else False:
879 ###### output: type_err3
880 .tmp.code:8:12: error: expected number but variable 'c' is string
881 .tmp.code:7:8: info: this is where 'c' was set to string
882 .tmp.code:8:12: error: Arithmetic returns number but string expected
883 .tmp.code:7:8: info: variable 'c' was set as string here.
884 .tmp.code:9:24: error: Boolean operation found where string expected
885 .tmp.code:10:12: error: Comparison returns Boolean but string expected
886 .tmp.code:7:8: info: variable 'c' was set as string here.
887 .tmp.code:11:21: error: Concat returns string but number expected
888 .tmp.code:12:8: error: string cannot be indexed
889 .tmp.code:12:8: error: string cannot be indexed
890 .tmp.code:21:13: error: expected number found string
891 .tmp.code:17:16: error: expected number, found string
892 .tmp.code:24:8: error: cannot assign value of type [5]number
893 .tmp.code:25:13: error: expected [5]number but variable 'a3' is [10]number
894 .tmp.code:23:36: info: this is where 'a3' was set to [10]number
895 .tmp.code:25:8: error: cannot assign value of type [5]number
896 .tmp.code:26:13: error: expected [5]number but variable 'a4' is [5]string
897 .tmp.code:23:51: info: this is where 'a4' was set to [5]string
898 .tmp.code:26:8: error: cannot assign value of type [5]number
899 .tmp.code:27:16: error: expected number found string
900 .tmp.code:28:16: error: expected string found Boolean
901 .tmp.code:29:12: error: have number but need string
902 .tmp.code:7:8: info: variable 'c' was set as string here.
903 .tmp.code:32:8: error: variable used but not declared: foo
904 .tmp.code:32:8: error: field reference attempted on none, not a struct
905 .tmp.code:32:16: error: expected none found number
906 .tmp.code:33:14: error: field reference attempted on string, not a struct
907 .tmp.code:34:14: error: cannot find requested field in foo
908 .tmp.code:35:17: error: have string but need number
909 .tmp.code:38:29: error: expected number (labels not permitted) found Boolean
910 oceani: type error in program - not running.
912 ###### test: type_err4
917 ###### output: type_err4
918 .tmp.code:3:14: error: variable used but not declared: b
919 .tmp.code:3:16: error: expected none found number
920 .tmp.code:3:14: info: variable 'b' was set as none here.
921 oceani: type error in program - not running.
924 oceani_failing_tests += type_err_const type_err_const1 missing_program bad_main
926 ###### test: type_err_const
929 bar ::= "string" + 56
936 // trigger duplicate-main error
941 ###### output: type_err_const
942 .tmp.code:6:8: error: name already declared: bar
943 .tmp.code:4:8: info: this is where 'bar' was first declared
944 .tmp.code:8:8: error: variable 'foo' redeclared
945 .tmp.code:3:8: info: this is where 'foo' was first declared
946 .tmp.code:12:5: error: function 'main' redeclared
947 .tmp.code:7:5: info: this is where 'main' was first declared
948 .tmp.code:13:8: error: variable 'foo' redeclared
949 .tmp.code:3:8: info: this is where 'foo' was first declared
950 .tmp.code:4:16: error: expected number found string
952 ###### test: type_err_const1
960 ###### output: type_err_const1
961 .tmp.code:3:12: Syntax error in constant: :
962 .tmp.code:4:12: Syntax error in constant: :
964 ###### test: missing_program
968 ###### output: missing_program
969 oceani: no main function found.
971 ###### test: bad_main
972 func main(foo:string)
975 ###### output: bad_main
976 .tmp.code:??:??: error: expected []string but variable 'foo' is string
977 .tmp.code:??:??: info: this is where 'NOTVAR' was set to string
978 oceani: main has wrong type.
980 Test for type errors with functions
983 oceani_failing_tests += func_err_args func_err_redeclare
985 ###### test: func_err_args
987 func test1(a:number; b:string; c:[3]Boolean)
990 func test2(a:number; b:string; c:[3]Boolean)
994 # use undefined names
1002 test1(1, "two", truth)
1004 test1(1, "lo", truth, 4)
1005 print test(), test1(1,2,3)
1009 func test4(a:number):string
1012 func test5(a:number):string
1019 func test6(a:number):foo
1024 ###### output: func_err_args
1025 .tmp.code:34:5: error: function cannot return value of type foo
1026 .tmp.code:28:14: error: expected string, found none
1027 .tmp.code:25:8: error: expected string, found number
1028 .tmp.code:15:14: error: insufficient arguments to function.
1029 .tmp.code:16:14: error: expected number found string
1030 .tmp.code:16:22: error: expected string found number
1031 .tmp.code:16:14: error: insufficient arguments to function.
1032 .tmp.code:18:17: error: expected string found number
1033 .tmp.code:19:14: error: too many arguments to function.
1034 .tmp.code:20:14: error: attempt to call a non-function.
1035 .tmp.code:20:32: error: expected string found number
1036 .tmp.code:20:28: error: insufficient arguments to function.
1037 .tmp.code:21:20: error: expected *invalid*type* but variable 'test2' is *invalid*type*
1038 .tmp.code:??:??: info: this is where 'NOTVAR' was set to *invalid*type*
1039 .tmp.code:10:14: error: variable used but not declared: a
1040 .tmp.code:10:17: error: variable used but not declared: z
1041 oceani: type error in program - not running.
1043 ###### test: func_err_redeclare
1045 func test1(a:number; b:string; c:[3]Boolean)
1057 ###### output: func_err_redeclare
1058 .tmp.code:5:5: error: function 'test1' redeclared
1059 .tmp.code:2:5: info: this is where 'test1' was first declared
1060 .tmp.code:9:5: error: function 'test1' redeclared
1061 .tmp.code:2:5: info: this is where 'test1' was first declared
1063 ## Test erroneous command line args
1065 To improve coverage, we want to test correct handling of strange command
1066 line arguments. These tests won't use code, so the exiting test types
1067 won't work. So we need to be able to explicitly give the command line,
1068 and the expected output, and have that tested and the coverage assessed.
1069 Rather than having to spell out the whole command name, just give "cmd",
1070 and discard that. Requiring but discarding the command make an empty
1071 command list possible.
1074 @for t in $(oceani_special_tests); do \
1075 echo -n "Test $$t ... ";\
1076 i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1077 ./md2c oceani-tests.mdc "output: $$t" | grep -v '^#' > .tmp.want; \
1078 ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1079 if ! cmp -s .tmp.want .tmp.have; then \
1080 echo "FAILED"; diff -u .tmp.want .tmp.have ; exit 1; fi ;\
1082 ./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
1084 ###### valgrind test code
1085 @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
1086 echo -n "Valgrind $$t.. "; \
1087 i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
1088 if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
1089 [ $$? -eq 42 ]; then \
1090 echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
1091 if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
1092 echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
1093 if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
1094 echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
1099 oceani_special_tests += "cmd"
1100 oceani_special_tests += "cmd,-zyx"
1101 oceani_special_tests += "cmd,nofile"
1102 oceani_special_tests += "cmd,/dev/null"
1103 oceani_special_tests += "cmd,--section,toast:nothing,oceani-tests.mdc"
1106 oceani: no input file given
1108 ###### output: cmd,-zyx
1109 ./oceani: invalid option -- 'z'
1110 Usage: oceani --trace --print --noexec --brackets --section=SectionName prog.ocn
1112 ###### output: cmd,nofile
1113 oceani: cannot open nofile
1115 ###### output: cmd,/dev/null
1116 oceani: could not find any code in /dev/null
1118 ###### output: cmd,--section,toast:nothing,oceani-tests.mdc
1119 oceani: cannot find section toast:nothing