Francesco Komauli
Developer
As we have seen during the course,
PBT is convenient because:
When PBT exhibits a counterexample, it means we can be in one of the following situations:
In both cases we can fix the problem and continue our development.
When no counterexamples are produced,
still there can be problems in our program.
Confidence in the fact that what the tool is testing is indeed what we defined in the specifications.
Inductive color := Red | Black.
Inductive tree :=
| Leaf : tree
| Node : color → tree → nat → tree → tree.
Fixpoint insert (x: nat) (t: tree): tree := ...
Inductive is_redblack : tree → color → nat → Prop :=
| IsRB_leaf: ∀ c, is_redblack Leaf c 0
| IsRB_r: ∀ n tl tr h,
is_redblack tl Red h →
is_redblack tr Red h →
is_redblack (Node Red tl n tr) Black h
| IsRB_b: ∀ c n tl tr h,
is_redblack tl Black h →
is_redblack tr Black h →
is_redblack (Node Black tl n tr) c (S h).
we expect that hold in our system
Definition insert_preserves_redblack :=
∀ x t h, is_redblack t Red h →
∃ h', is_redblack (insert x t) Red h'
Decisional procedures that check
whether a property of the system holds
Fixpoint is_redblack_dec (t: tree) (c: color): bool :=
match t with
| Leaf ⇒ true
| Node c' tl _ tr ⇒
match c' with
| Black ⇒
(black_height_dec tl == black_height_dec tr) &&
is_redblack_dec tl Black &&
is_redblack_dec tr Black
| Red ⇒
match c with
| Black ⇒
(black_height_dec tl == black_height_dec tr) &&
is_redblack_dec tl Red &&
is_redblack_dec tr Red
| Red ⇒ false
end
end
end.
Lemma is_redblack_exP:
∀ (t: tree) (c: color),
reflect (∃ n, is_redblack t c n)
(is_redblack_dec t c).
Program Fixpoint genRBTree_height (hc : nat*color) {wf wf_hc hc} : G tree :=
match hc with
| (0, Red) => returnGen Leaf
| (0, Black) => oneOf [returnGen Leaf;
(do! n <- arbitrary; returnGen (Node Red Leaf n Leaf))]
| (S h, Red) => liftGen4 Node (returnGen Black) (genRBTree_height (h, Black))
arbitrary (genRBTree_height (h, Black))
| (S h, Black) => do! c' <- genColor;
let h' := match c' with Red => S h | Black => h end in
liftGen4 Node (returnGen c') (genRBTree_height (h', c'))
arbitrary (genRBTree_height (h', c')) end.
Definition genRBTree := bindGen arbitrary (fun h => genRBTree_height (h, Red)).
Soundness: generate only values for which the property holds
Completeness: generate all the values for which the property holds
Correctness: soundness + completeness
x ∈ P <-> P x
{ x | P x }
Definition Pred (A: Type): Type := A → Prop.
Definition set_eq {A} (s s': Pred A) := ∀ A: s A ←→ s' A.
x ∈ G <-> Prob { x <- G } > 0
{ x | x <- G }
{ x | x <- G } = { x | P x }
Lemma genRBTree_correct:
genRBTree ←→ (fun t ⇒ ∃ h, is_redblack t Red h).
using QuickChick's combinators
Section Checker.
Context { Gen: Type → Type }
{ H: GenMonad Gen }.
Definition insert_is_redblack_checker: Property Gen :=
forAll arbitraryNat (fun n ⇒
forAll genRBTree (fun t ⇒
is_redblack_dec t Red ==>
is_redblack_dec (insert n t) Red)).
End Checker.
Whether we are testing
the model's high level specification
By considering checkers
as generators of testing results
(* High Level Specification *)
Definition insert_preserves_redblack :=
∀ x t h, is_redblack t Red h →
∃ h', is_redblack (insert x t) Red h'
(* Checker Correctness *)
Lemma insert_is_redblack_checker_correct :
semChecker insert_is_redblack_checker
↔ insert_preserves_redblack.
We proved that our checkers and generators are correct.
Tests have stronger guarantees of their behaviour.
We increased confidence about our model,
without formally proving it.
By Francesco Komauli