-----------------------------------------------------------------------------
-- |
-- Module      :  CSPM.FiringRules.Test.Test
-- Copyright   :  (c) Fontaine 2010
-- License     :  BSD
-- 
-- Maintainer  :  fontaine@cs.uni-duesseldorf.de
-- Stability   :  experimental
-- Portability :  GHC-only
--
-- QuickCheck tests for the modules CSPM.FiringRules.EnumerateEvents
-- and CSPM.FiringRules.FieldConstraints.
-- We check for soundness, completeness and that both approaches yield the same result. 
--
-----------------------------------------------------------------------------

{-# LANGUAGE StandaloneDeriving,FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
module CSPM.FiringRules.Test.Test
(
  main
)
where

import CSPM.CoreLanguage
import CSPM.CoreLanguage.Event (allEvents)

import CSPM.FiringRules.Rules
import CSPM.FiringRules.Verifier
import CSPM.FiringRules.Test.Mock1
import CSPM.FiringRules.Test.Mock2
import qualified CSPM.FiringRules.EnumerateEventsList as EnumNext
import qualified CSPM.FiringRules.FieldConstraints as FieldNext
import CSPM.FiringRules.HelperClasses

import System.Random
import Test.QuickCheck as QC
import Data.Maybe
import qualified Data.List as List
import qualified Data.Set as Set
import Control.Monad

-- | Run a number of QuickCheck tests (with fixed seed).
main :: IO ()
main = forM_ [1,2,3,4] $ \seed -> do
  putStrLn $ "\n\n\nSeed " ++ show seed
  mainDet seed

mainDet :: Int -> IO ()
mainDet i = do
  setStdGen $ mkStdGen i
  testAll

testAll :: IO ()
testAll = do
  testMock1
  testMock2
  testFields

testMock1 :: IO ()
testMock1 = do
  putStrLn "testing Mock1"
  quickCheck $ QC.label "generator Tau rules" 
    ((isJust . viewRuleTau) :: RuleTau M1 -> Bool)
  quickCheck $ QC.label "generator Tick rules"
    ((isJust . viewRuleTick) :: RuleTick M1 -> Bool)
  quickCheck $ QC.label "generator Event rules"
    ((isJust . viewRuleEvent) :: RuleEvent M1 -> Bool)
  quickCheck $ QC.label "sound enum Tick rules" 
    (sound_EnumRuleTick :: RuleTick M1 -> Bool)
  quickCheck $ QC.label "sound enum Tau rules"
    (sound_EnumRuleTau :: RuleTau M1 -> Bool)
  quickCheck $ QC.label "sound enum Event rules"
    (sound_EnumRuleEvent :: RuleEvent M1 -> Bool)
  quickCheck $ QC.label "complete enum Tick rules"
    (complete_enumTickRules :: RuleTick M1 -> Bool)
  quickCheck $ QC.label "complete enum Tau rules"
    (complete_enumTauRules :: RuleTau M1 -> Bool)
  quickCheck $ QC.label "complete enum Event rules"
    (complete_enumEventRules :: RuleEvent M1 -> Bool)
{-
  quickCheck $ QC.label "enum Event rules == evalEventRules"
    (korrect_evalEventRules :: RuleEvent M1 -> Bool)
  quickCheck $ QC.label "enum Tau rules == symRuleTau"
    (korrect_symTauRules :: RuleTau M1 -> Bool)
-}

testMock2 :: IO ()
testMock2 = do
  putStrLn "\n\ntesting Mock2\n\n"
  quickCheck $ QC.label "generator Tau rules" 
    ((isJust . viewRuleTau) :: RuleTau M2 -> Bool)
  quickCheck $ QC.label "generator Tick rules"
    ((isJust . viewRuleTick) :: RuleTick M2 -> Bool)
  quickCheck $ QC.label "generator Event rules"
    ((isJust . viewRuleEvent) :: RuleEvent M2 -> Bool)
  quickCheck $ QC.label "sound enum Tick rules" 
    (sound_EnumRuleTick :: RuleTick M2 -> Bool)
  quickCheck $ QC.label "sound enum Tau rules"
    (sound_EnumRuleTau :: RuleTau M2 -> Bool)
  quickCheck $ QC.label "sound enum Event rules"
    (sound_EnumRuleEvent :: RuleEvent M2 -> Bool)
  quickCheck $ QC.label "complete enum Tick rules"
    (complete_enumTickRules :: RuleTick M2 -> Bool)
  quickCheck $ QC.label "complete enum Tau rules"
    (complete_enumTauRules :: RuleTau M2 -> Bool)
  quickCheck $ QC.label "complete enum Event rules"
    (complete_enumEventRules :: RuleEvent M2 -> Bool)
{-
  quickCheck $ QC.label "enum Event rules == evalEventRules"
    (korrect_evalEventRules :: RuleEvent M2 -> Bool)
  quickCheck $ QC.label "enum Tau rules == symRuleTau"
    (korrect_symTauRules :: RuleTau M2 -> Bool)
-}

sound_EnumRuleTick :: CSP1 i => RuleTick i -> Bool
sound_EnumRuleTick r
  = all (checkRule proc . TickRule) $ EnumNext.tickTransitions proc
  where proc = viewProcBefore $ TickRule r

sound_EnumRuleTau :: CSP1 i => RuleTau i -> Bool
sound_EnumRuleTau r
  = all (checkRule proc . TauRule) $ EnumNext.tauTransitions proc
  where proc = viewProcBefore $ TauRule r

sound_EnumRuleEvent :: forall i. CSP1 i => RuleEvent i -> Bool
sound_EnumRuleEvent r
  = all (checkRule proc . EventRule) $ EnumNext.eventTransitions sigma proc
  where
    proc = viewProcBefore $ EventRule r
    sigma = allEvents (undefined :: i)

checkRule :: CSP1 i => Process i -> Rule i -> Bool
checkRule proc r
  = case viewRuleMaybe r of
      Nothing -> False
      Just (p,_,_) -> p == proc

complete_enumTickRules :: CSP1 i => RuleTick i -> Bool
complete_enumTickRules r
  = r `List.elem` (EnumNext.tickTransitions $ viewProcBefore $ TickRule r)

complete_enumTauRules :: CSP1 i => RuleTau i -> Bool
complete_enumTauRules r
  = r `List.elem` (EnumNext.tauTransitions $ viewProcBefore $ TauRule r)

complete_enumEventRules :: forall i. CSP1 i => RuleEvent i -> Bool
complete_enumEventRules r
  = r `List.elem` (EnumNext.eventTransitions sigma $ viewProcBefore $ EventRule r)
  where sigma = allEvents (undefined :: i)

{-
korrect_evalEventRules :: forall i. CSP1 i => RuleEvent i -> Bool
korrect_evalEventRules rule = ruleSet1 == ruleSet2 where
  ruleSet1 = Set.fromList $ enumRuleEvent sigma proc
  ruleSet2 = Set.fromList $ evalRuleEvents proc
  proc = viewProcBefore $ EventRule rule
  sigma = allEvents (undefined :: i)

korrect_symTauRules :: CSP1 i => RuleTau i -> Bool
korrect_symTauRules rule = ruleSet1 == ruleSet2 where
  ruleSet1 = Set.fromList $ buildRuleTau proc
  ruleSet2 = Set.fromList $ symRuleTau proc
  proc = viewProcBefore $ TauRule rule
-}

testFields :: IO ()
testFields = do
  putStrLn "\n\nTesting computeNext"
  quickCheck $ QC.label "sound_computeNext" 
    (sound_computeNext :: RuleEvent M2 -> Bool)

  quickCheck $ QC.label "complete_computeNext" 
    (complete_computeNext :: RuleEvent M2 -> Bool)

  quickCheck $ QC.label "FieldNext.eventTransitions == EnumNext.eventTransitions"
    (computeNext_eq_EnumRuleEvent :: RuleEvent M2 -> Bool)

  quickCheck $ QC.label "FieldNext.tauTransitions == EnumNext.tauTransitions"
    (fieldTau :: RuleTau M2 -> Bool)

  quickCheck $ QC.label "FieldNext.tickTransitions == EnumNext.tickTransitions"
    (fieldTick :: RuleTau M2 -> Bool)


sound_computeNext :: forall i. CSP2 i => RuleEvent i -> Bool
sound_computeNext r
  = all (checkRule proc . EventRule) $ FieldNext.eventTransitions sigma proc
  where
    proc = viewProcBefore $ EventRule r
    sigma = allEvents (undefined :: i)

complete_computeNext :: forall i. CSP2 i => RuleEvent i -> Bool
complete_computeNext r
  = r `List.elem` (FieldNext.eventTransitions sigma $ viewProcBefore $ EventRule r)
  where
    sigma = allEvents (undefined :: i)

computeNext_eq_EnumRuleEvent :: forall i. CSP2 i => RuleEvent i -> Bool
computeNext_eq_EnumRuleEvent rule = ruleSet1 == ruleSet2
  where
    ruleSet1 = Set.fromList $ FieldNext.eventTransitions sigma proc
    ruleSet2 = Set.fromList $ EnumNext.eventTransitions sigma proc
    proc = viewProcBefore $ EventRule rule
    sigma = allEvents (undefined :: i)

fieldTau :: forall i. CSP2 i => RuleTau i -> Bool
fieldTau rule = ruleSet1 == ruleSet2
  where
    ruleSet1 = Set.fromList $ EnumNext.tauTransitions proc
    ruleSet2 = Set.fromList $ FieldNext.tauTransitions proc
    proc = viewProcBefore $ TauRule rule

fieldTick :: forall i. CSP2 i => RuleTau i -> Bool
fieldTick rule = ruleSet1 == ruleSet2
  where
    ruleSet1 = Set.fromList $ EnumNext.tickTransitions proc
    ruleSet2 = Set.fromList $ FieldNext.tickTransitions proc
    proc = viewProcBefore $ TauRule rule