ribosome-test 0.4.0.0 → 0.9.9.9
raw patch · 45 files changed
+1178/−2572 lines, 45 filesdep +chiasma-testdep +exondep +lens-regex-pcredep −aesondep −bytestringdep −compositiondep ~basesetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: chiasma-test, exon, lens-regex-pcre, polysemy, polysemy-chronos, polysemy-plugin, polysemy-test, prelate, ribosome-host, ribosome-host-test
Dependencies removed: aeson, bytestring, composition, composition-extra, conduit, containers, cornea, data-default, directory, either, exceptions, filepath, free, hslogger, lens, lifted-async, lifted-base, messagepack, monad-control, mtl, nvim-hs, prettyprinter, prettyprinter-ansi-terminal, process, relude, resourcet, tasty-hedgehog, template-haskell, text, transformers, typed-process, unix, unliftio
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- LICENSE +1/−1
- Setup.hs +2/−0
- lib/Ribosome/Handler.hs +21/−0
- lib/Ribosome/Test.hs +164/−0
- lib/Ribosome/Test/Await.hs +0/−37
- lib/Ribosome/Test/Data/TestConfig.hs +28/−0
- lib/Ribosome/Test/Embed.hs +249/−322
- lib/Ribosome/Test/EmbedTmux.hs +83/−0
- lib/Ribosome/Test/Error.hs +47/−0
- lib/Ribosome/Test/Examples/Example1.hs +30/−0
- lib/Ribosome/Test/Exists.hs +0/−44
- lib/Ribosome/Test/File.hs +0/−41
- lib/Ribosome/Test/Functional.hs +0/−74
- lib/Ribosome/Test/Input.hs +0/−21
- lib/Ribosome/Test/Log.hs +22/−0
- lib/Ribosome/Test/Orphans.hs +0/−27
- lib/Ribosome/Test/Run.hs +0/−14
- lib/Ribosome/Test/Screenshot.hs +78/−45
- lib/Ribosome/Test/SocketTmux.hs +172/−0
- lib/Ribosome/Test/Tmux.hs +0/−271
- lib/Ribosome/Test/TmuxCommon.hs +63/−0
- lib/Ribosome/Test/Ui.hs +21/−13
- lib/Ribosome/Test/Unit.hs +0/−100
- lib/Ribosome/Test/Wait.hs +46/−0
- readme.md +0/−91
- ribosome-test.cabal +58/−125
- test/Main.hs +11/−31
- test/Ribosome/Test/AutocmdTest.hs +0/−49
- test/Ribosome/Test/EmbedTmuxTest.hs +12/−0
- test/Ribosome/Test/MappingTest.hs +0/−71
- test/Ribosome/Test/MenuTest.hs +0/−324
- test/Ribosome/Test/MsgpackTest.hs +0/−170
- test/Ribosome/Test/NvimMenuTest.hs +0/−164
- test/Ribosome/Test/PromptTest.hs +0/−39
- test/Ribosome/Test/ReportTest.hs +37/−0
- test/Ribosome/Test/RpcTest.hs +0/−154
- test/Ribosome/Test/ScratchTest.hs +0/−95
- test/Ribosome/Test/SettingTest.hs +0/−51
- test/Ribosome/Test/SocketTmuxTest.hs +12/−0
- test/Ribosome/Test/SyntaxTest.hs +10/−36
- test/Ribosome/Test/THTest.hs +0/−45
- test/Ribosome/Test/WatcherTest.hs +0/−46
- test/Ribosome/Test/WindowTest.hs +0/−43
- test/Skip.hs +11/−0
- test/TestError.hs +0/−28
@@ -1,4 +1,4 @@-Copyright (c) 2020 Torsten Schmits+Copyright (c) 2022 Torsten Schmits Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
@@ -0,0 +1,21 @@+module Ribosome.Handler (+ module Ribosome.Host.Data.RpcType,+ module Ribosome.Host.Handler,+) where++import Ribosome.Host.Data.RpcType (+ AutocmdEvents (..),+ AutocmdGroup (..),+ AutocmdOptions (..),+ AutocmdPatterns (..),+ CommandCompletion (..),+ CommandOptions (..),+ CompleteStyle (..),+ RpcType (..),+ )+import Ribosome.Host.Handler (+ complete,+ completeBuiltin,+ completeCustom,+ completeWith,+ )
@@ -0,0 +1,164 @@+module Ribosome.Test (+ -- * Introduction+ -- $intro++ -- * Embedded testing+ -- $embed++ -- * tmux testing+ -- $tmux++ -- * Embedded test API+ testPlugin,+ testEmbed,+ testPluginEmbed,+ runEmbedTest,+ runTest,+ testPluginConf,+ testPlugin_,+ testEmbedConf,+ testEmbed_,+ testEmbedLevel,+ testEmbedLevel_,+ testEmbedDebug,+ testEmbedDebug_,+ testEmbedTrace,+ testEmbedTrace_,+ runTestConf,+ runTestLogConf,+ EmbedStackWith,+ EmbedStack,+ EmbedHandlerStack,+ TestEffects,+ TestConfig (TestConfig),+ TmuxTestConfig (TmuxTestConfig),+ -- * Error handling+ module Ribosome.Test.Error,+ module Ribosome.Host.Data.Report,+ -- * Assertions for Neovim UI elements+ windowCountIs,+ cursorIs,+ currentCursorIs,+ awaitScreenshot,+ -- * Assertions that are made repeatedly until the succeed+ assertWait,+ assertWaitFor,+) where++import Ribosome.Host.Data.Report (resumeReportFail, stopReportToFail)+import Ribosome.Test.Data.TestConfig (TestConfig (TestConfig), TmuxTestConfig (TmuxTestConfig))+import Ribosome.Test.Embed (+ EmbedHandlerStack,+ EmbedStack,+ EmbedStackWith,+ TestEffects,+ runEmbedTest,+ runTest,+ runTestConf,+ runTestLogConf,+ testEmbed,+ testEmbedConf,+ testEmbedLevel,+ testEmbedLevel_,+ testEmbedTrace,+ testEmbedDebug,+ testEmbedTrace_,+ testEmbedDebug_,+ testEmbed_,+ testPlugin,+ testPluginConf,+ testPluginEmbed,+ testPlugin_,+ )+import Ribosome.Test.Error+import Ribosome.Test.Screenshot (awaitScreenshot)+import Ribosome.Test.Ui+import Ribosome.Test.Wait++-- $intro+-- This is the test library for the "Ribosome" Neoivm plugin framework.+--+-- Three different test environments are available:+--+-- - "Ribosome.Test.Embed" runs Neovim as a subprocess and connects over stdio+--+-- - "Ribosome.Test.EmbedTmux" is like "Ribosome.Test.Embed", but provides a tmux server+--+-- - "Ribosome.Test.SocketTmux" runs Neovim in a window in a fresh tmux server, either headless in a pseudo terminal+-- or in an @xterm@ instance+--+-- This module reexports "Ribosome.Test.Embed".++-- $embed+-- Running a test against an embedded Neovim process is the simplest approach that is suited for unit testing plugin+-- logic where the integration with Neovim startup isn't important.+--+-- Handlers can be registered in Neovim and triggered via RPC API functions like 'nvimCallFunction' and 'nvimCommand'.+-- Most of the time this is only interesting if a handler has complex parameters and you want to test that they are+-- decoded correctly, or that the handler is triggered properly by an autocmd.+-- In more basic cases, where only the interaction with Neovim from within the handler is relevant, it can simply be run+-- directly.+--+-- > import Polysemy.Test+-- > import Ribosome+-- > import Ribosome.Api+-- > import Ribosome.Test+-- >+-- > store ::+-- > Member (Rpc !! RpcError) r =>+-- > Args ->+-- > Handler r ()+-- > store (Args msg) =+-- > ignoreRpcError do+-- > nvimSetVar "message" msg+-- >+-- > test_direct :: UnitTest+-- > test_direct =+-- > testEmbed_ do+-- > store "test directly"+-- > assertEq "test directly" =<< nvimGetVar @Text "message"+-- >+-- > test_rpc :: UnitTest+-- > test_rpc =+-- > testPlugin_ [rpcCommand "Store" Sync store] do+-- > nvimCommand "Store test RPC"+-- > assertEq "test RPC" =<< nvimGetVar @Text "message"+--+-- See [Ribosome.Test.Embed]("Ribosome.Test.Embed") for more options.++-- $tmux+-- It is possible to run a standalone Neovim instance to test against.+-- This is useful to observe the UI's behaviour for debugging purposes, but might also be desired to test a feature in+-- the full environment that is used in production.+--+-- Ribosome provides a testing mode that starts a terminal with a tmux server, in which Neovim is executed as a regular+-- shell process.+-- Variants of this that run tmux in a pseudo terminal that is not rendered, or simply run a tmux server for use in an+-- embedded test, are also available.+--+-- In the terminal case, the test connects the plugin over a socket.+-- It is possible to take \"screenshots\" (capturing the tmux pane running Neovim) that are automatically stored in the+-- @fixtures@ directory of the test suite and compared to previous recordings on subsequent runs, as in this example+-- that runs tmux in a terminal and tests some syntax rules:+--+-- > import Polysemy.Test+-- > import Ribosome.Api+-- > import Ribosome.Syntax+-- > import Ribosome.Test+-- > import Ribosome.Test.SocketTmux+-- >+-- > syntax :: Syntax+-- > syntax =+-- > Syntax [syntaxMatch "TestColons" "::"] [+-- > syntaxHighlight "TestColons" [("cterm", "reverse"), ("ctermfg", "1"), ("gui", "reverse"), ("guifg", "#dc322f")]+-- > ] []+-- >+-- > test_syntax :: UnitTest+-- > test_syntax =+-- > testSocketTmuxGui do+-- > setCurrentBufferContent ["function :: String -> Int", "function _ = 5"]+-- > _ <- executeSyntax syntax+-- > awaitScreenshot False "syntax" 0+-- >+-- > See [Ribosome.Test.SocketTmux]("Ribosome.Test.SocketTmux") and [Ribosome.Test.EmbedTmux]("Ribosome.Test.EmbedTmux")+-- > for more options.
@@ -1,37 +0,0 @@-module Ribosome.Test.Await where--import Hedgehog (TestT)-import Control.Exception (throw)-import Control.Monad.Error.Class (MonadError (throwError), catchError)-import Hedgehog.Internal.Property (mkTestT, runTestT, Failure, Journal)--import Ribosome.Control.Concurrent.Wait (WaitError(Thrown, ConditionUnmet, NotStarted), waitIODef)--await ::- ∀ e a b m .- MonadError e m =>- MonadIO m =>- MonadBaseControl IO m =>- (a -> TestT m b) ->- m a ->- TestT m b-await assertion acquire = do- lift (waitIODef acquire' check') >>= \case- Right a -> pure a- Left (ConditionUnmet (Left (err, journal))) ->- mkTestT (pure (Left err, journal))- Left (ConditionUnmet (Right e)) ->- throwError e- Left (Thrown e) ->- throw e- Left NotStarted -> fail "await was not started"- where- acquire' :: m (Either e a)- acquire' =- catchError (Right <$> acquire) (pure . Left)- check' :: Either e a -> m (Either (Either (Failure, Journal) e) b)- check' (Right a) = do- (result, journal) <- runTestT (assertion a)- pure (mapLeft (Left . (,journal)) result)- check' (Left e) = do- pure (Left (Right e))
@@ -0,0 +1,28 @@+module Ribosome.Test.Data.TestConfig where++import qualified Chiasma.Test.Data.TmuxTestConfig as Chiasma++import Ribosome.Data.PluginConfig (PluginConfig (PluginConfig))+import Ribosome.Host.Data.HostConfig (HostConfig (HostConfig), dataLogConc)++data TestConfig =+ TestConfig {+ freeze :: Bool,+ plugin :: PluginConfig ()+ }+ deriving stock (Eq, Show, Generic)++instance Default TestConfig where+ def =+ TestConfig False (PluginConfig "test" (HostConfig def { dataLogConc = False }) unit)++data TmuxTestConfig =+ TmuxTestConfig {+ core :: TestConfig,+ tmux :: Chiasma.TmuxTestConfig+ }+ deriving stock (Eq, Show, Generic)++instance Default TmuxTestConfig where+ def =+ TmuxTestConfig def def { Chiasma.gui = False }
@@ -1,351 +1,278 @@-module Ribosome.Test.Embed where--import Chiasma.Test.Tmux (withProcessWait)-import Control.Concurrent.Async.Lifted (async, cancel, race)-import Control.Concurrent.Lifted (fork)-import Control.Exception.Lifted (bracket, try)-import Control.Monad.Trans.Resource (runResourceT)-import qualified Data.Map.Strict as Map (fromList, toList, union)-import Hedgehog (TestT)-import Hedgehog.Internal.Property (mkTestT, runTestT)-import Neovim (Neovim, Object)-import Neovim.API.Text (vim_command)-import qualified Neovim.Context.Internal as Internal (- Config,- Neovim(Neovim),- StateTransition(Failure, InitSuccess, Quit),- globalFunctionMap,- mkFunctionMap,- newConfig,- pluginSettings,- retypeConfig,- transitionTo,- )-import Neovim.Main (standalone)-import Neovim.Plugin (Plugin(Plugin), startPluginThreads)-import Neovim.Plugin.Internal (NeovimPlugin, wrapPlugin)-import Neovim.RPC.Common (RPCConfig, newRPCConfig)-import Neovim.RPC.EventHandler (runEventHandler)-import Neovim.RPC.SocketReader (runSocketReader)-import System.Directory (makeAbsolute)-import System.Exit (ExitCode)-import System.Log.Logger (Priority(ERROR), setLevel, updateGlobalLogger)-import qualified System.Posix.Signals as Signal (killProcess, signalProcess)-import System.Process (getPid)-import System.Process.Typed (- Process,- ProcessConfig,- createPipe,- getExitCode,- getStdin,- getStdout,- proc,- setStdin,- setStdout,- startProcess,- stopProcess,- unsafeProcessHandle,- )--import Ribosome.Api.Option (rtpCat)-import Ribosome.Control.Exception (tryAny)-import Ribosome.Control.Monad.Ribo (NvimE)-import Ribosome.Control.Ribosome (Ribosome(Ribosome), newRibosomeTMVar)-import qualified Ribosome.Data.ErrorReport as ErrorReport (ErrorReport(..))-import Ribosome.Error.Report.Class (ReportError(errorReport))-import Ribosome.Nvim.Api.IO (vimSetVar)-import Ribosome.Plugin.RpcHandler (RpcHandler(native))-import Ribosome.System.Time (sleep, sleepW)-import Ribosome.Test.Orphans ()--type Runner m = ∀ a . TestConfig -> m a -> m a+module Ribosome.Test.Embed (+ -- * Embedded Neovim testing+ -- $intro -newtype Vars =- Vars (Map Text Object)- deriving (Eq, Show)- deriving newtype (Default, Semigroup, Monoid)+ testPlugin,+ testEmbed,+ testPluginEmbed,+ runEmbedTest,+ runTest,+ testPluginConf,+ testPlugin_,+ testEmbedConf,+ testEmbed_,+ testEmbedLevel,+ testEmbedLevel_,+ testEmbedTrace,+ testEmbedDebug,+ testEmbedTrace_,+ testEmbedDebug_,+ runTestConf,+ runTestLogConf,+ EmbedStackWith,+ EmbedStack,+ EmbedHandlerStack,+ TestEffects,+) where --- |left biased-varsUnion :: Vars -> Vars -> Vars-varsUnion (Vars v1) (Vars v2) =- Vars (Map.union v1 v2)+import Log (Severity (Debug, Trace))+import Polysemy.Test (TestError, UnitTest) -varsFromList :: [(Text, Object)] -> Vars-varsFromList =- Vars . Map.fromList+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) -data TestConfig =- TestConfig {- tcPluginName :: Text,- tcExtraRtp :: Text,- tcLogPath :: FilePath,- tcTimeout :: Word,- tcCmdline :: Maybe [Text],- tcCmdArgs :: [Text],- tcVariables :: Vars- }+-- $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. -instance Default TestConfig where- def = TestConfig "ribosome" "test/u/fixtures/rtp" "test/u/temp/log" 10 def def def+-- |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+ ] -defaultTestConfigWith :: Text -> Vars -> TestConfig-defaultTestConfigWith name vars =- def { tcPluginName = name, tcVariables = vars }+-- |The full test stack below test effects and extra effects.+type EmbedHandlerStack =+ HandlerEffects ++ Reader PluginName : TestStack -defaultTestConfig :: Text -> TestConfig-defaultTestConfig name = defaultTestConfigWith name def+-- |The full test stack with additional effects.+type EmbedStackWith r =+ TestEffects ++ r ++ EmbedHandlerStack -setVars :: ∀ m e. NvimE e m => Vars -> m ()-setVars (Vars vars) =- traverse_ set (Map.toList vars)- where- set :: (Text, Object) -> m ()- set =- uncurry vimSetVar+-- |The full test stack with no additional effects.+type EmbedStack =+ EmbedStackWith '[] -setupPluginEnv ::- MonadIO m =>- NvimE e m =>+-- |Interpret the basic test effects without 'IO' related effects.+runTestLogConf ::+ Members [Error BootError, Resource, Race, Async, Embed IO] r => TestConfig ->- m ()-setupPluginEnv (TestConfig _ rtp _ _ _ _ vars) = do- absRtp <- liftIO $ makeAbsolute (toString rtp)- rtpCat (toText absRtp)- setVars vars--killPid :: Integral a => a -> IO ()-killPid =- void . tryAny . Signal.signalProcess Signal.killProcess . fromIntegral--killProcess :: Process i o e -> IO ()-killProcess prc = do- void $ try @_ @SomeException do- let handle = unsafeProcessHandle prc- mayPid <- getPid handle- traverse_ killPid mayPid--testNvimProcessConfig :: TestConfig -> ProcessConfig Handle Handle ()-testNvimProcessConfig TestConfig {..} =- setStdin createPipe . setStdout createPipe . proc "nvim" . fmap toString $ args <> tcCmdArgs- where- args = fromMaybe defaultArgs tcCmdline- defaultArgs = ["--embed", "-n", "-u", "NONE", "-i", "NONE"]+ InterpretersFor (Reader PluginName : TestConfStack) r+runTestLogConf (TestConfig freezeTime (PluginConfig name conf _)) =+ Host.runTestLogConf (Host.TestConfig freezeTime conf) .+ runReader name -startHandlers ::- MonadIO m =>- Handle ->- Handle ->+-- |Run the basic test effects as a "Hedgehog" test.+runTestConf ::+ HasCallStack => TestConfig ->- Internal.Config RPCConfig ->- m (IO ())-startHandlers stdoutHandle stdinHandle TestConfig{} nvimConf = do- socketReader <- liftIO (run runSocketReader stdoutHandle)- eventHandler <- liftIO (run runEventHandler stdinHandle)- atomically $ putTMVar (Internal.globalFunctionMap nvimConf) (Internal.mkFunctionMap [])- let stopEventHandlers = traverse_ @[] cancel [socketReader, eventHandler]- return stopEventHandlers- where- run runner hand = async . void $ runner hand emptyConf- emptyConf = nvimConf { Internal.pluginSettings = Nothing }+ Sem (Reader PluginName : TestStack) () ->+ UnitTest+runTestConf conf =+ runUnitTest .+ runTestLogConf conf -startStdioHandlers ::- MonadIO m =>- NvimProc ->+-- |Run the plugin stack and the test stack, using the supplied config.+runEmbedTest ::+ HasCallStack => TestConfig ->- Internal.Config RPCConfig ->- m (IO ())-startStdioHandlers prc =- startHandlers (getStdout prc) (getStdin prc)--runNeovimThunk :: Internal.Config e -> Neovim e a -> IO a-runNeovimThunk cfg (Internal.Neovim thunk) =- runReaderT (runResourceT thunk) cfg+ Sem EmbedHandlerStack () ->+ UnitTest+runEmbedTest conf =+ runTestConf conf .+ interpretPluginEmbed -type NvimProc = Process Handle Handle ()+-- |Run the plugin stack and the test stack, using the default config.+runTest ::+ HasCallStack =>+ Sem EmbedHandlerStack () ->+ UnitTest+runTest =+ runEmbedTest def -waitQuit :: NvimProc -> IO (Maybe ExitCode)-waitQuit prc =- wait 30- where- wait :: Int -> IO (Maybe ExitCode)- wait 0 = return Nothing- wait count = do- code <- getExitCode prc- case code of- Just a -> return $ Just a- Nothing -> do- sleep 0.1- wait $ count - 1+-- |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 -quitNvim :: Internal.Config e -> NvimProc -> IO ()-quitNvim testCfg prc = do- quitThread <- async $ runNeovimThunk testCfg quit- result <- waitQuit prc- case result of- Just _ -> return ()- Nothing -> killProcess prc- cancel quitThread- where- quit = vim_command "qall!"+-- |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 -shutdownNvim :: Internal.Config e -> NvimProc -> IO () -> IO ()-shutdownNvim _ prc stopEventHandlers = do- stopEventHandlers- killProcess prc+-- |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 -runTest ::- MonadIO m =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- TestConfig ->- Internal.Config env ->- n a ->- m a-runTest TestConfig{..} testCfg thunk = do- race (sleepW tcTimeout) (liftIO (runNeovimThunk testCfg (runExceptT $ native thunk))) >>= \case- Right (Right a) -> pure a- Right (Left e) -> fail . toString . unlines . ErrorReport._log . errorReport $ e- Left _ -> fail $ "test exceeded timeout of " <> show tcTimeout <> " seconds"+-- |Run a plugin test with RPC handlers.+testPlugin_ ::+ HasCallStack =>+ [RpcHandler EmbedHandlerStack] ->+ Sem EmbedStack () ->+ UnitTest+testPlugin_ =+ testPlugin @'[] id -runEmbeddedNvim ::- MonadIO m =>- MonadFail m =>- MonadBaseControl IO m =>- RpcHandler e env n =>- ReportError e =>+-- |Run a plugin test with extra effects but no RPC handlers.+testEmbedConf ::+ ∀ r .+ HasCallStack =>+ HigherOrder r EmbedHandlerStack => TestConfig ->- env ->- n a ->- NvimProc ->- m a-runEmbeddedNvim conf ribo thunk prc = do- nvimConf <- liftIO (Internal.newConfig (pure Nothing) newRPCConfig)- let testCfg = Internal.retypeConfig ribo nvimConf- bracket (startStdioHandlers prc conf nvimConf) (liftIO . shutdownNvim testCfg prc) (const $ runTest conf testCfg thunk)+ InterpretersFor r EmbedHandlerStack ->+ Sem (EmbedStackWith r) () ->+ UnitTest+testEmbedConf conf effs =+ testPluginConf @r conf effs mempty -withProcessTerm ::- MonadIO m =>- MonadBaseControl IO m =>- ProcessConfig stdin stdout stderr ->- (Process stdin stdout stderr -> m a) ->- m a-withProcessTerm config =- bracket (startProcess config) (try @_ @SomeException . stopProcess)+-- |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 -runEmbedded ::- MonadIO m =>- MonadFail m =>- MonadBaseControl IO m =>- RpcHandler e env n =>- ReportError e =>- TestConfig ->- env ->- n a ->- m a-runEmbedded conf ribo thunk = do- let pc = testNvimProcessConfig conf- withProcessWait pc $ runEmbeddedNvim conf ribo thunk+-- |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) -unsafeEmbeddedSpec ::- MonadIO m =>- MonadFail m =>- MonadBaseControl IO m =>- RpcHandler e env n =>- ReportError e =>- Runner n ->- TestConfig ->- env ->- n a ->- m a-unsafeEmbeddedSpec runner conf s spec =- runEmbedded conf s $ runner conf spec+-- |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 -unsafeEmbeddedSpecR ::- MonadIO m =>- MonadFail m =>- ReportError e =>- MonadBaseControl IO m =>- RpcHandler e (Ribosome env) n =>- Runner n ->- TestConfig ->- env ->- n a ->- m a-unsafeEmbeddedSpecR runner conf env spec = do- tv <- newRibosomeTMVar env- let ribo = Ribosome (tcPluginName conf) tv- unsafeEmbeddedSpec runner conf ribo spec+-- |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 -runPlugin ::- MonadIO m =>- MonadBaseControl IO m =>- Handle ->- Handle ->- [Neovim () NeovimPlugin] ->- Internal.Config c ->- m (MVar Internal.StateTransition)-runPlugin evHandlerHandle sockreaderHandle plugins baseConf = do- liftIO (updateGlobalLogger "Neovim.Plugin" (setLevel ERROR))- rpcConf <- liftIO newRPCConfig- let conf = Internal.retypeConfig rpcConf baseConf- ehTid <- (fmap void . async . liftIO) (runEventHandler evHandlerHandle conf { Internal.pluginSettings = Nothing })- srTid <- (fmap void . async . liftIO) (runSocketReader sockreaderHandle conf)- void $ fork $ liftIO $ startPluginThreads (Internal.retypeConfig () baseConf) plugins >>= \case- Left e -> do- putMVar (Internal.transitionTo conf) $ Internal.Failure e- standalone [ehTid, srTid] conf- Right (funMapEntries, pluginTids) -> do- atomically $ putTMVar (Internal.globalFunctionMap conf) (Internal.mkFunctionMap funMapEntries)- putMVar (Internal.transitionTo conf) Internal.InitSuccess- standalone (srTid : ehTid : pluginTids) conf- return (Internal.transitionTo conf)+-- |Run a plugin test without extra effects and RPC handlers.+testEmbed_ ::+ HasCallStack =>+ Sem EmbedStack () ->+ UnitTest+testEmbed_ =+ testPlugin_ mempty -inTestT ::- ∀ n m a .- TestT n a ->- (∀ x . n x -> m x) ->- TestT m a-inTestT ma f =- mkTestT (f (runTestT ma))+-- |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 -integrationSpec ::- ∀ n m e env a .- NvimE e n =>- MonadIO n =>- MonadIO m =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- TestConfig ->- Plugin env ->- TestT n a ->- TestT m a-integrationSpec conf plugin@(Plugin env _) thunk =- inTestT thunk \ na ->- withProcessTerm (testNvimProcessConfig conf) (run na)- where- run :: ∀ x . n x -> Process Handle Handle () -> m x- run na prc = do- nvimConf <- liftIO (Internal.newConfig (pure Nothing) (pure env))- bracket (acquire prc nvimConf) release (const $ runTest conf nvimConf (setupPluginEnv conf *> na))- acquire prc nvimConf =- liftIO (runPlugin (getStdin prc) (getStdout prc) [wrapPlugin plugin] nvimConf <* sleep 0.5)- release transitions =- tryPutMVar transitions Internal.Quit *> sleep 0.5+-- |Run a plugin test without extra effects and RPC handlers at the 'Debug' log level.+testEmbedDebug_ ::+ HasCallStack =>+ Sem EmbedStack () ->+ UnitTest+testEmbedDebug_ =+ testEmbedLevel_ Debug -integrationSpecDef ::- NvimE e n =>- MonadIO m =>- MonadIO n =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- Plugin env ->- TestT n a ->- TestT m a-integrationSpecDef =- integrationSpec def+-- |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
@@ -0,0 +1,83 @@+module Ribosome.Test.EmbedTmux where++import Polysemy.Test (UnitTest)++import Ribosome.Embed (HandlerEffects, interpretPluginEmbed)+import Ribosome.Host.Data.RpcHandler (RpcHandler)+import Ribosome.Host.Interpreter.Handlers (withHandlers)+import Ribosome.Test.Data.TestConfig (TmuxTestConfig)+import Ribosome.Test.Embed (TestEffects, testPluginEmbed)+import Ribosome.Test.TmuxCommon (TmuxStack, runTmuxNvim)++type HandlerStack =+ HandlerEffects ++ TmuxStack++type EmbedTmuxWith r =+ TestEffects ++ r ++ HandlerStack++type EmbedTmux =+ EmbedTmuxWith '[]++runEmbedTmuxTestConf ::+ HasCallStack =>+ TmuxTestConfig ->+ Sem HandlerStack () ->+ UnitTest+runEmbedTmuxTestConf conf =+ runTmuxNvim conf .+ interpretPluginEmbed++runEmbedTmuxTest ::+ HasCallStack =>+ Sem HandlerStack () ->+ UnitTest+runEmbedTmuxTest =+ runEmbedTmuxTestConf def++runEmbedTmuxGuiTest ::+ HasCallStack =>+ Sem HandlerStack () ->+ UnitTest+runEmbedTmuxGuiTest =+ runEmbedTmuxTestConf (def & #tmux . #gui .~ True)++testPluginEmbedTmuxConf ::+ ∀ r .+ HasCallStack =>+ Members HandlerStack (r ++ HandlerStack) =>+ TmuxTestConfig ->+ InterpretersFor r HandlerStack ->+ [RpcHandler (r ++ HandlerStack)] ->+ Sem (EmbedTmuxWith r) () ->+ UnitTest+testPluginEmbedTmuxConf conf effs handlers =+ runEmbedTmuxTestConf conf .+ effs .+ withHandlers handlers .+ testPluginEmbed++testPluginEmbedTmux ::+ ∀ r .+ HasCallStack =>+ Members HandlerStack (r ++ HandlerStack) =>+ InterpretersFor r HandlerStack ->+ [RpcHandler (r ++ HandlerStack)] ->+ Sem (EmbedTmuxWith r) () ->+ UnitTest+testPluginEmbedTmux =+ testPluginEmbedTmuxConf @r def++testPluginEmbedTmux_ ::+ HasCallStack =>+ [RpcHandler HandlerStack] ->+ Sem EmbedTmux () ->+ UnitTest+testPluginEmbedTmux_ =+ testPluginEmbedTmux @'[] id++testEmbedTmux ::+ HasCallStack =>+ Sem EmbedTmux () ->+ UnitTest+testEmbedTmux =+ testPluginEmbedTmux_ mempty
@@ -0,0 +1,47 @@+{-# options_haddock not-home #-}++module Ribosome.Test.Error where++import Polysemy.Test (TestError (TestError))++import Ribosome.Host.Data.Report (Reportable, mapReport, reportMessages)+import Ribosome.Host.Data.RpcHandler (Handler)++-- |Resume an effect and convert its error from @'Stop' err@ to @'Error' 'TestError'@.+resumeTestError ::+ ∀ eff err r .+ Show err =>+ Members [eff !! err, Error TestError] r =>+ InterpreterFor eff r+resumeTestError =+ resumeHoistError (TestError . show)++-- |Run a 'Handler', converting the @'Stop' 'Report'@ at its head to @'Error' 'TestError'@.+testHandler ::+ Member (Error TestError) r =>+ Handler r a ->+ Sem r a+testHandler =+ stopToErrorWith (TestError . reportMessages)++-- |Run a 'Handler' in a new thread and return an action that waits for the thread to terminate when sequenced.+-- Converts the @'Stop' 'Report'@ at its head to @'Error' 'TestError'@ when it is awaited.+testHandlerAsync ::+ Members [Error TestError, Async] r =>+ Handler r a ->+ Sem r (Sem r a)+testHandlerAsync h = do+ thread <- async do+ runStop h+ pure do+ testHandler . stopEither =<< note (TestError "async handler didn't produce result") =<< await thread++-- |Interpret @'Stop' err@ to @'Error' 'TestError'@ by using @err@'s instance of 'Reportable'.+testError ::+ ∀ err r a .+ Reportable err =>+ Member (Error TestError) r =>+ Sem (Stop err : r) a ->+ Sem r a+testError =+ testHandler . mapReport . raiseUnder
@@ -0,0 +1,30 @@+{-# options_haddock prune, hide #-}++-- |An example for the docs.+module Ribosome.Test.Examples.Example1 where++import Polysemy.Test++import Ribosome+import Ribosome.Api+import Ribosome.Test++store ::+ Member (Rpc !! RpcError) r =>+ Args ->+ Handler r ()+store (Args msg) =+ ignoreRpcError do+ nvimSetVar "message" msg++test_direct :: UnitTest+test_direct =+ testEmbed_ do+ store "test directly"+ assertEq "test directly" =<< nvimGetVar @Text "message"++test_rpc :: UnitTest+test_rpc =+ testPlugin_ [rpcCommand "Store" Sync store] do+ nvimCommand "Store test RPC"+ assertEq "test RPC" =<< nvimGetVar @Text "message"
@@ -1,44 +0,0 @@-module Ribosome.Test.Exists where--import Data.MessagePack (Object(ObjectInt))-import Neovim (Neovim, toObject)-import Neovim.API.Text (vim_call_function)-import UnliftIO.Exception (tryAny)--import Ribosome.Data.Text (capitalize)-import Ribosome.System.Time (epochSeconds, sleep)--retry ::- MonadIO m =>- MonadFail m =>- Double ->- Int ->- m a ->- (a -> m (Either Text b)) ->- m b-retry interval timeout thunk check = do- start <- epochSeconds- step start- where- step start = do- result <- thunk- checked <- check result- recurse start checked- recurse _ (Right b) = return b- recurse start (Left e) = do- current <- epochSeconds- if (current - start) < timeout- then do- sleep interval- step start- else fail (toString e)--waitForPlugin :: Text -> Double -> Int -> Neovim env ()-waitForPlugin name interval timeout =- retry interval timeout thunk check- where- thunk = tryAny $ vim_call_function "exists" $ fromList [toObject $ "*" <> capitalize name <> "Poll"]- check (Right (ObjectInt 1)) = return $ Right ()- check (Right a) = return $ Left $ errormsg <> "weird return type " <> show a- check (Left e) = return $ Left $ errormsg <> show e- errormsg = "plugin didn't start within " <> show timeout <> " seconds: "
@@ -1,41 +0,0 @@-module Ribosome.Test.File where--import qualified Data.ByteString as ByteString (readFile)-import System.Directory (canonicalizePath, createDirectoryIfMissing, removePathForcibly)-import System.FilePath ((</>))--testDir :: Text -> IO FilePath-testDir = canonicalizePath . toString---- raises exception if cwd is not the package root so we don't damage anything-tempDirIO :: Text -> FilePath -> IO FilePath-tempDirIO prefix path = do- base <- testDir prefix- let dir = base </> "temp"- removePathForcibly dir- createDirectoryIfMissing False dir- let absPath = dir </> path- createDirectoryIfMissing True absPath- return absPath--tempDir :: MonadIO m => Text -> FilePath -> m FilePath-tempDir prefix path =- liftIO $ tempDirIO prefix path--fixture ::- MonadIO m =>- Text ->- FilePath ->- m FilePath-fixture prefix path = do- base <- liftIO $ testDir prefix- return $ base </> "fixtures" </> path--fixtureContent ::- MonadIO m =>- Text ->- FilePath ->- m Text-fixtureContent prefix subPath = do- path <- fixture prefix subPath- decodeUtf8 <$> liftIO (ByteString.readFile path)
@@ -1,74 +0,0 @@-module Ribosome.Test.Functional where---- import Control.Exception (finally)--- import Control.Monad (when)--- import Control.Monad.IO.Class--- import Data.Foldable (traverse_)--- import Neovim (Neovim, vim_command')--- import System.Console.ANSI (Color(Green), ColorIntensity(Dull), ConsoleLayer(Foreground), SGR(SetColor, Reset), setSGR)--- import System.Directory (createDirectoryIfMissing, doesFileExist, getCurrentDirectory, makeAbsolute, removePathForcibly)--- import System.FilePath (takeDirectory, takeFileName, (</>))---- import Ribosome.Control.Ribo (Ribo)--- import Ribosome.Test.Embed (TestConfig(..), setupPluginEnv, unsafeEmbeddedSpec)--- import Ribosome.Test.Exists (waitForPlugin)--- import qualified Ribosome.Test.File as F (fixture, tempDir)---- jobstart :: MonadIO f => Text -> f Text--- jobstart cmd = do--- dir <- liftIO getCurrentDirectory--- return $ "call jobstart('" <> cmd <> "', { 'rpc': v:true, 'cwd': '" <> dir <> "' })"---- logFile :: TestConfig -> IO FilePath--- logFile TestConfig{..} = makeAbsolute $ tcLogPath <> "-spec"---- startPlugin :: TestConfig -> Neovim env ()--- startPlugin tc@TestConfig{..} = do--- absLogPath <- liftIO $ makeAbsolute tcLogPath--- absLogFile <- liftIO $ logFile tc--- liftIO $ createDirectoryIfMissing True (takeDirectory absLogPath)--- liftIO $ removePathForcibly absLogFile--- setupPluginEnv tc--- cmd <- jobstart $ "stack run -- -l " <> absLogFile <> " -v INFO"--- vim_command' cmd--- waitForPlugin tcPluginName 0.1 3---- fSpec :: TestConfig -> Neovim env () -> Neovim env ()--- fSpec conf spec = startPlugin conf >> spec---- showLog' :: Text -> IO ()--- showLog' output = do--- putStrLn ""--- setSGR [SetColor Foreground Dull Green]--- putStrLn "plugin output:"--- setSGR [Reset]--- traverse_ putStrLn (lines output)--- putStrLn ""---- showLog :: TestConfig -> IO ()--- showLog conf = do--- file <- logFile conf--- exists <- doesFileExist file--- when exists $ do--- output <- readFile file--- case output of--- [] -> return ()--- o -> showLog' o---- functionalSpec :: TestConfig -> Ribo () () -> IO ()--- functionalSpec conf spec =--- finally (unsafeEmbeddedSpec fSpec conf () spec) (showLog conf)---- fPrefix :: Text--- fPrefix = "f"---- tempDir :: FilePath -> Neovim e FilePath--- tempDir = F.tempDir fPrefix---- tempFile :: FilePath -> Neovim e FilePath--- tempFile file = do--- absDir <- tempDir $ takeDirectory file--- return $ absDir </> takeFileName file---- fixture :: MonadIO m => FilePath -> m FilePath--- fixture = F.fixture fPrefix
@@ -1,21 +0,0 @@-module Ribosome.Test.Input where--import Control.Concurrent.Lifted (fork, killThread)-import Control.Exception.Lifted (bracket)--import Ribosome.Api.Input (syntheticInput)-import Ribosome.Control.Monad.Ribo (NvimE)--withInput ::- NvimE e m =>- MonadIO m =>- MonadBaseControl IO m =>- Maybe Double ->- [Text] ->- m a ->- m a-withInput interval chars thunk =- bracket (fork input) killThread (const thunk)- where- input =- syntheticInput interval chars
@@ -0,0 +1,22 @@+module Ribosome.Test.Log where++import Log (Severity)++import Ribosome.Test.Data.TestConfig (TestConfig)++testLogLevelConf ::+ HasCallStack =>+ Severity ->+ (TestConfig -> a) ->+ TestConfig ->+ a+testLogLevelConf =+ undefined++testLogLevel ::+ HasCallStack =>+ Severity ->+ (TestConfig -> a) ->+ a+testLogLevel level f =+ testLogLevelConf level f def
@@ -1,27 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Ribosome.Test.Orphans where---- import Control.Monad.IO.Unlift (withRunInIO)--- import Neovim (Neovim)---- import Ribosome.Control.Monad.Ribo (Ribo(Ribo, unRibo))---- instance AssertM (Neovim e) where--- genericAssertFailure__ l =--- liftIO . genericAssertFailure__ l---- genericSubAssert l msg ma =--- withRunInIO (\f -> genericSubAssert l msg (f ma))---- instance AssertM (ExceptT e (Neovim s)) where--- genericAssertFailure__ l = liftIO . genericAssertFailure__ l---- genericSubAssert l msg =--- ExceptT . genericSubAssert l msg . runExceptT---- instance AssertM (Ribo s e) where--- genericAssertFailure__ l = liftIO . genericAssertFailure__ l---- genericSubAssert l msg =--- Ribo . ExceptT . genericSubAssert l msg . runExceptT . unRibo
@@ -1,14 +0,0 @@-module Ribosome.Test.Run where--import Hedgehog (TestT, property, test, withTests)-import Test.Tasty (TestName, TestTree)-import Test.Tasty.Hedgehog (testProperty)--type UnitTest = TestT IO ()--unitTest ::- TestName ->- UnitTest ->- TestTree-unitTest desc =- testProperty desc . withTests 1 . property . test
@@ -1,62 +1,95 @@ module Ribosome.Test.Screenshot where -import Chiasma.Data.TmuxError (TmuxError)-import Chiasma.Data.TmuxThunk (TmuxThunk)-import qualified Chiasma.Test.Screenshot as Chiasma (screenshot)-import Control.Monad.Catch (MonadMask)-import Control.Monad.Free.Class (MonadFree)-import Hedgehog (TestT, (===))+import Chiasma.Data.CodecError (CodecError)+import Chiasma.Effect.Codec (NativeCommandCodecE)+import Chiasma.Effect.TmuxClient (NativeTmux)+import qualified Chiasma.Test.Screenshot as Chiasma+import Chiasma.Tmux (withTmux)+import Chiasma.TmuxApi (Tmux)+import Control.Lens.Regex.Text (group, regex)+import Exon (exon)+import Hedgehog.Internal.Property (Failure)+import qualified Log+import Path (reldir)+import Polysemy.Chronos (ChronosTime)+import qualified Polysemy.Test as Test+import Polysemy.Test (Hedgehog, Test, TestError (TestError), (===))+import Prelude hiding (group)+import qualified Time+import Time (Seconds (Seconds)) -import Ribosome.Control.Monad.Ribo (MonadRibo, Nvim)-import Ribosome.Orphans ()-import Ribosome.Test.Orphans ()-import Ribosome.Test.Unit (fixture)-import Ribosome.Tmux.Run (runTmux)-import Ribosome.System.Time (sleep)-import Ribosome.Test.Await (await)+import Ribosome.Host.Effect.Log (StderrLog, stderrLog)+import Ribosome.Test.Wait (assertWait) +-- |Nvim appears to add random whitespace sequences, optionally interspersed with color codes, to empty lines.+-- This remotes that noise from lines starting with `~\ESC[39m` or `\ESC[94m~\ESC[39m`.+sanitize :: Text -> Text+sanitize =+ [regex|(\x{1b}\[94m)?~((\s|\x{1b}\[94m|\x{1b}\[39m)*)$|] . group 1 .~ ""+ screenshot ::- MonadFree TmuxThunk m =>- MonadIO m =>- Text ->+ Members [Tmux, Test, Error TestError, Embed IO] r => Bool ->+ Bool ->+ Text -> Int ->- m (Maybe ([Text], [Text]))-screenshot name record pane = do- storage <- fixture "screenshots"- Chiasma.screenshot record storage name pane+ Sem r (Maybe ([Text], [Text]))+screenshot record sane name pane = do+ storage <- Test.fixturePath [reldir|screenshots|]+ mapError TestError (Chiasma.screenshotSanitized (if sane then sanitize else id) record storage name pane) assertScreenshot ::- MonadIO m =>- MonadRibo m =>- MonadDeepError e TmuxError m =>- MonadBaseControl IO m =>- MonadMask m =>- Nvim m =>- Text ->+ HasCallStack =>+ Members [NativeTmux, NativeCommandCodecE, Stop CodecError] r =>+ Members [Hedgehog IO, Test, Error TestError, Error Failure, ChronosTime, Race, Embed IO] r => Bool ->+ Text -> Int ->- TestT m ()-assertScreenshot name record pane = do- lift (runTmux (screenshot name record pane)) >>= check+ Sem r ()+assertScreenshot sane name pane =+ withFrozenCallStack $ withTmux $ restop do+ assertWait (screenshot False sane name pane) (traverse_ check) where- check (Just (current, existing)) =+ check (current, existing) = existing === current- check Nothing =- return () -awaitScreenshot ::- MonadIO m =>- MonadMask m =>- MonadRibo m =>- MonadBaseControl IO m =>- MonadDeepError e TmuxError m =>- Nvim m =>+updateScreeshot ::+ HasCallStack =>+ Members [NativeTmux, NativeCommandCodecE, Stop CodecError] r =>+ Members [Hedgehog IO, Test, Error TestError, Error Failure, ChronosTime, StderrLog, Race, Embed IO] r =>+ Bool -> Text ->+ Int ->+ Sem r ()+updateScreeshot sane name pane =+ withTmux $ restop do+ stderrLog (Log.info [exon|Waiting for one second before storing new screenshot for '#{name}'|])+ Time.sleep (Seconds 1)+ void (screenshot sane True name pane)++awaitScreenshot' ::+ HasCallStack =>+ Members [NativeTmux, NativeCommandCodecE, Stop CodecError] r =>+ Members [Hedgehog IO, Test, Error TestError, Error Failure, ChronosTime, StderrLog, Race, Embed IO] r => Bool ->+ Bool ->+ Text -> Int ->- TestT m ()-awaitScreenshot name True pane =- sleep 1 <* lift (runTmux (screenshot name True pane))-awaitScreenshot name False pane =- await (const (assertScreenshot name False pane)) unit+ Sem r ()+awaitScreenshot' = \case+ True ->+ updateScreeshot+ False ->+ withFrozenCallStack assertScreenshot++awaitScreenshot ::+ HasCallStack =>+ Members [NativeTmux, NativeCommandCodecE, Stop CodecError] r =>+ Members [Hedgehog IO, Test, Error TestError, Error Failure, ChronosTime, StderrLog, Race, Embed IO] r =>+ Bool ->+ Text ->+ Int ->+ Sem r ()+awaitScreenshot record =+ withFrozenCallStack do+ awaitScreenshot' record True
@@ -0,0 +1,172 @@+-- |Socket tmux tests start a tmux server, then run a regular Neovim session in a pane and connect to Neovim over a+-- socket.+module Ribosome.Test.SocketTmux where++import Chiasma.Command.Pane (sendKeys)+import Chiasma.Data.CodecError (CodecError)+import Chiasma.Data.SendKeysParams (Key (Lit))+import Chiasma.Effect.Codec (NativeCommandCodecE)+import Chiasma.Effect.TmuxApi (Tmux)+import Chiasma.Effect.TmuxClient (NativeTmux)+import Chiasma.Tmux (withTmux)+import Exon (exon)+import Hedgehog.Internal.Property (Failure)+import Path (Abs, File, Path, reldir, relfile, (</>))+import Path.IO (doesPathExist)+import Polysemy.Chronos (ChronosTime)+import qualified Polysemy.Test as Test+import Polysemy.Test (Hedgehog, Test, UnitTest, assert)++import Ribosome.Host.Data.NvimSocket (NvimSocket (NvimSocket))+import Ribosome.Host.Data.RpcHandler (RpcHandler)+import Ribosome.Host.Interpreter.Handlers (withHandlers)+import Ribosome.Host.Path (pathText)+import Ribosome.Socket (SocketHandlerEffects, interpretPluginSocket)+import Ribosome.Test.Data.TestConfig (TmuxTestConfig)+import Ribosome.Test.Embed (TestEffects, testPluginEmbed)+import Ribosome.Test.TmuxCommon (TmuxStack, runTmuxNvim)+import Ribosome.Test.Wait (assertWait)+import Ribosome.Data.CustomConfig (CustomConfig (CustomConfig))++-- |The stack of internal effects for socket tmux tests.+type TmuxHandlerStack =+ SocketHandlerEffects ++ Reader (CustomConfig ()) : Reader NvimSocket : TmuxStack++-- |The socket tmux test stack with additional effects.+type SocketTmuxWith r =+ TestEffects ++ r ++ TmuxHandlerStack++-- |The socket tmux test stack with no additional effects.+type SocketTmux =+ SocketTmuxWith '[]++nvimCmdline :: Path Abs File -> Text+nvimCmdline socket =+ [exon|nvim --listen #{pathText socket} -n -u NONE -i NONE --clean|]++-- |Create a socket for Neovim to listen on and run a 'Reader' with it.+withSocketTmuxNvim ::+ Members [Test, Hedgehog IO, ChronosTime, Error Failure, Race, Embed IO] r =>+ Members [NativeTmux, NativeCommandCodecE, Stop CodecError] r =>+ InterpreterFor (Reader NvimSocket) r+withSocketTmuxNvim sem = do+ dir <- Test.tempDir [reldir|tmux-test|]+ let socket = dir </> [relfile|nvim-socket|]+ withTmux do+ restop @CodecError @Tmux $ sendKeys 0 [Lit (nvimCmdline socket)]+ assertWait (pure socket) (assert <=< doesPathExist)+ runReader (NvimSocket socket) sem++-- |Run the tmux test stack.+runSocketTmuxTestConf ::+ HasCallStack =>+ TmuxTestConfig ->+ Sem TmuxHandlerStack () ->+ UnitTest+runSocketTmuxTestConf conf =+ runTmuxNvim conf .+ withSocketTmuxNvim .+ runReader (CustomConfig ()) .+ interpretPluginSocket++-- |Run the tmux test stack, using a pty to host tmux.+runSocketTmuxTest ::+ HasCallStack =>+ Sem TmuxHandlerStack () ->+ UnitTest+runSocketTmuxTest =+ runSocketTmuxTestConf def++-- |Run the tmux test stack, using a terminal to tmux tmux.+runSocketTmuxGuiTest ::+ HasCallStack =>+ Sem TmuxHandlerStack () ->+ UnitTest+runSocketTmuxGuiTest =+ runSocketTmuxTestConf (def & #tmux . #gui .~ True)++-- |Run a plugin test against a Neovim process running in a fresh tmux session, connected over a socket.+testPluginSocketTmuxConf ::+ ∀ r .+ HasCallStack =>+ Members TmuxHandlerStack (r ++ TmuxHandlerStack) =>+ -- |Regular test config combined with tmux config+ TmuxTestConfig ->+ -- |Interpreter for custom effects+ InterpretersFor r TmuxHandlerStack ->+ -- |RPC handlers+ [RpcHandler (r ++ TmuxHandlerStack)] ->+ Sem (SocketTmuxWith r) () ->+ UnitTest+testPluginSocketTmuxConf conf effs handlers =+ runSocketTmuxTestConf conf .+ effs .+ withHandlers handlers .+ testPluginEmbed++-- |Run a plugin test against a Neovim process running in a fresh tmux session, connected over a socket.+testPluginSocketTmux ::+ ∀ r .+ HasCallStack =>+ Members TmuxHandlerStack (r ++ TmuxHandlerStack) =>+ -- |Interpreter for custom effects+ InterpretersFor r TmuxHandlerStack ->+ -- |RPC handlers+ [RpcHandler (r ++ TmuxHandlerStack)] ->+ Sem (SocketTmuxWith r) () ->+ UnitTest+testPluginSocketTmux =+ testPluginSocketTmuxConf @r def++-- |Run a plugin test against a Neovim process running in a fresh tmux session, connected over a socket.+testHandlersSocketTmux ::+ HasCallStack =>+ -- |RPC handlers+ [RpcHandler TmuxHandlerStack] ->+ Sem SocketTmux () ->+ UnitTest+testHandlersSocketTmux =+ testPluginSocketTmux @'[] id++-- |Run a plugin test against a Neovim process running in a fresh tmux session, connected over a socket.+testSocketTmux ::+ HasCallStack =>+ Sem SocketTmux () ->+ UnitTest+testSocketTmux =+ testHandlersSocketTmux mempty++-- |Run a plugin test against a Neovim process running in a fresh tmux session displayed in a terminal, connected over a+-- socket.+testPluginSocketTmuxGui ::+ ∀ r .+ HasCallStack =>+ Members TmuxHandlerStack (r ++ TmuxHandlerStack) =>+ -- |Interpreter for custom effects+ InterpretersFor r TmuxHandlerStack ->+ -- |RPC handlers+ [RpcHandler (r ++ TmuxHandlerStack)] ->+ Sem (SocketTmuxWith r) () ->+ UnitTest+testPluginSocketTmuxGui =+ testPluginSocketTmuxConf @r (def & #tmux . #gui .~ True)++-- |Run a plugin test against a Neovim process running in a fresh tmux session displayed in a terminal, connected over a+-- socket.+testHandlersSocketTmuxGui ::+ HasCallStack =>+ -- |RPC handlers+ [RpcHandler TmuxHandlerStack] ->+ Sem SocketTmux () ->+ UnitTest+testHandlersSocketTmuxGui =+ testPluginSocketTmuxGui @'[] id++-- |Run a plugin test against a Neovim process running in a fresh tmux session displayed in a terminal, connected over a+-- socket.+testSocketTmuxGui ::+ HasCallStack =>+ Sem SocketTmux () ->+ UnitTest+testSocketTmuxGui =+ testHandlersSocketTmuxGui mempty
@@ -1,271 +0,0 @@-module Ribosome.Test.Tmux where--import Hedgehog (TestT)-import Chiasma.Command.Pane (sendKeys)-import Chiasma.Data.TmuxError (TmuxError)-import Chiasma.Data.TmuxId (PaneId(PaneId))-import Chiasma.Monad.Stream (runTmux)-import Chiasma.Native.Api (TmuxNative(TmuxNative))-import Chiasma.Test.Tmux (TmuxTestConf, withSystemTempDir)-import qualified Chiasma.Test.Tmux as Chiasma (tmuxGuiSpec, tmuxSpec, tmuxSpec')-import Control.Exception.Lifted (bracket)-import Data.DeepPrisms (DeepPrisms)-import qualified Data.Text.IO as Text-import qualified Neovim.Context.Internal as Internal (- StateTransition(Quit),- newConfig,- retypeConfig,- )-import Neovim.Plugin (Plugin(Plugin))-import Neovim.Plugin.Internal (wrapPlugin)-import Neovim.RPC.Common (SocketType(UnixSocket), createHandle, newRPCConfig)-import System.Directory (doesPathExist)-import System.FilePath ((</>))--import Ribosome.Config.Setting (updateSetting)-import Ribosome.Config.Settings (tmuxSocket)-import Ribosome.Control.Concurrent.Wait (waitIOPredDef)-import Ribosome.Control.Exception (catchAny, tryAny)-import Ribosome.Control.Monad.Ribo (NvimE, Ribo)-import Ribosome.Control.Ribosome (Ribosome(Ribosome), newRibosomeTMVar)-import Ribosome.Error.Report.Class (ReportError)-import Ribosome.Msgpack.Encode (toMsgpack)-import Ribosome.Nvim.Api.IO (vimSetVar)-import Ribosome.Nvim.Api.RpcCall (RpcError)-import Ribosome.Plugin.RpcHandler (RpcHandler)-import Ribosome.System.Time (sleep)-import Ribosome.Test.Embed (- Runner,- TestConfig(..),- inTestT,- runPlugin,- runTest,- startHandlers,- testNvimProcessConfig,- withProcessTerm,- )-import Ribosome.Test.Orphans ()-import Ribosome.Test.Run (UnitTest)-import Ribosome.Test.Unit (uSpec)--runSocketNvimHs ::- MonadIO m =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- TestConfig ->- env ->- n a ->- Handle ->- m a-runSocketNvimHs conf ribo specThunk socket = do- nvimConf <- liftIO (Internal.newConfig (pure Nothing) newRPCConfig)- let testCfg = Internal.retypeConfig ribo nvimConf- bracket (startHandlers socket socket conf nvimConf) liftIO (const $ runTest conf testCfg specThunk)--externalNvimCmdline :: FilePath -> Text-externalNvimCmdline socket =- "nvim --listen " <> toText socket <> " -n -u NONE -i NONE"--startNvimInTmux ::- TmuxNative ->- FilePath ->- IO Handle-startNvimInTmux api temp = do- void $ runExceptT @TmuxError $ runTmux api $ sendKeys (PaneId 0) [externalNvimCmdline socket]- _ <- waitIOPredDef (pure socket) doesPathExist- catchAny err (createHandle (UnixSocket socket))- where- socket =- temp </> "nvim-socket"- err (SomeException e) =- fail ("startNvimInTmux: createHandle failed: " <> show e)--runGui ::- MonadIO m =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- TmuxNative ->- FilePath ->- TestConfig ->- env ->- n a ->- m a-runGui api temp conf ribo specThunk =- runSocketNvimHs conf ribo specThunk =<< liftIO (startNvimInTmux api temp)--unsafeGuiSpec ::- MonadIO m =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- TmuxNative ->- FilePath ->- Runner n ->- TestConfig ->- env ->- n a ->- m a-unsafeGuiSpec api temp runner conf s specThunk =- runGui api temp conf s $ runner conf specThunk--unsafeGuiSpecR ::- MonadIO m =>- MonadFail m =>- ReportError e =>- MonadBaseControl IO m =>- RpcHandler e (Ribosome env) n =>- TmuxNative ->- FilePath ->- Runner n ->- TestConfig ->- env ->- n a ->- m a-unsafeGuiSpecR api temp runner conf s specThunk = do- tv <- newRibosomeTMVar s- let ribo = Ribosome (tcPluginName conf) tv- unsafeGuiSpec api temp runner conf ribo specThunk--guiSpec ::- MonadIO m =>- MonadFail m =>- ReportError e =>- MonadBaseControl IO m =>- DeepPrisms e RpcError =>- TestConfig ->- TmuxNative ->- s ->- Ribo s e a ->- m a-guiSpec conf api env specThunk = do- withSystemTempDir run- where- run tempdir =- unsafeGuiSpecR api tempdir uSpec conf env specThunk--withTmux ::- DeepPrisms e RpcError =>- Ribo s e a ->- TmuxNative ->- Ribo s e a-withTmux thunk (TmuxNative (Just socket)) =- updateSetting tmuxSocket socket *> thunk-withTmux _ _ =- throwText "no socket in test tmux"--tmuxSpec ::- DeepPrisms e RpcError =>- ReportError e =>- TestConfig ->- s ->- TestT (Ribo s e) () ->- UnitTest-tmuxSpec conf env specThunk =- inTestT specThunk \ th ->- Chiasma.tmuxSpec \ api -> guiSpec conf api env (withTmux th api)--tmuxSpec' ::- DeepPrisms e RpcError =>- ReportError e =>- TmuxTestConf ->- TestConfig ->- s ->- TestT (Ribo s e) () ->- UnitTest-tmuxSpec' tmuxConf conf env specThunk =- inTestT specThunk \ th ->- Chiasma.tmuxSpec' tmuxConf \ api ->- guiSpec conf api env (withTmux th api)--tmuxSpecDef ::- DeepPrisms e RpcError =>- ReportError e =>- Default s =>- TestT (Ribo s e) () ->- UnitTest-tmuxSpecDef =- tmuxSpec def def--tmuxGuiSpec ::- DeepPrisms e RpcError =>- ReportError e =>- TestConfig ->- s ->- Ribo s e () ->- UnitTest-tmuxGuiSpec conf env specThunk =- Chiasma.tmuxGuiSpec run- where- run api = guiSpec conf api env (withTmux specThunk api)--tmuxGuiSpecDef ::- DeepPrisms e RpcError =>- ReportError e =>- Default s =>- Ribo s e () ->- UnitTest-tmuxGuiSpecDef =- tmuxGuiSpec def def--withTmuxInt ::- NvimE e m =>- MonadIO m =>- Text ->- m () ->- TmuxNative ->- m ()-withTmuxInt name thunk (TmuxNative (Just socket)) = do- () <- vimSetVar (name <> "_tmux_socket") (toMsgpack socket)- thunk-withTmuxInt _ _ _ =- throwText "no socket in test tmux"--runTmuxWithPlugin ::- MonadIO m =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- TmuxNative ->- TestConfig ->- Plugin env ->- n () ->- m ()-runTmuxWithPlugin api conf plugin@(Plugin env _) thunk = do- withSystemTempDir runProc- where- runProc temp =- either logError pure =<< (tryAny (withProcessTerm (testNvimProcessConfig conf) (run temp)))- run temp prc = do- nvimConf <- liftIO (Internal.newConfig (pure Nothing) (pure env))- bracket (acquire prc nvimConf temp) release (const $ runTest conf nvimConf thunk)- acquire _ nvimConf temp = do- socket <- liftIO (startNvimInTmux api temp)- runPlugin socket socket [wrapPlugin plugin] nvimConf <* sleep 0.5- release transitions =- tryPutMVar transitions Internal.Quit *> sleep 0.5- logError (SomeException e) =- liftIO (Text.hPutStr stderr ("runTmuxWithPlugin: nvim process failed with: " <> show e))--tmuxIntegrationSpecDef ::- NvimE e n =>- MonadIO m =>- MonadIO n =>- MonadFail m =>- ReportError e =>- RpcHandler e env n =>- MonadBaseControl IO m =>- Text ->- Plugin env ->- n () ->- m ()-tmuxIntegrationSpecDef name plugin specThunk =- Chiasma.tmuxGuiSpec run- where- run api =- runTmuxWithPlugin api def plugin (withTmuxInt name specThunk api)
@@ -0,0 +1,63 @@+module Ribosome.Test.TmuxCommon where++import Chiasma.Data.CodecError (CodecError)+import Chiasma.Data.RenderError (RenderError)+import Chiasma.Data.TmuxError (TmuxError)+import Chiasma.Effect.TmuxClient (NativeTmux)+import qualified Chiasma.Test.Data.TmuxTestConfig as Chiasma+import Chiasma.Test.Tmux (TestTmuxEffects, withSystemTempDir, withTestTmux)+import Polysemy.Test (UnitTest)++import Ribosome.Data.PluginName (PluginName)+import Ribosome.Host.Data.BootError (BootError (BootError))+import Ribosome.Host.Test.Run (TestStack)+import Ribosome.Test.Data.TestConfig (TmuxTestConfig (TmuxTestConfig))+import Ribosome.Test.Embed (runTestConf)++type TmuxErrors =+ [+ Stop CodecError,+ Error CodecError,+ Stop RenderError,+ Error RenderError,+ Stop TmuxError,+ Error TmuxError,+ Error Text+ ]++type TmuxBaseStack =+ TestTmuxEffects ++ TmuxErrors++type TmuxStack =+ NativeTmux : TmuxBaseStack ++ Reader PluginName : TestStack++interpretTmuxErrors ::+ Member (Error BootError) r =>+ InterpretersFor TmuxErrors r+interpretTmuxErrors =+ mapError BootError .+ mapError @TmuxError (BootError . show) .+ stopToError .+ mapError @RenderError (BootError . show) .+ stopToError .+ mapError @CodecError (BootError . show) .+ stopToError++withTmuxTest ::+ Members TestStack r =>+ Chiasma.TmuxTestConfig ->+ InterpretersFor TmuxBaseStack r+withTmuxTest conf =+ interpretTmuxErrors .+ withSystemTempDir .+ withTestTmux conf++runTmuxNvim ::+ HasCallStack =>+ TmuxTestConfig ->+ Sem TmuxStack () ->+ UnitTest+runTmuxNvim (TmuxTestConfig conf tmuxConf) =+ runTestConf conf .+ withTmuxTest tmuxConf .+ restop @TmuxError @NativeTmux
@@ -1,32 +1,40 @@+-- |Assertions for Neovim UI elements module Ribosome.Test.Ui where -import Hedgehog (TestT, (===))+import Polysemy.Test (Hedgehog, assertEq, (===))+ import Ribosome.Api.Window (currentCursor, cursor)-import Ribosome.Control.Monad.Ribo (NvimE)-import Ribosome.Nvim.Api.Data (Window)-import Ribosome.Nvim.Api.IO (nvimListWins)+import Ribosome.Host.Api.Data (Window)+import Ribosome.Host.Api.Effect (nvimListWins)+import Ribosome.Host.Effect.Rpc (Rpc) +-- |Assert the number of windows. windowCountIs ::- NvimE e m =>+ Monad m =>+ Members [Rpc, Hedgehog m] r => Int ->- TestT m ()+ Sem r () windowCountIs count = do wins <- nvimListWins- count === (length wins)+ count === length wins +-- |Assert the cursor position in a window. cursorIs ::- NvimE e m =>+ Monad m =>+ Members [Rpc, Hedgehog m] r => Int -> Int -> Window ->- TestT m ()+ Sem r () cursorIs line col =- ((line, col) ===) <=< lift . cursor+ assertEq (line, col) <=< cursor +-- |Assert the cursor position in the current window. currentCursorIs ::- NvimE e m =>+ Monad m =>+ Members [Rpc, Hedgehog m] r => Int -> Int ->- TestT m ()+ Sem r () currentCursorIs line col =- ((line, col) ===) =<< lift currentCursor+ assertEq (line, col) =<< currentCursor
@@ -1,100 +0,0 @@-module Ribosome.Test.Unit where--import Control.Exception.Lifted (bracket_)-import Hedgehog (TestT)-import Hedgehog.Internal.Property (mkTestT, runTestT)-import System.FilePath (takeDirectory, takeFileName, (</>))-import System.Log ()-import System.Log.Logger (Priority(DEBUG, WARNING), setLevel, updateGlobalLogger)--import Ribosome.Control.Monad.Ribo (MonadRibo, NvimE, pluginName)-import Ribosome.Control.Ribosome (Ribosome)-import Ribosome.Error.Report.Class (ReportError)-import Ribosome.Plugin.RpcHandler (RpcHandler)-import Ribosome.Test.Embed (Runner, TestConfig(..), setupPluginEnv, unsafeEmbeddedSpecR)-import qualified Ribosome.Test.File as F (fixture, fixtureContent, tempDir)-import Ribosome.Test.Orphans ()--uPrefix :: Text-uPrefix = "test"--uSpec :: (MonadIO m, NvimE e m) => Runner m-uSpec conf spec = do- setupPluginEnv conf- spec--unitSpec ::- MonadIO n =>- MonadIO m =>- NvimE e' n =>- MonadFail m =>- ReportError e =>- MonadBaseControl IO m =>- RpcHandler e (Ribosome env) n =>- TestConfig ->- env ->- TestT n a ->- TestT m a-unitSpec cfg env t = do- mkTestT (unsafeEmbeddedSpecR uSpec cfg env (runTestT t))--unitSpecDef ::- MonadIO n =>- MonadIO m =>- NvimE e' n =>- MonadFail m =>- ReportError e =>- MonadBaseControl IO m =>- RpcHandler e (Ribosome env) n =>- env ->- TestT n a ->- TestT m a-unitSpecDef =- unitSpec def--unitSpecDef' ::- MonadIO n =>- MonadIO m =>- NvimE e' n =>- MonadFail m =>- ReportError e =>- MonadBaseControl IO m =>- RpcHandler e (Ribosome ()) n =>- TestT n a ->- TestT m a-unitSpecDef' =- unitSpecDef ()--tempDir :: MonadIO m => FilePath -> m FilePath-tempDir = F.tempDir uPrefix--tempFile :: MonadIO m => FilePath -> m FilePath-tempFile file = do- absDir <- tempDir $ takeDirectory file- return $ absDir </> takeFileName file--fixture :: MonadIO m => FilePath -> m FilePath-fixture = F.fixture uPrefix--fixtureContent :: MonadIO m => FilePath -> m Text-fixtureContent = F.fixtureContent uPrefix--withLogAs ::- MonadIO m =>- MonadBaseControl IO m =>- Text ->- m a ->- m a-withLogAs name =- bracket_ (logLevel DEBUG) (logLevel WARNING)- where- logLevel =- liftIO . updateGlobalLogger (toString name) . setLevel--withLog ::- MonadRibo m =>- MonadBaseControl IO m =>- m a ->- m a-withLog thunk =- (`withLogAs` thunk) =<< pluginName
@@ -0,0 +1,46 @@+module Ribosome.Test.Wait where++import Hedgehog.Internal.Property (Failure, failWith, liftTest, mkTest)+import qualified Conc+import Conc (interpretAtomic)+import Polysemy.Test (Hedgehog, liftH)+import qualified Polysemy.Time as Time+import Polysemy.Time (MilliSeconds (MilliSeconds), Seconds (Seconds))++assertWaitFor ::+ Monad m =>+ HasCallStack =>+ Members [Hedgehog m, Time t d, Race, Error Failure, Embed IO] r =>+ TimeUnit t1 =>+ TimeUnit t2 =>+ t1 ->+ t2 ->+ Sem r a ->+ (a -> Sem r b) ->+ Sem r b+assertWaitFor timeout interval acquire test =+ withFrozenCallStack do+ interpretAtomic Nothing do+ Conc.timeout_ timeoutError timeout spin+ where+ spin = do+ a <- raise acquire+ catch (raise (test a)) \ e -> do+ atomicPut (Just e)+ Time.sleep interval+ spin+ timeoutError =+ atomicGet >>= liftH . \case+ Just e -> liftTest (mkTest (Left e, mempty))+ Nothing -> failWith Nothing "timed out before an assertion was made"++assertWait ::+ Monad m =>+ HasCallStack =>+ Members [Hedgehog m, Time t d, Race, Error Failure, Embed IO] r =>+ Sem r a ->+ (a -> Sem r b) ->+ Sem r b+assertWait acquire test =+ withFrozenCallStack do+ assertWaitFor (Seconds 3) (MilliSeconds 100) acquire test
@@ -1,91 +0,0 @@-# About--*ribosome* is an extension framework for [nvim-hs], a [Haskell] library that-provides a [Neovim] plugin engine.--*ribosome*'s structure is similar to that of vanilla [nvim-hs] plugins and is-intended to provide a more comfortable API on top of it.--# Basic Plugin--*ribosome* comes with a companion plugin manager, [chromatin], but you can-start plugins regularly with `jobstart()`.--First, add `ribosome` to your `myplugin.cabal`'s dependencies.--Your project should have an executable that looks like this:--```haskell-import Neovim (neovim, plugins, defaultConfig)-import MyPlugin.Plugin (plugin)--main :: IO ()-main =- neovim defaultConfig {plugins = [plugin]}-```--In `MyPlugin.Plugin`, you should write a plugin definition like this:--```haskell-module MyPlugin.Plugin where--import Data.Default.Class (Default(def))-import Neovim (Neovim, NeovimPlugin, Plugin, wrapPlugin)-import Neovim.Context.Internal (Config(customConfig), asks')-import Ribosome.Api.Echo (echom)-import Ribosome.Control.Monad.Ribo (MonadRibo, NvimE, Ribo)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Error.Report (reportError)-import Ribosome.Internal.IO (retypeNeovim)-import Ribosome.Plugin (RpcDef, autocmd, cmd, riboPlugin, rpcHandler)-import System.Log.Logger (Priority(ERROR), setLevel, updateGlobalLogger)--handleError :: Text -> Ribo () Text ()-handleError err =- echom err--hello :: NvimE e m => MonadRibo m => m ()-hello ::- echom "hello"--bufEnter :: NvimE e m => MonadRibo m => m ()-bufEnter ::- echom "BufEnter"--rpcHandlers :: [[RpcDef (Ribo () Error)]]-rpcHandlers =- [- $(rpcHandler (cmd []) 'hello),- $(rpcHandler (autocmd "BufEnter") 'bufEnter)- ]--plugin' :: Ribosome () -> Plugin (Ribosome ())-plugin' env =- riboPlugin "myplugin" env rpcHandlers def handleError def--initialize :: Neovim e (Ribosome Env)-initialize = do- liftIO $ updateGlobalLogger "Neovim.Plugin" (setLevel ERROR)- ribo <- newRibosome "myplugin" def- retypeNeovim (const ribo) (asks' customConfig)--plugin :: Neovim e NeovimPlugin-plugin =- wrapPlugin . plugin' =<< initialize-```--Follow the instructions for the bootstrapping vim config in the documentation-for [chromatin].-Now you can execute the command `:Hello`.--For more inspiration, check out some example projects: [proteome], [myo],-[uracil].--[nvim-hs]: https://github.com/neovimhaskell/nvim-hs-[Haskell]: https://www.haskell.org-[proteome]: https://github.com/tek/proteome-[myo]: https://github.com/tek/myo-[uracil]: https://github.com/tek/uracil-[chromatin]: https://github.com/tek/chromatin-[nvim-hs]: https://github.com/neovimhaskell/nvim-hs-[Neovim]: https://github.com/neovim/neovim
@@ -1,49 +1,36 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.34.4.+-- This file has been generated from package.yaml by hpack version 0.34.6. -- -- see: https://github.com/sol/hpack------ hash: d204ea32a519247a24754414a535360674e998d19dd95ea48b7fd7d7ab1044dc name: ribosome-test-version: 0.4.0.0-synopsis: test helpers for ribosome-description: Please see the README on GitHub at <https://github.com/tek/ribosome>+version: 0.9.9.9+synopsis: Test tools for Ribosome+description: See https://hackage.haskell.org/package/ribosome-test/docs/Ribosome-Test.html category: Neovim-homepage: https://github.com/tek/ribosome#readme-bug-reports: https://github.com/tek/ribosome/issues author: Torsten Schmits-maintainer: tek@tryp.io-copyright: 2021 Torsten Schmits+maintainer: hackage@tryp.io+copyright: 2022 Torsten Schmits license: BSD-2-Clause-Patent license-file: LICENSE build-type: Simple-extra-source-files:- readme.md -source-repository head- type: git- location: https://github.com/tek/ribosome- library exposed-modules:- Ribosome.Test.Await+ Ribosome.Handler+ Ribosome.Test+ Ribosome.Test.Data.TestConfig Ribosome.Test.Embed- Ribosome.Test.Exists- Ribosome.Test.File- Ribosome.Test.Functional- Ribosome.Test.Input- Ribosome.Test.Orphans- Ribosome.Test.Run+ Ribosome.Test.EmbedTmux+ Ribosome.Test.Error+ Ribosome.Test.Examples.Example1+ Ribosome.Test.Log Ribosome.Test.Screenshot- Ribosome.Test.Tmux+ Ribosome.Test.SocketTmux+ Ribosome.Test.TmuxCommon Ribosome.Test.Ui- Ribosome.Test.Unit- other-modules:- Paths_ribosome_test- autogen-modules:- Paths_ribosome_test+ Ribosome.Test.Wait hs-source-dirs: lib default-extensions:@@ -63,9 +50,11 @@ DeriveLift DeriveTraversable DerivingStrategies+ DerivingVia DisambiguateRecordFields DoAndIfThenElse DuplicateRecordFields+ EmptyCase EmptyDataDecls ExistentialQuantification FlexibleContexts@@ -80,8 +69,9 @@ MultiParamTypeClasses MultiWayIf NamedFieldPuns- OverloadedStrings+ OverloadedLabels OverloadedLists+ OverloadedStrings PackageImports PartialTypeSignatures PatternGuards@@ -92,6 +82,7 @@ RankNTypes RecordWildCards RecursiveDo+ RoleAnnotations ScopedTypeVariables StandaloneDeriving TemplateHaskell@@ -104,73 +95,41 @@ UndecidableInstances UnicodeSyntax ViewPatterns- ghc-options: -Wall -Wredundant-constraints -Wsimplifiable-class-constraints+ StandaloneKindSignatures+ OverloadedLabels+ ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Widentities -Wunused-packages -fplugin=Polysemy.Plugin build-depends:- aeson- , base ==4.*- , bytestring+ base >=4.12 && <5 , chiasma- , composition- , composition-extra- , conduit- , containers- , cornea- , data-default- , directory- , either- , exceptions- , filepath- , free+ , chiasma-test+ , exon , hedgehog- , hslogger- , lens- , lifted-async- , lifted-base- , messagepack- , monad-control- , mtl- , nvim-hs+ , lens-regex-pcre , path , path-io- , prettyprinter- , prettyprinter-ansi-terminal- , process- , relude >=0.7 && <1.2- , resourcet+ , polysemy+ , polysemy-chronos+ , polysemy-plugin+ , polysemy-test+ , prelate >=0.1 , ribosome- , tasty- , tasty-hedgehog- , template-haskell- , text- , transformers- , typed-process- , unix- , unliftio+ , ribosome-host+ , ribosome-host-test mixins: base hiding (Prelude)- , ribosome hiding (Ribosome.Prelude)- , ribosome (Ribosome.Prelude as Prelude)+ , prelate (Prelate as Prelude)+ , prelate hiding (Prelate) default-language: Haskell2010 -test-suite ribosome-unit+test-suite ribosome-test-unit type: exitcode-stdio-1.0 main-is: Main.hs other-modules:- Ribosome.Test.AutocmdTest- Ribosome.Test.MappingTest- Ribosome.Test.MenuTest- Ribosome.Test.MsgpackTest- Ribosome.Test.NvimMenuTest- Ribosome.Test.PromptTest- Ribosome.Test.RpcTest- Ribosome.Test.ScratchTest- Ribosome.Test.SettingTest+ Ribosome.Test.EmbedTmuxTest+ Ribosome.Test.ReportTest+ Ribosome.Test.SocketTmuxTest Ribosome.Test.SyntaxTest- Ribosome.Test.THTest- Ribosome.Test.WatcherTest- Ribosome.Test.WindowTest- TestError- Paths_ribosome_test+ Skip hs-source-dirs: test default-extensions:@@ -190,9 +149,11 @@ DeriveLift DeriveTraversable DerivingStrategies+ DerivingVia DisambiguateRecordFields DoAndIfThenElse DuplicateRecordFields+ EmptyCase EmptyDataDecls ExistentialQuantification FlexibleContexts@@ -207,8 +168,9 @@ MultiParamTypeClasses MultiWayIf NamedFieldPuns- OverloadedStrings+ OverloadedLabels OverloadedLists+ OverloadedStrings PackageImports PartialTypeSignatures PatternGuards@@ -219,6 +181,7 @@ RankNTypes RecordWildCards RecursiveDo+ RoleAnnotations ScopedTypeVariables StandaloneDeriving TemplateHaskell@@ -231,51 +194,21 @@ UndecidableInstances UnicodeSyntax ViewPatterns- ghc-options: -Wall -Wredundant-constraints -Wsimplifiable-class-constraints -threaded -rtsopts -with-rtsopts=-N+ StandaloneKindSignatures+ OverloadedLabels+ ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Widentities -Wunused-packages -fplugin=Polysemy.Plugin -threaded -rtsopts -with-rtsopts=-N build-depends:- aeson- , base ==4.*- , bytestring- , chiasma- , composition- , composition-extra- , conduit- , containers- , cornea- , data-default- , directory- , either- , exceptions- , filepath- , free- , hedgehog- , hslogger- , lens- , lifted-async- , lifted-base- , messagepack- , monad-control- , mtl- , nvim-hs- , path- , path-io- , prettyprinter- , prettyprinter-ansi-terminal- , process- , relude >=0.7 && <1.2- , resourcet+ base >=4.12 && <5+ , polysemy+ , polysemy-plugin+ , polysemy-test+ , prelate >=0.1 , ribosome+ , ribosome-host , ribosome-test , tasty- , tasty-hedgehog- , template-haskell- , text- , transformers- , typed-process- , unix- , unliftio mixins: base hiding (Prelude)- , ribosome hiding (Ribosome.Prelude)- , ribosome (Ribosome.Prelude as Prelude)+ , prelate (Prelate as Prelude)+ , prelate hiding (Prelate) default-language: Haskell2010
@@ -1,40 +1,20 @@ module Main where -import Ribosome.Test.AutocmdTest (test_autocmd)-import Ribosome.Test.MappingTest (test_mapping)-import Ribosome.Test.MenuTest (test_menu)-import Ribosome.Test.MsgpackTest (test_msgpack)-import Ribosome.Test.NvimMenuTest (test_nvimMenu)-import Ribosome.Test.PromptTest (test_promptSet)-import Ribosome.Test.RpcTest (test_rpc)-import Ribosome.Test.Run (unitTest)-import Ribosome.Test.ScratchTest (test_regularScratch, test_floatScratch)-import Ribosome.Test.SettingTest (test_settingSuccess, test_settingFail)--- import Ribosome.Test.SyntaxTest (test_syntax)-import Ribosome.Test.THTest ()-import Ribosome.Test.WatcherTest (test_varWatcher)-import Ribosome.Test.WindowTest (test_findMainWindowCreate, test_findMainWindowExisting)+import Polysemy.Test (unitTest)+import Ribosome.Test.EmbedTmuxTest (test_embedTmux)+import Ribosome.Test.ReportTest (test_report)+import Ribosome.Test.SocketTmuxTest (test_socketTmux)+import Ribosome.Test.SyntaxTest (test_syntax)+import Skip (skipUnlessX) import Test.Tasty (TestTree, defaultMain, testGroup) tests :: TestTree tests =- testGroup "all" [- unitTest "autocmd handler" test_autocmd,- unitTest "mapping for scratch buffer" test_mapping,- test_menu,- test_msgpack,- test_nvimMenu,- unitTest "set the prompt text" test_promptSet,- unitTest "rpc handlers" test_rpc,- unitTest "scratch buffer" test_regularScratch,- unitTest "floating scratch buffer" test_floatScratch,- unitTest "successfully read a setting" test_settingSuccess,- unitTest "read an unset setting" test_settingFail,- -- TODO flaky test- -- unitTest "syntax" test_syntax,- unitTest "variable watcher" test_varWatcher,- unitTest "find the existing main window" test_findMainWindowExisting,- unitTest "create the main window" test_findMainWindowCreate+ testGroup "ribosome" [+ unitTest "socket tmux" (skipUnlessX test_socketTmux),+ unitTest "embed tmux" (skipUnlessX test_embedTmux),+ unitTest "syntax" (skipUnlessX test_syntax),+ unitTest "report" (skipUnlessX test_report) ] main :: IO ()
@@ -1,49 +0,0 @@-module Ribosome.Test.AutocmdTest where--import Hedgehog ((===))-import Neovim (Plugin(..))-import TestError (RiboTest, handleTestError)--import Ribosome.Control.Monad.Ribo (NvimE)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Msgpack.Encode (MsgpackEncode(toMsgpack))-import Ribosome.Nvim.Api.IO (vimCommand, vimGetVar, vimSetVar)-import Ribosome.Orphans ()-import Ribosome.Plugin (autocmd, riboPlugin, rpcHandler)-import Ribosome.Test.Await (await)-import Ribosome.Test.Embed (integrationSpecDef)-import Ribosome.Test.Orphans ()-import Ribosome.Test.Run (UnitTest)--varName :: Text-varName =- "result"--result :: Int-result =- 5--testAuto ::- NvimE e m =>- m ()-testAuto =- vimSetVar varName (toMsgpack result)--$(return [])--autocmdPlugin :: IO (Plugin (Ribosome ()))-autocmdPlugin = do- env <- newRibosome "test" ()- return $ riboPlugin "test" env funcs [] handleTestError def- where- funcs = [$(rpcHandler (autocmd "BufWritePre") 'testAuto)]--autocmdSpec :: RiboTest ()-autocmdSpec = do- () <- vimCommand "doautocmd BufWritePre"- await (result ===) (vimGetVar varName)--test_autocmd :: UnitTest-test_autocmd = do- plug <- liftIO autocmdPlugin- integrationSpecDef plug autocmdSpec
@@ -0,0 +1,12 @@+module Ribosome.Test.EmbedTmuxTest where++import Polysemy.Test (UnitTest, assert)++import Ribosome.Host.Api.Effect (nvimGetVar, nvimSetVar)+import Ribosome.Test.EmbedTmux (testEmbedTmux)++test_embedTmux :: UnitTest+test_embedTmux =+ testEmbedTmux do+ nvimSetVar "test" True+ assert =<< nvimGetVar "test"
@@ -1,71 +0,0 @@-module Ribosome.Test.MappingTest where--import qualified Data.Map.Strict as Map (empty)-import Hedgehog ((===))-import Neovim (Plugin(..))-import TestError (RiboTest, handleTestError)--import Ribosome.Api.Buffer (currentBufferContent)-import Ribosome.Control.Monad.Ribo (MonadRibo, NvimE)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Data.Mapping (Mapping(Mapping), MappingIdent(MappingIdent))-import Ribosome.Data.ScratchOptions (ScratchOptions(ScratchOptions))-import Ribosome.Msgpack.Encode (MsgpackEncode(toMsgpack))-import Ribosome.Msgpack.Error (DecodeError)-import Ribosome.Nvim.Api.IO (nvimFeedkeys, vimCallFunction, vimGetVar, vimSetVar)-import Ribosome.Plugin (riboPlugin, rpcHandlerDef)-import Ribosome.Plugin.Mapping (MappingHandler, mappingHandler)-import Ribosome.Scratch (showInScratch)-import Ribosome.Test.Await (await)-import Ribosome.Test.Embed (integrationSpecDef)-import Ribosome.Test.Orphans ()-import Ribosome.Test.Run (UnitTest)--target :: [Text]-target = ["line 1", "line 2"]--go :: NvimE e m => m ()-go =- vimSetVar "number" (toMsgpack (13 :: Int))--mapping :: Mapping-mapping =- Mapping (MappingIdent "go") "a" "n" False True--mapHandler :: NvimE e m => MappingHandler m-mapHandler =- mappingHandler "go" go--setupMappingScratch ::- NvimE e m =>- MonadRibo m =>- MonadBaseControl IO m =>- MonadDeepError e DecodeError m =>- m ()-setupMappingScratch = do- _ <- showInScratch target options- return ()- where- options =- ScratchOptions False True False True True True False Nothing Nothing Nothing [] [mapping] "buffi"--$(return [])--mappingPlugin :: IO (Plugin (Ribosome ()))-mappingPlugin = do- env <- newRibosome "mapping" ()- return $ riboPlugin "mapping" env funcs [mapHandler] handleTestError Map.empty- where- funcs = [$(rpcHandlerDef 'setupMappingScratch)]--mappingSpec :: RiboTest ()-mappingSpec = do- () <- vimCallFunction "SetupMappingScratch" []- await (target ===) currentBufferContent- nvimFeedkeys "a" "x" False- await ((13 :: Int) ===) (vimGetVar "number")--test_mapping :: UnitTest-test_mapping = do- plug <- liftIO mappingPlugin- integrationSpecDef plug mappingSpec
@@ -1,324 +0,0 @@-module Ribosome.Test.MenuTest where--import Conduit (ConduitT, yield, yieldMany)-import Control.Concurrent.MVar.Lifted (modifyMVar_)-import Control.Lens (view)-import Control.Monad.Trans.Resource (ResourceT, runResourceT)-import qualified Data.Map.Strict as Map (fromList)-import Hedgehog ((===))-import Test.Tasty (TestTree, testGroup)--import Ribosome.Control.StrictRibosome (StrictRibosome)-import Ribosome.Menu.Action (menuContinue, menuExecute, menuQuit)-import Ribosome.Menu.Data.FilteredMenuItem (FilteredMenuItem(FilteredMenuItem))-import qualified Ribosome.Menu.Data.FilteredMenuItem as FilteredMenuItem (item)-import Ribosome.Menu.Data.Menu (Menu(Menu), MenuFilter(MenuFilter))-import qualified Ribosome.Menu.Data.Menu as Menu (items)-import Ribosome.Menu.Data.MenuAction (MenuAction)-import Ribosome.Menu.Data.MenuConfig (MenuConfig(MenuConfig))-import Ribosome.Menu.Data.MenuConsumer (MenuConsumer(MenuConsumer))-import Ribosome.Menu.Data.MenuConsumerAction (MenuConsumerAction)-import qualified Ribosome.Menu.Data.MenuEvent as MenuEvent (MenuEvent(..))-import Ribosome.Menu.Data.MenuItem (MenuItem(MenuItem), simpleMenuItem)-import qualified Ribosome.Menu.Data.MenuItem as MenuItem (MenuItem(_text), text)-import Ribosome.Menu.Data.MenuRenderEvent (MenuRenderEvent)-import qualified Ribosome.Menu.Data.MenuRenderEvent as MenuRenderEvent (MenuRenderEvent(..))-import Ribosome.Menu.Data.MenuUpdate (MenuUpdate(MenuUpdate))-import Ribosome.Menu.Prompt.Data.Prompt (Prompt(Prompt))-import Ribosome.Menu.Prompt.Data.PromptConfig (PromptConfig(PromptConfig), PromptFlag(StartInsert))-import Ribosome.Menu.Prompt.Data.PromptEvent (PromptEvent)-import qualified Ribosome.Menu.Prompt.Data.PromptEvent as PromptEvent (PromptEvent(..))-import qualified Ribosome.Menu.Prompt.Data.PromptState as PromptState (PromptState(..))-import Ribosome.Menu.Prompt.Run (basicTransition, noPromptRenderer)-import Ribosome.Menu.Run (runMenu)-import Ribosome.Menu.Simple (- basicMenu,- defaultMenu,- deleteByFilteredIndex,- fuzzyMenuItemMatcher,- markedMenuItems,- markedMenuItemsOnly,- selectedMenuItem,- simpleMenu,- )-import Ribosome.System.Time (sleep)-import Ribosome.Test.Run (UnitTest, unitTest)--promptInput ::- MonadIO m =>- [Text] ->- ConduitT () PromptEvent m ()-promptInput chars = do- lift $ sleep 0.1- yieldMany (PromptEvent.Character <$> chars)--menuItems ::- Monad m =>- [Text] ->- ConduitT () [MenuItem Text] m ()-menuItems =- yield . fmap (simpleMenuItem "name")--storePrompt ::- MonadBaseControl IO m =>- MVar [Prompt] ->- MenuUpdate m a Text ->- m (MenuConsumerAction m a, Menu Text)-storePrompt prompts (MenuUpdate event menu) =- check event- where- check (MenuEvent.PromptChange prompt) =- store prompt- check (MenuEvent.Mapping _ prompt) =- store prompt- check (MenuEvent.Quit _) =- menuQuit menu- check _ =- menuContinue menu- store prompt =- modifyMVar_ prompts (return . (prompt :)) *> menuContinue menu--render ::- MonadIO m =>- MonadBaseControl IO m =>- MVar [[FilteredMenuItem Text]] ->- MenuRenderEvent m a Text ->- m ()-render varItems (MenuRenderEvent.Render _ (Menu _ items _ _ _ _)) = do- modifyMVar_ varItems (return . (items :))- sleep 0.01-render _ (MenuRenderEvent.Quit _) =- return ()--type TestM = StateT (StrictRibosome ()) (ResourceT IO)--menuTest ::- (MenuUpdate TestM a Text -> TestM (MenuAction TestM a, Menu Text)) ->- [Text] ->- [Text] ->- IO [[FilteredMenuItem Text]]-menuTest handler items chars = do- itemsVar <- newMVar []- void $ runResourceT $ runMenu (conf itemsVar) `execStateT` def- readMVar itemsVar- where- conf itemsVar =- MenuConfig (menuItems items) (MenuConsumer handler) (render itemsVar) promptConfig def- promptConfig =- PromptConfig (promptInput chars) basicTransition noPromptRenderer [StartInsert]--promptTest :: [Text] -> [Text] -> IO ([[FilteredMenuItem Text]], [Prompt])-promptTest items chars = do- prompts <- newMVar []- itemsResult <- menuTest (basicMenu fuzzyMenuItemMatcher (storePrompt prompts)) items chars- (itemsResult,) <$> readMVar prompts--promptsTarget1 :: [Prompt]-promptsTarget1 =- uncurry one' <$> [- (1, PromptState.Insert),- (0, PromptState.Normal),- (0, PromptState.Normal),- (1, PromptState.Insert)- ]- where- one' c s = Prompt c s "i"--items1 :: [Text]-items1 =- [- "i1",- "j1",- "i2",- "i3",- "j2",- "i4"- ]--chars1 :: [Text]-chars1 =- [- "i",- "esc",- "k",- "a",- "2"- ]--itemsTarget1 :: [[MenuItem Text]]-itemsTarget1 =- [- item <$> ["i2"],- item <$> ["i1", "i2", "i3", "i4"]- ]- where- item =- simpleMenuItem "name"--test_pureMenuModeChange :: UnitTest-test_pureMenuModeChange = do- (items, prompts) <- liftIO (promptTest items1 chars1)- itemsTarget1 === (view FilteredMenuItem.item <$$> take 2 items)- promptsTarget1 === (take 4 $ reverse prompts)--chars2 :: [Text]-chars2 =- ["l", "o", "n", "g", "-", "i", "t", "e", "m"]--items2 :: [Text]-items2 =- [- "long",- "short",- "long-item",- "longitem"- ]--itemsTarget :: [MenuItem Text]-itemsTarget =- [simpleMenuItem "name" "long-item"]--test_pureMenuFilter :: UnitTest-test_pureMenuFilter = do- items <- liftIO (fst <$> promptTest items2 chars2)- [itemsTarget] === (view FilteredMenuItem.item <$$> take 1 items)--chars3 :: [Text]-chars3 =- ["i", "esc", "cr"]--items3 :: [Text]-items3 =- [- "item1",- "item2"- ]--exec ::- MonadIO m =>- MVar [Text] ->- Menu Text ->- Prompt ->- m (MenuConsumerAction m a, Menu Text)-exec var m@(Menu _ items _ _ _ _) _ =- swapMVar var (view (FilteredMenuItem.item . MenuItem.text) <$> items) *> menuQuit m--test_pureMenuExecute :: UnitTest-test_pureMenuExecute = do- var <- newMVar []- _ <- liftIO (menuTest (simpleMenu (Map.fromList [("cr", exec var)])) items3 chars3)- (items3 ===) =<< readMVar var--charsMulti :: [Text]-charsMulti =- ["esc", "k", "k", "space", "space", "space", "space", "j", "space", "cr"]--itemsMulti :: [Text]-itemsMulti =- [- "item1",- "item2",- "item3",- "item4",- "item5",- "item6"- ]--execMulti ::- MonadIO m =>- MVar (Maybe (NonEmpty Text)) ->- Menu Text ->- Prompt ->- m (MenuConsumerAction m a, Menu Text)-execMulti var m _ =- swapMVar var (MenuItem._text <$$> markedMenuItems m) *> menuQuit m--test_menuMultiMark :: UnitTest-test_menuMultiMark = do- var <- newMVar Nothing- _ <- liftIO (menuTest (defaultMenu (Map.fromList [("cr", execMulti var)])) itemsMulti charsMulti)- (Just ("item3" :| ["item4", "item5"]) ===) =<< readMVar var--charsToggle :: [Text]-charsToggle =- ["esc", "k", "space", "*", "i", "a", "b", "cr"]--itemsToggle :: [Text]-itemsToggle =- [- "a",- "ab",- "abc"- ]--execToggle ::- MonadIO m =>- MVar (Maybe (NonEmpty Text)) ->- Menu Text ->- Prompt ->- m (MenuConsumerAction m a, Menu Text)-execToggle var m _ =- swapMVar var (MenuItem._text <$$> markedMenuItemsOnly m) *> menuQuit m--test_menuToggle :: UnitTest-test_menuToggle = do- var <- newMVar Nothing- _ <- liftIO (menuTest (defaultMenu (Map.fromList [("cr", execToggle var)])) itemsToggle charsToggle)- (Nothing ===) =<< readMVar var--charsExecuteThunk :: [Text]-charsExecuteThunk =- ["esc", "cr", "k", "cr", "esc"]--itemsExecuteThunk :: [Text]-itemsExecuteThunk =- [- "a",- "b"- ]--execExecuteThunk ::- MonadIO m =>- MonadBaseControl IO m =>- MVar [Text] ->- Menu Text ->- Prompt ->- m (MenuConsumerAction m a, Menu Text)-execExecuteThunk var m _ =- menuExecute (modifyMVar_ var prepend) m- where- prepend a =- pure $ a ++ maybeToList (MenuItem._text <$> selectedMenuItem m)--test_menuExecuteThunk :: UnitTest-test_menuExecuteThunk = do- var <- newMVar []- _ <- liftIO (menuTest (defaultMenu (Map.fromList [("cr", execExecuteThunk var)])) itemsExecuteThunk charsExecuteThunk)- (["a", "b"] ===) =<< readMVar var--test_menuDeleteByFilteredIndex :: UnitTest-test_menuDeleteByFilteredIndex =- (target ===) . fmap (view MenuItem.text) . view Menu.items . deleteByFilteredIndex [1, 2] $ menu- where- target =- ["1", "2", "3", "5", "7", "8"]- menu =- Menu items filtered 0 [] (MenuFilter "") Nothing- items =- simpleMenuItem () <$> ["1", "2", "3", "4", "5", "6", "7", "8"]- filtered =- uncurry FilteredMenuItem . second menuItem <$> [(1, "2"), (3, "4"), (5, "6"), (7, "8")]- menuItem t =- MenuItem () t t--test_menu :: TestTree-test_menu =- testGroup "menu" [- unitTest "change mode" test_pureMenuModeChange,- unitTest "filter items" test_pureMenuFilter,- unitTest "execute an action" test_pureMenuExecute,- unitTest "mark multiple items" test_menuMultiMark,- unitTest "toggle marked items" test_menuToggle,- unitTest "execute a thunk action" test_menuExecuteThunk,- unitTest "delete by filtered index" test_menuDeleteByFilteredIndex- ]
@@ -1,170 +0,0 @@-module Ribosome.Test.MsgpackTest where--import qualified Data.Map.Strict as Map (fromList)-import Data.MessagePack (Object(..))-import Data.Text.Prettyprint.Doc (Doc, defaultLayoutOptions, layoutPretty)-import Data.Text.Prettyprint.Doc.Render.Terminal (AnsiStyle, renderStrict)-import Hedgehog ((===))-import Path (Abs, Dir, File, Path, Rel, absfile, relfile)-import Test.Tasty (TestTree, testGroup)--import Ribosome.Msgpack.Decode (MsgpackDecode(..), fromMsgpack)-import Ribosome.Msgpack.Encode (MsgpackEncode(..))-import qualified Ribosome.Msgpack.Util as Util (string)-import Ribosome.Test.Run (UnitTest, unitTest)--newtype NT =- NT Text- deriving (Eq, Show, Generic)- deriving newtype (MsgpackEncode, MsgpackDecode)--data Blob =- Blob {- key4 :: [[Int]],- key5 :: Map Int Text,- key6 :: NT,- key7 :: (Int, String)- }- deriving (Eq, Show, Generic, MsgpackEncode, MsgpackDecode)--data Prod =- Prod Text Int- deriving (Eq, Show, Generic, MsgpackEncode, MsgpackDecode)--data Dat =- Dat {- key1 :: Blob,- key2 :: Bool,- key3 :: Maybe Prod- }- deriving (Eq, Show, Generic, MsgpackEncode, MsgpackDecode)--dat :: Dat-dat = Dat (Blob [[1, 2], [3]] (Map.fromList [(1, "1"), (2, "2")]) (NT "nt") (91, "pair")) False (Just $ Prod "dat" 27)--os :: String -> Object-os = Util.string--i :: Int64 -> Object-i = ObjectInt--encodedBlob :: Object-encodedBlob =- ObjectMap $ Map.fromList [- (os "key4", ObjectArray [ObjectArray [i 1, i 2], ObjectArray [i 3]]),- (os "key5", ObjectMap $ Map.fromList [(i 1, os "1"), (i 2, os "2")]),- (os "key6", os "nt"),- (os "key7", ObjectArray [ObjectInt 91, ObjectString "pair"])- ]--encoded :: Object-encoded =- ObjectMap $ Map.fromList [- (os "key1", encodedBlob),- (os "key2", ObjectBool False),- (ObjectString "key3", ObjectArray [os "dat", i 27])- ]--test_encode :: UnitTest-test_encode =- encoded === toMsgpack dat--doc2Text :: Either (Doc AnsiStyle) a -> Either Text a-doc2Text =- mapLeft (renderStrict . layoutPretty defaultLayoutOptions)--decodeSubject :: Object-decodeSubject =- ObjectMap $ Map.fromList [- (os "key1", encodedBlob),- (os "key2", ObjectBool False),- (ObjectBinary "key3", ObjectArray [os "dat", i 27])- ]--test_decodeBasic :: UnitTest-test_decodeBasic =- Right dat === doc2Text (fromMsgpack decodeSubject)--data Nope =- Nope {- present :: Int,- absent :: Maybe Int- }- deriving (Eq, Show, Generic, MsgpackDecode)--encodedNope :: Object-encodedNope =- ObjectMap $ Map.fromList [(os "present", i 5)]--test_maybeMissing :: UnitTest-test_maybeMissing =- Right (Nope 5 Nothing) === doc2Text (fromMsgpack encodedNope)--test_decodeEither :: UnitTest-test_decodeEither =- Right (Left "text" :: Either Text Int) === doc2Text (fromMsgpack (ObjectBinary "text"))--data ST =- STL {- stName :: Text,- stCount :: Int- }- |- STR {- stName :: Text,- stDesc :: Text- }- deriving (Eq, Show, Generic, MsgpackEncode, MsgpackDecode)--sumName :: Text-sumName =- "sumName"--sumCount :: Int-sumCount =- 1313--encodedSum :: Object-encodedSum =- ObjectMap $ Map.fromList [- (toMsgpack @Text "stName", toMsgpack sumName),- (toMsgpack @Text "stCount", toMsgpack sumCount)- ]--test_encodeSum :: UnitTest-test_encodeSum =- encodedSum === toMsgpack (STL sumName sumCount)--test_decodeSum :: UnitTest-test_decodeSum =- Right (STL sumName sumCount) === doc2Text (fromMsgpack encodedSum)--encodedPath :: Object-encodedPath =- ObjectString "/path/to/file"--test_decodePath :: UnitTest-test_decodePath =- Right [absfile|/path/to/file|] === doc2Text (fromMsgpack @(Path Abs File) encodedPath)--test_failDecodePath :: UnitTest-test_failDecodePath =- Left "InvalidRelDir \"/path/to/file\"" === doc2Text (fromMsgpack @(Path Rel Dir) encodedPath)--test_encodePath :: UnitTest-test_encodePath =- ObjectString "path/to/file" === toMsgpack [relfile|path/to/file|]--test_msgpack :: TestTree-test_msgpack =- testGroup "msgpack" [- unitTest "encode" test_encode,- unitTest "decode" test_decodeBasic,- unitTest "decode Nothing" test_maybeMissing,- unitTest "decode Right" test_decodeEither,- unitTest "encode sum type" test_encodeSum,- unitTest "decode sum type" test_decodeSum,- unitTest "decode Path" test_decodePath,- unitTest "decode invalid Path" test_failDecodePath,- unitTest "encode Path" test_encodePath- ]
@@ -1,164 +0,0 @@-module Ribosome.Test.NvimMenuTest where--import Conduit (ConduitT, yield, yieldMany)-import Control.Concurrent.Lifted (fork, killThread)-import Control.Exception.Lifted (bracket)-import Control.Lens (element, (^?))-import qualified Data.Map.Strict as Map (empty, fromList)-import Hedgehog ((===))-import Test.Tasty (TestTree, testGroup)-import TestError (RiboTest, TestError)--import Ribosome.Api.Input (syntheticInput)-import Ribosome.Control.Monad.Ribo (Ribo)-import Ribosome.Menu.Action (menuReturn)-import qualified Ribosome.Menu.Data.FilteredMenuItem as FilteredMenuItem (item)-import Ribosome.Menu.Data.Menu (Menu(Menu))-import Ribosome.Menu.Data.MenuConsumerAction (MenuConsumerAction)-import Ribosome.Menu.Data.MenuItem (MenuItem, simpleMenuItem)-import qualified Ribosome.Menu.Data.MenuItem as MenuItem (text)-import Ribosome.Menu.Data.MenuResult (MenuResult)-import qualified Ribosome.Menu.Data.MenuResult as MenuResult (MenuResult(..))-import Ribosome.Menu.Prompt.Data.Prompt (Prompt(Prompt))-import Ribosome.Menu.Prompt.Data.PromptConfig (PromptConfig(PromptConfig), PromptFlag(StartInsert))-import Ribosome.Menu.Prompt.Data.PromptEvent (PromptEvent)-import qualified Ribosome.Menu.Prompt.Data.PromptEvent as PromptEvent (PromptEvent(..))-import Ribosome.Menu.Prompt.Nvim (getCharC, nvimPromptRenderer)-import Ribosome.Menu.Prompt.Run (basicTransition)-import Ribosome.Menu.Run (nvimMenu)-import Ribosome.Menu.Simple (Mappings, defaultMenu)-import Ribosome.Nvim.Api.IO (vimGetWindows)-import Ribosome.System.Time (sleep)-import Ribosome.Test.Run (UnitTest, unitTest)-import Ribosome.Test.Tmux (tmuxSpecDef)--promptInput ::- MonadIO m =>- [Text] ->- ConduitT () PromptEvent m ()-promptInput chars' = do- lift $ sleep 0.1- yieldMany (PromptEvent.Character <$> chars')--menuItems ::- Monad m =>- [Text] ->- ConduitT () [MenuItem Text] m ()-menuItems =- yield . fmap (simpleMenuItem "name")--chars :: [Text]-chars =- ["i", "t", "e", "esc", "k", "k", "k", "cr"]--items :: [Text]-items =- ("item" <>) . show <$> [(1 :: Int)..8]--exec ::- MonadIO m =>- Menu Text ->- Prompt ->- m (MenuConsumerAction m (Maybe Text), Menu Text)-exec m@(Menu _ items' selected _ _ _) _ =- menuReturn item m- where- item =- items' ^? element selected . FilteredMenuItem.item . MenuItem.text--promptConfig ::- ConduitT () PromptEvent (Ribo () TestError) () ->- PromptConfig (Ribo () TestError)-promptConfig source =- PromptConfig source basicTransition nvimPromptRenderer [StartInsert]--runNvimMenu ::- Mappings (Ribo () TestError) a Text ->- ConduitT () PromptEvent (Ribo () TestError) () ->- Ribo () TestError (MenuResult a)-runNvimMenu maps source =- nvimMenu def (menuItems items) (defaultMenu maps) (promptConfig source) Nothing--mappings :: Mappings (Ribo () TestError) (Maybe Text) Text-mappings =- Map.fromList [("cr", exec)]--nvimMenuSpec ::- ConduitT () PromptEvent (Ribo () TestError) () ->- RiboTest ()-nvimMenuSpec =- (MenuResult.Return (Just "item4") ===) <=< lift . runNvimMenu mappings--nvimMenuPureSpec :: RiboTest ()-nvimMenuPureSpec =- nvimMenuSpec (promptInput chars)--test_nvimMenuPure :: UnitTest-test_nvimMenuPure =- tmuxSpecDef nvimMenuPureSpec--nativeChars :: [Text]-nativeChars =- ["i", "t", "e", "<esc>", "k", "<c-k>", "k", "<cr>"]--nvimMenuNativeSpec :: RiboTest ()-nvimMenuNativeSpec =- bracket (fork input) killThread (const $ nvimMenuSpec (getCharC 0.1))- where- input =- syntheticInput (Just 0.2) nativeChars--test_nvimMenuNative :: UnitTest-test_nvimMenuNative =- tmuxSpecDef nvimMenuNativeSpec--nvimMenuInterruptSpec :: RiboTest ()-nvimMenuInterruptSpec = do- (MenuResult.Aborted ===) =<< spec- (1 ===) =<< length <$> vimGetWindows- where- spec :: RiboTest (MenuResult ())- spec =- lift (bracket (fork input) killThread (const run))- run =- nvimMenu def (menuItems items) (defaultMenu Map.empty) (promptConfig (getCharC 0.1)) Nothing- input =- syntheticInput (Just 0.2) ["<c-c>", "<cr>"]--test_nvimMenuInterrupt :: UnitTest-test_nvimMenuInterrupt =- tmuxSpecDef nvimMenuInterruptSpec--returnPrompt ::- MonadIO m =>- Menu Text ->- Prompt ->- m (MenuConsumerAction m Text, Menu Text)-returnPrompt m (Prompt _ _ text) =- menuReturn text m--navChars :: [Text]-navChars =- ["i", "t", "e", "m", "1", "<bs>", "<esc>", "h", "h", "h", "h", "h", "x", "a", "o", "<cr>"]--nvimMenuNavSpec :: RiboTest ()-nvimMenuNavSpec =- (MenuResult.Return "toem" ===) =<< lift run- where- run =- bracket (fork input) killThread (const $ runNvimMenu (Map.fromList [("cr", returnPrompt)]) (getCharC 0.1))- input =- syntheticInput (Just 0.2) navChars--test_nvimMenuNav :: UnitTest-test_nvimMenuNav =- tmuxSpecDef nvimMenuNavSpec--test_nvimMenu :: TestTree-test_nvimMenu =- testGroup "nvim menu" [- unitTest "pure" test_nvimMenuPure,- unitTest "native" test_nvimMenuNative,- unitTest "interrupt" test_nvimMenuInterrupt,- unitTest "navigation" test_nvimMenuNav- ]
@@ -1,39 +0,0 @@-module Ribosome.Test.PromptTest where--import Conduit (awaitForever, evalStateC, runConduit, yield, (.|))-import qualified Data.Conduit.Combinators as Conduit (last)-import Hedgehog (TestT, (===))-import TestError (TestError)--import Ribosome.Control.Monad.Ribo (Ribo)-import Ribosome.Menu.Prompt.Data.Prompt (Prompt(Prompt))-import Ribosome.Menu.Prompt.Data.PromptConfig (PromptConfig(PromptConfig), PromptFlag(StartInsert))-import Ribosome.Menu.Prompt.Data.PromptConsumed (PromptConsumed(Yes))-import Ribosome.Menu.Prompt.Data.PromptConsumerUpdate (PromptConsumerUpdate(PromptConsumerUpdate))-import qualified Ribosome.Menu.Prompt.Data.PromptEvent as PromptEvent (PromptEvent(..))-import qualified Ribosome.Menu.Prompt.Data.PromptState as PromptState (PromptState(..))-import Ribosome.Menu.Prompt.Run (basicTransition, noPromptRenderer, processPromptEvent)-import Ribosome.Test.Run (UnitTest)-import Ribosome.Test.Unit (unitSpecDef')--promptSetSpec :: TestT (Ribo () TestError) ()-promptSetSpec = do- update <- runConduit $ yield event .| exec .| Conduit.last- Just target === update- where- exec =- evalStateC initialPrompt (awaitForever (processPromptEvent config))- initialPrompt =- Prompt 1 PromptState.Normal "abc"- config =- PromptConfig (return ()) basicTransition noPromptRenderer [StartInsert]- event =- PromptEvent.Set (Prompt 10 PromptState.Normal text)- text =- "12345678"- target =- PromptConsumerUpdate event (Prompt 8 PromptState.Normal text) Yes--test_promptSet :: UnitTest-test_promptSet =- unitSpecDef' promptSetSpec
@@ -0,0 +1,37 @@+module Ribosome.Test.ReportTest where++import qualified Log+import Log (Severity (Error, Info))+import Polysemy.Test (UnitTest)++import Ribosome.Host.Api.Effect (nvimCommand, nvimInput)+import Ribosome.Host.Data.Execution (Execution (Sync))+import Ribosome.Host.Data.Report (Report (Report))+import Ribosome.Host.Data.RpcHandler (Handler)+import Ribosome.Host.Handler (rpcFunction)+import qualified Ribosome.Report as Report+import Ribosome.Test.Screenshot (awaitScreenshot)+import Ribosome.Test.SocketTmux (testHandlersSocketTmux)++stopInfo :: Handler r ()+stopInfo =+ stop (Report "report an info Stop by echoing" [] Info)++stopError :: Handler r ()+stopError =+ stop (Report "report an error Stop by responding" [] Error)++test_report :: UnitTest+test_report =+ testHandlersSocketTmux [rpcFunction "StopInfo" Sync stopInfo, rpcFunction "StopError" Sync stopError] do+ nvimCommand "mode"+ Log.debug "don't report Log.debug message"+ awaitScreenshot False "report-1" 0+ Log.info "report Log.info message"+ awaitScreenshot False "report-2" 0+ Report.warn "report Report.warn message" []+ awaitScreenshot False "report-3" 0+ nvimInput ":call StopInfo()<cr>"+ awaitScreenshot False "report-4" 0+ nvimInput ":call StopError()<cr>"+ awaitScreenshot False "report-5" 0
@@ -1,154 +0,0 @@-module Ribosome.Test.RpcTest where--import Data.DeepPrisms (DeepPrisms)-import qualified Data.List as List (lines)-import qualified Data.Map.Strict as Map (empty)-import Hedgehog ((===))-import Language.Haskell.TH hiding (reportError)-import Neovim (CommandArguments, Plugin(..))-import System.Log.Logger (Priority(DEBUG), setLevel, updateGlobalLogger)-import TestError (RiboTest, TestError)--import Ribosome.Api.Variable (setVar)-import Ribosome.Control.Monad.Ribo (MonadRibo, NvimE, Ribo)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Error.Report (reportError)-import Ribosome.Nvim.Api.IO (vimCommand, vimGetVar)-import Ribosome.Nvim.Api.RpcCall (RpcError)-import Ribosome.Plugin (RpcDef, cmd, riboPlugin, rpcHandler, sync)-import Ribosome.System.Time (sleep)-import Ribosome.Test.Embed (integrationSpecDef)-import Ribosome.Test.Run (UnitTest)--handleError ::- DeepPrisms e RpcError =>- TestError ->- Ribo s e ()-handleError =- reportError "test"--target :: Int-target =- 13--resultVar :: Text-resultVar =- "test_result"--handler ::- NvimE e m =>- m Int-handler = do- setVar resultVar target- return target--handlerCmdCmdArgs ::- NvimE e m =>- CommandArguments ->- Text ->- Text ->- Text ->- m Int-handlerCmdCmdArgs _ _ _ _ =- handler--handlerCmdNoCmdArgs ::- NvimE e m =>- Text ->- Text ->- Text ->- m Int-handlerCmdNoCmdArgs _ _ _ =- handler--handlerCmdNoArgs ::- NvimE e m =>- m Int-handlerCmdNoArgs =- handler--handlerCmdOneArg ::- NvimE e m =>- Text ->- m Int-handlerCmdOneArg _ =- handler--handlerCmdMaybeArg ::- NvimE e m =>- Maybe Text ->- m Int-handlerCmdMaybeArg _ =- handler--handlerCmdListArg ::- NvimE e m =>- [Text] ->- m Int-handlerCmdListArg _ =- handler--$(return [])--handlers ::- MonadRibo m =>- NvimE e m =>- [[RpcDef m]]-handlers =- [- $(rpcHandler (sync . cmd []) 'handlerCmdCmdArgs),- $(rpcHandler (sync . cmd []) 'handlerCmdNoCmdArgs),- $(rpcHandler (sync . cmd []) 'handlerCmdNoArgs),- $(rpcHandler (sync . cmd []) 'handlerCmdOneArg),- $(rpcHandler (sync . cmd []) 'handlerCmdMaybeArg),- $(rpcHandler (sync . cmd []) 'handlerCmdListArg)- ]--plugin :: IO (Plugin (Ribosome ()))-plugin = do- ribo <- newRibosome "test" def- pure $ riboPlugin "test" ribo handlers [] handleError Map.empty--successCommand ::- Text ->- RiboTest ()-successCommand cmd' = do- setVar resultVar (0 :: Int)- vimCommand cmd'- (target ===) =<< vimGetVar resultVar--failureCommand ::- Text ->- RiboTest ()-failureCommand cmd' = do- lift (setVar resultVar (0 :: Int))- catchAt recoverRpc (vimCommand cmd')- ((0 :: Int) ===) =<< vimGetVar resultVar- where- recoverRpc (_ :: RpcError) =- return ()--rpcSpec :: RiboTest ()-rpcSpec = do- successCommand "HandlerCmdCmdArgs a b c"- successCommand "HandlerCmdNoCmdArgs a b c"- successCommand "HandlerCmdNoArgs"- successCommand "HandlerCmdOneArg a"- successCommand "HandlerCmdMaybeArg a"- successCommand "HandlerCmdMaybeArg"- successCommand "HandlerCmdListArg"- successCommand "HandlerCmdListArg a b c"- failureCommand "HandlerCmdCmdArgs a b"- failureCommand "HandlerCmdNoCmdArgs a b"- failureCommand "HandlerCmdNoArgs a"- sleep 1--test_rpc :: UnitTest-test_rpc = do- liftIO $ updateGlobalLogger "test" (setLevel DEBUG)- plug <- liftIO plugin- when debug $ traverse_ putStrLn . List.lines $ $(stringE . pprint =<< rpcHandler (sync . cmd []) 'handlerCmdListArg)- integrationSpecDef plug rpcSpec- where- debug =- False
@@ -1,95 +0,0 @@-module Ribosome.Test.ScratchTest where--import qualified Data.Map.Strict as Map (toList)-import Hedgehog ((===))-import Neovim (Plugin(..))-import TestError (RiboTest, handleTestError)--import Ribosome.Api.Buffer (currentBufferContent)-import Ribosome.Control.Monad.Ribo (MonadRibo, NvimE, pluginInternalL)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import qualified Ribosome.Control.Ribosome as Ribosome (scratch)-import Ribosome.Data.FloatOptions (FloatOptions(FloatOptions), FloatRelative(Cursor))-import Ribosome.Data.ScratchOptions (ScratchOptions(ScratchOptions))-import Ribosome.Msgpack.Error (DecodeError)-import Ribosome.Nvim.Api.IO (vimCallFunction, vimCommand)-import Ribosome.Plugin (riboPlugin, rpcHandler, rpcHandlerDef, sync)-import Ribosome.Scratch (showInScratch)-import Ribosome.Test.Await (await)-import Ribosome.Test.Embed (integrationSpecDef)-import Ribosome.Test.Run (UnitTest)--target :: [Text]-target = ["line 1", "line 2"]--name :: Text-name =- "buffi"--makeScratch ::- NvimE e m =>- MonadRibo m =>- MonadBaseControl IO m =>- MonadDeepError e DecodeError m =>- m ()-makeScratch =- void $ showInScratch target (ScratchOptions False True False True True True False Nothing Nothing Nothing [] [] name)--floatOptions :: FloatOptions-floatOptions =- FloatOptions Cursor 30 2 1 1 True def Nothing--makeFloatScratch ::- NvimE e m =>- MonadRibo m =>- MonadBaseControl IO m =>- MonadDeepError e DecodeError m =>- m ()-makeFloatScratch =- void $ showInScratch target options- where- options =- ScratchOptions False True False True True True False (Just floatOptions) Nothing (Just 0) [] [] name--scratchCount ::- MonadRibo m =>- m Int-scratchCount =- length . Map.toList <$> pluginInternalL Ribosome.scratch--$(return [])--scratchPlugin :: IO (Plugin (Ribosome ()))-scratchPlugin = do- env <- newRibosome "test" ()- return $ riboPlugin "test" env funcs [] handleTestError def- where- funcs = [$(rpcHandlerDef 'makeScratch), $(rpcHandlerDef 'makeFloatScratch), $(rpcHandler sync 'scratchCount)]--scratchSpec :: Text -> RiboTest ()-scratchSpec fun = do- () <- vimCallFunction fun []- await ((1 :: Int) ===) scratches- await (target ===) currentBufferContent- vimCommand "bdelete"- await (0 ===) scratches- where- scratches = vimCallFunction "ScratchCount" []--regularScratchSpec :: RiboTest ()-regularScratchSpec =- scratchSpec "MakeScratch"--test_regularScratch :: UnitTest-test_regularScratch = do- plug <- liftIO scratchPlugin- integrationSpecDef plug regularScratchSpec--floatScratchSpec :: RiboTest ()-floatScratchSpec =- scratchSpec "MakeFloatScratch"--test_floatScratch :: UnitTest-test_floatScratch = do- plug <- liftIO scratchPlugin- integrationSpecDef plug floatScratchSpec
@@ -1,51 +0,0 @@-module Ribosome.Test.SettingTest where--import Data.Either.Combinators (swapEither)-import Hedgehog (TestT, evalEither, (===))--import Ribosome.Config.Setting (setting, updateSetting)-import Ribosome.Control.Monad.Ribo (Ribo)-import Ribosome.Data.Setting (Setting(Setting))-import Ribosome.Data.SettingError (SettingError)-import Ribosome.Error.Report.Class (ReportError(..))-import Ribosome.Nvim.Api.RpcCall (RpcError)-import Ribosome.Test.Run (UnitTest)-import Ribosome.Test.Unit (unitSpec)--data SettingSpecError =- Sett SettingError- |- Rpc RpcError- deriving Show--instance ReportError SettingSpecError where- errorReport = undefined--deepPrisms ''SettingSpecError--sett :: Setting Int-sett = Setting "name" True Nothing--settingSuccessSpec :: TestT (Ribo s SettingSpecError) ()-settingSuccessSpec = do- updateSetting sett 5- r <- lift (setting sett)- 5 === r--test_settingSuccess :: UnitTest-test_settingSuccess =- unitSpec def () settingSuccessSpec--settingFailSpec :: TestT (Ribo s SettingSpecError) ()-settingFailSpec = do- ea <- lift (catchAt catch $ Right <$> result)- void $ evalEither (swapEither ea)- where- result :: Ribo s SettingSpecError Int- result = setting sett- catch :: SettingError -> Ribo s SettingSpecError (Either SettingError Int)- catch = return . Left--test_settingFail :: UnitTest-test_settingFail =- unitSpec def () settingFailSpec
@@ -0,0 +1,12 @@+module Ribosome.Test.SocketTmuxTest where++import Polysemy.Test (UnitTest, assert)++import Ribosome.Host.Api.Effect (nvimGetVar, nvimSetVar)+import Ribosome.Test.SocketTmux (testSocketTmux)++test_socketTmux :: UnitTest+test_socketTmux =+ testSocketTmux do+ nvimSetVar "test" True+ assert =<< nvimGetVar "test"
@@ -1,48 +1,22 @@ module Ribosome.Test.SyntaxTest where -import TestError (TestError)-import Hedgehog (TestT)-import Chiasma.Data.TmuxError (TmuxError)-import Chiasma.Test.Tmux (TmuxTestConf(..))+import Polysemy.Test (UnitTest) import Ribosome.Api.Buffer (setCurrentBufferContent) import Ribosome.Api.Syntax (executeSyntax)-import Ribosome.Control.Monad.Ribo (Ribo)-import Ribosome.Data.Syntax (- Syntax(Syntax),- syntaxHighlight,- syntaxMatch,- )-import Ribosome.Error.Report.Class (ReportError(..))-import Ribosome.System.Time (sleep)-import Ribosome.Test.Embed (defaultTestConfig)-import Ribosome.Test.Run (UnitTest)+import Ribosome.Syntax (Syntax (Syntax), syntaxHighlight, syntaxMatch) import Ribosome.Test.Screenshot (awaitScreenshot)-import Ribosome.Test.Tmux (tmuxSpec')--data SyntaxSpecError =- Test TestError- |- Tmux TmuxError--deepPrisms ''SyntaxSpecError--instance ReportError SyntaxSpecError where- errorReport _ =- undefined+import Ribosome.Test.SocketTmux (testSocketTmux) syntax :: Syntax syntax =- Syntax [syntaxMatch "TestColons" "::"] [syntaxHighlight "TestColons"- [("cterm", "reverse"), ("ctermfg", "1"), ("gui", "reverse"), ("guifg", "#dc322f")]] []--syntaxSpec :: TestT (Ribo () SyntaxSpecError) ()-syntaxSpec = do- lift (setCurrentBufferContent ["function :: String -> Int", "function _ = 5"])- _ <- lift (executeSyntax syntax)- sleep 1- awaitScreenshot "syntax" False 0+ Syntax [syntaxMatch "TestColons" "::"] [+ syntaxHighlight "TestColons" [("cterm", "reverse"), ("ctermfg", "1"), ("gui", "reverse"), ("guifg", "#dc322f")]+ ] [] test_syntax :: UnitTest test_syntax =- tmuxSpec' def { ttcWidth = 300, ttcHeight = 51, ttcGui = False } (defaultTestConfig "syntax") def syntaxSpec+ testSocketTmux do+ setCurrentBufferContent ["function :: String -> Int", "function _ = 5"]+ _ <- executeSyntax syntax+ awaitScreenshot False "syntax" 0
@@ -1,45 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# OPTIONS_GHC -Wno-unused-imports #-}-{-# OPTIONS_GHC -Wno-unused-top-binds #-}-{- HLINT ignore -}--module Ribosome.Test.THTest where--import Data.Aeson (FromJSON)--- import Data.Default (def)-import GHC.Generics (Generic)-import Language.Haskell.TH-import Neovim (Plugin(..))--- import TestError (handleTestError)---- import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Msgpack.Decode (MsgpackDecode)-import Ribosome.Msgpack.Encode (MsgpackEncode)-import Ribosome.Plugin-import Ribosome.Test.Run (UnitTest)--data Par =- Par {- parA :: Int,- parB :: Int- }- deriving (Eq, Show, Generic, MsgpackDecode, MsgpackEncode, FromJSON)--handler :: Text -> m ()-handler =- undefined--$(return [])--plugin' :: IO (Plugin (Ribosome Int))-plugin' = do- -- ribo <- newRibosome ("test" :: Text) 1- undefined- -- return $ riboPlugin "test" ribo [$(rpcHandler (cmd []) 'handler)] [] handleTestError def--test_plug :: UnitTest-test_plug = do- return ()- -- _ <- plugin'- -- traverse_ putStrLn $ lines $(stringE . pprint =<< rpcHandler (sync . cmd []) 'handler)
@@ -1,46 +0,0 @@-module Ribosome.Test.WatcherTest where--import qualified Data.Map.Strict as Map (singleton)-import Data.MessagePack (Object)-import Hedgehog ((===))-import Neovim (Plugin(..))-import TestError (RiboTest, handleTestError)--import Ribosome.Api.Autocmd (doautocmd)-import Ribosome.Api.Variable (setVar)-import Ribosome.Control.Monad.Ribo (NvimE)-import Ribosome.Control.Ribosome (Ribosome, newRibosome)-import Ribosome.Nvim.Api.IO (vimGetVar)-import Ribosome.Plugin (riboPlugin)-import Ribosome.Test.Await (await)-import Ribosome.Test.Embed (integrationSpecDef)-import Ribosome.Test.Orphans ()-import Ribosome.Test.Run (UnitTest)--changed :: NvimE e m => Object -> m ()-changed _ =- setVar "number" =<< ((+) (1 :: Int)) <$> vimGetVar "number"--varWatcherPlugin :: IO (Plugin (Ribosome ()))-varWatcherPlugin = do- env <- newRibosome "varwatcher" ()- return $ riboPlugin "varwatcher" env [] [] handleTestError (Map.singleton "trigger" changed)--varWatcherSpec :: RiboTest ()-varWatcherSpec = do- setVar "number" (10 :: Int)- setVar "trigger" (5 :: Int)- await ((10 :: Int) ===) (vimGetVar "number")- await ((5 :: Int) ===) (vimGetVar "trigger")- doautocmd True "CmdlineLeave"- doautocmd True "CmdlineLeave"- await ((11 :: Int) ===) (vimGetVar "number")- setVar "trigger" (6 :: Int)- await ((6 :: Int) ===) (vimGetVar "trigger")- doautocmd True "CmdlineLeave"- await ((12 :: Int) ===) (vimGetVar "number")--test_varWatcher :: UnitTest-test_varWatcher = do- plug <- liftIO varWatcherPlugin- integrationSpecDef plug varWatcherSpec
@@ -1,43 +0,0 @@-module Ribosome.Test.WindowTest where--import Hedgehog ((===))-import TestError (RiboTest)--import Ribosome.Api.Window (ensureMainWindow)-import Ribosome.Msgpack.Encode (toMsgpack)-import Ribosome.Nvim.Api.Data (Window)-import Ribosome.Nvim.Api.IO (bufferSetOption, vimCommand, vimGetCurrentBuffer, vimGetCurrentWindow, vimGetWindows)-import Ribosome.Test.Run (UnitTest)-import Ribosome.Test.Unit (unitSpecDef')--setCurrentNofile :: RiboTest ()-setCurrentNofile = do- buf <- vimGetCurrentBuffer- bufferSetOption buf "buftype" (toMsgpack ("nofile" :: Text))--createNofile :: RiboTest Window-createNofile = do- initialWindow <- vimGetCurrentWindow- vimCommand "new"- setCurrentNofile- return initialWindow--findMainWindowExistingSpec :: RiboTest ()-findMainWindowExistingSpec = do- initialWindow <- createNofile- (initialWindow ===) =<< ensureMainWindow--test_findMainWindowExisting :: UnitTest-test_findMainWindowExisting =- unitSpecDef' findMainWindowExistingSpec--findMainWindowCreateSpec :: RiboTest ()-findMainWindowCreateSpec = do- setCurrentNofile- void createNofile- void ensureMainWindow- (3 ===) =<< length <$> vimGetWindows--test_findMainWindowCreate :: UnitTest-test_findMainWindowCreate =- unitSpecDef' findMainWindowCreateSpec
@@ -0,0 +1,11 @@+module Skip where++import qualified Data.Text.IO as Text+import Polysemy.Test (UnitTest)+import System.Environment (lookupEnv)++skipUnlessX :: UnitTest -> UnitTest+skipUnlessX test = do+ liftIO (lookupEnv "DISPLAY") >>= \case+ Just _ -> test+ Nothing -> liftIO (Text.putStrLn "Skipping test due to lack of display")
@@ -1,28 +0,0 @@-module TestError where--import Hedgehog (TestT)-import Ribosome.Control.Monad.Ribo (Nvim, NvimE, Ribo)-import Ribosome.Data.Mapping (MappingError)-import Ribosome.Error.Report.Class (ReportError(..))-import Ribosome.Log (showError)-import Ribosome.Msgpack.Error (DecodeError)-import Ribosome.Nvim.Api.RpcCall (RpcError)--data TestError =- Rpc RpcError- |- Decode DecodeError- |- Mapping MappingError- deriving (Show, Generic, ReportError)--deepPrisms ''TestError--handleTestError :: TestError -> Ribo s TestError ()-handleTestError =- showError "error in test:"--type RiboTest a = TestT (Ribo () TestError) a-type RiboT a = Ribo () TestError a--instance (Nvim m, MonadDeepError TestError RpcError m) => NvimE TestError (TestT m) where