{----------------------------------------------------------------- (c) 2009 Markus Dittrich This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 3 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. --------------------------------------------------------------------} -- | this routine tests some aspects of the event block parsing -- routines module Main where -- imports import Control.Monad.Writer import qualified Data.Map as M import Prelude import System.Exit -- local imports import Engine import GenericModel import PrettyPrint import InputParser import TestHelpers import TokenParser ----------------------------------------------------------------- -- -- test data specifications -- ----------------------------------------------------------------- -- | expected values for parser events. The first -- entry in the tuple is of type Bool giving the expected -- value of the evaluated trigger expression. The second -- is a Map of expected molecule counts after the actions -- have been evaluated type ExpectedOutput = (Bool, M.Map String Int) -- | set up the test data -- Format : (parse expression, expected result) type TestCase = (String, ExpectedOutput) -- | simple tests without access to local variables --- NOTE: for now we simply test if parsing succeeds simpleEventParseTests :: [TestCase] simpleEventParseTests = [ ("{ z == 100 } => { x = x }", (True, M.fromList [("x",1000),("y",2000),("z",100)])) , ("{ z== 100} => { x = x+y; y = y+1 }", (True, M.fromList [("x",3000),("y",2001),("z",100)])) , ("{z==121 } => { y=10 }", (False, M.fromList [("x",1000),("y",10),("z",100)])) , ("{z==101} => { x = x/2.0 }", (False, M.fromList [("x",500),("y",2000),("z",100)])) , ("{ z == 100}=>{ z= 121; x = 500; y=2000;}", (True, M.fromList [("x",500),("y",2000),("z",121)])) , ("{ y >= 121 }=>{z=1050}", (True, M.fromList [("x",1000),("y",2000),("z",1050)])) , ("{ z <= 100} => { x= sqrt(4)*100; y = x^2/10}", (True, M.fromList [("x",200),("y",4000),("z",100)])) , ("{ x >= 100} => { z= z; z = 2*z; z = 3*z;}", (True, M.fromList [("x",1000),("y",2000),("z",600)])) , ("{ z < 100} => { x= 10; y = z}", (False, M.fromList [("x",10),("y",100),("z",100)])) , ("{ x > 100} => { y= x+z}", (True, M.fromList [("x",1000),("y",1100),("z",100)])) , ("{ y == 2000 } => { x=1001 }", (True, M.fromList [("x",1001),("y",2000),("z",100)])) , ("{ z <= 302 } => { x = 10; y = 10; z= 200; z=10 }", (True, M.fromList [("x",10),("y",10),("z",10)])) , ("{ y == 100 } => { x = 10; y = 20; }", (False, M.fromList [("x",10),("y",20),("z",100)])) , ("{ z == 100 } => { x = 10; y = 20 }", (True, M.fromList [("x",10),("y",20),("z",100)])) , ("{ z == 100 } => { x = 10; y = 20 + z }", (True, M.fromList [("x",10),("y",120),("z",100)])) , ("{ x > 100 } => { x = 10*x+y; y = 20+z^2 }", (True, M.fromList [("x",12000),("y",10020),("z",100)])) , ("{ z == 100 } => { x = 1e6*exp(-TIME); y = x; }", (True, M.fromList [("x",4),("y",4),("z",100)])) , ("{ y == 100 } => { x = 2; x = y^x; y = x*exp(-TIME) }", (False, M.fromList [("x",4000000),("y",17),("z",100)])) , ("{ z == 100 } => { x = 10+z; y = sqrt(log((x^2))); }", (True, M.fromList [("x",110),("y",3),("z",100)])) ] ---------------------------------------------------------------- -- Molecule maps and definitions for specific simulation times ---------------------------------------------------------------- -- | testmap containing a set of molecules and their -- concentrations testMap_1 :: MoleculeMap testMap_1 = M.fromList [("x",1000),("y",2000),("z",100)] -- | a simulation time time_1 :: Double time_1 = 12.345 ---------------------------------------------------------------- -- -- main driver routines -- ---------------------------------------------------------------- -- | main test driver main :: IO () main = putStrLn "\n\n\nTesting Input Parser" -- check event parser >> (putStr $ color_string Cyan "\nEvent parse tests:\n") >> let eventParseOut = execWriter $ event_parser_test_driver testMap_1 time_1 simpleEventParseTests in examine_output eventParseOut >>= \eventParseStatus -> -- check event triggers and actions (putStr $ color_string Cyan "\n\nEvent trigger/action tests:\n") >> let eventActionOut = execWriter $ event_action_test_driver testMap_1 time_1 simpleEventParseTests in examine_output eventActionOut >>= \eventActionStatus -> -- evaluate status and return let status = eventParseStatus && eventActionStatus in if status == True then exitWith ExitSuccess else exitWith $ ExitFailure 1 -- | this driver parses the event expression and checks that -- there are no errors (there shouldn't be any) event_parser_test_driver :: MoleculeMap -> Double -> [TestCase] -> Writer [TestResult] () event_parser_test_driver _ _ [] = return () event_parser_test_driver mol t (x:xs) = let expr = fst x expected = snd x in -- parse expression case runParser parse_events testModelState "" expr of Left er -> tell [TestResult False expr (show expected) (show er)] Right _ -> tell [TestResult True expr (show expected) ("good parse")] >> event_parser_test_driver mol t xs -- | this driver parses the event expression and checks if -- the trigger and its actions action check out, i.e., we apply -- them to our current state and see if we get the proper number -- of molecules. -- NOTE: We do not thread the state through all tests but always -- start with our default, otherwise it becomes very tedious -- to add/remove tests. -- This parser should be run after the proper parsing has been -- verified via event_parser_test_driver event_action_test_driver :: MoleculeMap -> Double -> [TestCase] -> Writer [TestResult] () event_action_test_driver _ _ [] = return () event_action_test_driver mol t (x:xs) = let expr = fst x expectedTrigger = fst . snd $ x expectedMols = snd . snd $ x in -- parse expression case runParser parse_events testModelState "" expr of Left er -> tell [TestResult False expr "" (show er)] Right event -> -- make sure the trigger expression evaluated properly let actions = evtActions event outMols = execute_actions actions (SymbolTable mol M.empty) t outTrigger = check_trigger mol t expectedTrigger event in case outTrigger && ((molSymbols outMols) == expectedMols) of False -> tell [TestResult False expr (show expectedTrigger ++ " => " ++ show expectedMols) (show outTrigger ++ " => " ++ show (molSymbols outMols))] True -> tell [TestResult True expr "" ("good parse")] >> event_action_test_driver mol t xs -- | given an Event and a MoleculeMap check that the trigger -- evaluates to the expected value check_trigger :: MoleculeMap -> Double -> Bool -> Event -> Bool check_trigger molMap t expected (Event { evtTrigger = trigger }) = computed == expected where computed = compute_trigger (SymbolTable molMap M.empty) t trigger