oceani_tests :=
## test list
+ oceani_valg_tests := $(oceani_tests)
+ ## combine test lists
tests:: oceani_test_suite
oceani_test_suite: oceani coverage_oceani
@for i in coverage/#*.gcda; do mv $$i coverage/$${i##*#}; done
@gcov -o coverage oceani.mdc > /dev/null 2> /dev/null
@mv *.gcov coverage ; [ -f .gcov ] && mv .gcov coverage || true
- @ awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
- END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
- if (ran < (ran + skip) *0.94) exit(1) }' \
- coverage/oceani.mdc.gcov
@rm -f .tmp*
- @for T in $(oceani_tests); do \
+ @[ -n "$$SKIP_VALGRIND" ] || for T in $(oceani_valg_tests); do \
echo -n "Valgrind $$T.. "; \
i="$$IFS"; IFS=,; set $$T; IFS="$$i"; t=$$1; shift; \
- if ! valgrind --error-exitcode=1 --log-file=.tmp.valg ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} \
- > /dev/null 2>&1 ; then \
+ if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani --section "test: $$t" oceani-tests.mdc $${1+"$$@"} \
+ > /dev/null 2>&1 ; [ $$? -eq 42 ] ; then \
echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
echo " passed"; \
done
+ ## valgrind test code
+ @[ -n "$$SKIP_COVERAGE_CHECK" ] || awk '/NOTEST/ { next } /^ *[1-9]/ {ran+=1} /^ *###/ {skip+=1} \
+ END {printf "coverage: %6.2f%%\n", ran * 100 / (ran + skip); \
+ if (ran < (ran + skip) *0.972) exit(1) }' \
+ coverage/oceani.mdc.gcov
coverage_oceani: oceani.c
$(CC) $(CFLAGS) --coverage -fprofile-dir=coverage -o coverage_oceani oceani.c $(LDLIBS)
###### test: valvar
- program:
+ func main(argv:[argc::]string)
a := 23; b:=12 ; b1 := -b
print a, b, a+b, a-b, a*b, a/b, a%b
print a<b, a<=b, a>b, a>=b, a<a, a==b, a==a
###### test: setvar
- program:
+ func main()
a := 4
a = a * a
a = (a + a) * (a + a)
cake ::= "The cake is"
++ " a lie"
- program:
+ func main()
print "Hello World, what lovely oceans you have!"
print "are there", five, "?"
print pi, pie, "but", cake
###### test: varmerge
- program:
+ func main()
+ for i:=0; then i=i+1; while i < 5:
+ switch i:
+ case 0: num:="zero"
+ case 1: scratch:=42; num:="one"
+ case 2: num:="two"
+ case 3: num:="three"
+ else num:="many"
+ print num,", ",
+ print
+
for i:=0; then i=i+1; while i < 5:
switch i:
case 0: num:="zero"
case 2: num:="two"
case 3: num:="three"
else num:="many"
+ // re-declare a CondScope variable
+ num := i*i
print num,", ",
print
###### output: varmerge
zero , one , two , three , many ,
+ 0 , 1 , 4 , 9 , 16 ,
## Conditions and Loops
###### test: cond_loop
- program:
+ func main()
a := 4
if a < 5:
print "Success"
else
pass
print "sum 1..10 is", sum
+ if
+ PI1 := 22/7
+ use PI1 < pi
+ then
+ print "Smaller"
+ else
+ print 'larger'
###### output: cond_loop
Success
pi exceeds three
pi sufficient
sum 1..10 is 55
+ larger
## Say Hello
###### test: sayhello
- program As Bs astr bbools:
- A := $As; B := $Bs
- bbool := bbools == "True"
+ func main(av:[ac::number]string)
+ A := $av[1]; B := $av[2]
+ astr := av[3]
+ bbool := av[ac-1] == "True"
print "Hello World, what lovely oceans you have!"
/* When a variable is defined in both branches of an 'if',
* and used afterwards, the variables are merged.
a : number
a = A;
b:number = B
+ print "still", bigger // check for regression in scoping
if a > 0 and b > 0:
while a != b:
if a < b:
else
hi = mid
if hi - lo < 1:
+ lo = mid
use GiveUp
use True
do pass
case Found:
print "Yay, I found", target
case GiveUp:
- print "Closest I found was", mid
+ print "Closest I found was", lo
###### output: sayhello,55,33,hello,True
Hello World, what lovely oceans you have!
Is 55 bigger than 33 ? yes
double 33 is 66
+ still yes
GCD of 55 and 33 is 11
Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
hello was the str
Hello World, what lovely oceans you have!
Is 12 bigger than 60 ? no
double 60 is 120
+ still no
GCD of 12 and 60 is 12
Fibonacci: 1 1 2 3 5 8 13 21 34 55 89 144
I found the str over there
###### test list
oceani_tests += "insert_sort"
###### test: insert_sort
- program:
+ func main()
size::=55
list:[size]number
list[0] = 1_234
###### test: arrays
- program:
+ func main()
bools:[5]Boolean
strings:[4]string
struct baz { a:number; b:Boolean; }
- program:
+ func main
+ do
info:[4]foo
for i:=0; then i=i+1; while i < 4:
peter False 4
janine False 9
+## Functions
+
+Test functions. They don't return anything, so we need to get them to print
+
+###### test list
+ oceani_tests += functions func_ret_type
+
+###### test: functions
+
+ func test1
+ t: Boolean
+ do
+ if t:
+ print "true"
+
+ func noarg
+ do
+ pass
+
+ func twoarg
+ a:number
+ b:string
+ do
+ while a > 0:
+ print b
+ a = a - 1
+
+ func test(n:number; s:string)
+ if n >= 1:
+ print n,s,
+ test(n-1, "."++s)
+ else
+ print "done"
+
+ func to_polar
+ x:number; y:number
+ return
+ rho:number
+ theta:number
+ do
+ rho = x + y
+ theta = x - y
+
+ func main()
+ for i:=0; then i = i + 1; while i < 5:
+ test(i, " ")
+ angular := to_polar(32, 23)
+ print angular.rho, angular.theta
+
+###### output: functions
+ done
+ 1 done
+ 2 1 . done
+ 3 2 . 1 .. done
+ 4 3 . 2 .. 1 ... done
+ 55 9
+
+###### test: func_ret_type
+
+ func double(n:number):number
+ use n+n
+
+ func answer
+ prefix:string
+ suffix:string
+ return string
+ do
+ use prefix ++ suffix
+
+ func noarg_returns
+ return Boolean
+ do
+ use 22/7 == 3.14159
+
+ func main()
+ for j:=10; then j = j - 3; while j > -5:
+ print answer("dou","ble"), j, "is", double(j)
+
+###### output: func_ret_type
+ double 10 is 20
+ double 7 is 14
+ double 4 is 8
+ double 1 is 2
+ double -2 is -4
+
## Test code with syntax errors
Syntax errors aren't handled well yet - the result is almost always a
./coverage_oceani --section "test: $$t" oceani-tests.mdc > /dev/null 2>&1 ;\
done || true
+###### combine test lists
+ oceani_valg_tests += $(oceani_failing_tests)
+
###### test list
oceani_failing_tests := syn1
oceani_failing_tests += tokerr
###### test: syn1
- program:
+ func main()
if then else while do
###### output: syn1
.tmp.code:3:11: Syntax error in statement: then
###### test: tokerr
- program:
+ func main()
a := 1i // imaginary numbers aren't understood
b:[2i]number // array sizes are handled separately
c:[3.14159]Boolean // array sizes must be integers
###### test: type_err1
- program:
+ func main()
print "hello" ++ 5, 5 ++ "hello"
b ::= 3
###### test: type_err2
- program:
+ func main()
a := 1
a := 2
a ::= 3
a: number
b:string = "hello"
- program:
+ func main()
c := "hello"
c = c + 1
c = "hello" ++ (True and False)
bar:foo
foo.c = 43
print c.foo
+ print bar.c
+ print bar.b + 42
+
+ // trigger 'labels not permitted' error message
+ while 1 if True else False:
+ print
+ case 2: print "two"
###### output: type_err3
.tmp.code:8:12: error: expected number but variable 'c' is string
.tmp.code:32:8: error: field reference attempted on none, not a struct
.tmp.code:32:16: error: expected none found number
.tmp.code:33:14: error: field reference attempted on string, not a struct
+ .tmp.code:34:14: error: cannot find requested field in foo
+ .tmp.code:35:17: error: have string but need number
+ .tmp.code:38:29: error: expected number (labels not permitted) found Boolean
oceani: type error in program - not running.
###### test: type_err4
- program:
+ func main()
a:=1; b=2; c::=3
print a, b, c
oceani: type error in program - not running.
###### test list
- oceani_failing_tests += type_err_const type_err_const1
+ oceani_failing_tests += type_err_const type_err_const1 missing_program bad_main
###### test: type_err_const
const
bar ::= "string" + 56
const
bar ::= "baz"
- program:
+ func main()
foo := 4
print foo, bar
+ // trigger duplicate-main error
+ func main()
+ foo := 6
+ print bar, foo
+
###### output: type_err_const
.tmp.code:4:16: error: expected number found string
.tmp.code:6:8: error: name already declared: bar
.tmp.code:4:8: info: this is where 'bar' was first declared
.tmp.code:8:8: error: variable 'foo' redeclared
.tmp.code:3:8: info: this is where 'foo' was first declared
+ .tmp.code:12:5: error: function 'main' redeclared
+ .tmp.code:7:5: info: this is where 'main' was first declared
+ .tmp.code:13:8: error: variable 'foo' redeclared
+ .tmp.code:3:8: info: this is where 'foo' was first declared
###### test: type_err_const1
const
foo : number = 45
bar := "string"
- program:
+ func main()
foo := 4
print foo, bar
.tmp.code:3:12: Syntax error in constant: :
.tmp.code:4:12: Syntax error in constant: :
+###### test: missing_program
+ const
+ foo::="bar"
+
+###### output: missing_program
+ oceani: no main function found.
+
+###### test: bad_main
+ func main(foo:string)
+ print foo
+
+###### output: bad_main
+ .tmp.code:??:??: error: expected []string but variable 'foo' is string
+ .tmp.code:??:??: info: this is where 'NOTVAR' was set to string
+ oceani: main has wrong type.
+
+Test for type errors with functions
+
+###### test list
+ oceani_failing_tests += func_err_args func_err_redeclare
+
+###### test: func_err_args
+
+ func test1(a:number; b:string; c:[3]Boolean)
+ print a, b, c[1]
+
+ func test2(a:number; b:string; c:[3]Boolean)
+ print a, b, c[1]
+
+ func test3()
+ # use undefined names
+ print a, z
+
+ func main()
+ truth:[3]Boolean
+ truth[1] = True
+ test1(1,"hello")
+ test1("hello",1)
+ test1(1, "two", truth)
+ test1(1, 2, truth)
+ test1(1, "lo", truth, 4)
+ print test(), test1(1,2,3)
+ if test1 == test2:
+ pass
+
+ func test4(a:number):string
+ use a * a
+
+ func test5(a:number):string
+ print a
+
+ struct foo
+ a: number
+ b:string = "hello"
+
+ func test6(a:number):foo
+ b:foo
+ b.a = a
+ use b
+
+###### output: func_err_args
+ .tmp.code:34:5: error: function cannot return value of type foo
+ .tmp.code:28:14: error: expected string, found none
+ .tmp.code:25:8: error: expected string, found number
+ .tmp.code:15:14: error: insufficient arguments to function.
+ .tmp.code:16:14: error: expected number found string
+ .tmp.code:16:22: error: expected string found number
+ .tmp.code:16:14: error: insufficient arguments to function.
+ .tmp.code:18:17: error: expected string found number
+ .tmp.code:19:14: error: too many arguments to function.
+ .tmp.code:20:14: error: attempt to call a non-function.
+ .tmp.code:20:32: error: expected string found number
+ .tmp.code:20:28: error: insufficient arguments to function.
+ .tmp.code:21:20: error: expected *invalid*type* but variable 'test2' is *invalid*type*
+ .tmp.code:??:??: info: this is where 'NOTVAR' was set to *invalid*type*
+ .tmp.code:10:14: error: variable used but not declared: a
+ .tmp.code:10:17: error: variable used but not declared: z
+ oceani: type error in program - not running.
+
+###### test: func_err_redeclare
+
+ func test1(a:number; b:string; c:[3]Boolean)
+ print a, b, c[1]
+
+ func test1
+ do
+ pass
+
+ func test1
+ b:Boolean
+ do
+ pass
+
+###### output: func_err_redeclare
+ .tmp.code:5:5: error: function 'test1' redeclared
+ .tmp.code:2:5: info: this is where 'test1' was first declared
+ .tmp.code:9:5: error: function 'test1' redeclared
+ .tmp.code:2:5: info: this is where 'test1' was first declared
+
## Test erroneous command line args
To improve coverage, we want to test correct handling of strange command
echo "passed"; \
./coverage_oceani $${1+"$$@"} > /dev/null 2>&1 ;\
done || true
+###### valgrind test code
+ @[ -n "$$SKIP_VALGRIND" ] || for t in $(oceani_special_tests); do\
+ echo -n "Valgrind $$t.. "; \
+ i="$$IFS"; IFS=,; set $$t; IFS="$$i"; shift ;\
+ if valgrind --error-exitcode=42 --log-file=.tmp.valg ./oceani $${1+"$$@"} > .tmp.have 2>&1 ;\
+ [ $$? -eq 42 ]; then \
+ echo "FAILED"; cat .tmp.valg; exit 1; fi ; \
+ if grep 'LEAK SUMMARY' .tmp.valg > /dev/null; then \
+ echo "valgrind found LEAKS"; cat .tmp.valg ; exit 1 ; fi; \
+ if grep 'in use at exit [1-9]' .tmp.valg > /dev/null; then \
+ echo "valgrind found memory in use at exit"; cat .tmp.valg ; exit 1 ; fi; \
+ echo " passed"; \
+ done
###### test list
oceani_special_tests += "cmd"