11{-# LANGUAGE DisambiguateRecordFields #-}
2+ {-# LANGUAGE NamedFieldPuns #-}
23{-# LANGUAGE OverloadedStrings #-}
34{-# LANGUAGE ScopedTypeVariables #-}
45{-# LANGUAGE TypeApplications #-}
56
67module Cardano.Testnet.Test.Node.Shutdown
78 ( hprop_shutdown
9+ , hprop_shutdownOnSlotSynced
10+ , hprop_shutdownOnSigint
811 ) where
912
1013import Cardano.Api
1114import Control.Monad
1215import Data.Aeson
16+ import Data.Aeson.Types
1317import Data.Bifunctor
14- import qualified Data.ByteString.Lazy as LBS
18+ import qualified Data.ByteString.Lazy.Char8 as LBS
1519import Data.Functor ((<&>) )
1620import qualified Data.List as L
1721import Data.Maybe
@@ -34,9 +38,14 @@ import qualified System.Process as IO
3438import qualified Testnet.Property.Utils as H
3539
3640import Cardano.Testnet
41+ import Data.Either (isRight )
42+ import GHC.IO.Exception (ExitCode (ExitSuccess , ExitFailure ))
43+ import GHC.Stack (callStack )
44+ import System.Process (interruptProcessGroupOf )
3745import Testnet.Defaults
3846import Testnet.Process.Run (execCli_ , procNode )
3947import Testnet.Property.Utils
48+ import Testnet.Runtime
4049import Testnet.Start.Byron
4150import Testnet.Topology
4251
@@ -160,3 +169,89 @@ hprop_shutdown = H.integrationRetryWorkspace 2 "shutdown" $ \tempAbsBasePath' ->
160169 mExitCode === Just IO. ExitSuccess
161170
162171 return ()
172+
173+ hprop_shutdownOnSlotSynced :: Property
174+ hprop_shutdownOnSlotSynced = H. integrationRetryWorkspace 2 " shutdown-on-slot-synced" $ \ tempAbsBasePath' -> do
175+ -- Start a local test net
176+ -- TODO: Move yaml filepath specification into individual node options
177+ conf <- H. noteShowM $ mkConf tempAbsBasePath'
178+
179+ let maxSlot = 1500
180+ slotLen = 0.01
181+ let fastTestnetOptions = CardanoOnlyTestnetOptions $ cardanoDefaultTestnetOptions
182+ { cardanoEpochLength = 300
183+ , cardanoSlotLength = slotLen
184+ , cardanoNodes =
185+ [ BftTestnetNodeOptions [" --shutdown-on-slot-synced" , show maxSlot]
186+ , BftTestnetNodeOptions []
187+ , SpoTestnetNodeOptions
188+ ]
189+ }
190+ TestnetRuntime { bftNodes = node: _ } <- Cardano.Testnet. testnet fastTestnetOptions conf
191+
192+ -- Wait for the node to exit
193+ let timeout :: Int
194+ timeout = round (40 + (fromIntegral maxSlot * slotLen))
195+ mExitCodeRunning <- H. waitSecondsForProcess timeout (nodeProcessHandle node)
196+
197+ -- Check results
198+ when (isRight mExitCodeRunning) $ do
199+ H. cat (nodeStdout node)
200+ H. cat (nodeStderr node)
201+ mExitCodeRunning === Right ExitSuccess
202+
203+ logs <- H. readFile (nodeStdout node)
204+ slotTip <- case mapMaybe parseMsg $ reverse $ lines logs of
205+ [] -> H. failMessage callStack " Could not find close DB message."
206+ (Left err): _ -> H. failMessage callStack err
207+ (Right s): _ -> return s
208+
209+ let epsilon = 50
210+
211+ H. assert (maxSlot <= slotTip && slotTip <= maxSlot + epsilon)
212+
213+ hprop_shutdownOnSigint :: Property
214+ hprop_shutdownOnSigint = H. integrationRetryWorkspace 2 " shutdown-on-sigint" $ \ tempAbsBasePath' -> do
215+ -- Start a local test net
216+ -- TODO: Move yaml filepath specification into individual node options
217+ conf <- H. noteShowM $ mkConf tempAbsBasePath'
218+
219+ let fastTestnetOptions = CardanoOnlyTestnetOptions $ cardanoDefaultTestnetOptions
220+ { cardanoEpochLength = 300
221+ , cardanoSlotLength = 0.01
222+ }
223+ TestnetRuntime { bftNodes = node@ NodeRuntime {nodeProcessHandle}: _ }
224+ <- Cardano.Testnet. testnet fastTestnetOptions conf
225+
226+ -- send SIGINT
227+ H. evalIO $ interruptProcessGroupOf nodeProcessHandle
228+
229+ -- Wait for the node to exit
230+ mExitCodeRunning <- H. waitSecondsForProcess 5 nodeProcessHandle
231+
232+ -- Check results
233+ when (isRight mExitCodeRunning) $ do
234+ H. cat (nodeStdout node)
235+ H. cat (nodeStderr node)
236+ mExitCodeRunning === Right (ExitFailure 1 )
237+
238+ logs <- H. readFile (nodeStdout node)
239+ case mapMaybe parseMsg $ reverse $ lines logs of
240+ [] -> H. failMessage callStack " Could not find close DB message."
241+ (Left err): _ -> H. failMessage callStack err
242+ (Right _): _ -> pure ()
243+
244+
245+ parseMsg :: String -> Maybe (Either String Integer )
246+ parseMsg line = case decode $ LBS. pack line of
247+ Nothing -> Just $ Left $ " Expected JSON formated log message, but got: " ++ line
248+ Just obj -> Right <$> parseMaybe parseTipSlot obj
249+
250+ parseTipSlot :: Object -> Parser Integer
251+ parseTipSlot obj = do
252+ body <- obj .: " data"
253+ tip <- body .: " tip"
254+ kind <- body .: " kind"
255+ if kind == (" TraceOpenEvent.ClosedDB" :: String )
256+ then tip .: " slot"
257+ else mzero
0 commit comments