{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-}
module Main where
import Control.Concurrent (threadDelay)
import qualified GI.Gtk as Gtk
import GI.Gtk.Declarative
import GI.Gtk.Declarative.App.Simple
import Pipes
import System.Timeout
import Test.Hspec
main :: IO ()
main = hspec $
describe "run" $ do
it "processes events from inputs" $
runApp app { inputs = [yield IncState >> yield Close]} >>= (`shouldBe` Just 1)
it "finishes app on Exit when input still going" $ do
runApp app { inputs = [closeLoop] } >>= (`shouldBe` Just 0)
-- Propagating exception from the view/update/inputs even is
-- important to crash the application instead of keeping it in a
-- weird state.
it "propagates exceptions from view function" $
runApp app {view = const (error "oh no")} `shouldThrow` errorCall "oh no"
it "propagates exceptions from update handler itself" $
runApp app {inputs = [yield ThrowError]} `shouldThrow` errorCall "oh no"
it "propagates exceptions from the pipeline itself" $
runApp app {inputs = [error "oh no"]} `shouldThrow` errorCall "oh no"
describe "propagates exceptions from the Transition" $ do
it "when the maybe is an exception" $
runApp app { update = \s _ -> Transition s (pure $ error "oh no")
, inputs = [yield ThrowError]
} `shouldThrow` errorCall "oh no"
it "when the io is an exception" $
runApp app { update = \s _ -> Transition s (error "oh no")
, inputs = [yield ThrowError]
} `shouldThrow` errorCall "oh no"
it "when the newly generated event is an exception" $
-- Note: forcing the event by pattern matching is important to raise the exception
runApp app { update = \s ThrowError -> Transition s (pure $ Just (error "oh no"))
, inputs = [yield ThrowError]
} `shouldThrow` errorCall "oh no"
where
app = App
{ update = update'
, view = view'
, inputs = []
, initialState = 0
}
runApp = timeout 1000000 . run
closeLoop = do
yield Close
liftIO (threadDelay 1000000)
closeLoop
type AppState = Int
data AppEvent = IncState | ThrowError | Close
view' :: AppState -> AppView Gtk.Window AppEvent
view' _ = bin Gtk.Window [] (widget Gtk.Label [])
update' :: AppState -> AppEvent -> Transition AppState AppEvent
update' state = \case
IncState -> Transition (state + 1) (pure Nothing)
ThrowError -> error "oh no"
Close -> Exit