From 8ac04b7d7af9750f860afb4903a27bc44b75f617 Mon Sep 17 00:00:00 2001 From: quasicomputational Date: Mon, 12 Nov 2018 19:05:30 +0000 Subject: [PATCH] Put Arbitrary instances for Cabal types in their own package. This is with the intention of the new package, cabal-quickcheck-instances, being the blessed location for these orphans, as QuickCheck acquiring a Cabal dependency or vice-versa would be unsuitable. This reduces some duplication (some presumably deliberate, and some apparently accidental) and then some drift between the versions of these instances. Due to #1575, the modules for the new package are shared with Cabal's test-suite. This is less than ideal, but it's a workable hack. --- Cabal/Cabal.cabal | 14 +- .../Distribution/Arbitrary/Instances.hs | 300 ++++++++++++++++++ .../Distribution/Arbitrary/Util.hs | 98 ++++++ Cabal/cabal-quickcheck-instances/README.md | 4 + .../cabal-quickcheck-instances.cabal | 35 ++ Cabal/tests/Test/QuickCheck/Utils.hs | 29 -- Cabal/tests/UnitTests/Distribution/SPDX.hs | 47 +-- Cabal/tests/UnitTests/Distribution/System.hs | 12 +- Cabal/tests/UnitTests/Distribution/Version.hs | 95 +----- cabal-install/cabal-install.cabal.pp | 2 + .../Distribution/Client/ArbitraryInstances.hs | 181 ++--------- .../UnitTests/Distribution/Client/Glob.hs | 5 +- .../Distribution/Client/ProjectConfig.hs | 27 +- .../UnitTests/Distribution/Client/VCS.hs | 4 +- .../Distribution/Solver/Modular/QuickCheck.hs | 25 +- cabal.project | 2 +- 16 files changed, 518 insertions(+), 362 deletions(-) create mode 100644 Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Instances.hs create mode 100644 Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Util.hs create mode 100644 Cabal/cabal-quickcheck-instances/README.md create mode 100644 Cabal/cabal-quickcheck-instances/cabal-quickcheck-instances.cabal delete mode 100644 Cabal/tests/Test/QuickCheck/Utils.hs diff --git a/Cabal/Cabal.cabal b/Cabal/Cabal.cabal index 269eacf6a0e..b43cc2b6c64 100644 --- a/Cabal/Cabal.cabal +++ b/Cabal/Cabal.cabal @@ -487,10 +487,20 @@ library -- Small, fast running tests. test-suite unit-tests type: exitcode-stdio-1.0 - hs-source-dirs: tests + + -- Some of the tests need Arbitrary instances. Ideally, we would + -- depend on the cabal-quickcheck-instances library and avoid + -- sharing source, but #1575 makes that impossible (as we'd have a + -- cycle between packages). If/when that's fixed and we arrive in + -- the glorious component-based future, this ugly hack can be + -- removed. In the meantime, as a workaround, we share the source + -- between this component and the c-q-i package. + + hs-source-dirs: tests, cabal-quickcheck-instances other-modules: + Distribution.Arbitrary.Instances + Distribution.Arbitrary.Util Test.Laws - Test.QuickCheck.Utils UnitTests.Distribution.Compat.CreatePipe UnitTests.Distribution.Compat.ReadP UnitTests.Distribution.Compat.Time diff --git a/Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Instances.hs b/Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Instances.hs new file mode 100644 index 00000000000..517b2974234 --- /dev/null +++ b/Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Instances.hs @@ -0,0 +1,300 @@ +{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-} +module Distribution.Arbitrary.Instances () where + +import Control.Monad + ( liftM + , liftM2 + ) +import Data.Char + ( isAlphaNum + , isDigit + ) +import Data.List + ( intercalate + ) +import Distribution.Simple.Flag + ( Flag (..) + ) +import Distribution.Simple.InstallDirs + ( PathTemplate + , toPathTemplate + ) +import Distribution.Simple.Utils + ( lowercase + ) +import Distribution.SPDX + ( LicenseId + , LicenseExceptionId + , LicenseExpression (..) + , LicenseListVersion (..) + , LicenseRef + , SimpleLicenseExpression (..) + , licenseExceptionIdList + , licenseIdList + , mkLicenseRef' + ) +import Distribution.System + ( Arch + , OS + , Platform (..) + , knownArches + , knownOSs + ) +import Distribution.Types.Dependency + ( Dependency (..) + ) +import Distribution.Types.GenericPackageDescription + ( FlagName + , mkFlagName + ) +import Distribution.Types.LibraryName + ( LibraryName (..) + ) +import Distribution.Types.PackageName + ( PackageName + , mkPackageName + ) +import Distribution.Types.PackageVersionConstraint + ( PackageVersionConstraint (..) + ) +import Distribution.Types.UnqualComponentName + ( UnqualComponentName + , packageNameToUnqualComponentName + ) +import Distribution.Verbosity + ( Verbosity + ) +import Distribution.Version + ( Bound (..) + , LowerBound (..) + , UpperBound (..) + , Version + , VersionInterval + , VersionIntervals + , VersionRange (..) + , anyVersion + , earlierVersion + , intersectVersionRanges + , laterVersion + , majorBoundVersion + , mkVersion + , mkVersionIntervals + , orEarlierVersion + , orLaterVersion + , thisVersion + , unionVersionRanges + , version0 + , versionNumbers + , withinVersion + ) +import Test.QuickCheck + ( Arbitrary ( arbitrary, shrink ) + , elements + , frequency + , listOf1 + , oneof + , sized + , suchThat + ) + +import Distribution.Arbitrary.Util + +-- Instances from Distribution.Simple.Flag + +instance Arbitrary a => Arbitrary (Flag a) where + arbitrary = arbitraryFlag arbitrary + shrink NoFlag = [] + shrink (Flag x) = NoFlag : [ Flag x' | x' <- shrink x ] + +-- Instances from Distribution.Simple.InstallDirs + +instance Arbitrary PathTemplate where + arbitrary = toPathTemplate <$> arbitraryShortToken + shrink t = [ toPathTemplate s | s <- shrink (show t), not (null s) ] + +-- Instances from Distribution.System + +instance Arbitrary Arch where + arbitrary = elements knownArches + +instance Arbitrary OS where + arbitrary = elements knownOSs + +instance Arbitrary Platform where + arbitrary = liftM2 Platform arbitrary arbitrary + +-- Instances from Distribution.Types.Dependency + +instance Arbitrary Dependency where + arbitrary = Dependency <$> arbitrary <*> arbitrary <*> fmap getNonMEmpty arbitrary + +-- Instances from Distribution.Types.GenericPackageDescription + +instance Arbitrary FlagName where + arbitrary = mkFlagName <$> flagident + where + flagident = lowercase <$> shortListOf1 5 (elements flagChars) + `suchThat` (("-" /=) . take 1) + flagChars = "-_" ++ ['a'..'z'] + +-- Instances from Distribution.Types.LibraryName + +instance Arbitrary LibraryName where + arbitrary = elements =<< sequenceA [LSubLibName <$> arbitrary, pure LMainLibName] + +-- Instances from Distribution.Types.PackageName + +instance Arbitrary PackageName where + arbitrary = mkPackageName . intercalate "-" <$> shortListOf1 2 nameComponent + where + nameComponent = shortListOf1 5 (elements packageChars) + `suchThat` (not . all isDigit) + packageChars = filter isAlphaNum ['\0'..'\127'] + +-- Instances from Distribution.Types.PackageVersionConstraint + +instance Arbitrary PackageVersionConstraint where + arbitrary = PackageVersionConstraint <$> arbitrary <*> arbitrary + +-- Instances from Distribution.Types.UnqualComponentName + +instance Arbitrary UnqualComponentName where + -- same rules as package names + arbitrary = packageNameToUnqualComponentName <$> arbitrary + +-- Instances from Distribution.Verbosity + +instance Arbitrary Verbosity where + arbitrary = elements [minBound..maxBound] + +-- Instances from Distribution.Version + +instance Arbitrary Bound where + arbitrary = elements [ExclusiveBound, InclusiveBound] + +instance Arbitrary Version where + arbitrary = do + branch <- smallListOf1 $ + frequency [(3, return 0) + ,(3, return 1) + ,(2, return 2) + ,(2, return 3) + ,(1, return 0xfffd) + ,(1, return 0xfffe) -- max fitting into packed W64 + ,(1, return 0xffff) + ,(1, return 0x10000)] + return (mkVersion branch) + where + smallListOf1 = adjustSize (\n -> min 6 (n `div` 3)) . listOf1 + + shrink ver = [ mkVersion ns | ns <- shrink (versionNumbers ver) + , not (null ns) ] + +-- | Generating VersionIntervals +-- +-- This is a tad tricky as VersionIntervals is an abstract type, so we first +-- make a local type for generating the internal representation. Then we check +-- that this lets us construct valid 'VersionIntervals'. +-- + +instance Arbitrary VersionIntervals where + arbitrary = fmap mkVersionIntervals' arbitrary + where + mkVersionIntervals' :: [(Version, Bound)] -> VersionIntervals + mkVersionIntervals' = mkVersionIntervals . go version0 + where + go :: Version -> [(Version, Bound)] -> [VersionInterval] + go _ [] = [] + go v [(lv, lb)] = + [(LowerBound (addVersion lv v) lb, NoUpperBound)] + go v ((lv, lb) : (uv, ub) : rest) = + (LowerBound lv' lb, UpperBound uv' ub) : go uv' rest + where + lv' = addVersion v lv + uv' = addVersion lv' uv + + addVersion :: Version -> Version -> Version + addVersion xs ys = mkVersion $ z (versionNumbers xs) (versionNumbers ys) + where + z [] ys' = ys' + z xs' [] = xs' + z (x : xs') (y : ys') = x + y : z xs' ys' + +instance Arbitrary VersionRange where + arbitrary = sized verRangeExp + where + verRangeExp n = frequency $ + [ (2, return anyVersion) + , (1, liftM thisVersion arbitrary) + , (1, liftM laterVersion arbitrary) + , (1, liftM orLaterVersion arbitrary) + , (1, liftM orLaterVersion' arbitrary) + , (1, liftM earlierVersion arbitrary) + , (1, liftM orEarlierVersion arbitrary) + , (1, liftM orEarlierVersion' arbitrary) + , (1, liftM withinVersion arbitrary) + , (1, liftM majorBoundVersion arbitrary) + , (2, liftM VersionRangeParens arbitrary) + ] ++ if n == 0 then [] else + [ (2, liftM2 unionVersionRanges verRangeExp2 verRangeExp2) + , (2, liftM2 intersectVersionRanges verRangeExp2 verRangeExp2) + ] + where + verRangeExp2 = verRangeExp (n `div` 2) + + orLaterVersion' v = + unionVersionRanges (LaterVersion v) (ThisVersion v) + orEarlierVersion' v = + unionVersionRanges (EarlierVersion v) (ThisVersion v) + + shrink AnyVersion = [] + shrink (ThisVersion v) = map ThisVersion (shrink v) + shrink (LaterVersion v) = map LaterVersion (shrink v) + shrink (EarlierVersion v) = map EarlierVersion (shrink v) + shrink (OrLaterVersion v) = LaterVersion v : map OrLaterVersion (shrink v) + shrink (OrEarlierVersion v) = EarlierVersion v : map OrEarlierVersion (shrink v) + shrink (WildcardVersion v) = map WildcardVersion ( shrink v) + shrink (MajorBoundVersion v) = map MajorBoundVersion (shrink v) + shrink (VersionRangeParens vr) = vr : map VersionRangeParens (shrink vr) + shrink (UnionVersionRanges a b) = a : b : map (uncurry UnionVersionRanges) (shrink (a, b)) + shrink (IntersectVersionRanges a b) = a : b : map (uncurry IntersectVersionRanges) (shrink (a, b)) + +-- Instances from Distribution.SPDX + +instance Arbitrary LicenseId where + arbitrary = elements $ licenseIdList LicenseListVersion_3_2 + +instance Arbitrary LicenseExceptionId where + arbitrary = elements $ licenseExceptionIdList LicenseListVersion_3_2 + +instance Arbitrary LicenseExpression where + arbitrary = sized arb + where + arb n + | n <= 0 = ELicense <$> arbitrary <*> pure Nothing + | otherwise = oneof + [ ELicense <$> arbitrary <*> arbitrary + , EAnd <$> arbA <*> arbB + , EOr <$> arbA <*> arbB + ] + where + m = n `div` 2 + arbA = arb m + arbB = arb (n - m) + + shrink (EAnd a b) = a : b : map (uncurry EAnd) (shrink (a, b)) + shrink (EOr a b) = a : b : map (uncurry EOr) (shrink (a, b)) + shrink _ = [] + +instance Arbitrary LicenseRef where + arbitrary = mkLicenseRef' <$> ids' <*> ids + where + ids = listOf1 $ elements $ ['a'..'z'] ++ ['A' .. 'Z'] ++ ['0'..'9'] ++ "_-" + ids' = oneof [ pure Nothing, Just <$> ids ] + +instance Arbitrary SimpleLicenseExpression where + arbitrary = oneof + [ ELicenseId <$> arbitrary + , ELicenseIdPlus <$> arbitrary + , ELicenseRef <$> arbitrary + ] diff --git a/Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Util.hs b/Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Util.hs new file mode 100644 index 00000000000..2546880d71e --- /dev/null +++ b/Cabal/cabal-quickcheck-instances/Distribution/Arbitrary/Util.hs @@ -0,0 +1,98 @@ +module Distribution.Arbitrary.Util + ( adjustSize + , shortListOf + , shortListOf1 + , ShortToken(..) + , arbitraryShortToken + , NonMEmpty (..) + , arbitraryFlag + ) + where + +import Data.List + ( isPrefixOf + ) +import Distribution.Simple.Flag + ( Flag (..) + ) +import Test.QuickCheck + ( Arbitrary ( arbitrary, shrink ) + , Gen + , choose + , frequency + , resize + , sized + , suchThat + , vectorOf + ) + +-- | Adjust the size of the generated value. +-- +-- In general the size gets bigger and bigger linearly. For some types +-- it is not appropriate to generate ever bigger values but instead +-- to generate lots of intermediate sized values. You could do that using: +-- +-- > adjustSize (\n -> min n 5) +-- +-- Similarly, for some types the linear size growth may mean getting too big +-- too quickly relative to other values. So you may want to adjust how +-- quickly the size grows. For example dividing by a constant, or even +-- something like the integer square root or log. +-- +-- > adjustSize (\n -> n `div` 2) +-- +-- Putting this together we can make for example a relatively short list: +-- +-- > adjustSize (\n -> min 5 (n `div` 3)) (listOf1 arbitrary) +-- +-- Not only do we put a limit on the length but we also scale the growth to +-- prevent it from hitting the maximum size quite so early. +-- +adjustSize :: (Int -> Int) -> Gen a -> Gen a +adjustSize adjust gen = sized (\n -> resize (adjust n) gen) + +shortListOf :: Int -> Gen a -> Gen [a] +shortListOf bound gen = + sized $ \n -> do + k <- choose (0, (n `div` 2) `min` bound) + vectorOf k gen + +shortListOf1 :: Int -> Gen a -> Gen [a] +shortListOf1 bound gen = + sized $ \n -> do + k <- choose (1, 1 `max` ((n `div` 2) `min` bound)) + vectorOf k gen + +newtype ShortToken = ShortToken { getShortToken :: String } + deriving Show + +instance Arbitrary ShortToken where + arbitrary = + ShortToken <$> + (shortListOf1 5 (choose ('#', '~')) + `suchThat` (not . ("[]" `isPrefixOf`))) + --TODO: [code cleanup] need to replace parseHaskellString impl to stop + -- accepting Haskell list syntax [], ['a'] etc, just allow String syntax. + -- Workaround, don't generate [] as this does not round trip. + + + shrink (ShortToken cs) = + [ ShortToken cs' | cs' <- shrink cs, not (null cs') ] + +arbitraryShortToken :: Gen String +arbitraryShortToken = getShortToken <$> arbitrary + +newtype NonMEmpty a = NonMEmpty { getNonMEmpty :: a } + deriving (Eq, Ord, Show) + +instance (Arbitrary a, Monoid a, Eq a) => Arbitrary (NonMEmpty a) where + arbitrary = NonMEmpty <$> (arbitrary `suchThat` (/= mempty)) + shrink (NonMEmpty x) = [ NonMEmpty x' | x' <- shrink x, x' /= mempty ] + +arbitraryFlag :: Gen a -> Gen (Flag a) +arbitraryFlag genA = + sized $ \sz -> + case sz of + 0 -> pure NoFlag + _ -> frequency [ (1, pure NoFlag) + , (3, Flag <$> genA) ] diff --git a/Cabal/cabal-quickcheck-instances/README.md b/Cabal/cabal-quickcheck-instances/README.md new file mode 100644 index 00000000000..bf54120d456 --- /dev/null +++ b/Cabal/cabal-quickcheck-instances/README.md @@ -0,0 +1,4 @@ +This package defines canonical orphan instances of the `Arbitrary` +class from `QuickCheck` for types from `Cabal`. Not all instances are +provided yet; if you need one that isn't, please consider submitting a +PR! diff --git a/Cabal/cabal-quickcheck-instances/cabal-quickcheck-instances.cabal b/Cabal/cabal-quickcheck-instances/cabal-quickcheck-instances.cabal new file mode 100644 index 00000000000..80360eabf37 --- /dev/null +++ b/Cabal/cabal-quickcheck-instances/cabal-quickcheck-instances.cabal @@ -0,0 +1,35 @@ +Cabal-Version: >= 1.10 +Name: cabal-quickcheck-instances +Version: 2.5.0.0 +Synopsis: Arbitrary instances for types from Cabal. +Description: + Blessed orphan instances for \'Arbitrary\' (from the QuickCheck + library) for types from Cabal. +homepage: http://www.haskell.org/cabal/ +bug-reports: https://github.com/haskell/cabal/issues +License: BSD3 +License-File: LICENSE +Author: Cabal Development Team (see AUTHORS file) +Maintainer: Cabal Development Team +Copyright: 2003-2018, Cabal Development Team +Category: Test +Build-type: Simple +Extra-Source-Files: + README.md + +library + default-language: Haskell2010 + + build-depends: + base >= 4.6 && < 5 , + Cabal >= 2.5 && < 2.6 , + QuickCheck >= 2.11.3 && < 2.12 + + exposed-modules: + Distribution.Arbitrary.Instances + Distribution.Arbitrary.Util + + ghc-options: -Wall -fno-ignore-asserts -fwarn-tabs + if impl(ghc >= 8.0) + ghc-options: -Wcompat -Wnoncanonical-monad-instances + -Wnoncanonical-monadfail-instances diff --git a/Cabal/tests/Test/QuickCheck/Utils.hs b/Cabal/tests/Test/QuickCheck/Utils.hs deleted file mode 100644 index 72b517be24f..00000000000 --- a/Cabal/tests/Test/QuickCheck/Utils.hs +++ /dev/null @@ -1,29 +0,0 @@ -module Test.QuickCheck.Utils where - -import Test.QuickCheck.Gen - - --- | Adjust the size of the generated value. --- --- In general the size gets bigger and bigger linearly. For some types --- it is not appropriate to generate ever bigger values but instead --- to generate lots of intermediate sized values. You could do that using: --- --- > adjustSize (\n -> min n 5) --- --- Similarly, for some types the linear size growth may mean getting too big --- too quickly relative to other values. So you may want to adjust how --- quickly the size grows. For example dividing by a constant, or even --- something like the integer square root or log. --- --- > adjustSize (\n -> n `div` 2) --- --- Putting this together we can make for example a relatively short list: --- --- > adjustSize (\n -> min 5 (n `div` 3)) (listOf1 arbitrary) --- --- Not only do we put a limit on the length but we also scale the growth to --- prevent it from hitting the maximum size quite so early. --- -adjustSize :: (Int -> Int) -> Gen a -> Gen a -adjustSize adjust gen = sized (\n -> resize (adjust n) gen) diff --git a/Cabal/tests/UnitTests/Distribution/SPDX.hs b/Cabal/tests/UnitTests/Distribution/SPDX.hs index cc32c93cee4..589a7f5096c 100644 --- a/Cabal/tests/UnitTests/Distribution/SPDX.hs +++ b/Cabal/tests/UnitTests/Distribution/SPDX.hs @@ -1,4 +1,4 @@ -{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-} +{-# OPTIONS_GHC -fno-warn-deprecations #-} module UnitTests.Distribution.SPDX (spdxTests) where import Distribution.Compat.Prelude.Internal @@ -8,6 +8,8 @@ import Distribution.SPDX import Distribution.Parsec.Class (eitherParsec) import Distribution.Pretty (prettyShow) +import Distribution.Arbitrary.Instances () + import Test.Tasty import Test.Tasty.QuickCheck @@ -113,46 +115,3 @@ shouldAcceptProp = conjoin $ shouldRejectProp :: Property shouldRejectProp = conjoin $ map (\l -> counterexample (prettyShow l) (not $ isAcceptableLicense l)) shouldReject - -------------------------------------------------------------------------------- --- Instances -------------------------------------------------------------------------------- - -instance Arbitrary LicenseId where - arbitrary = elements $ licenseIdList LicenseListVersion_3_2 - -instance Arbitrary LicenseExceptionId where - arbitrary = elements $ licenseExceptionIdList LicenseListVersion_3_2 - -instance Arbitrary LicenseRef where - arbitrary = mkLicenseRef' <$> ids' <*> ids - where - ids = listOf1 $ elements $ ['a'..'z'] ++ ['A' .. 'Z'] ++ ['0'..'9'] ++ "_-" - ids' = oneof [ pure Nothing, Just <$> ids ] - -instance Arbitrary SimpleLicenseExpression where - arbitrary = oneof - [ ELicenseId <$> arbitrary - , ELicenseIdPlus <$> arbitrary - , ELicenseRef <$> arbitrary - ] - -instance Arbitrary LicenseExpression where - arbitrary = sized arb - where - arb n - | n <= 0 = ELicense <$> arbitrary <*> pure Nothing - | otherwise = oneof - [ ELicense <$> arbitrary <*> arbitrary - , EAnd <$> arbA <*> arbB - , EOr <$> arbA <*> arbB - ] - where - m = n `div` 2 - arbA = arb m - arbB = arb (n - m) - - shrink (EAnd a b) = a : b : map (uncurry EAnd) (shrink (a, b)) - shrink (EOr a b) = a : b : map (uncurry EOr) (shrink (a, b)) - shrink _ = [] - diff --git a/Cabal/tests/UnitTests/Distribution/System.hs b/Cabal/tests/UnitTests/Distribution/System.hs index ce644962001..05d36b97cf1 100644 --- a/Cabal/tests/UnitTests/Distribution/System.hs +++ b/Cabal/tests/UnitTests/Distribution/System.hs @@ -1,9 +1,8 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} module UnitTests.Distribution.System ( tests ) where -import Control.Monad (liftM2) +import Distribution.Arbitrary.Instances () import Distribution.Text (Text(..), display, simpleParse) import Distribution.System import Test.Tasty @@ -18,12 +17,3 @@ tests = , testProperty "Text Arch round trip" (textRoundtrip :: Arch -> Property) , testProperty "Text Platform round trip" (textRoundtrip :: Platform -> Property) ] - -instance Arbitrary OS where - arbitrary = elements knownOSs - -instance Arbitrary Arch where - arbitrary = elements knownArches - -instance Arbitrary Platform where - arbitrary = liftM2 Platform arbitrary arbitrary diff --git a/Cabal/tests/UnitTests/Distribution/Version.hs b/Cabal/tests/UnitTests/Distribution/Version.hs index 17fc3238d60..fef0cdbef51 100644 --- a/Cabal/tests/UnitTests/Distribution/Version.hs +++ b/Cabal/tests/UnitTests/Distribution/Version.hs @@ -1,6 +1,5 @@ {-# LANGUAGE StandaloneDeriving, DeriveDataTypeable #-} -{-# OPTIONS_GHC -fno-warn-orphans - -fno-warn-incomplete-patterns +{-# OPTIONS_GHC -fno-warn-incomplete-patterns -fno-warn-deprecations -fno-warn-unused-binds #-} --FIXME module UnitTests.Distribution.Version (versionTests) where @@ -20,7 +19,7 @@ import Test.Tasty import Test.Tasty.QuickCheck import qualified Test.Laws as Laws -import Test.QuickCheck.Utils +import Distribution.Arbitrary.Instances () import Data.Maybe (fromJust) import Data.Function (on) @@ -132,24 +131,6 @@ versionTests = -- -- , property prop_parse_disp5 -- ] -instance Arbitrary Version where - arbitrary = do - branch <- smallListOf1 $ - frequency [(3, return 0) - ,(3, return 1) - ,(2, return 2) - ,(2, return 3) - ,(1, return 0xfffd) - ,(1, return 0xfffe) -- max fitting into packed W64 - ,(1, return 0xffff) - ,(1, return 0x10000)] - return (mkVersion branch) - where - smallListOf1 = adjustSize (\n -> min 6 (n `div` 3)) . listOf1 - - shrink ver = [ mkVersion ns | ns <- shrink (versionNumbers ver) - , not (null ns) ] - newtype VersionArb = VersionArb [Int] deriving (Eq,Ord,Show) @@ -171,45 +152,6 @@ instance Arbitrary VersionArb where , all (>=0) xs' ] -instance Arbitrary VersionRange where - arbitrary = sized verRangeExp - where - verRangeExp n = frequency $ - [ (2, return anyVersion) - , (1, liftM thisVersion arbitrary) - , (1, liftM laterVersion arbitrary) - , (1, liftM orLaterVersion arbitrary) - , (1, liftM orLaterVersion' arbitrary) - , (1, liftM earlierVersion arbitrary) - , (1, liftM orEarlierVersion arbitrary) - , (1, liftM orEarlierVersion' arbitrary) - , (1, liftM withinVersion arbitrary) - , (1, liftM majorBoundVersion arbitrary) - , (2, liftM VersionRangeParens arbitrary) - ] ++ if n == 0 then [] else - [ (2, liftM2 unionVersionRanges verRangeExp2 verRangeExp2) - , (2, liftM2 intersectVersionRanges verRangeExp2 verRangeExp2) - ] - where - verRangeExp2 = verRangeExp (n `div` 2) - - orLaterVersion' v = - unionVersionRanges (LaterVersion v) (ThisVersion v) - orEarlierVersion' v = - unionVersionRanges (EarlierVersion v) (ThisVersion v) - - shrink AnyVersion = [] - shrink (ThisVersion v) = map ThisVersion (shrink v) - shrink (LaterVersion v) = map LaterVersion (shrink v) - shrink (EarlierVersion v) = map EarlierVersion (shrink v) - shrink (OrLaterVersion v) = LaterVersion v : map OrLaterVersion (shrink v) - shrink (OrEarlierVersion v) = EarlierVersion v : map OrEarlierVersion (shrink v) - shrink (WildcardVersion v) = map WildcardVersion ( shrink v) - shrink (MajorBoundVersion v) = map MajorBoundVersion (shrink v) - shrink (VersionRangeParens vr) = vr : map VersionRangeParens (shrink vr) - shrink (UnionVersionRanges a b) = a : b : map (uncurry UnionVersionRanges) (shrink (a, b)) - shrink (IntersectVersionRanges a b) = a : b : map (uncurry IntersectVersionRanges) (shrink (a, b)) - --------------------- -- Version properties -- @@ -429,39 +371,6 @@ prop_simplifyVersionRange2'' r r' = -- VersionIntervals -- --- | Generating VersionIntervals --- --- This is a tad tricky as VersionIntervals is an abstract type, so we first --- make a local type for generating the internal representation. Then we check --- that this lets us construct valid 'VersionIntervals'. --- - -instance Arbitrary VersionIntervals where - arbitrary = fmap mkVersionIntervals' arbitrary - where - mkVersionIntervals' :: [(Version, Bound)] -> VersionIntervals - mkVersionIntervals' = mkVersionIntervals . go version0 - where - go :: Version -> [(Version, Bound)] -> [VersionInterval] - go _ [] = [] - go v [(lv, lb)] = - [(LowerBound (addVersion lv v) lb, NoUpperBound)] - go v ((lv, lb) : (uv, ub) : rest) = - (LowerBound lv' lb, UpperBound uv' ub) : go uv' rest - where - lv' = addVersion v lv - uv' = addVersion lv' uv - - addVersion :: Version -> Version -> Version - addVersion xs ys = mkVersion $ z (versionNumbers xs) (versionNumbers ys) - where - z [] ys' = ys' - z xs' [] = xs' - z (x : xs') (y : ys') = x + y : z xs' ys' - -instance Arbitrary Bound where - arbitrary = elements [ExclusiveBound, InclusiveBound] - -- | Check that our VersionIntervals' arbitrary instance generates intervals -- that satisfies the invariant. -- diff --git a/cabal-install/cabal-install.cabal.pp b/cabal-install/cabal-install.cabal.pp index bcd9725a87a..bb5a2d23479 100644 --- a/cabal-install/cabal-install.cabal.pp +++ b/cabal-install/cabal-install.cabal.pp @@ -532,6 +532,7 @@ bytestring, cabal-lib-client, cabal-install-solver-dsl, + cabal-quickcheck-instances, Cabal, containers, deepseq, @@ -599,6 +600,7 @@ Cabal, cabal-lib-client, cabal-install-solver-dsl, + cabal-quickcheck-instances, containers, deepseq >= 1.2, hashable, diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ArbitraryInstances.hs b/cabal-install/tests/UnitTests/Distribution/Client/ArbitraryInstances.hs index 9df876cb802..7262e006d0d 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ArbitraryInstances.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ArbitraryInstances.hs @@ -2,159 +2,25 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module UnitTests.Distribution.Client.ArbitraryInstances ( - adjustSize, - shortListOf, - shortListOf1, - arbitraryFlag, - ShortToken(..), - arbitraryShortToken, - NonMEmpty(..), NoShrink(..), + CanonicalPackageVersionConstraint(..), + CanonicalVersionRange(..) ) where -import Data.Char -import Data.List #if !MIN_VERSION_base(4,8,0) import Data.Monoid import Control.Applicative #endif -import Control.Monad -import Distribution.Version -import Distribution.Types.Dependency import Distribution.Types.PackageVersionConstraint -import Distribution.Types.UnqualComponentName -import Distribution.Types.LibraryName -import Distribution.Package -import Distribution.System -import Distribution.Verbosity - -import Distribution.Simple.Setup -import Distribution.Simple.InstallDirs - import Distribution.Utils.NubList +import Distribution.Version import Distribution.Client.IndexUtils.Timestamp -import Test.QuickCheck - +import Distribution.Arbitrary.Instances () -adjustSize :: (Int -> Int) -> Gen a -> Gen a -adjustSize adjust gen = sized (\n -> resize (adjust n) gen) - -shortListOf :: Int -> Gen a -> Gen [a] -shortListOf bound gen = - sized $ \n -> do - k <- choose (0, (n `div` 2) `min` bound) - vectorOf k gen - -shortListOf1 :: Int -> Gen a -> Gen [a] -shortListOf1 bound gen = - sized $ \n -> do - k <- choose (1, 1 `max` ((n `div` 2) `min` bound)) - vectorOf k gen - -newtype ShortToken = ShortToken { getShortToken :: String } - deriving Show - -instance Arbitrary ShortToken where - arbitrary = - ShortToken <$> - (shortListOf1 5 (choose ('#', '~')) - `suchThat` (not . ("[]" `isPrefixOf`))) - --TODO: [code cleanup] need to replace parseHaskellString impl to stop - -- accepting Haskell list syntax [], ['a'] etc, just allow String syntax. - -- Workaround, don't generate [] as this does not round trip. - - - shrink (ShortToken cs) = - [ ShortToken cs' | cs' <- shrink cs, not (null cs') ] - -arbitraryShortToken :: Gen String -arbitraryShortToken = getShortToken <$> arbitrary - -instance Arbitrary Version where - arbitrary = do - branch <- shortListOf1 4 $ - frequency [(3, return 0) - ,(3, return 1) - ,(2, return 2) - ,(1, return 3)] - return (mkVersion branch) - where - - shrink ver = [ mkVersion branch' | branch' <- shrink (versionNumbers ver) - , not (null branch') ] - -instance Arbitrary VersionRange where - arbitrary = canonicaliseVersionRange <$> sized verRangeExp - where - verRangeExp n = frequency $ - [ (2, return anyVersion) - , (1, liftM thisVersion arbitrary) - , (1, liftM laterVersion arbitrary) - , (1, liftM orLaterVersion arbitrary) - , (1, liftM orLaterVersion' arbitrary) - , (1, liftM earlierVersion arbitrary) - , (1, liftM orEarlierVersion arbitrary) - , (1, liftM orEarlierVersion' arbitrary) - , (1, liftM withinVersion arbitrary) - , (2, liftM VersionRangeParens arbitrary) - ] ++ if n == 0 then [] else - [ (2, liftM2 unionVersionRanges verRangeExp2 verRangeExp2) - , (2, liftM2 intersectVersionRanges verRangeExp2 verRangeExp2) - ] - where - verRangeExp2 = verRangeExp (n `div` 2) - - orLaterVersion' v = - unionVersionRanges (laterVersion v) (thisVersion v) - orEarlierVersion' v = - unionVersionRanges (earlierVersion v) (thisVersion v) - - canonicaliseVersionRange = fromVersionIntervals . toVersionIntervals - -instance Arbitrary PackageName where - arbitrary = mkPackageName . intercalate "-" <$> shortListOf1 2 nameComponent - where - nameComponent = shortListOf1 5 (elements packageChars) - `suchThat` (not . all isDigit) - packageChars = filter isAlphaNum ['\0'..'\127'] - -instance Arbitrary Dependency where - arbitrary = Dependency <$> arbitrary <*> arbitrary <*> fmap getNonMEmpty arbitrary - -instance Arbitrary PackageVersionConstraint where - arbitrary = PackageVersionConstraint <$> arbitrary <*> arbitrary - -instance Arbitrary UnqualComponentName where - -- same rules as package names - arbitrary = packageNameToUnqualComponentName <$> arbitrary - -instance Arbitrary LibraryName where - arbitrary = elements =<< sequenceA [LSubLibName <$> arbitrary, pure LMainLibName] - -instance Arbitrary OS where - arbitrary = elements knownOSs - -instance Arbitrary Arch where - arbitrary = elements knownArches - -instance Arbitrary Platform where - arbitrary = Platform <$> arbitrary <*> arbitrary - -instance Arbitrary a => Arbitrary (Flag a) where - arbitrary = arbitraryFlag arbitrary - shrink NoFlag = [] - shrink (Flag x) = NoFlag : [ Flag x' | x' <- shrink x ] - -arbitraryFlag :: Gen a -> Gen (Flag a) -arbitraryFlag genA = - sized $ \sz -> - case sz of - 0 -> pure NoFlag - _ -> frequency [ (1, pure NoFlag) - , (3, Flag <$> genA) ] +import Test.QuickCheck instance (Arbitrary a, Ord a) => Arbitrary (NubList a) where @@ -162,21 +28,6 @@ instance (Arbitrary a, Ord a) => Arbitrary (NubList a) where shrink xs = [ toNubList [] | (not . null) (fromNubList xs) ] -- try empty, otherwise don't shrink as it can loop -instance Arbitrary Verbosity where - arbitrary = elements [minBound..maxBound] - -instance Arbitrary PathTemplate where - arbitrary = toPathTemplate <$> arbitraryShortToken - shrink t = [ toPathTemplate s | s <- shrink (fromPathTemplate t), not (null s) ] - - -newtype NonMEmpty a = NonMEmpty { getNonMEmpty :: a } - deriving (Eq, Ord, Show) - -instance (Arbitrary a, Monoid a, Eq a) => Arbitrary (NonMEmpty a) where - arbitrary = NonMEmpty <$> (arbitrary `suchThat` (/= mempty)) - shrink (NonMEmpty x) = [ NonMEmpty x' | x' <- shrink x, x' /= mempty ] - newtype NoShrink a = NoShrink { getNoShrink :: a } deriving (Eq, Ord, Show) @@ -191,3 +42,25 @@ instance Arbitrary IndexState where arbitrary = frequency [ (1, pure IndexStateHead) , (50, IndexStateTime <$> arbitrary) ] + + +newtype CanonicalPackageVersionConstraint = CanonicalPackageVersionConstraint + { getCanonicalPackageVersionConstraint :: PackageVersionConstraint } + deriving (Show) + +instance Arbitrary CanonicalPackageVersionConstraint where + arbitrary = f <$> arbitrary <*> arbitrary + where + f pkgs range = CanonicalPackageVersionConstraint $ + PackageVersionConstraint pkgs (getCanonicalVersionRange range) + + shrink = fmap CanonicalPackageVersionConstraint . shrink . getCanonicalPackageVersionConstraint + +newtype CanonicalVersionRange = CanonicalVersionRange + { getCanonicalVersionRange :: VersionRange } + deriving (Show) + +instance Arbitrary CanonicalVersionRange where + arbitrary = fmap canonicalise arbitrary + where + canonicalise = CanonicalVersionRange . fromVersionIntervals . toVersionIntervals diff --git a/cabal-install/tests/UnitTests/Distribution/Client/Glob.hs b/cabal-install/tests/UnitTests/Distribution/Client/Glob.hs index 8cd446d64f9..d2b8a1aa4e4 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/Glob.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/Glob.hs @@ -12,7 +12,10 @@ import Distribution.Text (display, parse, simpleParse) import Distribution.Compat.ReadP import Distribution.Client.Glob -import UnitTests.Distribution.Client.ArbitraryInstances + +import Distribution.Arbitrary.Util + ( shortListOf1 + ) import Test.Tasty import Test.Tasty.QuickCheck diff --git a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs index 6649e3f65ad..ec09593ed9b 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/ProjectConfig.hs @@ -21,10 +21,8 @@ import Distribution.Simple.Compiler import Distribution.Simple.Setup import Distribution.Simple.InstallDirs import qualified Distribution.Compat.ReadP as Parse -import Distribution.Simple.Utils import Distribution.Simple.Program.Types import Distribution.Simple.Program.Db -import Distribution.Types.PackageVersionConstraint import Distribution.Client.Types import Distribution.Client.Dependency.Types @@ -41,6 +39,14 @@ import Distribution.Solver.Types.Settings import Distribution.Client.ProjectConfig import Distribution.Client.ProjectConfig.Legacy +import Distribution.Arbitrary.Util + ( NonMEmpty (..) + , ShortToken (..) + , arbitraryFlag + , arbitraryShortToken + , shortListOf + , shortListOf1 + ) import UnitTests.Distribution.Client.ArbitraryInstances import Test.Tasty @@ -167,7 +173,7 @@ prop_roundtrip_printparse_all config = prop_roundtrip_printparse_packages :: [PackageLocationString] -> [PackageLocationString] -> [SourceRepo] - -> [PackageVersionConstraint] + -> [CanonicalPackageVersionConstraint] -> Bool prop_roundtrip_printparse_packages pkglocstrs1 pkglocstrs2 repos named = roundtrip_printparse @@ -175,7 +181,7 @@ prop_roundtrip_printparse_packages pkglocstrs1 pkglocstrs2 repos named = projectPackages = map getPackageLocationString pkglocstrs1, projectPackagesOptional = map getPackageLocationString pkglocstrs2, projectPackagesRepo = repos, - projectPackagesNamed = named + projectPackagesNamed = map getCanonicalPackageVersionConstraint named } prop_roundtrip_printparse_buildonly :: ProjectConfigBuildOnly -> Bool @@ -280,7 +286,7 @@ instance Arbitrary ProjectConfig where <$> (map getPackageLocationString <$> arbitrary) <*> (map getPackageLocationString <$> arbitrary) <*> shortListOf 3 arbitrary - <*> arbitrary + <*> (map getCanonicalPackageVersionConstraint <$> arbitrary) <*> arbitrary <*> arbitrary <*> arbitrary @@ -427,7 +433,7 @@ instance Arbitrary ProjectConfigShared where <*> arbitrary <*> arbitraryFlag arbitraryShortToken <*> arbitraryConstraints - <*> shortListOf 2 arbitrary + <*> shortListOf 2 (fmap getCanonicalPackageVersionConstraint arbitrary) <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary @@ -800,7 +806,7 @@ instance Arbitrary UserConstraint where arbitrary = UserConstraint <$> arbitrary <*> arbitrary instance Arbitrary PackageProperty where - arbitrary = oneof [ PackagePropertyVersion <$> arbitrary + arbitrary = oneof [ PackagePropertyVersion . getCanonicalVersionRange <$> arbitrary , pure PackagePropertyInstalled , pure PackagePropertySource , PackagePropertyFlags . mkFlagAssignment <$> shortListOf1 3 arbitrary @@ -810,13 +816,6 @@ instance Arbitrary PackageProperty where instance Arbitrary OptionalStanza where arbitrary = elements [minBound..maxBound] -instance Arbitrary FlagName where - arbitrary = mkFlagName <$> flagident - where - flagident = lowercase <$> shortListOf1 5 (elements flagChars) - `suchThat` (("-" /=) . take 1) - flagChars = "-_" ++ ['a'..'z'] - instance Arbitrary PreSolver where arbitrary = elements [minBound..maxBound] diff --git a/cabal-install/tests/UnitTests/Distribution/Client/VCS.hs b/cabal-install/tests/UnitTests/Distribution/Client/VCS.hs index 46700bf2ef0..f238a370821 100644 --- a/cabal-install/tests/UnitTests/Distribution/Client/VCS.hs +++ b/cabal-install/tests/UnitTests/Distribution/Client/VCS.hs @@ -27,9 +27,11 @@ import System.FilePath import System.Directory import System.Random +import Distribution.Arbitrary.Util + ( shortListOf1 + ) import Test.Tasty import Test.Tasty.QuickCheck -import UnitTests.Distribution.Client.ArbitraryInstances import UnitTests.TempTestDir (withTestDir, removeDirectoryRecursiveHack) diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs index ae4589a72ed..c0419ae2352 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/QuickCheck.hs @@ -42,6 +42,8 @@ import Distribution.Solver.Types.Variable import Distribution.Verbosity import Distribution.Version +import Distribution.Arbitrary.Instances () + import UnitTests.Distribution.Solver.Modular.DSL import UnitTests.Distribution.Solver.Modular.QuickCheck.Utils ( testPropertyWithSeed ) @@ -398,21 +400,25 @@ instance Arbitrary IndependentGoals where shrink (IndependentGoals indep) = [IndependentGoals False | indep] -instance Arbitrary UnqualComponentName where +newtype PrefixedUnqualComponentName + = PrefixedUnqualComponentName + { getPrefixedUnqualComponentName :: UnqualComponentName } + +instance Arbitrary PrefixedUnqualComponentName where -- The "component-" prefix prevents component names and build-depends -- dependency names from overlapping. -- TODO: Remove the prefix once the QuickCheck tests support dependencies on -- internal libraries. arbitrary = - mkUnqualComponentName <$> (\c -> "component-" ++ [c]) <$> elements "ABC" + PrefixedUnqualComponentName . mkUnqualComponentName <$> (\c -> "component-" ++ [c]) <$> elements "ABC" instance Arbitrary Component where arbitrary = oneof [ return ComponentLib - , ComponentSubLib <$> arbitrary - , ComponentExe <$> arbitrary - , ComponentFLib <$> arbitrary - , ComponentTest <$> arbitrary - , ComponentBench <$> arbitrary + , ComponentSubLib . getPrefixedUnqualComponentName <$> arbitrary + , ComponentExe . getPrefixedUnqualComponentName <$> arbitrary + , ComponentFLib . getPrefixedUnqualComponentName <$> arbitrary + , ComponentTest . getPrefixedUnqualComponentName <$> arbitrary + , ComponentBench . getPrefixedUnqualComponentName <$> arbitrary , return ComponentSetup ] @@ -478,11 +484,6 @@ instance Arbitrary OptionalStanza where shrink BenchStanzas = [TestStanzas] shrink TestStanzas = [] -instance Arbitrary VersionRange where - arbitrary = error "arbitrary not implemented: VersionRange" - - shrink vr = [noVersion | vr /= noVersion] - -- Randomly sorts solver variables using 'hash'. -- TODO: Sorting goals with this function is very slow. instance Arbitrary VarOrdering where diff --git a/cabal.project b/cabal.project index 71a1156d389..54e6d7eea08 100644 --- a/cabal.project +++ b/cabal.project @@ -1,4 +1,4 @@ -packages: Cabal/ cabal-testsuite/ cabal-install/ solver-benchmarks/ pretty-show-1.6.16/ +packages: Cabal/ cabal-testsuite/ cabal-install/ Cabal/cabal-quickcheck-instances/ solver-benchmarks/ pretty-show-1.6.16/ constraints: unix >= 2.7.1.0 -- Uncomment to allow picking up extra local unpacked deps: