Skip to content

Commit

Permalink
Merge #2998
Browse files Browse the repository at this point in the history
2998: Reduce boilerplate needed to use `liftShrink` functions. r=jonathanknowles a=jonathanknowles

## Issue Number

None

## Background

Our `test-utils` package currently provides the `liftShrink` family of functions, which make it possible to write shrinkers in a compositional style.

These functions are especially useful when defining shrinkers for record types: if you already have a shrinker for each of the fields, then you can build a shrinker for record values by composing the individual shrinkers with an appropriate `liftShrink`.

However, when using these functions to shrink records, callers currently have to define boilerplate functions to convert between records and tuples.

## Summary

This PR removes the need to define boilerplate tuple conversion functions.

For example, we can make the following simplification:

```patch
 shrinkSelectionParams :: SelectionParams -> [SelectionParams]
 shrinkSelectionParams =
-    shrinkMapBy ofTuple toTuple $ liftShrink9
+    liftShrink9 SelectionParams
         shrinkAssetsToBurn
         shrinkAssetsToMint
         shrinkOutputsToCover
         shrinkRewardWithdrawal
         shrinkCertificateDepositsTaken
         shrinkCertificateDepositsReturned
         shrinkCollateralRequirement
         shrinkUTxOAvailableForCollateral
         shrinkUTxOAvailableForInputs
-  where
-    toTuple (SelectionParams a b c d e f g h i) = (a, b, c, d, e, f, g, h, i)
-    ofTuple (a, b, c, d, e, f, g, h, i) = (SelectionParams a b c d e f g h i)
```

Co-authored-by: Jonathan Knowles <[email protected]>
  • Loading branch information
iohk-bors[bot] and jonathanknowles authored Nov 3, 2021
2 parents c8cbdb8 + b5ea488 commit 159c80a
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ genSelectionSkeleton = SelectionSkeleton

shrinkSelectionSkeleton :: SelectionSkeleton -> [SelectionSkeleton]
shrinkSelectionSkeleton =
shrinkMapBy tupleToSkeleton skeletonToTuple $ liftShrink3
liftShrink3 SelectionSkeleton
shrinkSkeletonInputCount
shrinkSkeletonOutputs
shrinkSkeletonChange
Expand All @@ -80,6 +80,3 @@ shrinkSelectionSkeleton =
shrinkSkeletonChange =
shrinkList $
shrinkMapBy Set.fromList Set.toList (shrinkList shrinkAssetId)

skeletonToTuple (SelectionSkeleton a b c) = (a, b, c)
tupleToSkeleton (a, b, c) = (SelectionSkeleton a b c)
15 changes: 5 additions & 10 deletions lib/core/src/Cardano/Wallet/Primitive/Types/Tx/Gen.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE RecordWildCards #-}
Expand Down Expand Up @@ -55,6 +56,8 @@ import Data.Text.Class
( FromText (..) )
import Data.Word
( Word32 )
import GHC.Generics
( Generic )
import Test.QuickCheck
( Gen
, arbitrary
Expand Down Expand Up @@ -105,7 +108,7 @@ data TxWithoutId = TxWithoutId
, withdrawals :: !(Map RewardAccount Coin)
, scriptValidity :: !(Maybe TxScriptValidity)
}
deriving (Eq, Ord, Show)
deriving (Eq, Generic, Ord, Show)

genTxWithoutId :: Gen TxWithoutId
genTxWithoutId = TxWithoutId
Expand All @@ -119,7 +122,7 @@ genTxWithoutId = TxWithoutId

shrinkTxWithoutId :: TxWithoutId -> [TxWithoutId]
shrinkTxWithoutId =
shrinkMapBy tupleToTxWithoutId txWithoutIdToTuple $ liftShrink7
liftShrink7 TxWithoutId
(liftShrink shrinkCoinPositive)
(shrinkList (liftShrink2 shrinkTxIn shrinkCoinPositive))
(shrinkList (liftShrink2 shrinkTxIn shrinkCoinPositive))
Expand All @@ -134,14 +137,6 @@ txWithoutIdToTx tx@TxWithoutId {..} = Tx {txId = mockHash tx, ..}
txToTxWithoutId :: Tx -> TxWithoutId
txToTxWithoutId Tx {..} = TxWithoutId {..}

txWithoutIdToTuple :: TxWithoutId -> _
txWithoutIdToTuple (TxWithoutId a1 a2 a3 a4 a5 a6 a7) =
(a1, a2, a3, a4, a5, a6, a7)

tupleToTxWithoutId :: _ -> TxWithoutId
tupleToTxWithoutId (a1, a2, a3, a4, a5, a6, a7) =
(TxWithoutId a1 a2 a3 a4 a5 a6 a7)

genTxScriptValidity :: Gen TxScriptValidity
genTxScriptValidity = genericArbitrary

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -614,16 +614,13 @@ genSelectionParams genPreselectedInputs genUTxOIndex' = do

shrinkSelectionParams :: SelectionParams -> [SelectionParams]
shrinkSelectionParams =
shrinkMapBy tupleToParams paramsToTuple $ liftShrink6
liftShrink6 SelectionParams
(shrinkList shrinkTxOut)
(shrinkUTxOSelection)
(shrinkCoin)
(shrinkCoin)
(shrinkTokenMap)
(shrinkTokenMap)
where
paramsToTuple (SelectionParams a b c d e f) = (a, b, c, d, e, f)
tupleToParams (a, b, c, d, e, f) = (SelectionParams a b c d e f)

prop_performSelection_small
:: MockSelectionConstraints
Expand Down Expand Up @@ -2044,14 +2041,11 @@ genMockSelectionConstraints = MockSelectionConstraints
shrinkMockSelectionConstraints
:: MockSelectionConstraints -> [MockSelectionConstraints]
shrinkMockSelectionConstraints =
shrinkMapBy tupleToMock mockToTuple $ liftShrink4
liftShrink4 MockSelectionConstraints
shrinkMockAssessTokenBundleSize
shrinkMockComputeMinimumAdaQuantity
shrinkMockComputeMinimumCost
shrinkMockComputeSelectionLimit
where
mockToTuple (MockSelectionConstraints a b c d) = (a, b, c, d)
tupleToMock (a, b, c, d) = (MockSelectionConstraints a b c d)

unMockSelectionConstraints :: MockSelectionConstraints -> SelectionConstraints
unMockSelectionConstraints m = SelectionConstraints
Expand Down
11 changes: 2 additions & 9 deletions lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelectionSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ import Test.QuickCheck
, scale
, shrink
, shrinkList
, shrinkMapBy
, vectorOf
, (===)
)
Expand Down Expand Up @@ -533,7 +532,7 @@ genMockSelectionConstraints = MockSelectionConstraints
shrinkMockSelectionConstraints
:: MockSelectionConstraints -> [MockSelectionConstraints]
shrinkMockSelectionConstraints =
shrinkMapBy toMock unMock $ liftShrink8
liftShrink8 MockSelectionConstraints
shrinkMockAssessTokenBundleSize
shrinkCertificateDepositAmount
shrinkMockComputeMinimumAdaQuantity
Expand All @@ -542,9 +541,6 @@ shrinkMockSelectionConstraints =
shrinkMaximumCollateralInputCount
shrinkMinimumCollateralPercentage
shrinkMockUTxOSuitableForCollateral
where
unMock (MockSelectionConstraints a b c d e f g h) = (a, b, c, d, e, f, g, h)
toMock (a, b, c, d, e, f, g, h) = (MockSelectionConstraints a b c d e f g h)

unMockSelectionConstraints :: MockSelectionConstraints -> SelectionConstraints
unMockSelectionConstraints m = SelectionConstraints
Expand Down Expand Up @@ -640,7 +636,7 @@ genSelectionParams = SelectionParams

shrinkSelectionParams :: SelectionParams -> [SelectionParams]
shrinkSelectionParams =
shrinkMapBy ofTuple toTuple $ liftShrink9
liftShrink9 SelectionParams
shrinkAssetsToBurn
shrinkAssetsToMint
shrinkOutputsToCover
Expand All @@ -650,9 +646,6 @@ shrinkSelectionParams =
shrinkCollateralRequirement
shrinkUTxOAvailableForCollateral
shrinkUTxOAvailableForInputs
where
toTuple (SelectionParams a b c d e f g h i) = (a, b, c, d, e, f, g, h, i)
ofTuple (a, b, c, d, e, f, g, h, i) = (SelectionParams a b c d e f g h i)

--------------------------------------------------------------------------------
-- Assets to mint and burn
Expand Down
2 changes: 2 additions & 0 deletions lib/test-utils/cardano-wallet-test-utils.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ library
, directory
, either
, fmt
, generic-lens
, hspec-core
, hspec-expectations
, hspec-golden-aeson
Expand All @@ -64,6 +65,7 @@ library
hs-source-dirs:
src
exposed-modules:
Data.Generic.Fields
Test.Hspec.Extra
Test.Hspec.Goldens
Test.QuickCheck.Extra
Expand Down
120 changes: 120 additions & 0 deletions lib/test-utils/src/Data/Generic/Fields.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeApplications #-}

-- |
-- Copyright: © 2021 IOHK
-- License: Apache-2.0
--
-- Generic functions and types relating to fields of data types.
--
module Data.Generic.Fields
(
-- * Generic constraints for data types with fields
HasFields1
, HasFields2
, HasFields3
, HasFields4
, HasFields5
, HasFields6
, HasFields7
, HasFields8
, HasFields9

-- * Generic conversion of values to tuples
, toTuple1
, toTuple2
, toTuple3
, toTuple4
, toTuple5
, toTuple6
, toTuple7
, toTuple8
, toTuple9
)
where

import Data.Generics.Internal.VL.Lens
( (^.) )
import Data.Generics.Product.Positions
( HasPosition', position' )

--------------------------------------------------------------------------------
-- Generic constraints for data types with fields
--------------------------------------------------------------------------------

type HasFields1 r a =
(HasPosition' 1 r a)

type HasFields2 r a b =
(HasFields1 r a, HasPosition' 2 r b)

type HasFields3 r a b c =
(HasFields2 r a b, HasPosition' 3 r c)

type HasFields4 r a b c d =
(HasFields3 r a b c, HasPosition' 4 r d)

type HasFields5 r a b c d e =
(HasFields4 r a b c d, HasPosition' 5 r e)

type HasFields6 r a b c d e f =
(HasFields5 r a b c d e, HasPosition' 6 r f)

type HasFields7 r a b c d e f g =
(HasFields6 r a b c d e f, HasPosition' 7 r g)

type HasFields8 r a b c d e f g h =
(HasFields7 r a b c d e f g, HasPosition' 8 r h)

type HasFields9 r a b c d e f g h i =
(HasFields8 r a b c d e f g h, HasPosition' 9 r i)

--------------------------------------------------------------------------------
-- Generic conversion of values to tuples
--------------------------------------------------------------------------------

toTuple1 :: HasFields1 r a => r -> (a)
toTuple1 r = (r ^. position' @1)

toTuple2 :: HasFields2 r a b => r -> (a, b)
toTuple2 r = (a, r ^. position' @2)
where
(a) = toTuple1 r

toTuple3 :: HasFields3 r a b c => r -> (a, b, c)
toTuple3 r = (a, b, r ^. position' @3)
where
(a, b) = toTuple2 r

toTuple4 :: HasFields4 r a b c d => r -> (a, b, c, d)
toTuple4 r = (a, b, c, r ^. position' @4)
where
(a, b, c) = toTuple3 r

toTuple5 :: HasFields5 r a b c d e => r -> (a, b, c, d, e)
toTuple5 r = (a, b, c, d, r ^. position' @5)
where
(a, b, c, d) = toTuple4 r

toTuple6 :: HasFields6 r a b c d e f => r -> (a, b, c, d, e, f)
toTuple6 r = (a, b, c, d, e, r ^. position' @6)
where
(a, b, c, d, e) = toTuple5 r

toTuple7 :: HasFields7 r a b c d e f g => r -> (a, b, c, d, e, f, g)
toTuple7 r = (a, b, c, d, e, f, r ^. position' @7)
where
(a, b, c, d, e, f) = toTuple6 r

toTuple8 :: HasFields8 r a b c d e f g h => r -> (a, b, c, d, e, f, g, h)
toTuple8 r = (a, b, c, d, e, f, g, r ^. position' @8)
where
(a, b, c, d, e, f, g) = toTuple7 r

toTuple9 :: HasFields9 r a b c d e f g h i => r -> (a, b, c, d, e, f, g, h, i)
toTuple9 r = (a, b, c, d, e, f, g, h, r ^. position' @9)
where
(a, b, c, d, e, f, g, h) = toTuple8 r
Loading

0 comments on commit 159c80a

Please sign in to comment.