HMock 0.5.0.0 → 0.5.1.0
raw patch · 11 files changed
+192/−89 lines, 11 filesdep ~basedep ~mtldep ~template-haskellPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, mtl, template-haskell
API changes (from Hackage documentation)
Files
- CHANGELOG.md +7/−0
- HMock.cabal +3/−3
- src/Test/HMock.hs +1/−1
- src/Test/HMock/Internal/State.hs +7/−3
- src/Test/HMock/Internal/TH.hs +31/−29
- src/Test/HMock/TH.hs +14/−11
- test/Classes.hs +86/−19
- test/Demo.hs +0/−1
- test/QuasiMock.hs +28/−16
- test/QuasiMockBase.hs +3/−2
- test/TH.hs +12/−4
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for hmock +## 0.5.1.0 -- 2021-09-25++* HMock works with more classes with superclass constraints.+* Classes with default methods are now fully mockable.+* Builds with GHC 9.2.1+* Fixes for GHC 8.4 and 8.6+ ## 0.5.0.0 -- 2021-09-25 * HMock now depends on `Predicate` from the `explainable-predicates` package.
HMock.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: HMock-version: 0.5.0.0+version: 0.5.1.0 synopsis: A flexible mock framework for testing effectful code. description: HMock is a flexible mock framework for testing effectful code in Haskell. Tests can set up expectations about@@ -42,7 +42,7 @@ Test.HMock.Internal.Step, Test.HMock.Internal.TH, Test.HMock.Internal.Util- build-depends: base >=4.11.0 && < 4.16,+ build-depends: base >=4.11.0 && < 4.17, constraints >= 0.13 && < 0.14, containers >= 0.6.2 && < 0.7, data-default >= 0.7.1 && < 0.8,@@ -53,7 +53,7 @@ mtl >= 2.2.2 && < 2.3, stm >= 2.5.0 && < 2.6, syb >= 0.7.2 && < 0.8,- template-haskell >= 2.14 && < 2.18,+ template-haskell >= 2.14 && < 2.19, transformers-base >= 0.4.5 && < 0.5, unliftio >= 0.2.18 && < 0.3, hs-source-dirs: src
src/Test/HMock.hs view
@@ -34,7 +34,7 @@ -- copyFile :: MonadFilesystem m => 'FilePath' -> 'FilePath' -> m () -- copyFile a b = readFile a >>= writeFile b ----- 'Test.HMock.TH.makeMockable' ''MonadFilesystem+-- 'Test.HMock.TH.makeMockable' [t|MonadFilesystem|] -- -- spec = describe "copyFile" '$' -- it "reads a file and writes its contents to another file" '$'
src/Test/HMock/Internal/State.hs view
@@ -36,7 +36,7 @@ import Test.HMock.Mockable (Mockable (..)) import UnliftIO ( MonadIO,- MonadUnliftIO,+ MonadUnliftIO(withRunInIO), STM, TVar, atomically,@@ -198,12 +198,16 @@ MonadBase b, MonadCatch, MonadMask,- MonadThrow,- MonadUnliftIO+ MonadThrow ) instance MonadTrans MockT where lift = MockT . lift++-- Note: The 'MonadUnliftIO' instance is implemented manually because deriving+-- it causes compilation failure in GHC 8.6 and 8.8. (See issue #23.)+instance MonadUnliftIO m => MonadUnliftIO (MockT m) where+ withRunInIO inner = MockT $ withRunInIO $ \run -> inner (run . unMockT) -- | Applies a function to the base monad of 'MockT'. mapMockT :: (m a -> m b) -> MockT m a -> MockT m b
src/Test/HMock/Internal/TH.hs view
@@ -24,14 +24,13 @@ ) where -import Control.Monad.Extra (mapMaybeM)+import Control.Monad.Extra (mapMaybeM, concatMapM) import Data.Generics-import Data.List ((\\))+import Data.List ((\\), nub) import Data.Maybe (catMaybes, fromMaybe) import Language.Haskell.TH import Language.Haskell.TH.Syntax (NameFlavour (..)) import Test.HMock.Internal.Util (choices)-import Data.Foldable (foldl') #if MIN_VERSION_template_haskell(2,17,0) @@ -188,50 +187,53 @@ -- | Attempts to produce sufficient constraints for the given 'Type' to be an -- instance of the given class 'Name'. resolveInstance :: Name -> [Type] -> Q (Maybe Cxt)-resolveInstance cls args- | all isTypeVar args = return (Just [foldl' AppT (ConT cls) args])- where- isTypeVar :: Type -> Bool- isTypeVar (VarT _) = True- isTypeVar _ = False resolveInstance cls args = do decs <- reifyInstances cls args- result <- traverse (tryInstance args) decs- case catMaybes result of- [cx] -> return (Just (filter (not . null . freeTypeVars) cx))+ results <- catMaybes <$> traverse (tryInstance args) decs+ case results of+ [cx] -> pure (Just cx) _ -> return Nothing where tryInstance :: [Type] -> InstanceDec -> Q (Maybe Cxt) tryInstance actualArgs (InstanceD _ cx instType _) = case splitTypeApp instType of- Just (cls', instArgs) | cls' == cls ->- unifyWithin [] instArgs actualArgs >>= \case- Just tbl ->- let cx' = substTypeVars tbl <$> cx- in fmap concat . sequence <$> mapM resolveInstanceType cx'- Nothing -> return Nothing+ Just (cls', instArgs)+ | cls' == cls ->+ unifyWithin [] instArgs actualArgs >>= \case+ Just tbl -> simplifyContext (substTypeVars tbl <$> cx)+ Nothing -> return Nothing _ -> return Nothing tryInstance _ _ = return Nothing -- | Attempts to produce sufficient constraints for the given 'Type' to be a -- satisfied constraint. The type should be a class applied to its type -- parameters.+--+-- Unlike 'simplifyContext', this function always resolves the top-level+-- constraint, and returns 'Nothing' if it cannot do so. resolveInstanceType :: Type -> Q (Maybe Cxt)-resolveInstanceType t = case splitTypeApp t of- Just (cls, args) -> resolveInstance cls args- Nothing -> return Nothing+resolveInstanceType t =+ maybe (pure Nothing) (uncurry resolveInstance) (splitTypeApp t) -- | Simplifies a context with complex types (requiring FlexibleContexts) to try -- to obtain one with all constraints applied to variables.+--+-- Should return Nothing if and only if the simplified contraint is+-- unsatisfiable, which is the case if and only if it contains a component with+-- no type variables. simplifyContext :: Cxt -> Q (Maybe Cxt)-simplifyContext (p : preds) =- case splitTypeApp p of- Just (cls, args) ->- resolveInstance cls args >>= \case- Just cxt' -> fmap (cxt' ++) <$> simplifyContext preds- Nothing -> return Nothing- _ -> fmap (p :) <$> simplifyContext preds-simplifyContext [] = return (Just [])+simplifyContext preds+ | all isVarApp preds = return (Just preds)+ | otherwise = do+ let simplifyPred t = fromMaybe [t] <$> resolveInstanceType t+ components <- concatMapM simplifyPred preds+ if any (null . freeTypeVars) components+ then return Nothing+ else return (Just (nub components))+ where+ isVarApp (ConT _) = True+ isVarApp (AppT t (VarT _)) | isVarApp t = True+ isVarApp _ = False -- | Remove instance context from a method. --
src/Test/HMock/TH.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE ExplicitNamespaces #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TemplateHaskell #-}@@ -177,7 +176,7 @@ [Dec] -> Q Instance makeInstance options ty cx tbl ps m members = do- processedMembers <- mapM (getMethod ty m tbl) members+ processedMembers <- mapM (getMethod ty m tbl) $ filter isRelevantMember members (extraMembers, methods) <- partitionEithers <$> zipWithM memberOrMethod members processedMembers return $@@ -190,6 +189,10 @@ instExtraMembers = extraMembers } where+ isRelevantMember :: Dec -> Bool+ isRelevantMember DefaultSigD {} = False+ isRelevantMember _ = True+ memberOrMethod :: Dec -> Either [String] Method -> Q (Either Dec Method) memberOrMethod dec (Left warnings) = do when (mockVerbose options) $ mapM_ reportWarning warnings@@ -620,12 +623,8 @@ argCxt :: Type -> Q (Maybe Cxt) argCxt argTy | not (isKnownType method argTy) = return Nothing- | VarT v <- argTy =- Just <$> sequence [[t|Eq $(varT v)|], [t|Show $(varT v)|]]- | otherwise = do- eqCxt <- resolveInstance ''Eq [argTy]- showCxt <- resolveInstance ''Show [argTy]- return ((++) <$> eqCxt <*> showCxt)+ | otherwise =+ simplifyContext [AppT (ConT ''Eq) argTy, AppT (ConT ''Show) argTy] deriveForMockT :: MakeMockableOptions -> Type -> Q [Dec] deriveForMockT options ty = do@@ -660,8 +659,12 @@ AppT (ConT ''MonadIO) (VarT (instMonadVar inst)) ] - simplifyContext- (substTypeVar (instMonadVar inst) (AppT (ConT ''MockT) (VarT m)) <$> cx)+ let mockTConstraints =+ substTypeVar+ (instMonadVar inst)+ (AppT (ConT ''MockT) (VarT m))+ <$> cx+ simplifyContext mockTConstraints >>= \case Just cxMockT -> (: [])@@ -689,7 +692,7 @@ actionExp (v : vs) e = actionExp vs [|$e $(varE v)|] body argVars = do- defaultCxt <- resolveInstance ''Default [methodResult method]+ defaultCxt <- simplifyContext [AppT (ConT ''Default) (methodResult method)] let someMockMethod = case defaultCxt of Just [] -> [|mockMethod|] _ -> [|mockDefaultlessMethod|]
test/Classes.hs view
@@ -1,7 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-}@@ -17,11 +19,13 @@ module Classes where import Control.DeepSeq (NFData (rnf))-import Control.Exception (evaluate)+import Control.Exception (SomeException, evaluate)+import Control.Monad.Reader (MonadReader) import Control.Monad.Trans (MonadIO, liftIO) import Data.Default (Default (def)) import Data.Dynamic (Typeable) import Data.Kind (Type)+import Data.List (isInfixOf) import Language.Haskell.TH.Syntax hiding (Type) import QuasiMock import Test.HMock@@ -36,6 +40,9 @@ deriveRecursive (Just AnyclassStrategy) ''NFData ''Dec +anyQMonadFailure :: SomeException -> Bool+anyQMonadFailure e = "(Q monad failure)" `isInfixOf` show e+ class MonadSimple m where simple :: String -> m () @@ -69,7 +76,7 @@ _ <- runQ (makeMockable [t|MonadSimple|]) return () - missingGADTs `shouldThrow` anyException+ missingGADTs `shouldThrow` anyQMonadFailure it "fails when TypeFamilies is disabled" $ example $ do@@ -80,7 +87,7 @@ _ <- runQ (makeMockable [t|MonadSimple|]) return () - missingTypeFamilies `shouldThrow` anyException+ missingTypeFamilies `shouldThrow` anyQMonadFailure it "fails when DataKinds is disabled" $ example $ do@@ -91,7 +98,7 @@ _ <- runQ (makeMockable [t|MonadSimple|]) return () - missingDataKinds `shouldThrow` anyException+ missingDataKinds `shouldThrow` anyQMonadFailure it "fails when FlexibleInstances is disabled" $ example $ do@@ -103,7 +110,7 @@ _ <- runQ (makeMockable [t|MonadSimple|]) return () - missingDataKinds `shouldThrow` anyException+ missingDataKinds `shouldThrow` anyQMonadFailure it "fails when MultiParamTypeClasses is disabled" $ example $ do@@ -117,7 +124,7 @@ _ <- runQ (makeMockable [t|MonadSimple|]) return () - missingDataKinds `shouldThrow` anyException+ missingDataKinds `shouldThrow` anyQMonadFailure it "fails when ScopedTypeVariables is disabled" $ example $ do@@ -131,11 +138,12 @@ _ <- runQ (makeMockable [t|MonadSimple|]) return () - missingDataKinds `shouldThrow` anyException+ missingDataKinds `shouldThrow` anyQMonadFailure it "fails when too many params are given" $ example $ do let tooManyParams = runMockT $ do+ allowUnexpected $ QReifyInstances_ anything anything |-> [] $(onReify [|expectAny|] ''MonadSimple) expect $ QReport_ anything (hasSubstr "is applied to too many arguments")@@ -143,7 +151,7 @@ _ <- runQ (makeMockable [t|MonadSimple IO|]) return () - tooManyParams `shouldThrow` anyException+ tooManyParams `shouldThrow` anyQMonadFailure it "is mockable" $ example $ do@@ -271,6 +279,36 @@ success failure `shouldThrow` anyException +class MonadReader String m => MonadPassThroughSuper m where+ withPassThroughSuper :: m ()++makeMockable [t|MonadPassThroughSuper|]++passThroughSuperTests :: SpecWith ()+passThroughSuperTests = describe "MonadPassThroughSuper" $ do+ it "generates mock impl" $+ example $ do+ decs <- runMockT $ do+ allowUnexpected $ QReifyInstances_ anything anything |-> []+ $(onReify [|expectAny|] ''MonadPassThroughSuper)+ expectAny $+ QReifyInstances_+ (eq ''MonadReader)+ $( qMatch+ [p|+ [ VarT _,+ AppT (ConT (Name (OccName "MockT") _)) (VarT _)+ ]+ |]+ )+ |-> $( reifyInstancesStatic+ ''MonadReader+ [VarT (mkName "r"), AppT (ConT ''MockT) (VarT (mkName "v"))]+ )++ runQ (makeMockable [t|MonadPassThroughSuper|])+ evaluate (rnf decs)+ class MonadMPTC a m where mptc :: a -> m () mptcList :: [a] -> m ()@@ -429,6 +467,7 @@ it "fails without RankNTypes" $ example $ do let missingRankNTypes = runMockT $ do+ allowUnexpected $ QReifyInstances_ anything anything |-> [] $(onReify [|expectAny|] ''MonadPolyArg) expectAny $ QIsExtEnabled RankNTypes |-> False expect $@@ -437,7 +476,7 @@ _ <- runQ (makeMockable [t|MonadPolyArg|]) return () - missingRankNTypes `shouldThrow` anyException+ missingRankNTypes `shouldThrow` anyQMonadFailure it "is mockable" $ example $ do@@ -543,6 +582,33 @@ x <- polyResult (3 :: Int) liftIO $ x `shouldBe` 3 +class MonadDefaultSignatures m where+ methodWithDefault :: Int -> m Int+ default methodWithDefault :: MonadIO m => Int -> m Int+ methodWithDefault = liftIO . methodWithDefault++instance MonadDefaultSignatures IO where+ methodWithDefault = pure++makeMockable [t|MonadDefaultSignatures|]++defaultSignaturesTests :: SpecWith ()+defaultSignaturesTests = describe "MonadDefaultSignatures" $ do+ it "generates mock impl" $+ example $ do+ decs <- runMockT $ do+ allowUnexpected $ QReifyInstances_ anything anything |-> []+ $(onReify [|expectAny|] ''MonadDefaultSignatures)+ runQ (makeMockable [t|MonadDefaultSignatures|])+ evaluate (rnf decs)+ it "is mockable" $+ example . runMockT $ do+ expect $+ MethodWithDefault_ anything+ |=> \MethodWithDefault {} -> return 42+ x <- methodWithDefault 0+ liftIO $ x `shouldBe` 42+ class MonadExtraneousMembers m where data SomeDataType m favoriteNumber :: SomeDataType m -> Int@@ -612,6 +678,7 @@ it "fails to derive MockT when class has extra methods" $ example $ do let unmockableMethods = runMockT $ do+ allowUnexpected $ QReifyInstances_ anything anything |-> [] $(onReify [|expectAny|] ''MonadExtraneousMembers) expect $ QReport_ anything (hasSubstr "has unmockable methods")@@ -619,7 +686,7 @@ _ <- runQ (makeMockable [t|MonadExtraneousMembers|]) return () - unmockableMethods `shouldThrow` anyException+ unmockableMethods `shouldThrow` anyQMonadFailure it "is mockable" $ example $ do@@ -682,7 +749,7 @@ decs <- runMockT $ do allowUnexpected $ QReifyInstances_ anything anything |-> [] $(onReify [|expectAny|] ''MonadManyReturns)- $(onReifyInstances [|expectAny|] ''Default [ConT ''NoDefault])+ $(onReifyInstances [|expectAny|] ''Default [[t|NoDefault|]]) runQ (makeMockable [t|MonadManyReturns|]) evaluate (rnf decs)@@ -738,10 +805,8 @@ decs <- runMockT $ do allowUnexpected $ QReifyInstances_ anything anything |-> [] $(onReify [|expectAny|] ''MonadNestedNoDef)- $( [t|(NoDefault, String)|]- >>= onReifyInstances [|expectAny|] ''Default . (: [])- )- $(onReifyInstances [|expectAny|] ''Default [ConT ''NoDefault])+ $(onReifyInstances [|expectAny|] ''Default [[t|(NoDefault, String)|]])+ $(onReifyInstances [|expectAny|] ''Default [[t|NoDefault|]]) runQ (makeMockable [t|MonadNestedNoDef|]) evaluate (rnf decs)@@ -763,7 +828,7 @@ _ <- runQ (makeMockable [t|Int|]) return () - wrongKind `shouldThrow` anyException+ wrongKind `shouldThrow` anyQMonadFailure it "fails when given an unexpected type constructor" $ example $ do@@ -773,7 +838,7 @@ _ <- runQ (makeMockable [t|(Int, String)|]) return () - notClass `shouldThrow` anyException+ notClass `shouldThrow` anyQMonadFailure it "fails when class has no params" $ example $ do@@ -787,7 +852,7 @@ _ <- runQ (makeMockable [t|ClassWithNoParams|]) return () - tooManyParams `shouldThrow` anyException+ tooManyParams `shouldThrow` anyQMonadFailure it "fails when class has no mockable methods" $ example $ do@@ -797,7 +862,7 @@ _ <- runQ (makeMockable [t|Show|]) return () - noMockableMethods `shouldThrow` anyException+ noMockableMethods `shouldThrow` anyQMonadFailure classTests :: SpecWith () classTests = describe "makeMockable" $ do@@ -805,6 +870,7 @@ suffixTests setupTests superTests+ passThroughSuperTests mptcTests fdSpecializedTests fdGeneralTests@@ -812,6 +878,7 @@ polyArgTests unshowableArgTests monadInArgTests+ defaultSignaturesTests extraneousMembersTests rankNTests defaultReturnTests
test/Demo.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}
test/QuasiMock.hs view
@@ -51,6 +51,11 @@ qRecover = error "qRecover" qReifyAnnotations = error "qReifyAnnotations" +#if MIN_VERSION_template_haskell(2, 18, 0)+ qGetDoc l = mockMethod (QGetDoc l)+ qPutDoc l s = mockMethod (QPutDoc l s)+#endif+ functionType :: [Type] -> Bool functionType = everything (||) (mkQ False isArrow) where@@ -68,25 +73,32 @@ $(onReify [|allowUnexpected|] ''Enum) $(onReify [|allowUnexpected|] ''Monad) - $(onReifyInstances [|allowUnexpected|] ''Show [ConT ''String])- $(onReifyInstances [|allowUnexpected|] ''Eq [ConT ''String])- $(onReifyInstances [|allowUnexpected|] ''Show [ConT ''Char])- $(onReifyInstances [|allowUnexpected|] ''Eq [ConT ''Char])- $(onReifyInstances [|allowUnexpected|] ''Show [ConT ''Int])- $(onReifyInstances [|allowUnexpected|] ''Eq [ConT ''Int])- $(onReifyInstances [|allowUnexpected|] ''Show [ConT ''Bool])- $(onReifyInstances [|allowUnexpected|] ''Eq [ConT ''Bool])- $(onReifyInstances [|allowUnexpected|] ''Default [TupleT 0])- $(onReifyInstances [|allowUnexpected|] ''Default [ConT ''String])- $(onReifyInstances [|allowUnexpected|] ''Default [ConT ''Int])- $( onReifyInstances- [|allowUnexpected|]- ''Default- [AppT (ConT ''Maybe) (ConT ''Bool)]- )+ $(onReifyInstances [|allowUnexpected|] ''Show [[t|String|]])+ $(onReifyInstances [|allowUnexpected|] ''Eq [[t|String|]])+ $(onReifyInstances [|allowUnexpected|] ''Show [[t|Char|]])+ $(onReifyInstances [|allowUnexpected|] ''Eq [[t|Char|]])+ $(onReifyInstances [|allowUnexpected|] ''Show [[t|Int|]])+ $(onReifyInstances [|allowUnexpected|] ''Eq [[t|Int|]])+ $(onReifyInstances [|allowUnexpected|] ''Show [[t|Bool|]])+ $(onReifyInstances [|allowUnexpected|] ''Eq [[t|Bool|]])+ $(onReifyInstances [|allowUnexpected|] ''Default [[t|()|]])+ $(onReifyInstances [|allowUnexpected|] ''Default [[t|String|]])+ $(onReifyInstances [|allowUnexpected|] ''Default [[t|Int|]])+ $(onReifyInstances [|allowUnexpected|] ''Default [[t|Maybe Bool|]]) allowUnexpected $ QReifyInstances_ (eq ''Show) (is functionType) |-> [] allowUnexpected $ QReifyInstances_ (eq ''Eq) (is functionType) |-> []++ allowUnexpected $+ QReifyInstances_+ (eq ''Show)+ (elemsAre [$(qMatch [p|VarT _|])])+ |-> $(reifyInstancesStatic ''Show [VarT (mkName "a")])+ allowUnexpected $+ QReifyInstances_+ (eq ''Eq)+ (elemsAre [$(qMatch [p|VarT _|])])+ |-> $(reifyInstancesStatic ''Eq [VarT (mkName "a")]) allowUnexpected $ QReifyInstances_
test/QuasiMockBase.hs view
@@ -46,7 +46,8 @@ reifyInstancesStatic :: Name -> [Type] -> Q Exp reifyInstancesStatic n ts = reifyInstances n ts >>= lift -onReifyInstances :: Q Exp -> Name -> [Type] -> Q Exp-onReifyInstances handler n ts = do+onReifyInstances :: Q Exp -> Name -> [Q Type] -> Q Exp+onReifyInstances handler n qts = do+ ts <- sequence qts result <- reifyInstances n ts [|$handler (QReifyInstances $(lift n) $(lift ts) |-> $(lift result))|]
test/TH.hs view
@@ -10,9 +10,12 @@ import Test.HMock import Test.HMock.Internal.TH (resolveInstance, unifyTypes) import Test.Hspec+import Test.Predicates (anything, eq) data NotShowable +class NoInstances a+ $(pure []) thUtilSpec :: SpecWith ()@@ -49,6 +52,13 @@ liftIO $ result `shouldBe` Just [(v, ConT ''Char)] describe "resolveInstance" $ do+ it "fails for missing instances" $+ example $+ runMockT $ do+ allowUnexpected $ QReifyInstances_ (eq ''NoInstances) anything |-> []+ result <- runQ $ resolveInstance ''NoInstances [ConT ''String]+ liftIO $ result `shouldBe` Nothing+ it "finds unrestricted instances" $ example $ runMockT $ do@@ -66,10 +76,8 @@ example $ runMockT $ do $(onReify [|expectAny|] ''NotShowable)- $(onReifyInstances [|expectAny|] ''Show [ConT ''NotShowable])- $( [t|(Int, NotShowable)|]- >>= onReifyInstances [|expectAny|] ''Show . (: [])- )+ $(onReifyInstances [|expectAny|] ''Show [[t|NotShowable|]])+ $(onReifyInstances [|expectAny|] ''Show [[t|(Int, NotShowable)|]]) t <- runQ [t|(Int, NotShowable)|] result <- runQ $ resolveInstance ''Show [t]