ribosome-test-0.9.9.9: lib/Ribosome/Test/Embed.hs
module Ribosome.Test.Embed (
-- * Embedded Neovim testing
-- $intro
testPlugin,
testEmbed,
testPluginEmbed,
runEmbedTest,
runTest,
testPluginConf,
testPlugin_,
testEmbedConf,
testEmbed_,
testEmbedLevel,
testEmbedLevel_,
testEmbedTrace,
testEmbedDebug,
testEmbedTrace_,
testEmbedDebug_,
runTestConf,
runTestLogConf,
EmbedStackWith,
EmbedStack,
EmbedHandlerStack,
TestEffects,
) where
import Log (Severity (Debug, Trace))
import Polysemy.Test (TestError, UnitTest)
import Ribosome.Data.PluginConfig (PluginConfig (PluginConfig))
import Ribosome.Data.PluginName (PluginName)
import Ribosome.Data.SettingError (SettingError)
import Ribosome.Effect.Scratch (Scratch)
import Ribosome.Effect.Settings (Settings)
import Ribosome.Embed (HandlerEffects, embedPlugin, interpretPluginEmbed)
import Ribosome.Host.Data.BootError (BootError)
import Ribosome.Host.Data.HostConfig (setStderr)
import Ribosome.Host.Data.Report (Report)
import Ribosome.Host.Data.RpcError (RpcError)
import Ribosome.Host.Data.RpcHandler (RpcHandler)
import Ribosome.Host.Effect.Rpc (Rpc)
import Ribosome.Host.Error (resumeBootError)
import Ribosome.Host.Interpret (HigherOrder)
import Ribosome.Host.Interpreter.Handlers (withHandlers)
import Ribosome.Host.Interpreter.Host (HostDeps)
import qualified Ribosome.Host.Test.Data.TestConfig as Host
import qualified Ribosome.Host.Test.Run as Host
import Ribosome.Host.Test.Run (TestConfStack, TestStack, runUnitTest)
import Ribosome.Plugin.Builtin (BuiltinHandlersDeps)
import Ribosome.Test.Data.TestConfig (TestConfig (TestConfig))
import Ribosome.Test.Error (testError, testHandler)
import Ribosome.Test.Log (testLogLevel)
-- $intro
-- The function 'testPluginEmbed' starts an embedded Neovim subprocess and a Ribosome main loop, then executes the
-- supplied 'Sem'.
--
-- This can be interpreted into a "Hedgehog" 'Hedgehog.TestT' by using the functions 'runEmbedTest' and 'runTest'.
--
-- The functions 'testPluginConf' and 'testPlugin' run a full Ribosome plugin with RPC handlers and extra effects in
-- addition to the above.
-- This can be used to test calling RPC handlers from Neovim, which usually shouldn't be necessary but may be helpful
-- for some edge cases.
--
-- The functions 'testEmbedConf' and 'testEmbed' run tests with extra effects, but no handlers.
-- This is the most advisable way to test plugins, running handlers directly as Haskell functions instead of routing
-- them through Neovim, in particular for those that don't have any parameters.
-- |The extra effects that tests are expected to use, related to errors.
--
-- The plugin effects 'Scratch', 'Settings' and 'Rpc' are allowed without 'Resume', causing tests to terminate
-- immediately if one of these effects is used and throws an error.
--
-- Additionally, the two core errors, 'LogReport' and 'RpcError' are executed directly via 'Stop'.
type TestEffects =
[
Stop Report,
Stop RpcError,
Scratch,
Settings,
Rpc
]
-- |The full test stack below test effects and extra effects.
type EmbedHandlerStack =
HandlerEffects ++ Reader PluginName : TestStack
-- |The full test stack with additional effects.
type EmbedStackWith r =
TestEffects ++ r ++ EmbedHandlerStack
-- |The full test stack with no additional effects.
type EmbedStack =
EmbedStackWith '[]
-- |Interpret the basic test effects without 'IO' related effects.
runTestLogConf ::
Members [Error BootError, Resource, Race, Async, Embed IO] r =>
TestConfig ->
InterpretersFor (Reader PluginName : TestConfStack) r
runTestLogConf (TestConfig freezeTime (PluginConfig name conf _)) =
Host.runTestLogConf (Host.TestConfig freezeTime conf) .
runReader name
-- |Run the basic test effects as a "Hedgehog" test.
runTestConf ::
HasCallStack =>
TestConfig ->
Sem (Reader PluginName : TestStack) () ->
UnitTest
runTestConf conf =
runUnitTest .
runTestLogConf conf
-- |Run the plugin stack and the test stack, using the supplied config.
runEmbedTest ::
HasCallStack =>
TestConfig ->
Sem EmbedHandlerStack () ->
UnitTest
runEmbedTest conf =
runTestConf conf .
interpretPluginEmbed
-- |Run the plugin stack and the test stack, using the default config.
runTest ::
HasCallStack =>
Sem EmbedHandlerStack () ->
UnitTest
runTest =
runEmbedTest def
-- |Run the test plugin effects, 'TestEffects', and start an embedded Neovim subprocess.
testPluginEmbed ::
Members (HostDeps er) r =>
Members BuiltinHandlersDeps r =>
Members [Settings !! SettingError, Error TestError] r =>
InterpretersFor TestEffects r
testPluginEmbed =
embedPlugin .
resumeBootError @Rpc .
resumeBootError @Settings .
resumeBootError @Scratch .
testError .
testHandler .
insertAt @4
-- |Run a full plugin test, using extra effects and RPC handlers.
testPluginConf ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
TestConfig ->
InterpretersFor r EmbedHandlerStack ->
[RpcHandler (r ++ EmbedHandlerStack)] ->
Sem (EmbedStackWith r) () ->
UnitTest
testPluginConf conf effs handlers =
runEmbedTest conf .
effs .
withHandlers handlers .
testPluginEmbed
-- |Run a full plugin test, using extra effects and RPC handlers.
testPlugin ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
InterpretersFor r EmbedHandlerStack ->
[RpcHandler (r ++ EmbedHandlerStack)] ->
Sem (EmbedStackWith r) () ->
UnitTest
testPlugin =
testPluginConf @r def
-- |Run a plugin test with RPC handlers.
testPlugin_ ::
HasCallStack =>
[RpcHandler EmbedHandlerStack] ->
Sem EmbedStack () ->
UnitTest
testPlugin_ =
testPlugin @'[] id
-- |Run a plugin test with extra effects but no RPC handlers.
testEmbedConf ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
TestConfig ->
InterpretersFor r EmbedHandlerStack ->
Sem (EmbedStackWith r) () ->
UnitTest
testEmbedConf conf effs =
testPluginConf @r conf effs mempty
-- |Run a plugin test with extra effects but no RPC handlers.
testEmbed ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
InterpretersFor r EmbedHandlerStack ->
Sem (EmbedStackWith r) () ->
UnitTest
testEmbed =
testEmbedConf @r def
-- |Run a plugin test with extra effects but no RPC handlers.
--
-- Takes a log level, for which the default is to only print critical errors.
testEmbedLevel ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
Severity ->
InterpretersFor r EmbedHandlerStack ->
Sem (EmbedStackWith r) () ->
UnitTest
testEmbedLevel level =
testEmbedConf @r (def & #plugin . #host %~ setStderr level)
-- |Run a plugin test with extra effects but no RPC handlers at the 'Debug' log level.
testEmbedDebug ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
InterpretersFor r EmbedHandlerStack ->
Sem (EmbedStackWith r) () ->
UnitTest
testEmbedDebug effs =
testLogLevel Debug \ conf -> testEmbedConf @r conf effs
-- |Run a plugin test with extra effects but no RPC handlers at the 'Trace' log level for debugging RPC traffic.
testEmbedTrace ::
∀ r .
HasCallStack =>
HigherOrder r EmbedHandlerStack =>
InterpretersFor r EmbedHandlerStack ->
Sem (EmbedStackWith r) () ->
UnitTest
testEmbedTrace effs =
testLogLevel Trace \ conf -> testEmbedConf @r conf effs
-- |Run a plugin test without extra effects and RPC handlers.
testEmbed_ ::
HasCallStack =>
Sem EmbedStack () ->
UnitTest
testEmbed_ =
testPlugin_ mempty
-- |Run a plugin test without extra effects and RPC handlers.
--
-- Takes a log level, for which the default is to only print critical errors.
testEmbedLevel_ ::
HasCallStack =>
Severity ->
Sem EmbedStack () ->
UnitTest
testEmbedLevel_ level =
testEmbedConf @'[] (def & #plugin . #host %~ setStderr level) id
-- |Run a plugin test without extra effects and RPC handlers at the 'Debug' log level.
testEmbedDebug_ ::
HasCallStack =>
Sem EmbedStack () ->
UnitTest
testEmbedDebug_ =
testEmbedLevel_ Debug
-- |Run a plugin test without extra effects and RPC handlers at the 'Trace' log level for debugging RPC traffic.
testEmbedTrace_ ::
HasCallStack =>
Sem EmbedStack () ->
UnitTest
testEmbedTrace_ =
testEmbedLevel_ Trace