packages feed

HUnit-1.2.4.3: tests/HUnitTestOptimize.hs

-- HUnitTestOptimize.hs
--
-- The purpose of this file is to test whether certain issues occur with optimization.
-- It should be built and run with each level of optimization.  I.e., -O0, -O1, -O2
--
-- With some versions and optimization levels of HUnit and GHC, tests were getting
-- optimized out.  This is a very bad thing and needs to be tested for.

module Main (main) where

import Control.Monad (unless)
import Test.HUnit

-- Used to include the optimization level in the test results
optimizationLevel :: String
#if defined(O0)
optimizationLevel = "-O0"
#elif defined(O1)
optimizationLevel = "-O1"
#elif defined(O2)
optimizationLevel = "-O2"
#else
optimizationLevel = "unknown optimization level"
#endif

-- A test runner that doesn't print the results of the tests; it only tabulates
-- the results of the tests.  In this context, it's used to verify that no
-- tests were optimized away or otherwise lost.
simpleTestRunner :: Test -> IO Counts
simpleTestRunner t = do 
    (counts', _) <- runTestText nullAccum t
    return counts'
    where
        nullAccum = PutText (\ _ _ _ -> return ()) ()

-- Some combinations of HUnit, GHC, and optimization levels cause tests to be
-- optimized away.  This section verifies that all tests of a type are
-- performed.
optimizationTests :: IO ()
optimizationTests = do
    counts2 <- simpleTestRunner $ TestLabel "Basic Optimization Tests" $ TestList [ 
        True ~=? True,
        False ~=? True,
        TestCase $ assertEqual "both true" True True,
        TestCase $ assertEqual "false true" False True,
        TestCase $ assertEqual "fa" False True,
        TestCase $ assertEqual "f" False True,
        TestCase $ (False @?= True),
        TestCase $ unless (False == True) (assertFailure "f")
        ]
    -- Verify results of counts2
    -- We can't use HUnit because it's possible that some tests have been
    -- optimized away, so we'll just do it manually.
    unless 
        (cases counts2 == 8)
        (putStrLn $ "Failure: Basic Optimization (" ++ optimizationLevel ++ "): expected 8 test cases; only " ++ (show $ cases counts2) ++ " found.  Some may have been optimized out.")
    unless 
        (tried counts2 == 8)
        (putStrLn $ "Failure: Basic Optimization (" ++ optimizationLevel ++ "): expected to try 8 test cases; only " ++ (show $ tried counts2) ++ " tried.  Some may have been optimized out.")
    unless 
        (errors counts2 == 0)
        (putStrLn $ "Failure: Basic Optimization (" ++ optimizationLevel ++ "): expected 0 errors; " ++ (show $ errors counts2) ++ " found.")
    unless
        (failures counts2 == 6)
        (putStrLn $ "Failure: Basic Optimization (" ++ optimizationLevel ++ "): expected 6 failed cases; only " ++ (show $ failures counts2) ++ " failed.  Some may have been optimized out.")
    return ()

-- Added in 1.4.2.3
-- When certain errors occur in a list of tests, the subsequent tests in the
-- list weren't being run.  This test verifies that this does not happen.
undefinedSwallowsTests :: IO ()
undefinedSwallowsTests = do
    -- Added in 1.2.4.3 because the second test case will never be run
    -- (in prior versions)
    counts2 <- simpleTestRunner . TestList $ [
        TestCase $ ('f' : undefined) @?= "bar",
        TestCase $ "foo" @?= "bar"
        ]
    _ <- runTestTT $ TestLabel ("Undefined Swallows Tests (" ++ optimizationLevel ++ ")") $ TestList [
        TestCase $ assertEqual "cases" (cases counts2) 2, 
        TestCase $ assertEqual "tried" (tried counts2) 2, 
        TestCase $ assertEqual "errors" (errors counts2) 1, 
        TestCase $ assertEqual "failures" (failures counts2) 1
        ]
    return ()

-- A simple sequencer of the 2 tests
main :: IO ()
main = do
    optimizationTests
    undefinedSwallowsTests