hspec-golden-aeson 0.2.1.0 → 0.3.0.0
raw patch · 5 files changed
+188/−146 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Test.Aeson.Internal.GoldenSpecs: ModuleName :: FilePath -> ModuleName
- Test.Aeson.Internal.GoldenSpecs: TopDir :: FilePath -> TopDir
- Test.Aeson.Internal.GoldenSpecs: TypeName :: FilePath -> TypeName
- Test.Aeson.Internal.GoldenSpecs: TypeNameInfo :: TopDir -> Maybe ModuleName -> TypeName -> TypeNameInfo a
- Test.Aeson.Internal.GoldenSpecs: [typeNameModuleName] :: TypeNameInfo a -> Maybe ModuleName
- Test.Aeson.Internal.GoldenSpecs: [typeNameTopDir] :: TypeNameInfo a -> TopDir
- Test.Aeson.Internal.GoldenSpecs: [typeNameTypeName] :: TypeNameInfo a -> TypeName
- Test.Aeson.Internal.GoldenSpecs: [unModuleName] :: ModuleName -> FilePath
- Test.Aeson.Internal.GoldenSpecs: [unTopDir] :: TopDir -> FilePath
- Test.Aeson.Internal.GoldenSpecs: [unTypeName] :: TypeName -> FilePath
- Test.Aeson.Internal.GoldenSpecs: data TypeNameInfo a
- Test.Aeson.Internal.GoldenSpecs: fromTypeable :: forall a. Arbitrary a => Typeable a => Settings -> Proxy a -> IO (TypeNameInfo a)
- Test.Aeson.Internal.GoldenSpecs: newtype ModuleName
- Test.Aeson.Internal.GoldenSpecs: newtype TopDir
- Test.Aeson.Internal.GoldenSpecs: newtype TypeName
+ Test.Aeson.GenericSpecs: [comparisonFile] :: Settings -> ComparisonFile
+ Test.Aeson.GenericSpecs: mkGoldenFileForType :: forall a. (ToJSON a, ToADTArbitrary a) => Int -> Proxy a -> FilePath -> IO ()
+ Test.Aeson.Internal.ADT.GoldenSpecs: mkGoldenFileForType :: forall a. (ToJSON a, ToADTArbitrary a) => Int -> Proxy a -> FilePath -> IO ()
+ Test.Aeson.Internal.Utils: FaultyFile :: ComparisonFile
+ Test.Aeson.Internal.Utils: ModuleName :: FilePath -> ModuleName
+ Test.Aeson.Internal.Utils: OverwriteGoldenFile :: ComparisonFile
+ Test.Aeson.Internal.Utils: TopDir :: FilePath -> TopDir
+ Test.Aeson.Internal.Utils: TypeName :: FilePath -> TypeName
+ Test.Aeson.Internal.Utils: TypeNameInfo :: TopDir -> Maybe ModuleName -> TypeName -> TypeNameInfo a
+ Test.Aeson.Internal.Utils: [comparisonFile] :: Settings -> ComparisonFile
+ Test.Aeson.Internal.Utils: [typeNameModuleName] :: TypeNameInfo a -> Maybe ModuleName
+ Test.Aeson.Internal.Utils: [typeNameTopDir] :: TypeNameInfo a -> TopDir
+ Test.Aeson.Internal.Utils: [typeNameTypeName] :: TypeNameInfo a -> TypeName
+ Test.Aeson.Internal.Utils: [unModuleName] :: ModuleName -> FilePath
+ Test.Aeson.Internal.Utils: [unTopDir] :: TopDir -> FilePath
+ Test.Aeson.Internal.Utils: [unTypeName] :: TypeName -> FilePath
+ Test.Aeson.Internal.Utils: data ComparisonFile
+ Test.Aeson.Internal.Utils: data TypeNameInfo a
+ Test.Aeson.Internal.Utils: instance GHC.Classes.Eq (Test.Aeson.Internal.Utils.TypeNameInfo a)
+ Test.Aeson.Internal.Utils: instance GHC.Classes.Eq Test.Aeson.Internal.Utils.ModuleName
+ Test.Aeson.Internal.Utils: instance GHC.Classes.Eq Test.Aeson.Internal.Utils.TopDir
+ Test.Aeson.Internal.Utils: instance GHC.Classes.Eq Test.Aeson.Internal.Utils.TypeName
+ Test.Aeson.Internal.Utils: instance GHC.Read.Read (Test.Aeson.Internal.Utils.TypeNameInfo a)
+ Test.Aeson.Internal.Utils: instance GHC.Read.Read Test.Aeson.Internal.Utils.ModuleName
+ Test.Aeson.Internal.Utils: instance GHC.Read.Read Test.Aeson.Internal.Utils.TopDir
+ Test.Aeson.Internal.Utils: instance GHC.Read.Read Test.Aeson.Internal.Utils.TypeName
+ Test.Aeson.Internal.Utils: instance GHC.Show.Show (Test.Aeson.Internal.Utils.TypeNameInfo a)
+ Test.Aeson.Internal.Utils: instance GHC.Show.Show Test.Aeson.Internal.Utils.ModuleName
+ Test.Aeson.Internal.Utils: instance GHC.Show.Show Test.Aeson.Internal.Utils.TopDir
+ Test.Aeson.Internal.Utils: instance GHC.Show.Show Test.Aeson.Internal.Utils.TypeName
+ Test.Aeson.Internal.Utils: mkTypeNameInfo :: forall a. Arbitrary a => Typeable a => Settings -> Proxy a -> IO (TypeNameInfo a)
+ Test.Aeson.Internal.Utils: newtype ModuleName
+ Test.Aeson.Internal.Utils: newtype TopDir
+ Test.Aeson.Internal.Utils: newtype TypeName
- Test.Aeson.GenericSpecs: Settings :: GoldenDirectoryOption -> Bool -> Int -> Settings
+ Test.Aeson.GenericSpecs: Settings :: GoldenDirectoryOption -> Bool -> Int -> ComparisonFile -> Settings
- Test.Aeson.Internal.GoldenSpecs: compareWithGolden :: forall a. (Arbitrary a, ToJSON a, FromJSON a) => TypeNameInfo a -> Proxy a -> FilePath -> IO ()
+ Test.Aeson.Internal.GoldenSpecs: compareWithGolden :: forall a. (Arbitrary a, ToJSON a, FromJSON a) => TypeNameInfo a -> Proxy a -> FilePath -> ComparisonFile -> IO ()
- Test.Aeson.Internal.Utils: Settings :: GoldenDirectoryOption -> Bool -> Int -> Settings
+ Test.Aeson.Internal.Utils: Settings :: GoldenDirectoryOption -> Bool -> Int -> ComparisonFile -> Settings
Files
- hspec-golden-aeson.cabal +73/−60
- src/Test/Aeson/GenericSpecs.hs +20/−16
- src/Test/Aeson/Internal/ADT/GoldenSpecs.hs +21/−0
- src/Test/Aeson/Internal/GoldenSpecs.hs +12/−59
- src/Test/Aeson/Internal/Utils.hs +62/−11
hspec-golden-aeson.cabal view
@@ -1,64 +1,77 @@-name: hspec-golden-aeson-version: 0.2.1.0-synopsis: Use tests to monitor changes in Aeson serialization-description: Use tests to monitor changes in Aeson serialization-homepage: https://github.com/plow-technologies/hspec-golden-aeson#readme-license: BSD3-license-file: LICENSE-author: James M.C. Haver II-maintainer: mchaver@gmail.com-copyright: 2016 Plow Technologies-category: Testing-build-type: Simple-cabal-version: >=1.10--library- hs-source-dirs: src- exposed-modules: Test.Aeson.GenericSpecs- Test.Aeson.Internal.ADT.GoldenSpecs- Test.Aeson.Internal.ADT.RoundtripSpecs- Test.Aeson.Internal.GoldenSpecs- Test.Aeson.Internal.RoundtripSpecs- Test.Aeson.Internal.RandomSamples- Test.Aeson.Internal.Utils+-- This file has been generated from package.yaml by hpack version 0.17.1.+--+-- see: https://github.com/sol/hpack - build-depends: base >= 4.7 && < 5- , aeson- , aeson-pretty- , bytestring- , directory- , filepath- , hspec- , random- , quickcheck-arbitrary-adt >= 0.2.0.0- , QuickCheck- , transformers- default-language: Haskell2010- ghc-options: -Wall-test-suite test- type: exitcode-stdio-1.0- hs-source-dirs: test- main-is: Spec.hs- other-modules: Test.Aeson.GenericSpecsSpec- Test.Types- Test.Types.AlteredSelector- Test.Types.BrokenSerialization- Test.Types.MismatchedToAndFromSerialization- Test.Types.NewSelector- Test.Utils- build-depends: base- , aeson- , directory- , hspec- , hspec-core- , hspec-golden-aeson- , silently- , quickcheck-arbitrary-adt- , QuickCheck- , transformers- ghc-options: -Wall -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -fno-warn-name-shadowing- default-language: Haskell2010+name: hspec-golden-aeson+version: 0.3.0.0+synopsis: Use tests to monitor changes in Aeson serialization+description: Use tests to monitor changes in Aeson serialization+category: Testing+homepage: https://github.com/plow-technologies/hspec-golden-aeson#readme+bug-reports: https://github.com/plow-technologies/hspec-golden-aeson/issues+author: James M.C. Haver II+maintainer: mchaver@gmail.com+copyright: 2016 Plow Technologies+license: BSD3+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10 source-repository head- type: git+ type: git location: https://github.com/plow-technologies/hspec-golden-aeson++library+ hs-source-dirs:+ src+ ghc-options: -Wall+ build-depends:+ base >= 4.7 && < 5+ , aeson+ , aeson-pretty+ , bytestring+ , directory+ , filepath+ , hspec+ , random+ , quickcheck-arbitrary-adt >= 0.2.0.0+ , QuickCheck+ , transformers+ exposed-modules:+ Test.Aeson.GenericSpecs+ Test.Aeson.Internal.ADT.GoldenSpecs+ Test.Aeson.Internal.ADT.RoundtripSpecs+ Test.Aeson.Internal.GoldenSpecs+ Test.Aeson.Internal.RoundtripSpecs+ Test.Aeson.Internal.RandomSamples+ Test.Aeson.Internal.Utils+ other-modules:+ Paths_hspec_golden_aeson+ default-language: Haskell2010++test-suite test+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs:+ test+ ghc-options: -Wall+ build-depends:+ base+ , aeson+ , directory+ , hspec+ , hspec-core+ , hspec-golden-aeson+ , silently+ , quickcheck-arbitrary-adt+ , QuickCheck+ , transformers+ other-modules:+ Test.Aeson.GenericSpecsSpec+ Test.Types+ Test.Types.AlteredSelector+ Test.Types.BrokenSerialization+ Test.Types.MismatchedToAndFromSerialization+ Test.Types.NewSelector+ Test.Utils+ default-language: Haskell2010
src/Test/Aeson/GenericSpecs.hs view
@@ -16,33 +16,37 @@ {-# LANGUAGE ScopedTypeVariables #-} -module Test.Aeson.GenericSpecs (- -- * Arbitrary testing- goldenSpecs-, roundtripSpecs-, roundtripAndGoldenSpecs+module Test.Aeson.GenericSpecs+ (+ -- * Arbitrary testing+ goldenSpecs+ , roundtripSpecs+ , roundtripAndGoldenSpecs -- * ToADTArbitrary testing-, goldenADTSpecs-, roundtripADTSpecs-, roundtripAndGoldenADTSpecs-, roundtripAndGoldenADTSpecsWithSettings+ , goldenADTSpecs+ , roundtripADTSpecs+ , roundtripAndGoldenADTSpecs+ , roundtripAndGoldenADTSpecsWithSettings + -- * Make Files+ , mkGoldenFileForType+ -- * Util-, shouldBeIdentity-, GoldenDirectoryOption(..)-, Settings(..)-, defaultSettings+ , shouldBeIdentity+ , GoldenDirectoryOption(..)+ , Settings(..)+ , defaultSettings -- * re-exports-, Proxy(..)-) where+ , Proxy(..)+ ) where import Data.Aeson (FromJSON, ToJSON) import Data.Proxy import Data.Typeable -import Test.Aeson.Internal.ADT.GoldenSpecs (goldenADTSpecs)+import Test.Aeson.Internal.ADT.GoldenSpecs (goldenADTSpecs, mkGoldenFileForType) import Test.Aeson.Internal.ADT.RoundtripSpecs (roundtripADTSpecs) import Test.Aeson.Internal.GoldenSpecs (goldenSpecs) import Test.Aeson.Internal.RoundtripSpecs (roundtripSpecs)
src/Test/Aeson/Internal/ADT/GoldenSpecs.hs view
@@ -41,6 +41,10 @@ import Test.QuickCheck import Test.QuickCheck.Arbitrary.ADT ++import Data.Monoid ((<>))++ -- | Tests to ensure that JSON encoding has not unintentionally changed. This -- could be caused by the following: --@@ -160,3 +164,20 @@ where correctedSampleSize = if sampleSize <= 0 then 1 else sampleSize gen = setSeed rSeed $ replicateM correctedSampleSize (toADTArbitrary (Proxy :: Proxy a))++-- | Make a Golden File for the Proxy of a type if the file does not exist.+mkGoldenFileForType :: forall a. (ToJSON a, ToADTArbitrary a) => Int -> Proxy a -> FilePath -> IO ()+mkGoldenFileForType sampleSize Proxy goldenPath = do+ (typeName, constructors) <- fmap (adtTypeName &&& adtCAPs) <$> generate $ toADTArbitrary (Proxy :: Proxy a)+ mapM_+ (\constructor -> do+ let goldenFile = goldenPath <> "/" <> typeName <> ".json"+ exists <- doesFileExist goldenFile+ if exists+ then pure ()+ else do+ createDirectoryIfMissing True (takeDirectory goldenFile)+ rSeed <- randomIO :: IO Int+ rSamples <- mkRandomADTSamplesForConstructor sampleSize (Proxy :: Proxy a) (capConstructor constructor) rSeed+ writeFile goldenFile $ encodePretty rSamples+ ) constructors
src/Test/Aeson/Internal/GoldenSpecs.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE NamedFieldPuns #-}+ {-| Module : Test.Aeson.Internal.GoldenSpecs Description : Golden tests for Arbitrary@@ -10,6 +10,7 @@ Internal module, use at your own risk. -} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -36,10 +37,6 @@ import Test.Hspec import Test.QuickCheck ---- -- | Tests to ensure that JSON encoding has not unintentionally changed. This -- could be caused by the following: --@@ -62,7 +59,7 @@ goldenSpecsWithNote :: forall a. (Typeable a, Arbitrary a, ToJSON a, FromJSON a) => Settings -> Proxy a -> Maybe String -> Spec goldenSpecsWithNote settings@Settings{..} proxy mNote = do- typeNameInfo <- runIO $ fromTypeable settings proxy+ typeNameInfo <- runIO $ mkTypeNameInfo settings proxy goldenSpecsWithNotePlain settings typeNameInfo mNote -- | same as 'goldenSpecsWithNote' but does not require a Typeable, Eq or Show instance.@@ -77,7 +74,7 @@ it ("produces the same JSON as is found in " ++ goldenFile) $ do exists <- doesFileExist goldenFile if exists- then compareWithGolden typeNameInfo proxy goldenFile+ then compareWithGolden typeNameInfo proxy goldenFile comparisonFile else createGoldenfile settings proxy goldenFile @@ -85,8 +82,8 @@ -- the golden file and compare the with the JSON in the golden file. compareWithGolden :: forall a . ( Arbitrary a, ToJSON a, FromJSON a) =>- TypeNameInfo a -> Proxy a -> FilePath -> IO ()-compareWithGolden typeNameInfo proxy goldenFile = do+ TypeNameInfo a -> Proxy a -> FilePath -> ComparisonFile ->IO ()+compareWithGolden typeNameInfo proxy goldenFile comparisonFile = do goldenSeed <- readSeed =<< readFile goldenFile sampleSize <- readSampleSize =<< readFile goldenFile newSamples <- mkRandomSamples sampleSize proxy goldenSeed@@ -99,12 +96,15 @@ where whenFails :: forall b c . IO c -> IO b -> IO b whenFails = flip onException- faultyFile = mkFaultyFile typeNameInfo + filePath =+ case comparisonFile of+ FaultyFile -> mkFaultyFile typeNameInfo+ OverwriteGoldenFile -> goldenFile writeComparisonFile newSamples = do- writeFile faultyFile (encodePretty newSamples)+ writeFile filePath (encodePretty newSamples) putStrLn $ "\n" ++- "INFO: Written the current encodings into " ++ faultyFile ++ "."+ "INFO: Written the current encodings into " ++ filePath ++ "." -- | The golden files do not exist. Create it. createGoldenfile :: forall a . (Arbitrary a, ToJSON a) =>@@ -149,50 +149,3 @@ correctedSampleSize = if sampleSize <= 0 then 1 else sampleSize gen :: Gen [a] gen = setSeed rSeed $ replicateM correctedSampleSize (arbitrary :: Gen a)------------------------------------------------------------ Handle creating names-----------------------------------------------------newtype TopDir = TopDir {unTopDir :: FilePath}-newtype ModuleName = ModuleName {unModuleName :: FilePath}-newtype TypeName = TypeName {unTypeName :: FilePath}---data TypeNameInfo a = TypeNameInfo {- typeNameTopDir :: TopDir,- typeNameModuleName :: Maybe ModuleName,- typeNameTypeName :: TypeName- }---{--- Nothing -> topDir </> show (typeRep proxy) <.> "json"-- Just moduleName -> topDir </> moduleName </> show (typeRep proxy) <.> "json"---}---fromTypeable :: forall a . Arbitrary a => Typeable a => Settings -> Proxy a -> IO (TypeNameInfo a)-fromTypeable (Settings {useModuleNameAsSubDirectory- ,goldenDirectoryOption}) proxy = do- maybeModuleName <- maybeModuleNameIO- return $ TypeNameInfo (TopDir topDir )- (ModuleName <$> maybeModuleName )- (TypeName typeName)- where- typeName = show (typeRep proxy)- maybeModuleNameIO = if useModuleNameAsSubDirectory- then do- arbA <- generate (arbitrary :: Gen a)- return $ Just $ tyConModule . typeRepTyCon . typeOf $ arbA- else return Nothing-- topDir = case goldenDirectoryOption of- GoldenDirectory -> "golden"- CustomDirectoryName d -> d
src/Test/Aeson/Internal/Utils.hs view
@@ -1,13 +1,13 @@ {-| Module : Test.Aeson.Internal.Utils-Description : Utility types, functions and values+Description : Internal types, functions and values Copyright : (c) Plow Technologies, 2016 License : BSD3 Maintainer : mchaver@gmail.com Stability : Beta -} -+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE RankNTypes #-} @@ -18,6 +18,7 @@ import Data.Aeson import Data.ByteString.Lazy (ByteString) import Data.Proxy+import Data.Typeable import Prelude @@ -25,20 +26,23 @@ import Test.QuickCheck -data Settings = Settings {- goldenDirectoryOption :: GoldenDirectoryOption -- ^ use a custom directory name or use the generic "golden" directory.--, useModuleNameAsSubDirectory :: Bool -- ^ If true, use the module name in the file path, otherwise ignore it.+data ComparisonFile+ = FaultyFile+ | OverwriteGoldenFile -, sampleSize :: Int -- ^ How many instances of each type you want. If you use ADT versions than it will use the sample size for each constructor.-}+data Settings = Settings + { goldenDirectoryOption :: GoldenDirectoryOption -- ^ use a custom directory name or use the generic "golden" directory.+ , useModuleNameAsSubDirectory :: Bool -- ^ If true, use the module name in the file path, otherwise ignore it.+ , sampleSize :: Int -- ^ How many instances of each type you want. If you use ADT versions than it will use the sample size for each constructor.+ , comparisonFile :: ComparisonFile+ } -- | A custom directory name or a preselected directory name. data GoldenDirectoryOption = CustomDirectoryName String | GoldenDirectory -- | The default settings for general use cases. defaultSettings :: Settings-defaultSettings = Settings GoldenDirectory False 5+defaultSettings = Settings GoldenDirectory False 5 FaultyFile -- | put brackets around a String. addBrackets :: String -> String@@ -70,10 +74,10 @@ Left msg -> throwIO $ ErrorCall ("aeson couldn't parse value: " ++ msg) +-- | Used to eliminate the need for an Eq instance newtype JsonShow a = JsonShow a --instance ToJSON a => Show (JsonShow a ) where +instance ToJSON a => Show (JsonShow a) where show (JsonShow v) = show . encode $ v instance ToJSON a => ToJSON (JsonShow a) where@@ -84,3 +88,50 @@ instance Arbitrary a => Arbitrary (JsonShow a) where arbitrary = JsonShow <$> arbitrary ++--------------------------------------------------+-- Handle creating names+--------------------------------------------------++newtype TopDir =+ TopDir+ { unTopDir :: FilePath+ } deriving (Eq,Read,Show)++newtype ModuleName =+ ModuleName+ { unModuleName :: FilePath+ } deriving (Eq,Read,Show)++newtype TypeName =+ TypeName+ { unTypeName :: FilePath+ } deriving (Eq,Read,Show)++data TypeNameInfo a =+ TypeNameInfo+ { typeNameTopDir :: TopDir+ , typeNameModuleName :: Maybe ModuleName+ , typeNameTypeName :: TypeName+ } deriving (Eq,Read,Show)++mkTypeNameInfo :: forall a . Arbitrary a => Typeable a => Settings -> Proxy a -> IO (TypeNameInfo a)+mkTypeNameInfo (Settings { useModuleNameAsSubDirectory+ , goldenDirectoryOption}) proxy = do+ maybeModuleName <- maybeModuleNameIO+ return $ TypeNameInfo (TopDir topDir )+ (ModuleName <$> maybeModuleName )+ (TypeName typeName)+ where+ typeName = show (typeRep proxy)+ maybeModuleNameIO =+ if useModuleNameAsSubDirectory+ then do+ arbA <- generate (arbitrary :: Gen a)+ return $ Just $ tyConModule . typeRepTyCon . typeOf $ arbA+ else return Nothing++ topDir =+ case goldenDirectoryOption of+ GoldenDirectory -> "golden"+ CustomDirectoryName d -> d