forked from dfinity/motoko-base
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[DMS-78] Add property tests for PersistentOrderedSet
Add property tests for PersistentOrderedSet.
- Loading branch information
Showing
1 changed file
with
286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
// @testmode wasi | ||
|
||
import Set "../src/PersistentOrderedSet"; | ||
import Nat "../src/Nat"; | ||
import Iter "../src/Iter"; | ||
import Debug "../src/Debug"; | ||
import Array "../src/Array"; | ||
|
||
import Suite "mo:matchers/Suite"; | ||
import T "mo:matchers/Testable"; | ||
import M "mo:matchers/Matchers"; | ||
|
||
let { run; test; suite } = Suite; | ||
|
||
let natSet = Set.SetOps<Nat>(Nat.compare); | ||
|
||
class SetMatcher(expected : Set.Set<Nat>) : M.Matcher<Set.Set<Nat>> { | ||
public func describeMismatch(actual : Set.Set<Nat>, _description : M.Description) { | ||
Debug.print(debug_show (Iter.toArray(Set.elements(actual))) # " should be " # debug_show (Iter.toArray(Set.elements(expected)))) | ||
}; | ||
|
||
public func matches(actual : Set.Set<Nat>) : Bool { | ||
natSet.equals(actual, expected) | ||
} | ||
}; | ||
|
||
object Random { | ||
var number = 4711; | ||
public func next() : Nat { | ||
number := (15485863 * number + 5) % 15485867; | ||
number | ||
}; | ||
|
||
public func nextNat(range: (Nat, Nat)): Nat { | ||
let n = next(); | ||
let v = n % (range.1 - range.0 + 1) + range.0; | ||
v | ||
}; | ||
|
||
public func nextEntries(range: (Nat, Nat), size: Nat): [Nat] { | ||
Array.tabulate<Nat>(size, func(_ix) { | ||
let key = nextNat(range); key }) | ||
} | ||
}; | ||
|
||
func setGenN(samples_number: Nat, size: Nat, range: (Nat, Nat), chunkSize: Nat): Iter.Iter<[Set.Set<Nat>]> { | ||
object { | ||
var n = 0; | ||
public func next(): ?([Set.Set<Nat>]) { | ||
n += 1; | ||
if (n > samples_number) { | ||
null | ||
} else { | ||
?Array.tabulate<Set.Set<Nat>>(chunkSize, func _i = natSet.fromIter(Random.nextEntries(range, size).vals())) | ||
} | ||
} | ||
} | ||
}; | ||
|
||
func run_all_props(range: (Nat, Nat), size: Nat, set_samples: Nat, query_samples: Nat) { | ||
func prop(name: Text, f: Set.Set<Nat> -> Bool): Suite.Suite { | ||
var error_msg: Text = ""; | ||
test(name, do { | ||
var error = true; | ||
label stop for(sets in setGenN(set_samples, size, range, 1)) { | ||
if (not f(sets[0])) { | ||
error_msg := "Property \"" # name # "\" failed\n"; | ||
error_msg #= "\n s: " # debug_show(Iter.toArray(Set.elements(sets[0]))); | ||
break stop; | ||
} | ||
}; | ||
error_msg | ||
}, M.describedAs(error_msg, M.equals(T.text("")))) | ||
}; | ||
|
||
func prop2(name: Text, f: (Set.Set<Nat>, Set.Set<Nat>) -> Bool): Suite.Suite { | ||
var error_msg: Text = ""; | ||
test(name, do { | ||
var error = true; | ||
label stop for(sets in setGenN(set_samples, size, range, 2)) { | ||
if (not f(sets[0], sets[1])) { | ||
error_msg := "Property \"" # name # "\" failed\n"; | ||
error_msg #= "\n s1: " # debug_show(Iter.toArray(Set.elements(sets[0]))); | ||
error_msg #= "\n s2: " # debug_show(Iter.toArray(Set.elements(sets[1]))); | ||
break stop; | ||
} | ||
}; | ||
error_msg | ||
}, M.describedAs(error_msg, M.equals(T.text("")))) | ||
}; | ||
|
||
func prop3(name: Text, f: (Set.Set<Nat>, Set.Set<Nat>, Set.Set<Nat>) -> Bool): Suite.Suite { | ||
var error_msg: Text = ""; | ||
test(name, do { | ||
var error = true; | ||
label stop for(sets in setGenN(set_samples, size, range, 3)) { | ||
if (not f(sets[0], sets[1], sets[2])) { | ||
error_msg := "Property \"" # name # "\" failed\n"; | ||
error_msg #= "\n s1: " # debug_show(Iter.toArray(Set.elements(sets[0]))); | ||
error_msg #= "\n s2: " # debug_show(Iter.toArray(Set.elements(sets[1]))); | ||
error_msg #= "\n s3: " # debug_show(Iter.toArray(Set.elements(sets[2]))); | ||
break stop; | ||
} | ||
}; | ||
error_msg | ||
}, M.describedAs(error_msg, M.equals(T.text("")))) | ||
}; | ||
|
||
func prop_with_elem(name: Text, f: (Set.Set<Nat>, Nat) -> Bool): Suite.Suite { | ||
var error_msg: Text = ""; | ||
test(name, do { | ||
label stop for(sets in setGenN(set_samples, size, range, 1)) { | ||
for (_query_ix in Iter.range(0, query_samples-1)) { | ||
let key = Random.nextNat(range); | ||
if (not f(sets[0], key)) { | ||
error_msg #= "Property \"" # name # "\" failed"; | ||
error_msg #= "\n s: " # debug_show(Iter.toArray(Set.elements(sets[0]))); | ||
error_msg #= "\n e: " # debug_show(key); | ||
break stop; | ||
} | ||
} | ||
}; | ||
error_msg | ||
}, M.describedAs(error_msg, M.equals(T.text("")))) | ||
}; | ||
|
||
run( | ||
suite("Property tests", | ||
[ | ||
suite("empty", [ | ||
test("not contains(empty(), e)", label res : Bool { | ||
for (_query_ix in Iter.range(0, query_samples-1)) { | ||
let elem = Random.nextNat(range); | ||
if(natSet.contains(Set.empty<Nat>(), elem)) | ||
break res(false); | ||
}; | ||
true; | ||
}, M.equals(T.bool(true))) | ||
]), | ||
|
||
suite("contains & put", [ | ||
prop_with_elem("contains(put(s, e), e)", func (s, e) { | ||
natSet.contains(natSet.put(s, e), e) | ||
}), | ||
prop_with_elem("put(put(s, e), e) == put(s, e)", func (s, e) { | ||
let s1 = natSet.put(s, e); | ||
let s2 = natSet.put(natSet.put(s, e), e); | ||
SetMatcher(s1).matches(s2) | ||
}), | ||
]), | ||
|
||
suite("delete", [ | ||
prop_with_elem("not contains(s, e) ==> delete(s, e) == s", func (s, e) { | ||
if (not natSet.contains(s, e)) { | ||
SetMatcher(s).matches(natSet.delete(s, e)) | ||
} else { true } | ||
}), | ||
prop_with_elem("delete(put(s, e), e) == s", func (s, e) { | ||
if (not natSet.contains(s, e)) { | ||
SetMatcher(s).matches(natSet.delete(natSet.put(s, e), e)) | ||
} else { true } | ||
}), | ||
prop_with_elem("delete(delete(s, e), e)) == delete(s, e)", func (s, e) { | ||
let s1 = natSet.delete(natSet.delete(s, e), e); | ||
let s2 = natSet.delete(s, e); | ||
SetMatcher(s2).matches(s1) | ||
}) | ||
]), | ||
|
||
suite("size", [ | ||
prop_with_elem("size(put(s, e)) == size(s) + int(not contains(s, e))", func (s, e) { | ||
Set.size(natSet.put(s, e)) == Set.size(s) + (if (not natSet.contains(s, e)) {1} else {0}) | ||
}), | ||
prop_with_elem("size(delete(s, e)) + int(contains(s, e)) == size(s)", func (s, e) { | ||
Set.size(natSet.delete(s, e)) + (if (natSet.contains(s, e)) {1} else {0}) == Set.size(s) | ||
}) | ||
]), | ||
|
||
suite("iter", [ | ||
prop("fromIter(elements(s)) == s", func (s) { | ||
SetMatcher(s).matches(natSet.fromIter(Set.elements(s))) | ||
}) | ||
]), | ||
|
||
suite("mapFilter", [ | ||
prop_with_elem("not contains(mapFilter(s, (!=e)), e)", func (s, e) { | ||
not natSet.contains(natSet.mapFilter<Nat>(s, | ||
func (ei) { if (ei != e) {?ei} else {null}}), e) | ||
}), | ||
prop_with_elem("contains(mapFilter(put(s, e), (==e)), e)", func (s, e) { | ||
natSet.contains(natSet.mapFilter<Nat>(natSet.put(s, e), | ||
func (ei) { if (ei == e) {?ei} else {null}}), e) | ||
}) | ||
]), | ||
|
||
suite("map", [ | ||
prop("map(s, id) == s", func (s) { | ||
SetMatcher(s).matches(natSet.map<Nat>(s, func (e) {e})) | ||
}) | ||
]), | ||
|
||
suite("set operations", [ | ||
prop("isSubset(s, s)", func (s) { | ||
natSet.isSubset(s, s) | ||
}), | ||
prop("isSubset(empty(), s)", func (s) { | ||
natSet.isSubset(Set.empty(), s) | ||
}), | ||
prop_with_elem("isSubset(delete(s, e), s)", func (s, e) { | ||
natSet.isSubset(natSet.delete(s, e), s) | ||
}), | ||
prop_with_elem("contains(s, e) ==> not isSubset(s, delete(s, e))", func (s, e) { | ||
if (natSet.contains(s, e)) { | ||
not natSet.isSubset(s, natSet.delete(s, e)) | ||
} else { true } | ||
}), | ||
prop_with_elem("isSubset(s, put(s, e))", func (s, e) { | ||
natSet.isSubset(s, natSet.put(s, e)) | ||
}), | ||
prop_with_elem("not contains(s, e) ==> not isSubset(put(s, e), s)", func (s, e) { | ||
if (not natSet.contains(s, e)) { | ||
not natSet.isSubset(natSet.put(s, e), s) | ||
} else { true } | ||
}), | ||
prop("intersect(empty(), s) == empty()", func (s) { | ||
SetMatcher(Set.empty()).matches(natSet.intersect(Set.empty(), s)) | ||
}), | ||
prop("intersect(s, empty()) == empty()", func (s) { | ||
SetMatcher(Set.empty()).matches(natSet.intersect(s, Set.empty())) | ||
}), | ||
prop("union(s, empty()) == s", func (s) { | ||
SetMatcher(s).matches(natSet.union(s, Set.empty())) | ||
}), | ||
prop("union(empty(), s) == s", func (s) { | ||
SetMatcher(s).matches(natSet.union(Set.empty(), s)) | ||
}), | ||
prop("diff(empty(), s) == empty()", func (s) { | ||
SetMatcher(Set.empty()).matches(natSet.diff(Set.empty(), s)) | ||
}), | ||
prop("diff(s, empty()) == s", func (s) { | ||
SetMatcher(s).matches(natSet.diff(s, Set.empty())) | ||
}), | ||
prop("intersect(s, s) == s", func (s) { | ||
SetMatcher(s).matches(natSet.intersect(s, s)) | ||
}), | ||
prop("union(s, s) == s", func (s) { | ||
SetMatcher(s).matches(natSet.union(s, s)) | ||
}), | ||
prop("diff(s, s) == empty()", func (s) { | ||
SetMatcher(Set.empty()).matches(natSet.diff(s, s)) | ||
}), | ||
prop2("intersect(s1, s2) == intersect(s2, s1)", func (s1, s2) { | ||
SetMatcher(natSet.intersect(s1, s2)).matches(natSet.intersect(s2, s1)) | ||
}), | ||
prop2("union(s1, s2) == union(s2, s1)", func (s1, s2) { | ||
SetMatcher(natSet.union(s1, s2)).matches(natSet.union(s2, s1)) | ||
}), | ||
prop2("isSubset(diff(s1, s2), s1)", func (s1, s2) { | ||
natSet.isSubset(natSet.diff(s1, s2), s1) | ||
}), | ||
prop2("intersect(diff(s1, s2), s2) == empty()", func (s1, s2) { | ||
SetMatcher(natSet.intersect(natSet.diff(s1, s2), s2)).matches(Set.empty()) | ||
}), | ||
prop3("union(union(s1, s2), s3) == union(s1, union(s2, s3))", func (s1, s2, s3) { | ||
SetMatcher(natSet.union(natSet.union(s1, s2), s3)).matches(natSet.union(s1, natSet.union(s2, s3))) | ||
}), | ||
prop3("intersect(intersect(s1, s2), s3) == intersect(s1, intersect(s2, s3))", func (s1, s2, s3) { | ||
SetMatcher(natSet.intersect(natSet.intersect(s1, s2), s3)).matches(natSet.intersect(s1, natSet.intersect(s2, s3))) | ||
}), | ||
prop3("union(s1, intersect(s2, s3)) == intersect(union(s1, s2), union(s1, s3))", func (s1, s2, s3) { | ||
SetMatcher(natSet.union(s1, natSet.intersect(s2, s3))).matches( | ||
natSet.intersect(natSet.union(s1, s2), natSet.union(s1, s3))) | ||
}), | ||
prop3("intersect(s1, union(s2, s3)) == union(intersect(s1, s2), intersect(s1, s3))", func (s1, s2, s3) { | ||
SetMatcher(natSet.intersect(s1, natSet.union(s2, s3))).matches( | ||
natSet.union(natSet.intersect(s1, s2), natSet.intersect(s1, s3))) | ||
}), | ||
]), | ||
])) | ||
}; | ||
|
||
run_all_props((1, 3), 0, 1, 10); | ||
run_all_props((1, 5), 5, 100, 100); | ||
run_all_props((1, 10), 10, 100, 100); | ||
run_all_props((1, 100), 20, 100, 100); | ||
run_all_props((1, 1000), 100, 100, 100); |