generic-diff-instances (empty) → 0.1.0.0
raw patch · 11 files changed
+682/−0 lines, 11 filesdep +QuickCheckdep +basedep +containers
Dependencies added: QuickCheck, base, containers, generic-diff, generic-diff-instances, generics-sop, hspec, sop-core, text
Files
- CHANGELOG.md +14/−0
- README.md +12/−0
- generic-diff-instances.cabal +101/−0
- src/Generics/Diff/Special/Map.hs +68/−0
- src/Generics/Diff/Special/Seq.hs +22/−0
- src/Generics/Diff/Special/Set.hs +66/−0
- src/Generics/Diff/Special/Tree.hs +89/−0
- test/Generics/Diff/PropertyTestsSpec.hs +55/−0
- test/Generics/Diff/UnitTestsSpec.hs +231/−0
- test/Spec.hs +1/−0
- test/Util.hs +23/−0
+ CHANGELOG.md view
@@ -0,0 +1,14 @@+# Changelog++All notable changes to `generic-diff-instances` will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),+and this project adheres to [Haskell Package Versioning Policy](https://pvp.haskell.org).++## [Unreleased]++### Added++- `Diff` instances for `Map`, `Seq`, `Set` and `Tree` from `containers`.++[unreleased]: https://github.com/fpringle/generic-diff/compare/7dbc273...HEAD
+ README.md view
@@ -0,0 +1,12 @@+[](https://github.com/fpringle/generic-diff/actions/workflows/haskell.yml)++# `generic-diff` instances++The [generic-diff](https://hackage.haskell.org/package/generic-diff) package+aims to be lightweight and not force any instances which might have more than+one interpretation.++This package provides a more comprehensive set of instances for types from a+range of common packages.++Currently we provide instances for [Map](https://hackage-content.haskell.org/package/containers-0.8/docs/Data-Map-Lazy.html#t:Map), [Seq](https://hackage-content.haskell.org/package/containers-0.8/docs/Data-Sequence.html#t:Seq), [Set](https://hackage-content.haskell.org/package/containers-0.8/docs/Data-Set.html#t:Set) and [Tree](https://hackage-content.haskell.org/package/containers-0.8/docs/Data-Tree.html#t:Tree) from the containers package.
+ generic-diff-instances.cabal view
@@ -0,0 +1,101 @@+cabal-version: 3.0+name: generic-diff-instances+version: 0.1.0.0+synopsis: Diff instances for common types+description:+ The [generic-diff](https://hackage.haskell.org/package/generic-diff) package+ aims to be lightweight and not force any instances which might have more than+ one interpretation.++ This package provides a more comprehensive set of instances for types from a+ range of common packages.+license: BSD-3-Clause+author: Frederick Pringle+maintainer: freddyjepringle@gmail.com+copyright: Copyright(c) Frederick Pringle 2025+homepage: https://github.com/fpringle/generic-diff+category: Generics, Test+build-type: Simple+extra-doc-files: CHANGELOG.md+ README.md+tested-with:+ GHC == 9.12.2+ GHC == 9.10.1+ GHC == 9.8.2+ GHC == 9.6.5+ GHC == 9.4.8+ GHC == 9.2.8+ GHC == 9.0.2+ GHC == 8.10.7+ GHC == 8.6.5++common warnings+ ghc-options: -Wall++common deps+ build-depends:+ , base >= 4.12 && < 5+ , generic-diff >= 0.1 && < 0.2+ , sop-core >= 0.4.0.1 && < 0.6+ , generics-sop >= 0.4 && < 0.6+ , text >= 1.1 && < 2.2+ , containers >= 0.5.9.2 && < 0.9++common extensions+ default-extensions:+ AllowAmbiguousTypes+ ConstraintKinds+ DataKinds+ DefaultSignatures+ DeriveGeneric+ FlexibleContexts+ FlexibleInstances+ GADTs+ LambdaCase+ OverloadedStrings+ PolyKinds+ RankNTypes+ RecordWildCards+ ScopedTypeVariables+ StandaloneDeriving+ TypeApplications+ TypeFamilies+ TypeOperators+ UndecidableInstances+ ViewPatterns++library+ import:+ warnings+ , deps+ , extensions+ exposed-modules:+ Generics.Diff.Special.Seq+ Generics.Diff.Special.Map+ Generics.Diff.Special.Set+ Generics.Diff.Special.Tree++ hs-source-dirs: src+ default-language: Haskell2010++test-suite generic-diff-instances-test+ import:+ warnings+ , deps+ , extensions+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules:+ Generics.Diff.UnitTestsSpec+ Generics.Diff.PropertyTestsSpec+ Util+ build-tool-depends:+ hspec-discover:hspec-discover+ ghc-options: -Wno-orphans+ build-depends:+ , generic-diff+ , generic-diff-instances+ , QuickCheck+ , hspec
+ src/Generics/Diff/Special/Map.hs view
@@ -0,0 +1,68 @@+{-# OPTIONS_GHC -Wno-orphans #-}++{- | A worked example of implementing 'SpecialDiff' (and thereby 'Diff') for 'Map's.++We make the choice to prioritise speed over exhaustiveness: in other words we stop when we find+one difference between the two input maps. Alternatively, we could have gone the other way and+enumerated all the difference between the inputs, using some kind of intersection test. This is left+as an exercise for the reader.+-}+module Generics.Diff.Special.Map+ ( MapDiffError (..)+ )+where++import Control.Applicative ((<|>))+import Data.Map (Map)+import qualified Data.Map.Internal as Map+import Generics.Diff+import Generics.Diff.Render+import Generics.Diff.Special++-- | For 'Map's, we only pick out (maximum) one difference between the two inputs. There are three possibilities:+data MapDiffError k v+ = -- | A key is found in both maps, but they have different values.+ DiffAtKey k (DiffError v)+ | -- | The right set contains an element that isn't found in the left set+ LeftMissingKey k+ | -- | The left set contains an element that isn't found in the right set+ RightMissingKey k+ deriving (Show, Eq)++{- | Render a 'MapDiffError'. This is a top-level function because we'll use it in the implementations+of 'renderSpecialDiffError' for both 'Map' and 'Data.IntMap.IntMap'.+-}+mapDiffErrorDoc :: (Show k) => MapDiffError k v -> Doc+mapDiffErrorDoc = \case+ -- Since we have a nested 'DiffError' on the value, we use 'makeDoc'.+ DiffAtKey k err ->+ let lns = pure ("Both maps contain key " <> showB k <> " but the values differ:")+ in makeDoc lns err+ LeftMissingKey k ->+ linesDoc $ pure $ "The right map contains key " <> showB k <> " but the left doesn't"+ RightMissingKey k ->+ linesDoc $ pure $ "The left map contains key " <> showB k <> " but the right doesn't"++instance (Show k, Ord k, Diff v) => SpecialDiff (Map k v) where+ type SpecialDiffError (Map k v) = MapDiffError k v++ -- base cases+ specialDiff Map.Tip Map.Tip = Nothing+ specialDiff Map.Tip (Map.Bin _ k _ _ _) = Just $ LeftMissingKey k+ specialDiff (Map.Bin _ k _ _ _) Map.Tip = Just $ RightMissingKey k+ -- recursive set, using Map.split+ specialDiff (Map.Bin _ k lVal left right) r = case Map.lookup k r of+ Nothing -> Just $ RightMissingKey k+ Just rVal ->+ -- first we check if the values are different (using the 'Diff' instance on v)+ case diff lVal rVal of+ Error err -> Just $ DiffAtKey k err+ Equal ->+ -- otherwise, split and recurse+ let (less, more) = Map.split k r+ in specialDiff left less <|> specialDiff right more++ renderSpecialDiffError = mapDiffErrorDoc++instance (Show k, Ord k, Diff v) => Diff (Map k v) where+ diff = diffWithSpecial
+ src/Generics/Diff/Special/Seq.hs view
@@ -0,0 +1,22 @@+{-# OPTIONS_GHC -Wno-orphans #-}++-- | A worked example of implementing 'SpecialDiff' (and thereby 'Diff') for 'Seq's.+module Generics.Diff.Special.Seq () where++import Data.Foldable (toList)+import Data.Function (on)+import Data.Sequence (Seq)+import Generics.Diff+import Generics.Diff.Render+import Generics.Diff.Special++{- | Just as with the instance for lists or non-empty lists (see "Generics.Diff.Special.List"),+we can use 'ListDiffError', 'diffListWith' and 'listDiffErrorDoc'.+-}+instance (Diff a) => SpecialDiff (Seq a) where+ type SpecialDiffError (Seq a) = ListDiffError a+ specialDiff = diffListWith diff `on` toList+ renderSpecialDiffError = listDiffErrorDoc "sequence"++instance (Diff a) => Diff (Seq a) where+ diff = diffWithSpecial
+ src/Generics/Diff/Special/Set.hs view
@@ -0,0 +1,66 @@+{-# OPTIONS_GHC -Wno-orphans #-}++{- | A worked example of implementing 'SpecialDiff' (and thereby 'Diff') for 'Set's.++We make the choice to prioritise speed over exhaustiveness: in other words we stop when we find+one difference between the two input sets. Alternatively, we could have gone the other way and+enumerated all the difference between the inputs, using some kind of intersection test. This is left+as an exercise for the reader.++Note that the implementation for maps in "Generics.Diff.Special.Map" is very similar; this is since a+@'Set' k@ can be seen as equivalent to @Map k ()@.+-}+module Generics.Diff.Special.Set+ ( SetDiffError (..)+ )+where++import Control.Applicative ((<|>))+import Data.Set (Set)+import qualified Data.Set.Internal as Set+import Generics.Diff+import Generics.Diff.Render+import Generics.Diff.Special++-- | For 'Set's, we only pick out (maximum) one difference between the two inputs. There are two possibilities:+data SetDiffError k+ = -- | The right set contains an element that isn't found in the left set+ LeftMissingKey k+ | -- | The left set contains an element that isn't found in the right set+ RightMissingKey k+ deriving (Show, Eq)++{- | Render a 'SetDiffError'. This is a top-level function because we'll use it in the implementations+of 'renderSpecialDiffError' for both 'Set' and 'Data.IntSet.IntSet'.++There are no nested 'DiffError's here, so we use 'linesDoc'.+-}+setDiffErrorDoc :: (Show k) => SetDiffError k -> Doc+setDiffErrorDoc = \case+ LeftMissingKey k ->+ linesDoc $ pure $ "The right set contains key " <> showB k <> " but the left doesn't"+ RightMissingKey k ->+ linesDoc $ pure $ "The left set contains key " <> showB k <> " but the right doesn't"++-- First we define an instance of 'SpecialDiff'. We need 'Show' and 'Eq' so that 'SetDiffError'+-- also has these instances; we need 'Ord' to compare elements of the set.+instance (Show k, Eq k, Ord k) => SpecialDiff (Set k) where+ type SpecialDiffError (Set k) = SetDiffError k++ -- base cases+ specialDiff Set.Tip Set.Tip = Nothing+ specialDiff Set.Tip (Set.Bin _ k _ _) = Just $ LeftMissingKey k+ specialDiff (Set.Bin _ k _ _) Set.Tip = Just $ RightMissingKey k+ -- recursive step, using Set.split+ specialDiff (Set.Bin _ k left right) r =+ if Set.notMember k r+ then Just $ RightMissingKey k+ else+ let (less, more) = Set.split k r+ in specialDiff left less <|> specialDiff right more++ renderSpecialDiffError = setDiffErrorDoc++-- Now we can implement 'Diff' using 'diffWithSpecial'.+instance (Show k, Ord k) => Diff (Set k) where+ diff = diffWithSpecial
+ src/Generics/Diff/Special/Tree.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE DerivingVia #-}+{-# OPTIONS_GHC -Wno-orphans #-}++{- | A worked example of implementing 'SpecialDiff' (and thereby 'Diff') for 'Tree.Tree's.++As with other 3rd-party types, there are different approaches we can take here. We'll show 2 of them:++- using 'gspecialDiffNested';+- using 'SpecialDiff' and a custom diff type.+-}+module Generics.Diff.Special.Tree where++import Control.Applicative+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.Text.Lazy.Builder as TB+import qualified Data.Tree as Tree+import Generics.Diff+import Generics.Diff.Render+import Generics.Diff.Special+import Generics.SOP.GGP++------------------------------------------------------------+-- Using gspecialDiffNested++-- | Generically-derived instance.+instance (Diff a) => SpecialDiff (Tree.Tree a) where+ type SpecialDiffError (Tree.Tree a) = DiffErrorNested (GCode (Tree.Tree a))+ specialDiff = gspecialDiffNested+ renderSpecialDiffError = diffErrorNestedDoc++instance (Diff a) => Diff (Tree.Tree a) where+ diff = diffWithSpecial++------------------------------------------------------------+-- Using SpecialDiff++{- | A newtype wrapper around 'Tree.Tree' to demonstrate one alternate way we could hand-write+a 'SpecialDiff' instance.+-}+newtype CustomTree a = CustomTree (Tree.Tree a)+ deriving (Show) via (Tree.Tree a)++-- | Where are we in the tree? Each element of the list says which child node we step to next.+newtype TreePath = TreePath [Int]+ deriving (Show, Eq) via [Int]++-- | A custom error type for 'CustomTree'.+data CustomTreeDiffError a+ = DiffAtNode TreePath (DiffError a)+ | WrongLengthsOfChildren TreePath Int Int+ deriving (Show, Eq)++-- | Render a tree path as a 'TB.Builder'+renderTreePath :: TreePath -> TB.Builder+renderTreePath (TreePath []) = "<root>"+renderTreePath (TreePath (x : xs)) = mconcat $ showB x : ["->" <> showB y | y <- xs]++instance (Diff a) => SpecialDiff (CustomTree a) where+ type SpecialDiffError (CustomTree a) = CustomTreeDiffError a++ renderSpecialDiffError = \case+ DiffAtNode path err ->+ let ls = pure $ "Diff between nodes at path " <> renderTreePath path+ in makeDoc ls err+ WrongLengthsOfChildren path l r ->+ let ls =+ ("Child lists at path " <> renderTreePath path <> " are wrong lengths")+ :| [ "Length of left child list: " <> showB l+ , "Length of right child list: " <> showB r+ ]+ in linesDoc ls++ specialDiff (CustomTree l) (CustomTree r) = go [] l r+ where+ go curPath (Tree.Node n1 f1) (Tree.Node n2 f2) =+ case diff n1 n2 of+ Error err -> Just $ DiffAtNode curTreePath err+ Equal ->+ let go' n = go (n : curPath)+ goChildren _ [] [] = Nothing+ goChildren n [] ys = Just $ WrongLengthsOfChildren curTreePath n (n + length ys)+ goChildren n xs [] = Just $ WrongLengthsOfChildren curTreePath (n + length xs) n+ goChildren n (x : xs) (y : ys) = go' n x y <|> goChildren (n + 1) xs ys+ in goChildren 0 f1 f2+ where+ curTreePath = TreePath $ reverse curPath++instance (Diff a) => Diff (CustomTree a) where+ diff = diffWithSpecial
+ test/Generics/Diff/PropertyTestsSpec.hs view
@@ -0,0 +1,55 @@+{-# OPTIONS_GHC -Wno-partial-fields #-}++module Generics.Diff.PropertyTestsSpec where++import Data.Fixed+import Data.Map (Map)+import Data.Proxy+import Data.Sequence (Seq)+import Data.Set (Set)+import Data.Tree (Tree)+import Data.Version+import Foreign.C.Types+import Generics.Diff+import Generics.Diff.Instances ()+import Generics.Diff.Special.Map ()+import Generics.Diff.Special.Seq ()+import Generics.Diff.Special.Set ()+import Generics.Diff.Special.Tree ()+import qualified Test.Hspec as H+import qualified Test.Hspec.QuickCheck as H+import qualified Test.QuickCheck as Q+import Util++spec :: H.Spec+spec = do+ H.describe "x == y => x `diff` y == Equal" $+ manyTypes propEqualGivesEqual+ H.describe "x `diff` y == Equal => x == y" $+ manyTypes propEqualMeansEqual++-- | If the two inputs are equal, 'diff' should return 'Equal'.+propEqualGivesEqual :: forall a. (Q.Arbitrary a, Diff a, Show a) => Proxy a -> Q.Property+propEqualGivesEqual _ = Q.property $ \a -> propDiffResult @a a a Equal++-- | If the two inputs are not equal, 'diff' should never return 'Equal'.+propEqualMeansEqual :: forall a. (Q.Arbitrary a, Eq a, Diff a, Show a) => Proxy a -> Q.Property+propEqualMeansEqual _ = Q.property $ \leftValue rightValue ->+ leftValue /= rightValue Q.==>+ diff @a leftValue rightValue /= Equal++manyTypes :: (forall x. (Q.Arbitrary x, Eq x, Diff x, Show x) => Proxy x -> Q.Property) -> H.Spec+manyTypes prop = do+ H.prop "Set Char" $ prop $ Proxy @(Set Char)+ H.prop "Set Int" $ prop $ Proxy @(Set Int)++ H.prop "Seq Rational" $ prop $ Proxy @(Seq Rational)+ H.prop "Seq Version" $ prop $ Proxy @(Seq Version)+ H.prop "Seq CLong" $ prop $ Proxy @(Seq CLong)++ H.prop "Tree CChar" $ prop $ Proxy @(Tree CChar)+ H.prop "Tree Uni" $ prop $ Proxy @(Tree Uni)+ H.prop "Tree Deci" $ prop $ Proxy @(Tree Deci)++ H.prop "Map Int Char" $ prop $ Proxy @(Map Int Char)+ H.prop "Map Char Int" $ prop $ Proxy @(Map Char Int)
+ test/Generics/Diff/UnitTestsSpec.hs view
@@ -0,0 +1,231 @@+{-# OPTIONS_GHC -Wno-partial-fields #-}++module Generics.Diff.UnitTestsSpec where++import Data.Foldable+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq+import Data.Set (Set)+import qualified Data.Set as Set+import qualified Data.Text as T+import Data.Tree (Tree)+import qualified Data.Tree as Tree+import Generics.Diff+import Generics.Diff.Instances ()+import Generics.Diff.Special.Map as Map+import Generics.Diff.Special.Seq ()+import Generics.Diff.Special.Set as Set+import Generics.Diff.Special.Tree+import Generics.SOP+import Generics.SOP.GGP+import qualified Test.Hspec as H+import qualified Test.Hspec.QuickCheck as H+import Util++spec :: H.Spec+spec =+ H.describe "Unit tests" $ do+ H.describe "Map" $ traverse_ specTestSet mapTestSets+ H.describe "Set" $ traverse_ specTestSet setTestSets+ H.describe "Seq" $ traverse_ specTestSet seqTestSets+ H.describe "Tree" $ traverse_ specTestSet treeTestSets+ H.describe "CustomTree" $ traverse_ specTestSet customTreeTestSets++specTestSet :: (Diff a, Show a) => TestSet a -> H.Spec+specTestSet TestSet {..} =+ H.prop (T.unpack setName) $+ propDiffResult leftValue rightValue expectedDiffResult++data TestSet a = TestSet+ { setName :: T.Text+ , leftValue :: a+ , rightValue :: a+ , expectedDiffResult :: DiffResult a+ }+ deriving (Show)++setTestSets :: [TestSet (Set Int)]+setTestSets =+ [ TestSet+ { setName = "Equal"+ , leftValue = value1+ , rightValue = value1+ , expectedDiffResult = Equal+ }+ , TestSet+ { setName = "Diff, LeftMissingKey"+ , leftValue = value1+ , rightValue = value2+ , expectedDiffResult = Error error2+ }+ , TestSet+ { setName = "Diff, RightMissingKey"+ , leftValue = value1+ , rightValue = value3+ , expectedDiffResult = Error error3+ }+ ]+ where+ value1 = Set.fromList [1, 3]++ value2 = Set.fromList [1, 2, 3]+ error2 = DiffSpecial $ Set.LeftMissingKey 2++ value3 = Set.fromList [1]+ error3 = DiffSpecial $ Set.RightMissingKey 3++mapTestSets :: [TestSet (Map Int String)]+mapTestSets =+ [ TestSet+ { setName = "Equal"+ , leftValue = value1+ , rightValue = value1+ , expectedDiffResult = Equal+ }+ , TestSet+ { setName = "Diff, DiffAtKey"+ , leftValue = value1+ , rightValue = value2+ , expectedDiffResult = Error error2+ }+ , TestSet+ { setName = "Diff, LeftMissingKey"+ , leftValue = value1+ , rightValue = value3+ , expectedDiffResult = Error error3+ }+ , TestSet+ { setName = "Diff, RightMissingKey"+ , leftValue = value1+ , rightValue = value4+ , expectedDiffResult = Error error4+ }+ ]+ where+ value1 = Map.fromList [(1, "one"), (3, "three")]++ value2 = Map.fromList [(1, "one"), (3, "THREE")]+ error2 = DiffSpecial $ Map.DiffAtKey 3 TopLevelNotEqual++ value3 = Map.fromList [(1, "one"), (2, "two"), (3, "three")]+ error3 = DiffSpecial $ Map.LeftMissingKey 2++ value4 = Map.fromList [(1, "one")]+ error4 = DiffSpecial $ Map.RightMissingKey 3++seqTestSets :: [TestSet (Seq Int)]+seqTestSets =+ [ TestSet+ { setName = "Equal"+ , leftValue = value1+ , rightValue = value1+ , expectedDiffResult = Equal+ }+ , TestSet+ { setName = "Diff, WrongLengths"+ , leftValue = value1+ , rightValue = value2+ , expectedDiffResult = Error error2+ }+ , TestSet+ { setName = "Diff, DiffAtIndex"+ , leftValue = value1+ , rightValue = value3+ , expectedDiffResult = Error error3+ }+ ]+ where+ value1 = Seq.fromList [1, 3]++ value2 = Seq.fromList [1, 3, 4]+ error2 = DiffSpecial $ WrongLengths 2 3++ value3 = Seq.fromList [1, 2]+ error3 = DiffSpecial $ DiffAtIndex 1 TopLevelNotEqual++treeTestSets :: [TestSet (Tree Int)]+treeTestSets =+ [ TestSet+ { setName = "Equal"+ , leftValue = value1+ , rightValue = value1+ , expectedDiffResult = Equal+ }+ , TestSet+ { setName = "Diff, FieldMismatch, level 1"+ , leftValue = value1+ , rightValue = value2+ , expectedDiffResult = Error error2+ }+ , TestSet+ { setName = "Diff, FieldMismatch, level 2, WrongLengths"+ , leftValue = value1+ , rightValue = value3+ , expectedDiffResult = Error error3+ }+ , TestSet+ { setName = "Diff, FieldMismatch, level 2, DiffAtIndex"+ , leftValue = value1+ , rightValue = value4+ , expectedDiffResult = Error error4+ }+ ]+ where+ value1 = Tree.Node 1 [Tree.Node 2 [], Tree.Node 3 [Tree.Node 4 [], Tree.Node 5 []]]++ value2 = Tree.Node 2 []+ error2 = DiffSpecial $ FieldMismatch $ DiffAtField $ Z $ nodeInfo :*: Z TopLevelNotEqual++ value3 = Tree.Node 1 [Tree.Node 2 []]+ error3 =+ let e = DiffSpecial $ WrongLengths 2 1+ in DiffSpecial $ FieldMismatch $ DiffAtField $ Z $ nodeInfo :*: S (Z e)++ value4 = Tree.Node 1 [Tree.Node 2 [], Tree.Node 4 []]+ error4 =+ let e = DiffSpecial $ DiffAtIndex 1 $ DiffSpecial $ FieldMismatch $ DiffAtField $ Z $ nodeInfo :*: Z TopLevelNotEqual+ in DiffSpecial $ FieldMismatch $ DiffAtField $ Z $ nodeInfo :*: S (Z e)++ nodeInfo :: ConstructorInfo '[Int, [Tree Int]]+ nodeInfo :* _ = constructorInfo $ gdatatypeInfo $ Proxy @(Tree Int)++customTreeTestSets :: [TestSet (CustomTree Int)]+customTreeTestSets =+ [ TestSet+ { setName = "Equal"+ , leftValue = value1+ , rightValue = value1+ , expectedDiffResult = Equal+ }+ , TestSet+ { setName = "Diff, DiffAtNode, level 1"+ , leftValue = value1+ , rightValue = value2+ , expectedDiffResult = Error error2+ }+ , TestSet+ { setName = "Diff, WrongLengthsOfChildren, level 2"+ , leftValue = value1+ , rightValue = value3+ , expectedDiffResult = Error error3+ }+ , TestSet+ { setName = "Diff, DiffAtNode, level 2"+ , leftValue = value1+ , rightValue = value4+ , expectedDiffResult = Error error4+ }+ ]+ where+ value1 = CustomTree $ Tree.Node 1 [Tree.Node 2 [], Tree.Node 3 [Tree.Node 4 [], Tree.Node 5 []]]++ value2 = CustomTree $ Tree.Node 2 []+ error2 = DiffSpecial $ DiffAtNode (TreePath []) TopLevelNotEqual++ value3 = CustomTree $ Tree.Node 1 [Tree.Node 2 []]+ error3 = DiffSpecial $ WrongLengthsOfChildren (TreePath []) 2 1++ value4 = CustomTree $ Tree.Node 1 [Tree.Node 2 [], Tree.Node 4 []]+ error4 = DiffSpecial $ DiffAtNode (TreePath [1]) TopLevelNotEqual
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/Util.hs view
@@ -0,0 +1,23 @@+module Util where++import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TB+import Generics.Diff+import Generics.Diff.Instances ()+import Generics.Diff.Render+import qualified Test.QuickCheck as Q++propDiffResult :: (Diff a, Show a) => a -> a -> DiffResult a -> Q.Property+propDiffResult leftValue rightValue expectedDiffResult =+ let actualDiffResult = diff leftValue rightValue+ eq = expectedDiffResult == actualDiffResult+ showDiffResult = TL.unpack . TB.toLazyText . renderDiffResult+ addLabel =+ if eq+ then Q.property+ else+ Q.counterexample ("Expected DiffResult:\n" <> showDiffResult expectedDiffResult)+ . Q.counterexample ("Actual DiffResult:\n" <> showDiffResult actualDiffResult)+ . Q.counterexample ("Left value:\n" <> show leftValue)+ . Q.counterexample ("Right value:\n" <> show rightValue)+ in addLabel eq