1 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE TypeOperators #-}
4 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 module UnitTests
.Distribution
.Client
.ArbitraryInstances
24 import Distribution
.Client
.Compat
.Prelude
27 import Data
.Char (isLetter
)
28 import Data
.List
((\\))
30 import Distribution
.Simple
.Setup
31 import Distribution
.Types
.Flag
(mkFlagAssignment
)
33 import Distribution
.Client
.BuildReports
.Types
(BuildReport
, InstallOutcome
, Outcome
, ReportLevel
(..))
34 import Distribution
.Client
.CmdInstall
.ClientInstallFlags
(InstallMethod
)
35 import Distribution
.Client
.Glob
(FilePathGlob
(..), FilePathGlobRel
(..), FilePathRoot
(..), GlobPiece
(..))
36 import Distribution
.Client
.IndexUtils
.ActiveRepos
(ActiveRepoEntry
(..), ActiveRepos
(..), CombineStrategy
(..))
37 import Distribution
.Client
.IndexUtils
.IndexState
(RepoIndexState
(..), TotalIndexState
, makeTotalIndexState
)
38 import Distribution
.Client
.IndexUtils
.Timestamp
(Timestamp
, epochTimeToTimestamp
)
39 import Distribution
.Client
.Targets
40 import Distribution
.Client
.Types
(RepoName
(..), WriteGhcEnvironmentFilesPolicy
)
41 import Distribution
.Client
.Types
.AllowNewer
42 import Distribution
.Client
.Types
.OverwritePolicy
(OverwritePolicy
)
43 import Distribution
.Solver
.Types
.OptionalStanza
(OptionalStanza
(..), OptionalStanzaMap
, OptionalStanzaSet
, optStanzaSetFromList
, optStanzaTabulate
)
44 import Distribution
.Solver
.Types
.PackageConstraint
(PackageProperty
(..))
46 import Data
.Coerce
(Coercible
, coerce
)
47 import Network
.URI
(URI
(..), URIAuth
(..), isUnreserved
)
48 import Test
.QuickCheck
52 , arbitraryBoundedEnum
66 import Test
.QuickCheck
.GenericArbitrary
(genericArbitrary
)
67 import Test
.QuickCheck
.Instances
.Cabal
()
69 -- note: there are plenty of instances defined in ProjectConfig test file.
70 -- they should be moved here or into Cabal-quickcheck
72 -------------------------------------------------------------------------------
74 -------------------------------------------------------------------------------
76 data Shrinker a
= Shrinker a
[a
]
78 instance Functor Shrinker
where
79 fmap f
(Shrinker x xs
) = Shrinker
(f x
) (map f xs
)
81 instance Applicative Shrinker
where
82 pure x
= Shrinker x
[]
84 Shrinker f fs
<*> Shrinker x xs
= Shrinker
(f x
) (map f xs
++ map ($ x
) fs
)
86 runShrinker
:: Shrinker a
-> [a
]
87 runShrinker
(Shrinker _ xs
) = xs
89 shrinker
:: Arbitrary a
=> a
-> Shrinker a
90 shrinker x
= Shrinker x
(shrink x
)
92 shrinkerAla
:: (Coercible a b
, Arbitrary b
) => (a
-> b
) -> a
-> Shrinker a
93 shrinkerAla pack
= shrinkerPP pack coerce
95 -- | shrinker with pre and post functions.
96 shrinkerPP
:: Arbitrary b
=> (a
-> b
) -> (b
-> a
) -> a
-> Shrinker a
97 shrinkerPP pack unpack x
= Shrinker x
(map unpack
(shrink
(pack x
)))
99 -------------------------------------------------------------------------------
100 -- Non-Cabal instances
101 -------------------------------------------------------------------------------
103 instance Arbitrary URI
where
106 <$> elements
["file:", "http:", "https:"]
107 <*> (Just
<$> arbitrary
)
108 <*> (('/' :) <$> arbitraryURIToken
)
109 <*> (('?
' :) <$> arbitraryURIToken
)
112 instance Arbitrary URIAuth
where
115 <$> pure
"" -- no password as this does not roundtrip
116 <*> arbitraryURIToken
119 arbitraryURIToken
:: Gen
String
121 shortListOf1
6 (elements
(filter isUnreserved
['\0' .. '\255']))
123 arbitraryURIPort
:: Gen
String
125 oneof
[pure
"", (':' :) <$> shortListOf1
4 (choose
('0', '9'))]
127 -------------------------------------------------------------------------------
128 -- cabal-install (and Cabal) types
129 -------------------------------------------------------------------------------
131 adjustSize
:: (Int -> Int) -> Gen a
-> Gen a
132 adjustSize adjust gen
= sized
(\n -> resize
(adjust n
) gen
)
134 shortListOf
:: Int -> Gen a
-> Gen
[a
]
135 shortListOf bound gen
=
137 k
<- choose
(0, (n `
div`
2) `
min` bound
)
140 shortListOf1
:: Int -> Gen a
-> Gen
[a
]
141 shortListOf1 bound gen
=
143 k
<- choose
(1, 1 `
max`
((n `
div`
2) `
min` bound
))
146 newtype ShortToken
= ShortToken
{getShortToken
:: String}
149 instance Arbitrary ShortToken
where
152 <$> ( shortListOf1
5 (choose
('#', '~
'))
153 `suchThat`
(all (`
notElem`
"{}"))
154 `suchThat`
(not . ("[]" `
isPrefixOf`
))
157 -- TODO: [code cleanup] need to replace parseHaskellString impl to stop
158 -- accepting Haskell list syntax [], ['a'] etc, just allow String syntax.
159 -- Workaround, don't generate [] as this does not round trip.
161 shrink
(ShortToken cs
) =
162 [ShortToken cs
' | cs
' <- shrink cs
, not (null cs
')]
164 arbitraryShortToken
:: Gen
String
165 arbitraryShortToken
= getShortToken
<$> arbitrary
167 newtype NonMEmpty a
= NonMEmpty
{getNonMEmpty
:: a
}
168 deriving (Eq
, Ord
, Show)
170 instance (Arbitrary a
, Monoid a
, Eq a
) => Arbitrary
(NonMEmpty a
) where
171 arbitrary
= NonMEmpty
<$> (arbitrary `suchThat`
(/= mempty
))
172 shrink
(NonMEmpty x
) = [NonMEmpty x
' | x
' <- shrink x
, x
' /= mempty
]
174 newtype NoShrink a
= NoShrink
{getNoShrink
:: a
}
175 deriving (Eq
, Ord
, Show)
177 instance Arbitrary a
=> Arbitrary
(NoShrink a
) where
178 arbitrary
= NoShrink
<$> arbitrary
181 instance Arbitrary Timestamp
where
182 -- note: no negative timestamps
184 -- >>> utcTimeToPOSIXSeconds $ UTCTime (fromGregorian 100000 01 01) 0
185 -- >>> 3093527980800s
187 arbitrary
= maybe (toEnum 0) id . epochTimeToTimestamp
. (`
mod`
3093527980800) . abs <$> arbitrary
189 instance Arbitrary RepoIndexState
where
192 [ (1, pure IndexStateHead
)
193 , (50, IndexStateTime
<$> arbitrary
)
196 instance Arbitrary TotalIndexState
where
197 arbitrary
= makeTotalIndexState
<$> arbitrary
<*> arbitrary
199 instance Arbitrary WriteGhcEnvironmentFilesPolicy
where
200 arbitrary
= arbitraryBoundedEnum
202 arbitraryFlag
:: Gen a
-> Gen
(Flag a
)
203 arbitraryFlag
= liftArbitrary
205 instance Arbitrary RepoName
where
206 -- TODO: rename refinement?
207 arbitrary
= RepoName
<$> (mk `suchThat`
\x
-> not $ "--" `
isPrefixOf` x
)
209 mk
= (:) <$> lead
<*> rest
212 [c | c
<- ['\NUL
' .. '\255'], isAlpha c || c `
elem`
"_-."]
216 [c | c
<- ['\NUL
' .. '\255'], isAlphaNum c || c `
elem`
"_-."]
219 instance Arbitrary ReportLevel
where
220 arbitrary
= arbitraryBoundedEnum
222 instance Arbitrary OverwritePolicy
where
223 arbitrary
= arbitraryBoundedEnum
225 instance Arbitrary InstallMethod
where
226 arbitrary
= arbitraryBoundedEnum
228 -------------------------------------------------------------------------------
230 -------------------------------------------------------------------------------
232 instance Arbitrary ActiveRepos
where
233 arbitrary
= ActiveRepos
<$> shortListOf
5 arbitrary
235 instance Arbitrary ActiveRepoEntry
where
238 [ (10, ActiveRepo
<$> arbitrary
<*> arbitrary
)
239 , (1, ActiveRepoRest
<$> arbitrary
)
242 instance Arbitrary CombineStrategy
where
243 arbitrary
= arbitraryBoundedEnum
244 shrink
= shrinkBoundedEnum
246 -------------------------------------------------------------------------------
248 -------------------------------------------------------------------------------
250 instance Arbitrary AllowNewer
where
251 arbitrary
= AllowNewer
<$> arbitrary
253 instance Arbitrary AllowOlder
where
254 arbitrary
= AllowOlder
<$> arbitrary
256 instance Arbitrary RelaxDeps
where
260 , mkRelaxDepSome
<$> shortListOf1
3 arbitrary
264 instance Arbitrary RelaxDepMod
where
265 arbitrary
= elements
[RelaxDepModNone
, RelaxDepModCaret
]
267 shrink RelaxDepModCaret
= [RelaxDepModNone
]
270 instance Arbitrary RelaxDepScope
where
271 arbitrary
= genericArbitrary
272 shrink
= genericShrink
274 instance Arbitrary RelaxDepSubject
where
275 arbitrary
= genericArbitrary
276 shrink
= genericShrink
278 instance Arbitrary RelaxedDep
where
279 arbitrary
= genericArbitrary
280 shrink
= genericShrink
282 -------------------------------------------------------------------------------
284 -------------------------------------------------------------------------------
286 instance Arbitrary UserConstraintScope
where
287 arbitrary
= genericArbitrary
288 shrink
= genericShrink
290 instance Arbitrary UserQualifier
where
293 [ pure UserQualToplevel
294 , UserQualSetup
<$> arbitrary
295 -- -- TODO: Re-enable UserQualExe tests once we decide on a syntax.
296 -- , UserQualExe <$> arbitrary <*> arbitrary
299 instance Arbitrary UserConstraint
where
300 arbitrary
= genericArbitrary
301 shrink
= genericShrink
303 instance Arbitrary PackageProperty
where
306 [ PackagePropertyVersion
<$> arbitrary
307 , pure PackagePropertyInstalled
308 , pure PackagePropertySource
309 , PackagePropertyFlags
. mkFlagAssignment
<$> shortListOf1
3 arbitrary
310 , PackagePropertyStanzas
. (\x
-> [x
]) <$> arbitrary
313 instance Arbitrary OptionalStanza
where
314 arbitrary
= elements
[minBound .. maxBound]
316 instance Arbitrary OptionalStanzaSet
where
317 arbitrary
= fmap optStanzaSetFromList arbitrary
319 instance Arbitrary a
=> Arbitrary
(OptionalStanzaMap a
) where
323 return $ optStanzaTabulate
$ \x
-> case x
of
327 -------------------------------------------------------------------------------
329 -------------------------------------------------------------------------------
331 instance Arbitrary BuildReport
where
332 arbitrary
= genericArbitrary
333 shrink
= genericShrink
335 instance Arbitrary InstallOutcome
where
336 arbitrary
= genericArbitrary
337 shrink
= genericShrink
339 instance Arbitrary Outcome
where
340 arbitrary
= genericArbitrary
341 shrink
= genericShrink
343 -------------------------------------------------------------------------------
345 -------------------------------------------------------------------------------
347 instance Arbitrary FilePathGlob
where
349 (FilePathGlob
<$> arbitrary
<*> arbitrary
)
350 `suchThat` validFilePathGlob
352 shrink
(FilePathGlob root pathglob
) =
353 [ FilePathGlob root
' pathglob
'
354 |
(root
', pathglob
') <- shrink
(root
, pathglob
)
355 , validFilePathGlob
(FilePathGlob root
' pathglob
')
358 validFilePathGlob
:: FilePathGlob
-> Bool
359 validFilePathGlob
(FilePathGlob FilePathRelative pathglob
) =
361 GlobDirTrailing
-> False
362 GlobDir
[Literal
"~"] _
-> False
363 GlobDir
[Literal
(d
: ":")] _
364 | isLetter d
-> False
366 validFilePathGlob _
= True
368 instance Arbitrary FilePathRoot
where
371 [ (3, pure FilePathRelative
)
372 , (1, pure
(FilePathRoot unixroot
))
373 , (1, FilePathRoot
<$> windrive
)
374 , (1, pure FilePathHomeDir
)
378 windrive
= do d
<- choose
('A
', 'Z
'); return (d
: ":\\")
380 shrink FilePathRelative
= []
381 shrink
(FilePathRoot _
) = [FilePathRelative
]
382 shrink FilePathHomeDir
= [FilePathRelative
]
384 instance Arbitrary FilePathGlobRel
where
385 arbitrary
= sized
$ \sz
->
389 [ pure GlobDirTrailing
390 , GlobFile
<$> (getGlobPieces
<$> arbitrary
)
392 <$> (getGlobPieces
<$> arbitrary
)
393 <*> resize
(sz `
div`
2) arbitrary
396 shrink GlobDirTrailing
= []
397 shrink
(GlobFile glob
) =
399 : [GlobFile
(getGlobPieces glob
') | glob
' <- shrink
(GlobPieces glob
)]
400 shrink
(GlobDir glob pathglob
) =
403 : [ GlobDir
(getGlobPieces glob
') pathglob
'
404 |
(glob
', pathglob
') <- shrink
(GlobPieces glob
, pathglob
)
407 newtype GlobPieces
= GlobPieces
{getGlobPieces
:: [GlobPiece
]}
410 instance Arbitrary GlobPieces
where
411 arbitrary
= GlobPieces
. mergeLiterals
<$> shortListOf1
5 arbitrary
413 shrink
(GlobPieces glob
) =
414 [ GlobPieces
(mergeLiterals
(getNonEmpty glob
'))
415 | glob
' <- shrink
(NonEmpty glob
)
418 mergeLiterals
:: [GlobPiece
] -> [GlobPiece
]
419 mergeLiterals
(Literal a
: Literal b
: ps
) = mergeLiterals
(Literal
(a
++ b
) : ps
)
420 mergeLiterals
(Union
as : ps
) = Union
(map mergeLiterals
as) : mergeLiterals ps
421 mergeLiterals
(p
: ps
) = p
: mergeLiterals ps
422 mergeLiterals
[] = []
424 instance Arbitrary GlobPiece
where
425 arbitrary
= sized
$ \sz
->
427 [ (3, Literal
<$> shortListOf1
10 (elements globLiteralChars
))
429 , (1, Union
<$> resize
(sz `
div`
2) (shortListOf1
5 (shortListOf1
5 arbitrary
)))
432 shrink
(Literal str
) =
436 , all (`
elem` globLiteralChars
) str
'
440 [ Union
(map getGlobPieces
(getNonEmpty
as'))
441 |
as' <- shrink
(NonEmpty
(map GlobPieces
as))
444 globLiteralChars
:: [Char]
445 globLiteralChars
= ['\0' .. '\128'] \\ "*{},/\\"