{-# OPTIONS_GHC -fno-warn-orphans -Wwarn #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Stack.StoreSpec where

import           Control.Applicative
import qualified Data.ByteString as BS
import           Data.Containers (mapFromList, setFromList)
import           Data.Int
import           Data.Map (Map)
import           Data.Sequences (fromList)
import           Data.Set (Set)
import           Data.Store.TH
import           Data.Text (Text)
import qualified Data.Vector.Unboxed as UV
import           Data.Word
import           Language.Haskell.TH
import           Language.Haskell.TH.ReifyMany
import           Prelude
import           Stack.Build.Cache (BuildCache(..))
import           Stack.PackageDump
import           Stack.Types
import           Test.Hspec
import           Test.SmallCheck.Series

-- NOTE: these were copied from Data.Store. Should probably be moved to
-- smallcheck.

instance (Monad m, Serial m k, Serial m a, Ord k) => Serial m (Map k a) where
    series = fmap mapFromList series

instance Monad m => Serial m Text where
    series = fmap fromList series

instance (Monad m, Serial m a, UV.Unbox a) => Serial m (UV.Vector a) where
    series = fmap fromList series

instance Monad m => Serial m BS.ByteString where
    series = fmap BS.pack series

instance (Monad m, Serial m a, Ord a) => Serial m (Set a) where
    series = fmap setFromList series

addMinAndMaxBounds :: forall a. (Bounded a, Eq a, Num a) => [a] -> [a]
addMinAndMaxBounds xs =
    (if (minBound :: a) `notElem` xs then [minBound] else []) ++
    (if (maxBound :: a) `notElem` xs && (maxBound :: a) /= minBound then maxBound : xs else xs)

$(do let ns = [ ''Int64, ''Word64, ''Word, ''Word8
              ]
         f n = [d| instance Monad m => Serial m $(conT n) where
                      series = generate (\_ -> addMinAndMaxBounds [0, 1]) |]
     concat <$> mapM f ns)

$(do let tys = [ ''InstalledCacheInner
               , ''PackageCacheMap
               , ''MiniBuildPlan
               , ''BuildCache
               , ''ConfigCache
               ]
     ns <- reifyManyWithoutInstances ''Serial tys (`notElem` [''UV.Vector])
     let f n = [d| instance Monad m => Serial m $(conT n) |]
     concat <$> mapM f ns)

verbose :: Bool
verbose = False

spec :: Spec
spec = do
    describe "Roundtrips binary formats" $ do
        $(smallcheckManyStore False 6
            [ [t| InstalledCacheInner |]
            , [t| PackageCacheMap |]
            , [t| BuildCache |]
            ])
        -- Blows up with > 5
        $(smallcheckManyStore False 5
            [ [t| MiniBuildPlan |]
            ])
        -- Blows up with > 4
        $(smallcheckManyStore False 4
            [ [t| ConfigCache |]
            ])
