diff --git a/nix/pkgs/haskell/materialized-unix/.plan.nix/plutus-ledger-api.nix b/nix/pkgs/haskell/materialized-unix/.plan.nix/plutus-ledger-api.nix index a3d65eaaff3..cbf6ec7f58b 100644 --- a/nix/pkgs/haskell/materialized-unix/.plan.nix/plutus-ledger-api.nix +++ b/nix/pkgs/haskell/materialized-unix/.plan.nix/plutus-ledger-api.nix @@ -42,6 +42,7 @@ (hsPkgs."cardano-crypto" or (errorHandler.buildDepError "cardano-crypto")) (hsPkgs."flat" or (errorHandler.buildDepError "flat")) (hsPkgs."hashable" or (errorHandler.buildDepError "hashable")) + (hsPkgs."hedgehog" or (errorHandler.buildDepError "hedgehog")) (hsPkgs."plutus-core" or (errorHandler.buildDepError "plutus-core")) (hsPkgs."memory" or (errorHandler.buildDepError "memory")) (hsPkgs."mtl" or (errorHandler.buildDepError "mtl")) @@ -86,10 +87,13 @@ depends = [ (hsPkgs."base" or (errorHandler.buildDepError "base")) (hsPkgs."plutus-ledger-api" or (errorHandler.buildDepError "plutus-ledger-api")) + (hsPkgs."hedgehog" or (errorHandler.buildDepError "hedgehog")) (hsPkgs."tasty" or (errorHandler.buildDepError "tasty")) + (hsPkgs."tasty-hedgehog" or (errorHandler.buildDepError "tasty-hedgehog")) (hsPkgs."tasty-hunit" or (errorHandler.buildDepError "tasty-hunit")) ]; buildable = true; + modules = [ "Spec/Interval" ]; hsSourceDirs = [ "test" ]; mainPath = [ "Spec.hs" ]; }; diff --git a/plutus-ledger-api/plutus-ledger-api.cabal b/plutus-ledger-api/plutus-ledger-api.cabal index 7a9923ab7cc..43e16e1dbb4 100644 --- a/plutus-ledger-api/plutus-ledger-api.cabal +++ b/plutus-ledger-api/plutus-ledger-api.cabal @@ -63,6 +63,7 @@ library cardano-crypto -any, flat -any, hashable -any, + hedgehog -any, plutus-core -any, memory -any, mtl -any, @@ -83,8 +84,12 @@ test-suite plutus-ledger-api-test type: exitcode-stdio-1.0 main-is: Spec.hs hs-source-dirs: test + other-modules: + Spec.Interval build-depends: base >=4.9 && <5, plutus-ledger-api -any, + hedgehog -any, tasty -any, + tasty-hedgehog -any, tasty-hunit -any diff --git a/plutus-ledger-api/test/Spec.hs b/plutus-ledger-api/test/Spec.hs index bf4641bfd99..c8405e4bd65 100644 --- a/plutus-ledger-api/test/Spec.hs +++ b/plutus-ledger-api/test/Spec.hs @@ -7,6 +7,7 @@ import Data.Either import Data.Maybe import Plutus.V1.Ledger.Api import Plutus.V1.Ledger.Examples +import qualified Spec.Interval main :: IO () main = defaultMain tests @@ -25,4 +26,5 @@ tests :: TestTree tests = testGroup "plutus-ledger-api" [ alwaysTrue , alwaysFalse + , Spec.Interval.tests ] diff --git a/plutus-ledger-api/test/Spec/Interval.hs b/plutus-ledger-api/test/Spec/Interval.hs new file mode 100644 index 00000000000..ffb2644522e --- /dev/null +++ b/plutus-ledger-api/test/Spec/Interval.hs @@ -0,0 +1,82 @@ +{-# LANGUAGE TypeApplications #-} + +module Spec.Interval where + +import Data.List (sort) +import Hedgehog (Property, forAll, property) +import qualified Hedgehog +import qualified Hedgehog.Gen as Gen +import qualified Hedgehog.Range as Range +import qualified Plutus.V1.Ledger.Interval as Interval +import Plutus.V1.Ledger.Slot +import Test.Tasty +import Test.Tasty.HUnit +import Test.Tasty.Hedgehog (testProperty) + +alwaysIsNotEmpty :: TestTree +alwaysIsNotEmpty = + testCase "always is not empty" $ + assertBool "always" (not $ Interval.isEmpty (Interval.always :: Interval.Interval Slot)) + +neverIsEmpty :: TestTree +neverIsEmpty = + testCase "never is empty" $ + assertBool "never" (Interval.isEmpty (Interval.never :: Interval.Interval Slot)) + +intvlIsEmpty :: Property +intvlIsEmpty = property $ do + (i1, i2) <- forAll $ (,) <$> Gen.integral (fromIntegral <$> Range.linearBounded @Int) <*> Gen.integral (fromIntegral <$> Range.linearBounded @Int) + let (from, to) = (min i1 i2, max i1 i2) + nonEmpty = Interval.interval (Slot from) (Slot to) + empty = Interval.interval (Slot to) (Slot from) + Hedgehog.assert $ from == to || Interval.isEmpty empty + Hedgehog.assert $ not $ Interval.isEmpty nonEmpty + +intvlIntersection :: Property +intvlIntersection = property $ do + ints <- forAll $ traverse (const $ Gen.integral (fromIntegral <$> Range.linearBounded @Int)) [1 .. 4] + let [i1, i2, i3, i4] = Slot <$> sort ints + outer = Interval.interval i1 i4 + inner = Interval.interval i2 i3 + intersec = outer `Interval.intersection` inner + + lower = Interval.interval i1 i2 + higher = Interval.interval i2 i3 + common = Interval.interval i2 i2 + intersec2 = lower `Interval.intersection` higher + + Hedgehog.assert $ intersec == inner + Hedgehog.assert $ intersec2 == common + +intvlIntersectionWithAlwaysNever :: Property +intvlIntersectionWithAlwaysNever = property $ do + (i1, i2) <- forAll $ (,) <$> Gen.integral (fromIntegral <$> Range.linearBounded @Int) <*> Gen.integral (fromIntegral <$> Range.linearBounded @Int) + let (from, to) = (min i1 i2, max i1 i2) + i = Interval.interval (Slot from) (Slot to) + e = Interval.interval (Slot to) (Slot from) + + Hedgehog.assert $ Interval.never == i `Interval.intersection` Interval.never + Hedgehog.assert $ i == i `Interval.intersection` Interval.always + Hedgehog.assert $ e == e `Interval.intersection` i + +intvlOverlaps :: Property +intvlOverlaps = property $ do + (i1, i2) <- forAll $ (,) <$> Gen.integral (fromIntegral <$> Range.linearBounded @Int) <*> Gen.integral (fromIntegral <$> Range.linearBounded @Int) + let (from, to) = (min i1 i2, max i1 i2) + i = Interval.interval (Slot from) (Slot to) + + Hedgehog.assert $ i `Interval.overlaps` i + Hedgehog.assert $ Interval.always `Interval.overlaps` i + Hedgehog.assert $ not $ Interval.never `Interval.overlaps` i + +tests :: TestTree +tests = + testGroup + "plutus-ledger-api-interval" + [ neverIsEmpty + , alwaysIsNotEmpty + , testProperty "interval intersection" intvlIntersection + , testProperty "interval intersection with always/never" intvlIntersectionWithAlwaysNever + , testProperty "interval isEmpty" intvlIsEmpty + , testProperty "interval overlaps" intvlOverlaps + ]