LinearSplit-0.2: tests/Properties.hs
-- | Tests for the Utils.LinearSplit module.
module Main where
import Data.LinearSplit
import Test.QuickCheck hiding (ranges)
import Test.Framework (Test, defaultMain, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
-- | A datatype to model the generation the arbitrary splitting processers.
-- There are the restrictions to items of type Item Int Double
data Split = Split {
chunks :: Int,
items :: [Item Int Double],
threshold :: Double
} deriving (Show)
-- | A datatype to generate a positive item weight. The weight
-- is restricted to the arbitrary values from 0.0 to 1000000.0
data Weight = W {
unW :: Double
}
instance Arbitrary Weight where
arbitrary = do
w <- choose (0.0, 1000000.0)
return $ W w
instance Arbitrary Split where
arbitrary = do
n <- choose (1,25) :: Gen Int
ws <- arbitrary :: Gen [Weight]
thr <- choose (0.0, 10000.0) :: Gen Double
let mkItem (id, W w) = Item id w
let is = map mkItem $ zip [1..length ws] ws
return $ Split n is thr
-- | Different partition strategies to test
splitters (Split n xs t) = --map ranges
[lPartition n xs, ltPartition n xs t, byLength n xs, byAvgCost n xs]
where
byLength n xs =
let fit = length xs `div` n
fun ys = fit > length ys
in gPartition fun n xs
byAvgCost n xs =
let fit = (sum . map weight) xs / fromIntegral n
fun ys = fit > (sum . map weight) ys
in gPartition fun n xs
-- | Ensure that the sum of the items weights equals to
-- the total ranges costs
prop_totalCost s =
let totalCosts = map (floor . sum . map rangeCost) (splitters s)
itemsCost = floor $ sum $ map weight (items s)
in all (== itemsCost) totalCosts
-- | The optimal algorithm has to produce the lowest partition cost
prop_bestCost s =
let (n,xs) = (chunks s, items s)
bestCost = partitionCost (lPartition (chunks s) (items s))
in all (>= bestCost) $ map partitionCost (splitters s)
-- | Ensure that the real number of ranges no more than required
prop_numRanges = forAll (arbitrary :: Gen Split) $ \s ->
all (<= chunks s) $ map length (splitters s)
-- | Ensure that the items after splitting the same as the items
-- before splitting
prop_equal s =
let inpIds = map item (items s)
outIds = concatMap (map item)
in all (inpIds ==) (map outIds (splitters s))
-- | Reverse working items preserves the optimal cost
prop_reverse s =
let (n,xs) = (chunks s, items s)
in partitionCost (lPartition n xs) ==
partitionCost (lPartition n (reverse xs))
-- | Testing helpers
--rangeCost :: [Item Int Double] -> Double
rangeCost = sum . map weight
partitionCost = floor . maxCost where
maxCost = foldr (max . rangeCost) 0.0
main :: IO ()
main = defaultMain tests
tests :: [Test]
tests =
[ testProperty "numRanges" prop_numRanges
, testProperty "equal" prop_equal
, testProperty "reverse" prop_reverse
, testProperty "totalCost" prop_totalCost
, testProperty "bestCost" prop_bestCost
]