-- | This module tests Haggle by comparing its results to those of FGL. -- This assumes that FGL is reasonably correct. -- -- The arbitrary instance for GraphPair generates a list of edges and -- then constructs equivalent FGL and Haggle graphs. The quickcheck -- properties for each operation try to ensure that the two implementations -- return the same results. module Main ( main ) where import Test.Framework ( defaultMain, testGroup, Test ) import Test.Framework.Providers.QuickCheck2 ( testProperty ) import Test.QuickCheck import Control.Arrow ( first, second ) import Control.Monad ( replicateM ) import qualified Data.Foldable as F import Data.Maybe ( isNothing ) import qualified Data.Set as S import qualified Data.Graph.Inductive as FGL import qualified Data.Graph.Haggle as HGL import qualified Data.Graph.Haggle.VertexLabelAdapter as HGL import qualified Data.Graph.Haggle.SimpleBiDigraph as HGL import qualified Data.Graph.Haggle.Algorithms.DFS as HGL import qualified Data.Graph.Haggle.Algorithms.Dominators as HGL -- import Debug.Trace -- debug = flip trace type BaseGraph = FGL.Gr Int () type TestGraph = HGL.VertexLabeledGraph HGL.SimpleBiDigraph Int data GraphPair = GP [(Int, Int)] BaseGraph TestGraph instance Arbitrary GraphPair where arbitrary = sized mkGraphPair instance Show GraphPair where show (GP es _ _) = show es newtype NodeId = NID Int deriving (Show) instance Arbitrary NodeId where arbitrary = sized mkNodeId where mkNodeId n = do i <- choose (0, n) return (NID i) mkGraphPair :: Int -> Gen GraphPair mkGraphPair sz = do nEdges <- choose (2, 2 * sz) srcs <- replicateM nEdges (choose (0, sz)) dsts <- replicateM nEdges (choose (0, sz)) let edges = unique $ zip srcs dsts nids = unique (srcs ++ dsts) ns = zip nids nids bg = FGL.mkGraph ns (map (\(s, d) -> (s, d, ())) edges) (tg, _) = HGL.fromEdgeList HGL.newMSimpleBiDigraph edges return $! GP edges bg tg main :: IO () main = defaultMain tests tests :: [Test] tests = [ testProperty "prop_sameVertexCount" prop_sameVertexCount , testProperty "prop_sameEdgeCount" prop_sameEdgeCount , testProperty "prop_sameSuccessorsAtLabel" prop_sameSuccessorsAtLabel , testProperty "prop_samePredecessorsAtLabel" prop_samePredecessorsAtLabel , testProperty "prop_dfsSame" prop_dfsSame , testProperty "prop_sameComponents" prop_sameComponents , testProperty "prop_sameNoComponents" prop_sameNoComponents , testProperty "prop_immDominatorsSame" prop_immDominatorsSame , testProperty "prop_dominatorsSame" prop_dominatorsSame ] prop_sameVertexCount :: GraphPair -> Bool prop_sameVertexCount (GP _ bg tg) = length (FGL.nodes bg) == length (HGL.vertices tg) prop_sameEdgeCount :: GraphPair -> Bool prop_sameEdgeCount (GP _ bg tg) = length (FGL.edges bg) == length (HGL.edges tg) prop_sameSuccessorsAtLabel :: (NodeId, GraphPair) -> Bool prop_sameSuccessorsAtLabel (NID nid, GP _ bg tg) | not (FGL.gelem nid bg) && isNothing (vertexFromLabel tg nid) = True | otherwise = bss == tss where bss = S.fromList $ fmap Just $ FGL.suc bg nid ts = maybe [] (map (HGL.vertexLabel tg) . HGL.successors tg) (vertexFromLabel tg nid) tss = S.fromList ts prop_samePredecessorsAtLabel :: (NodeId, GraphPair) -> Bool prop_samePredecessorsAtLabel (NID nid, GP _ bg tg) | not (FGL.gelem nid bg) && isNothing (vertexFromLabel tg nid) = True | otherwise = bss == tss where bss = S.fromList $ fmap Just $ FGL.pre bg nid ts = maybe [] (map (HGL.vertexLabel tg) . HGL.predecessors tg) (vertexFromLabel tg nid) tss = S.fromList ts -- Note that this is only checking the *set* of vertices reached. Unfortunately, -- verifying the *order* is difficult because there are many valid DFS orders -- (depending on the order edges are stored). A test using the DFS number -- (derived from the depth in the depth-first tree) would be a good complement -- to this. prop_dfsSame :: (NodeId, GraphPair) -> Bool prop_dfsSame (NID root, GP _ bg tg) = S.fromList bres == S.fromList tres where bres = map Just $ FGL.dfs [root] bg v = vertexFromLabel tg root tres = maybe [] (map (HGL.vertexLabel tg) . HGL.dfs tg . (:[])) v prop_immDominatorsSame :: (NodeId, GraphPair) -> Bool prop_immDominatorsSame (NID root, GP _ bg tg) | not (FGL.gelem root bg) && isNothing (vertexFromLabel tg root) = True | otherwise = S.fromList bdoms == S.fromList tdoms where bdoms = FGL.iDom bg root toLabs (v1, v2) = let Just v1l = HGL.vertexLabel tg v1 Just v2l = HGL.vertexLabel tg v2 in (v1l, v2l) tdoms = maybe [] (map toLabs . HGL.immediateDominators tg) (vertexFromLabel tg root) prop_dominatorsSame :: (NodeId, GraphPair) -> Bool prop_dominatorsSame (NID root, GP _ bg tg) | not (FGL.gelem root bg) && isNothing (vertexFromLabel tg root) = True | otherwise = S.fromList (map (first Just) bdoms) == S.fromList (map (first (HGL.vertexLabel tg)) tdoms) where bdoms = map (second (S.fromList . map Just)) $ FGL.dom bg root Just rv = vertexFromLabel tg root tdoms = map (second (S.fromList . map (HGL.vertexLabel tg))) $ HGL.dominators tg rv prop_sameComponents :: GraphPair -> Bool prop_sameComponents (GP _ bg tg) = bcs == tcs where bcs = S.map (S.fromList . map Just) $ S.fromList $ FGL.components bg tcs = S.map (S.fromList . map (HGL.vertexLabel tg)) $ S.fromList $ HGL.components tg prop_sameNoComponents :: GraphPair -> Bool prop_sameNoComponents (GP _ bg tg) = FGL.noComponents bg == HGL.noComponents tg -- Helpers vertexFromLabel :: TestGraph -> Int -> Maybe HGL.Vertex vertexFromLabel g lbl = F.find labelMatch (HGL.vertices g) where labelMatch v = Just lbl == (HGL.vertexLabel g v) unique :: (Ord a) => [a] -> [a] unique = S.toList . S.fromList