packages feed

sbv-14.0: SBVTestSuite/TestSuite/Basics/TPCaching.hs

-----------------------------------------------------------------------------
-- |
-- Module    : TestSuite.Basics.TPCaching
-- Copyright : (c) Levent Erkok
-- License   : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Tests for the TP proof caching mechanism.
-----------------------------------------------------------------------------

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeAbstractions    #-}

{-# OPTIONS_GHC -Wall -Werror #-}

module TestSuite.Basics.TPCaching(tests) where

import Utils.SBVTestFramework

import Data.SBV.TP (runTPWith, lemma, calc, recall, tpStats, (|-), (=:), qed)

import Control.Monad (void)

import Data.Char (isSpace)
import Data.List (isPrefixOf, dropWhileEnd)

import Control.DeepSeq (($!!))

-- | Strip timing info [0.05s] from the end of output lines.
stripTiming :: String -> String
stripTiming s
  | (_, rest@('[':_)) <- break (== '[') (dropWhileEnd isSpace s)
  , last rest == ']'
  = dropWhileEnd isSpace $ take (length s - length rest) s
  | True
  = s

-- | Filter out the statistics summary line from verbose output.
isStatsLine :: String -> Bool
isStatsLine s = "[SBV:" `isPrefixOf` dropWhile isSpace s

-- | Clean captured verbose output: strip timing and stats.
cleanStatsOutput :: String -> String
cleanStatsOutput = unlines . map stripTiming . filter (not . isStatsLine) . lines

-- Test suite
tests :: TestTree
tests = testGroup "Basics.TPCaching"
   [
   -- Normal mode: recall when cache is empty (cache miss).
   -- The proof runs from scratch; recallWith shows "Lemma:" one-liner.
     goldenCapturedIO "tpCache_miss" $ \rf -> do
        let cfg = z3 { redirectVerbose = Just rf }
        void $ runTPWith cfg $
           recall (lemma "fact" sTrue [])

   -- Normal mode: direct proof then recall (cache hit).
   -- The direct proof shows "Lemma:", the recall shows "Cached:".
   , goldenCapturedIO "tpCache_hit" $ \rf -> do
        let cfg = z3 { redirectVerbose = Just rf }
        void $ runTPWith cfg $ do
           _ <- lemma "fact" sTrue []
           recall (lemma "fact" sTrue [])

   -- Normal mode: same proposition proved under two names, then recalled (aliases).
   -- The recall shows "Cached:" with "(a.k.a. ...)" listing the other name.
   , goldenCapturedIO "tpCache_alias" $ \rf -> do
        let cfg = z3 { redirectVerbose = Just rf }
        void $ runTPWith cfg $ do
           _ <- lemma "nameA" sTrue []
           _ <- lemma "nameB" sTrue []
           recall (lemma "nameC" sTrue [])

   -- Normal mode: calc proof with steps, then recall (cache hit).
   -- The direct proof shows each step; the recall collapses to one line.
   , goldenCapturedIO "tpCache_calcCollapse" $ \rf -> do
        let cfg = z3 { redirectVerbose = Just rf }
        void $ runTPWith cfg $ do
           let addZeroProof = calc "addZero"
                                   (\(Forall @"x" (x :: SInteger)) -> x + 0 .== x) $
                                   \x -> [] |- x + 0
                                            =: (x :: SInteger)
                                            =: qed
           _ <- addZeroProof
           recall addZeroProof

   -- Normal mode: nested recall.
   -- First run proves inner and outer. Second run (via recall) hits cache for outer.
   , goldenCapturedIO "tpCache_nested" $ \rf -> do
        let cfg = z3 { redirectVerbose = Just rf }
        void $ runTPWith cfg $ do
           let myProof = do _ <- recall (lemma "inner" sTrue [])
                            lemma "outer" sTrue []
           _ <- myProof
           recall myProof

   -- Stats mode: recall when cache is empty (cache miss).
   -- In stats mode, recall does NOT suppress inner output, so full proof steps are visible.
   , goldenCapturedIO "tpCache_statsMiss" $ \rf -> do
        let cfg = (tpStats z3) { redirectVerbose = Just rf }
        void $ runTPWith cfg $
           recall (calc "addZero"
                        (\(Forall @"x" (x :: SInteger)) -> x + 0 .== x) $
                        \x -> [] |- x + 0
                                 =: (x :: SInteger)
                                 =: qed)
        contents <- readFile rf
        writeFile rf $!! cleanStatsOutput contents

   -- Stats mode: direct proof then recall (cache hit).
   -- Direct proof shows full steps; recall shows "Cached:" one-liner.
   , goldenCapturedIO "tpCache_statsHit" $ \rf -> do
        let cfg = (tpStats z3) { redirectVerbose = Just rf }
        void $ runTPWith cfg $ do
           let addZeroProof = calc "addZero"
                                   (\(Forall @"x" (x :: SInteger)) -> x + 0 .== x) $
                                   \x -> [] |- x + 0
                                            =: (x :: SInteger)
                                            =: qed
           _ <- addZeroProof
           recall addZeroProof
        contents <- readFile rf
        writeFile rf $!! cleanStatsOutput contents

   -- Stats mode: nested recall showing inner cache dynamics.
   -- First recall misses (shows full inner proofs). Second recall hits (shows "Cached:").
   , goldenCapturedIO "tpCache_statsNested" $ \rf -> do
        let cfg = (tpStats z3) { redirectVerbose = Just rf }
        void $ runTPWith cfg $ do
           _ <- recall (lemma "inner" sTrue [])
           _ <- lemma "outer" sTrue []
           _ <- recall (lemma "inner" sTrue [])
           recall (lemma "outer" sTrue [])
        contents <- readFile rf
        writeFile rf $!! cleanStatsOutput contents
   ]