content: module Test.Framework.Runners.Statistics (
          TestCount, testCountTestTypes, testCountForType, adjustTestCount, testCountTotal,
          TestStatistics(..), ts_pending_tests, ts_no_failures,
          initialTestStatistics, updateTestStatistics,
          totalRunTestsList, gatherStatistics
    ) where
  
  import Test.Framework.Core (TestTypeName)
  import Test.Framework.Runners.Core
  
  import Data.Map (Map)
  import qualified Data.Map as Map
  import Data.Monoid
  
  
  -- | Records a count of the various kinds of test that have been run
  newtype TestCount = TestCount { unTestCount :: Map TestTypeName Int }
  
  testCountTestTypes :: TestCount -> [TestTypeName]
  testCountTestTypes = Map.keys . unTestCount
  
  testCountForType :: String -> TestCount -> Int
  testCountForType test_type = Map.findWithDefault 0 test_type . unTestCount
  
  adjustTestCount :: String -> Int -> TestCount -> TestCount
  adjustTestCount test_type amount = TestCount . Map.insertWith (+) test_type amount . unTestCount
  
  
  -- | The number of tests of all kinds recorded in the given 'TestCount'
  testCountTotal :: TestCount -> Int
  testCountTotal = sum . Map.elems . unTestCount
  
  instance Monoid TestCount where
      mempty = TestCount $ Map.empty
      mappend (TestCount tcm1) (TestCount tcm2) = TestCount $ Map.unionWith (+) tcm1 tcm2
  
  minusTestCount :: TestCount -> TestCount -> TestCount
  minusTestCount (TestCount tcm1) (TestCount tcm2) = TestCount $ Map.unionWith (-) tcm1 tcm2
  
  
  -- | Records information about the run of a number of tests, such
  -- as how many tests have been run, how many are pending and how
  -- many have passed or failed.
  data TestStatistics = TestStatistics {
          ts_total_tests :: TestCount,
          ts_run_tests :: TestCount,
          ts_passed_tests :: TestCount,
          ts_failed_tests :: TestCount
      }
  
  instance Monoid TestStatistics where
      mempty = TestStatistics mempty mempty mempty mempty
      mappend (TestStatistics tot1 run1 pas1 fai1) (TestStatistics tot2 run2 pas2 fai2) = TestStatistics (tot1 `mappend` tot2) (run1 `mappend` run2) (pas1 `mappend` pas2) (fai1 `mappend` fai2)
  
  ts_pending_tests :: TestStatistics -> TestCount
  ts_pending_tests ts = ts_total_tests ts `minusTestCount` ts_run_tests ts
  
  ts_no_failures :: TestStatistics -> Bool
  ts_no_failures ts = testCountTotal (ts_failed_tests ts) <= 0
  
  -- | Create some test statistics that simply records the total number of
  -- tests to be run, ready to be updated by the actual test runs.
  initialTestStatistics :: TestCount -> TestStatistics
  initialTestStatistics total_tests = TestStatistics {
          ts_total_tests = total_tests,
          ts_run_tests = mempty,
          ts_passed_tests = mempty,
          ts_failed_tests = mempty
      }
  
  updateTestStatistics :: (Int -> TestCount) -> Bool -> TestStatistics -> TestStatistics
  updateTestStatistics count_constructor test_suceeded test_statistics = test_statistics {
          ts_run_tests    = ts_run_tests test_statistics    `mappend` (count_constructor 1),
          ts_failed_tests = ts_failed_tests test_statistics `mappend` (count_constructor (if test_suceeded then 0 else 1)),
          ts_passed_tests = ts_passed_tests test_statistics `mappend` (count_constructor (if test_suceeded then 1 else 0))
      }
  
  
  totalRunTests :: RunTest a -> TestCount
  totalRunTests (RunTest _ test_type _) = adjustTestCount test_type 1 mempty
  totalRunTests (RunTestGroup _ tests)  = totalRunTestsList tests
  
  totalRunTestsList :: [RunTest a] -> TestCount
  totalRunTestsList = mconcat . map totalRunTests
  
  gatherStatistics :: [FinishedTest] -> TestStatistics
  gatherStatistics = mconcat . map f
    where
      f (RunTest _ test_type (_, success)) = singleTestStatistics test_type success
      f (RunTestGroup _ tests)             = gatherStatistics tests
  
      singleTestStatistics :: String -> Bool -> TestStatistics
      singleTestStatistics test_type success = TestStatistics {
              ts_total_tests = one,
              ts_run_tests = one,
              ts_passed_tests = if success then one else mempty,
              ts_failed_tests = if success then mempty else one
          }
        where one = adjustTestCount test_type 1 mempty
  