Documentation

Plausible.Testable

Testable Class #

Testable propositions have a procedure that can generate counter-examples together with a proof that they invalidate the proposition.

This is a port of the Haskell QuickCheck library.

Creating Customized Instances #

The type classes Testable, SampleableExt and Shrinkable are the means by which Plausible creates samples and tests them. For instance, the proposition ∀ i j : Nat, i ≤ j has a Testable instance because Nat is sampleable and i ≤ j is decidable. Once Plausible finds the Testable instance, it can start using the instance to repeatedly creating samples and checking whether they satisfy the property. Once it has found a counter-example it will then use a Shrinkable instance to reduce the example. This allows the user to create new instances and apply Plausible to new situations.

What do I do if I'm testing a property about my newly defined type? #

Let us consider a type made for a new formalization:

structure MyType where
  x : Nat
  y : Nat
  h : x ≤ y
  deriving Repr

How do we test a property about MyType? For instance, let us consider Testable.check <| ∀ a b : MyType, a.y ≤ b.x → a.x ≤ b.y. Writing this property as is will give us an error because we do not have an instance of Shrinkable MyType and SampleableExt MyType. We can define one as follows:

instance : Shrinkable MyType where
  shrink := fun ⟨x, y, _⟩ =>
    let proxy := Shrinkable.shrink (x, y - x)
    proxy.map (fun (fst, snd) => ⟨fst, fst + snd, by omega⟩)

instance : SampleableExt MyType :=
  SampleableExt.mkSelfContained do
    let x ← SampleableExt.interpSample Nat
    let xyDiff ← SampleableExt.interpSample Nat
    return ⟨x, x + xyDiff, by omega⟩

Again, we take advantage of the fact that other types have useful Shrinkable implementations, in this case Prod.

Main definitions #

References #

inductive Plausible.TestResult (p : Prop) :

Result of trying to disprove p

  • success {p : Prop} : Unit ⊕' pTestResult p

    Succeed when we find another example satisfying p. In success h, h is an optional proof of the proposition. Without the proof, all we know is that we found one example where p holds. With a proof, the one test was sufficient to prove that p holds and we do not need to keep finding examples.

  • gaveUp {p : Prop} : NatTestResult p

    Give up when a well-formed example cannot be generated. gaveUp n tells us that n invalid examples were tried.

  • failure {p : Prop} : ¬pList StringNatTestResult p

    A counter-example to p; the strings specify values for the relevant variables. failure h vs n also carries a proof that p does not hold. This way, we can guarantee that there will be no false positive. The last component, n, is the number of times that the counter-example was shrunk.

Instances For
    @[implicit_reducible]

    Configuration for testing a property.

    • numInst : Nat

      How many test instances to generate.

    • maxSize : Nat

      The maximum size of the values to generate.

    • numRetries : Nat
    • traceDiscarded : Bool

      Enable tracing of values that didn't fulfill preconditions and were thus discarded.

    • traceSuccesses : Bool

      Enable tracing of values that fulfilled the property and were thus discarded.

    • traceShrink : Bool

      Enable basic tracing of shrinking.

    • traceShrinkCandidates : Bool

      Enable tracing of all attempted values during shrinking.

    • randomSeed : Option Nat

      Hard code the seed to use for the RNG

    • quiet : Bool

      Disable output.

    • sorryIfNoTestable : Bool

      If true, when the Testable instance required to begin testing cannot be synthesized, silently admit the goal with sorry instead of throwing an error.

    Instances For

      Allow elaboration of Configuration arguments to tactics.

      Instances For

        PrintableProp p allows one to print a proposition so that Plausible can indicate how values relate to each other. It's basically a poor man's delaborator.

        Instances
          @[implicit_reducible, instance 100]

          Testable p uses random examples to try to disprove p.

          Instances
            Instances For
              @[implicit_reducible]
              def Plausible.TestResult.combine {p q : Prop} :
              Unit ⊕' (pq)Unit ⊕' pUnit ⊕' q

              Applicative combinator proof carrying test results.

              Instances For

                Combine the test result for properties p and q to create a test for their conjunction.

                Instances For

                  Combine the test result for properties p and q to create a test for their disjunction.

                  Instances For
                    def Plausible.TestResult.imp {p q : Prop} (h : qp) (r : TestResult p) :
                    optParam (Unit ⊕' (pq)) (PSum.inl ())TestResult q

                    If q → p, then ¬ p → ¬ q which means that testing p can allow us to find counter-examples to q.

                    Instances For
                      def Plausible.TestResult.iff {p q : Prop} (h : q p) (r : TestResult p) :

                      Test q by testing p and proving the equivalence between the two.

                      Instances For
                        def Plausible.TestResult.addInfo {p q : Prop} (x : String) (h : qp) (r : TestResult p) :
                        optParam (Unit ⊕' (pq)) (PSum.inl ())TestResult q

                        When we assign a value to a universally quantified variable, we record that value using this function so that our counter-examples can be informative.

                        Instances For
                          def Plausible.TestResult.addVarInfo {p q : Prop} {γ : Type u_1} [Repr γ] (var : String) (x : γ) (h : qp) (r : TestResult p) :
                          optParam (Unit ⊕' (pq)) (PSum.inl ())TestResult q

                          Add some formatting to the information recorded by addInfo.

                          Instances For

                            A configuration with all the trace options enabled, useful for debugging.

                            Instances For
                              Instances For
                                def Plausible.Testable.slimTrace {m : TypeType u_1} [Pure m] (s : String) :

                                A dbgTrace with special formatting

                                Instances For
                                  @[implicit_reducible]
                                  @[implicit_reducible]
                                  @[implicit_reducible]
                                  @[implicit_reducible]
                                  instance Plausible.Testable.decGuardTestable {p : Prop} {var : String} [PrintableProp p] [Decidable p] {β : pProp} [(h : p) → Testable (β h)] :
                                  Testable (NamedBinder var (∀ (h : p), β h))
                                  @[implicit_reducible]
                                  instance Plausible.Testable.forallTypesTestable {var : String} {f : TypeProp} [Testable (f Int)] :
                                  Testable (NamedBinder var (∀ (x : Type), f x))
                                  @[implicit_reducible, instance 100]
                                  instance Plausible.Testable.forallTypesULiftTestable {var : String} {f : Type u → Prop} [Testable (f (ULift Int))] :
                                  Testable (NamedBinder var (∀ (x : Type u), f x))

                                  Format the counter-examples found in a test failure.

                                  Instances For

                                    Increase the number of shrinking steps in a test result.

                                    Instances For
                                      @[implicit_reducible]
                                      instance Plausible.Testable.instInhabitedOptionTOfPure {α : Type u} {m : Type u → Type u_1} [Pure m] :
                                      partial def Plausible.Testable.minimizeAux {α : Sort u_1} [SampleableExt α] {β : αProp} [(x : α) → Testable (β x)] (cfg : Configuration) (var : String) (x : SampleableExt.proxy α) (n : Nat) :

                                      Shrink a counter-example x by using Shrinkable.shrink x, picking the first candidate that falsifies a property and recursively shrinking that one. The process is guaranteed to terminate because shrink x produces a proof that all the values it produces are smaller (according to SizeOf) than x.

                                      def Plausible.Testable.minimize {α : Sort u_1} [SampleableExt α] {β : αProp} [(x : α) → Testable (β x)] (cfg : Configuration) (var : String) (x : SampleableExt.proxy α) (r : TestResult (β (SampleableExt.interp x))) :

                                      Once a property fails to hold on an example, look for smaller counter-examples to show the user.

                                      Instances For
                                        @[implicit_reducible]
                                        instance Plausible.Testable.varTestable {var : String} {α : Type u_1} [SampleableExt α] {β : αProp} [(x : α) → Testable (β x)] :
                                        Testable (NamedBinder var (∀ (x : α), β x))

                                        Test a universal property by creating a sample of the right type and instantiating the bound variable with it.

                                        @[implicit_reducible]
                                        instance Plausible.Testable.propVarTestable {var : String} {β : PropProp} [(b : Bool) → Testable (β (b = true))] :
                                        Testable (NamedBinder var (∀ (p : Prop), β p))

                                        Test a universal property about propositions

                                        @[implicit_reducible, instance 10000]
                                        instance Plausible.Testable.unusedVarTestable {var : String} {α : Sort u_1} {β : Prop} [Nonempty α] [Testable β] :
                                        Testable (NamedBinder var (∀ (a : α), β))
                                        @[implicit_reducible, instance 2000]
                                        instance Plausible.Testable.subtypeVarTestable {var : String} {α : Type u} {p β : αProp} [(x : α) → PrintableProp (p x)] [(x : α) → Testable (β x)] [SampleableExt (Subtype p)] {var' : String} :
                                        Testable (NamedBinder var (∀ (x : α), NamedBinder var' (p xβ x)))
                                        @[implicit_reducible, instance 100]
                                        @[implicit_reducible]
                                        instance Plausible.Eq.printableProp {α : Type u_1} [Repr α] {x y : α} :
                                        @[implicit_reducible]
                                        instance Plausible.Ne.printableProp {α : Type u_1} [Repr α] {x y : α} :
                                        @[implicit_reducible]
                                        instance Plausible.LE.printableProp {α : Type u_1} [Repr α] [LE α] {x y : α} :
                                        @[implicit_reducible]
                                        instance Plausible.LT.printableProp {α : Type u_1} [Repr α] [LT α] {x y : α} :
                                        @[implicit_reducible]
                                        @[implicit_reducible]
                                        @[implicit_reducible]
                                        @[implicit_reducible]
                                        @[implicit_reducible]
                                        @[implicit_reducible]
                                        @[implicit_reducible]
                                        def Plausible.retry {p : Prop} (cmd : Gen (TestResult p)) :

                                        Execute cmd and repeat every time the result is gaveUp (at most n times).

                                        Instances For
                                          def Plausible.giveUp {p : Prop} (x : Nat) :

                                          Count the number of times the test procedure gave up.

                                          Instances For

                                            Try n times to find a counter-example for p.

                                            Instances For

                                              Try to find a counter-example of p.

                                              Instances For

                                                Run a test suite for p in IO using the global RNG in stdGenRef.

                                                Instances For

                                                  Traverse the syntax of a proposition to find universal quantifiers quantifiers and add NamedBinder annotations next to them.

                                                  @[reducible, inline]

                                                  DecorationsOf p is used as a hint to mk_decorations to specify that the goal should be satisfied with a proposition equivalent to p with added annotations.

                                                  Instances For

                                                    In a goal of the shape DecorationsOf p, mk_decoration examines the syntax of p and adds NamedBinder around universal quantifications to improve error messages. This tool can be used in the declaration of a function as follows:

                                                    def foo (p : Prop) (p' : Decorations.DecorationsOf p := by mk_decorations) [Testable p'] : ...
                                                    

                                                    p is the parameter given by the user, p' is a definitionally equivalent proposition where the quantifiers are annotated with NamedBinder.

                                                    Instances For
                                                      def Plausible.Testable.check (p : Prop) (cfg : Configuration := { }) (p' : Decorations.DecorationsOf p := by mk_decorations) [Testable p'] :

                                                      Run a test suite for p and throw an exception if p does not hold.

                                                      Instances For