packages feed

ribosome-test 0.3.0.1 → 0.4.0.0

raw patch · 30 files changed

+2203/−375 lines, 30 filesdep +aesondep +basedep +compositiondep −HTFdep −base-nopreludedep −unliftio-coredep ~bytestringdep ~chiasmadep ~cornea

Dependencies added: aeson, base, composition, composition-extra, conduit, containers, either, hedgehog, lens, lifted-async, path, path-io, prettyprinter, prettyprinter-ansi-terminal, ribosome-test, tasty, tasty-hedgehog, template-haskell

Dependencies removed: HTF, base-noprelude, unliftio-core

Dependency ranges changed: bytestring, chiasma, cornea, data-default, directory, exceptions, filepath, free, hslogger, lifted-base, messagepack, monad-control, mtl, nvim-hs, process, relude, resourcet, ribosome, text, transformers, typed-process, unix, unliftio

Files

LICENSE view
@@ -1,55 +1,34 @@-# Blue Oak Model License--Version 1.0.0--## Purpose--This license gives everyone as much permission to work with-this software as possible, while protecting contributors-from liability.--## Acceptance--In order to receive this license, you must agree to its-rules.  The rules of this license are both obligations-under that agreement and conditions to your license.-You must not do anything with this software that triggers-a rule that you cannot or will not follow.--## Copyright--Each contributor licenses you to do everything with this-software that would otherwise infringe that contributor's-copyright in it.--## Notices--You must ensure that everyone who gets a copy of-any part of this software from you, with or without-changes, also gets the text of this license or a link to-<https://blueoakcouncil.org/license/1.0.0>.--## Excuse+Copyright (c) 2020 Torsten Schmits -If anyone notifies you in writing that you have not-complied with [Notices](#notices), you can keep your-license by taking all practical steps to comply within 30-days after the notice.  If you do not do so, your license-ends immediately.+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the+following conditions are met: -## Patent+  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following+  disclaimer.+  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following+  disclaimer in the documentation and/or other materials provided with the distribution. -Each contributor licenses you to do everything with this-software that would otherwise infringe any patent claims-they can license or become able to license.+Subject to the terms and conditions of this license, each copyright holder and contributor hereby grants to those+receiving rights under this license a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except+for failure to satisfy the conditions of this license) patent license to make, have made, use, offer to sell, sell,+import, and otherwise transfer this software, where such license applies only to those patent claims, already acquired+or hereafter acquired, licensable by such copyright holder or contributor that are necessarily infringed by: -## Reliability+  (a) their Contribution(s) (the licensed copyrights of copyright holders and non-copyrightable additions of+  contributors, in source or binary form) alone; or+  (b) combination of their Contribution(s) with the work of authorship to which such Contribution(s) was added by such+  copyright holder or contributor, if, at the time the Contribution is added, such addition causes such combination to+  be necessarily infringed. The patent license shall not apply to any other combinations which include the Contribution. -No contributor can revoke this license.+Except as expressly stated above, no rights or licenses from any copyright holder or contributor is granted under this+license, whether expressly, by implication, estoppel or otherwise. -## No Liability+DISCLAIMER -***As far as the law allows, this software comes as is,-without any warranty or condition, and no contributor-will be liable to anyone for any damages related to this-software or this license, under any kind of legal claim.***+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
− lib/Prelude.hs
@@ -1,5 +0,0 @@-module Prelude (-  module Ribosome.PreludeExport-) where--import Ribosome.PreludeExport
lib/Ribosome/Test/Await.hs view
@@ -1,47 +1,37 @@ module Ribosome.Test.Await where +import Hedgehog (TestT) import Control.Exception (throw)-import Control.Monad.Error.Class (MonadError, catchError)-import Control.Monad.IO.Class (MonadIO)-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.Control (MonadBaseControl)-import Control.Monad.Trans.Except (ExceptT, runExceptT)-import Test.Framework.AssertM (AssertM)+import Control.Monad.Error.Class (MonadError (throwError), catchError)+import Hedgehog.Internal.Property (mkTestT, runTestT, Failure, Journal) -import Ribosome.Control.Concurrent.Wait (WaitError(Thrown), waitIODef)+import Ribosome.Control.Concurrent.Wait (WaitError(Thrown, ConditionUnmet, NotStarted), waitIODef)  await ::-  Show e =>+  ∀ e a b m .   MonadError e m =>   MonadIO m =>-  MonadFail m =>   MonadBaseControl IO m =>-  AssertM m =>-  (a -> m b) ->+  (a -> TestT m b) ->   m a ->-  m b+  TestT m b await assertion acquire = do-  r <- waitIODef acquire' check'-  either failure return r-  where-    acquire' = catchError (Right <$> acquire) (return . Left . show)-    check' (Right a) = Right <$> assertion a-    check' (Left e) = return (Left e)-    failure (Thrown e) = throw e-    failure e = fail $ "await failed with " <> show e--await' ::-  ∀ a b m.-  MonadIO m =>-  MonadFail m =>-  MonadBaseControl IO m =>-  AssertM (ExceptT () m) =>-  (a -> m b) ->-  m a ->-  m b-await' assertion acquire = do-  r <- runExceptT $ await assertion' (lift acquire)-  either (const $ fail "internal error") return r+  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-    assertion' :: a -> ExceptT () m b-    assertion' = lift . assertion+    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))
lib/Ribosome/Test/Embed.hs view
@@ -1,15 +1,13 @@ module Ribosome.Test.Embed where -import Control.Concurrent (forkIO)-import Control.Monad.IO.Class (MonadIO, liftIO)-import Control.Monad.Reader (runReaderT)-import Control.Monad.Trans.Except (runExceptT)+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 Data.Default (Default(def))-import Data.Foldable (traverse_)-import Data.Functor (void)-import Data.Maybe (fromMaybe)-import GHC.IO.Handle (Handle)+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 (@@ -24,7 +22,7 @@   transitionTo,   ) import Neovim.Main (standalone)-import Neovim.Plugin (Plugin, startPluginThreads)+import Neovim.Plugin (Plugin(Plugin), startPluginThreads) import Neovim.Plugin.Internal (NeovimPlugin, wrapPlugin) import Neovim.RPC.Common (RPCConfig, newRPCConfig) import Neovim.RPC.EventHandler (runEventHandler)@@ -44,15 +42,13 @@   proc,   setStdin,   setStdout,+  startProcess,+  stopProcess,   unsafeProcessHandle,-  withProcess,   )-import UnliftIO.Async (async, cancel, race)-import UnliftIO.Exception (bracket, tryAny)-import UnliftIO.MVar (MVar)-import UnliftIO.STM (putTMVar)  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(..))@@ -62,12 +58,22 @@ import Ribosome.System.Time (sleep, sleepW) import Ribosome.Test.Orphans () -type Runner m = TestConfig -> m () -> m ()+type Runner m = ∀ a . TestConfig -> m a -> m a  newtype Vars =-  Vars [(Text, Object)]-  deriving (Eq, Show, Semigroup, Monoid, Default)+  Vars (Map Text Object)+  deriving (Eq, Show)+  deriving newtype (Default, Semigroup, Monoid) +-- |left biased+varsUnion :: Vars -> Vars -> Vars+varsUnion (Vars v1) (Vars v2) =+  Vars (Map.union v1 v2)++varsFromList :: [(Text, Object)] -> Vars+varsFromList =+  Vars . Map.fromList+ data TestConfig =   TestConfig {     tcPluginName :: Text,@@ -80,23 +86,28 @@   }  instance Default TestConfig where-  def = TestConfig "ribosome" "test/f/fixtures/rtp" "test/f/temp/log" 10 def def (Vars [])+  def = TestConfig "ribosome" "test/u/fixtures/rtp" "test/u/temp/log" 10 def def def  defaultTestConfigWith :: Text -> Vars -> TestConfig defaultTestConfigWith name vars =   def { tcPluginName = name, tcVariables = vars }  defaultTestConfig :: Text -> TestConfig-defaultTestConfig name = defaultTestConfigWith name (Vars [])+defaultTestConfig name = defaultTestConfigWith name def  setVars :: ∀ m e. NvimE e m => Vars -> m () setVars (Vars vars) =-  traverse_ set vars+  traverse_ set (Map.toList vars)   where     set :: (Text, Object) -> m ()-    set = uncurry vimSetVar+    set =+      uncurry vimSetVar -setupPluginEnv :: (MonadIO m, NvimE e m) => TestConfig -> m ()+setupPluginEnv ::+  MonadIO m =>+  NvimE e m =>+  TestConfig ->+  m () setupPluginEnv (TestConfig _ rtp _ _ _ _ vars) = do   absRtp <- liftIO $ makeAbsolute (toString rtp)   rtpCat (toText absRtp)@@ -108,9 +119,10 @@  killProcess :: Process i o e -> IO () killProcess prc = do-  let handle = unsafeProcessHandle prc-  mayPid <- getPid handle-  traverse_ killPid mayPid+  void $ try @_ @SomeException do+    let handle = unsafeProcessHandle prc+    mayPid <- getPid handle+    traverse_ killPid mayPid  testNvimProcessConfig :: TestConfig -> ProcessConfig Handle Handle () testNvimProcessConfig TestConfig {..} =@@ -119,18 +131,29 @@     args = fromMaybe defaultArgs tcCmdline     defaultArgs = ["--embed", "-n", "-u", "NONE", "-i", "NONE"] -startHandlers :: Handle -> Handle -> TestConfig -> Internal.Config RPCConfig -> IO (IO ())-startHandlers stdoutHandle stdinHandle TestConfig{..} nvimConf = do-  socketReader <- run runSocketReader stdoutHandle-  eventHandler <- run runEventHandler stdinHandle+startHandlers ::+  MonadIO m =>+  Handle ->+  Handle ->+  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]+  let stopEventHandlers = traverse_ @[] cancel [socketReader, eventHandler]   return stopEventHandlers   where     run runner hand = async . void $ runner hand emptyConf     emptyConf = nvimConf { Internal.pluginSettings = Nothing } -startStdioHandlers :: NvimProc -> TestConfig -> Internal.Config RPCConfig -> IO (IO ())+startStdioHandlers ::+  MonadIO m =>+  NvimProc ->+  TestConfig ->+  Internal.Config RPCConfig ->+  m (IO ()) startStdioHandlers prc =   startHandlers (getStdout prc) (getStdin prc) @@ -169,115 +192,160 @@ shutdownNvim _ prc stopEventHandlers = do   stopEventHandlers   killProcess prc-  -- quitNvim testCfg prc  runTest ::-  RpcHandler e env m =>+  MonadIO m =>+  MonadFail m =>   ReportError e =>+  RpcHandler e env n =>+  MonadBaseControl IO m =>   TestConfig ->   Internal.Config env ->-  m () ->-  IO ()+  n a ->+  m a runTest TestConfig{..} testCfg thunk = do-  result <- race (sleepW tcTimeout) (runNeovimThunk testCfg (runExceptT $ native thunk))-  case result of-    Right (Right _) -> return ()+  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"  runEmbeddedNvim ::-  RpcHandler e env m =>+  MonadIO m =>+  MonadFail m =>+  MonadBaseControl IO m =>+  RpcHandler e env n =>   ReportError e =>   TestConfig ->   env ->-  m () ->+  n a ->   NvimProc ->-  IO ()+  m a runEmbeddedNvim conf ribo thunk prc = do-  nvimConf <- Internal.newConfig (pure Nothing) newRPCConfig+  nvimConf <- liftIO (Internal.newConfig (pure Nothing) newRPCConfig)   let testCfg = Internal.retypeConfig ribo nvimConf-  bracket (startStdioHandlers prc conf nvimConf) (shutdownNvim testCfg prc) (const $ runTest conf testCfg thunk)+  bracket (startStdioHandlers prc conf nvimConf) (liftIO . shutdownNvim testCfg prc) (const $ runTest conf testCfg thunk) +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)+ runEmbedded ::-  RpcHandler e env m =>+  MonadIO m =>+  MonadFail m =>+  MonadBaseControl IO m =>+  RpcHandler e env n =>   ReportError e =>   TestConfig ->   env ->-  m () ->-  IO ()+  n a ->+  m a runEmbedded conf ribo thunk = do   let pc = testNvimProcessConfig conf-  withProcess pc $ runEmbeddedNvim conf ribo thunk+  withProcessWait pc $ runEmbeddedNvim conf ribo thunk  unsafeEmbeddedSpec ::-  RpcHandler e env m =>+  MonadIO m =>+  MonadFail m =>+  MonadBaseControl IO m =>+  RpcHandler e env n =>   ReportError e =>-  Runner m ->+  Runner n ->   TestConfig ->   env ->-  m () ->-  IO ()+  n a ->+  m a unsafeEmbeddedSpec runner conf s spec =   runEmbedded conf s $ runner conf spec  unsafeEmbeddedSpecR ::-  RpcHandler e (Ribosome env) m =>+  MonadIO m =>+  MonadFail m =>   ReportError e =>-  Runner m ->+  MonadBaseControl IO m =>+  RpcHandler e (Ribosome env) n =>+  Runner n ->   TestConfig ->   env ->-  m () ->-  IO ()+  n a ->+  m a unsafeEmbeddedSpecR runner conf env spec = do   tv <- newRibosomeTMVar env   let ribo = Ribosome (tcPluginName conf) tv   unsafeEmbeddedSpec runner conf ribo spec  runPlugin ::+  MonadIO m =>+  MonadBaseControl IO m =>   Handle ->   Handle ->   [Neovim () NeovimPlugin] ->   Internal.Config c ->-  IO (MVar Internal.StateTransition)+  m (MVar Internal.StateTransition) runPlugin evHandlerHandle sockreaderHandle plugins baseConf = do-  updateGlobalLogger "Neovim.Plugin" (setLevel ERROR)-  rpcConf <- newRPCConfig+  liftIO (updateGlobalLogger "Neovim.Plugin" (setLevel ERROR))+  rpcConf <- liftIO newRPCConfig   let conf = Internal.retypeConfig rpcConf baseConf-  ehTid <- async $ runEventHandler evHandlerHandle conf { Internal.pluginSettings = Nothing }-  srTid <- async $ runSocketReader sockreaderHandle conf-  void $ forkIO $ startPluginThreads (Internal.retypeConfig () baseConf) plugins >>= \case+  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+      standalone (srTid : ehTid : pluginTids) conf   return (Internal.transitionTo conf) -runEmbeddedWithPlugin ::-  RpcHandler e () m =>+inTestT ::+  ∀ n m a .+  TestT n a ->+  (∀ x . n x -> m x) ->+  TestT m a+inTestT ma f =+  mkTestT (f (runTestT ma))++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 ->-  m () ->-  IO ()-runEmbeddedWithPlugin conf plugin thunk =-  withProcess (testNvimProcessConfig conf) run+  TestT n a ->+  TestT m a+integrationSpec conf plugin@(Plugin env _) thunk =+  inTestT thunk \ na ->+    withProcessTerm (testNvimProcessConfig conf) (run na)   where-    run prc = do-      nvimConf <- Internal.newConfig (pure Nothing) (pure ())-      bracket (acquire prc nvimConf) release (const $ runTest conf nvimConf thunk)+    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 =-      runPlugin (getStdin prc) (getStdout prc) [wrapPlugin plugin] nvimConf <* sleep 0.5+      liftIO (runPlugin (getStdin prc) (getStdout prc) [wrapPlugin plugin] nvimConf <* sleep 0.5)     release transitions =       tryPutMVar transitions Internal.Quit *> sleep 0.5  integrationSpecDef ::-  RpcHandler e () m =>+  NvimE e n =>+  MonadIO m =>+  MonadIO n =>+  MonadFail m =>   ReportError e =>+  RpcHandler e env n =>+  MonadBaseControl IO m =>   Plugin env ->-  m () ->-  IO ()+  TestT n a ->+  TestT m a integrationSpecDef =-  runEmbeddedWithPlugin def+  integrationSpec def
lib/Ribosome/Test/Exists.hs view
@@ -1,8 +1,5 @@-module Ribosome.Test.Exists(-  waitForPlugin,-) where+module Ribosome.Test.Exists where -import Control.Monad.IO.Class (MonadIO) import Data.MessagePack (Object(ObjectInt)) import Neovim (Neovim, toObject) import Neovim.API.Text (vim_call_function)
lib/Ribosome/Test/File.hs view
@@ -1,12 +1,11 @@ module Ribosome.Test.File where -import Control.Monad.IO.Class (MonadIO, liftIO) import qualified Data.ByteString as ByteString (readFile) import System.Directory (canonicalizePath, createDirectoryIfMissing, removePathForcibly) import System.FilePath ((</>))  testDir :: Text -> IO FilePath-testDir prefix = canonicalizePath $ "test" </> toString prefix+testDir = canonicalizePath . toString  -- raises exception if cwd is not the package root so we don't damage anything tempDirIO :: Text -> FilePath -> IO FilePath
lib/Ribosome/Test/Input.hs view
@@ -4,6 +4,7 @@ import Control.Exception.Lifted (bracket)  import Ribosome.Api.Input (syntheticInput)+import Ribosome.Control.Monad.Ribo (NvimE)  withInput ::   NvimE e m =>
lib/Ribosome/Test/Orphans.hs view
@@ -2,28 +2,26 @@  module Ribosome.Test.Orphans where -import Control.Monad.IO.Class (liftIO)-import Control.Monad.IO.Unlift (withRunInIO)-import Neovim (Neovim)-import Test.Framework.AssertM (AssertM(..))+-- import Control.Monad.IO.Unlift (withRunInIO)+-- import Neovim (Neovim) -import Ribosome.Control.Monad.Ribo (Ribo(Ribo, unRibo))+-- import Ribosome.Control.Monad.Ribo (Ribo(Ribo, unRibo)) -instance AssertM (Neovim e) where-  genericAssertFailure__ l =-    liftIO . genericAssertFailure__ l+-- instance AssertM (Neovim e) where+--   genericAssertFailure__ l =+--     liftIO . genericAssertFailure__ l -  genericSubAssert l msg ma =-    withRunInIO (\f -> genericSubAssert l msg (f ma))+--   genericSubAssert l msg ma =+--     withRunInIO (\f -> genericSubAssert l msg (f ma)) -instance AssertM (ExceptT e (Neovim s)) where-  genericAssertFailure__ l = liftIO . genericAssertFailure__ l+-- instance AssertM (ExceptT e (Neovim s)) where+--   genericAssertFailure__ l = liftIO . genericAssertFailure__ l -  genericSubAssert l msg =-    ExceptT . genericSubAssert l msg . runExceptT+--   genericSubAssert l msg =+--     ExceptT . genericSubAssert l msg . runExceptT -instance AssertM (Ribo s e) where-  genericAssertFailure__ l = liftIO . genericAssertFailure__ l+-- instance AssertM (Ribo s e) where+--   genericAssertFailure__ l = liftIO . genericAssertFailure__ l -  genericSubAssert l msg =-    Ribo . ExceptT . genericSubAssert l msg . runExceptT . unRibo+--   genericSubAssert l msg =+--     Ribo . ExceptT . genericSubAssert l msg . runExceptT . unRibo
+ lib/Ribosome/Test/Run.hs view
@@ -0,0 +1,14 @@+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
lib/Ribosome/Test/Screenshot.hs view
@@ -1,23 +1,19 @@-{-# OPTIONS_GHC -F -pgmF htfpp #-}- 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.DeepError (MonadDeepError) import Control.Monad.Free.Class (MonadFree)-import Control.Monad.IO.Class (MonadIO)-import Data.Text (Text)-import Test.Framework-import Test.Framework.AssertM (AssertM(..))+import Hedgehog (TestT, (===))  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)  screenshot ::   MonadFree TmuxThunk m =>@@ -28,23 +24,39 @@   m (Maybe ([Text], [Text])) screenshot name record pane = do   storage <- fixture "screenshots"-  Chiasma.screenshot record storage (toString name) pane+  Chiasma.screenshot record storage name pane  assertScreenshot ::-  AssertM m =>   MonadIO m =>   MonadRibo m =>   MonadDeepError e TmuxError m =>+  MonadBaseControl IO m =>   MonadMask m =>   Nvim m =>   Text ->   Bool ->   Int ->-  m ()-assertScreenshot name record pane =-  runTmux (screenshot name record pane) >>= check+  TestT m ()+assertScreenshot name record pane = do+  lift (runTmux (screenshot name record pane)) >>= check   where     check (Just (current, existing)) =-      gassertEqual existing current+      existing === current     check Nothing =       return ()++awaitScreenshot ::+  MonadIO m =>+  MonadMask m =>+  MonadRibo m =>+  MonadBaseControl IO m =>+  MonadDeepError e TmuxError m =>+  Nvim m =>+  Text ->+  Bool ->+  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
lib/Ribosome/Test/Tmux.hs view
@@ -1,170 +1,203 @@ 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)+import Chiasma.Test.Tmux (TmuxTestConf, withSystemTempDir) import qualified Chiasma.Test.Tmux as Chiasma (tmuxGuiSpec, tmuxSpec, tmuxSpec')-import Control.Monad.Trans.Except (runExceptT)+import Control.Exception.Lifted (bracket) import Data.DeepPrisms (DeepPrisms)-import Data.Default (Default(def))-import Data.Functor (void)-import qualified Neovim.Context.Internal as Internal (Config, newConfig, retypeConfig)-import Neovim.RPC.Common (RPCConfig, SocketType(UnixSocket), createHandle, newRPCConfig)+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 UnliftIO (throwString)-import UnliftIO.Directory (doesPathExist)-import UnliftIO.Exception (bracket)-import UnliftIO.Temporary (withTempDirectory)  import Ribosome.Config.Setting (updateSetting) import Ribosome.Config.Settings (tmuxSocket) import Ribosome.Control.Concurrent.Wait (waitIOPredDef)-import Ribosome.Control.Monad.Ribo (Ribo)+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.Test.Embed (Runner, TestConfig(..), runTest, startHandlers)+import Ribosome.System.Time (sleep)+import Ribosome.Test.Embed (+  Runner,+  TestConfig(..),+  inTestT,+  runPlugin,+  runTest,+  startHandlers,+  testNvimProcessConfig,+  withProcessTerm,+  ) import Ribosome.Test.Orphans ()-import Ribosome.Test.Unit (tempDir, uSpec)--startSocketHandlers :: FilePath -> TestConfig -> Internal.Config RPCConfig -> IO (IO ())-startSocketHandlers socket testConfig nvimConf = do-  handle <- createHandle (UnixSocket socket)-  startHandlers handle handle testConfig nvimConf+import Ribosome.Test.Run (UnitTest)+import Ribosome.Test.Unit (uSpec) -runTmuxNvim ::-  (RpcHandler e env m, ReportError e) =>+runSocketNvimHs ::+  MonadIO m =>+  MonadFail m =>+  ReportError e =>+  RpcHandler e env n =>+  MonadBaseControl IO m =>   TestConfig ->   env ->-  m () ->-  FilePath ->-  IO ()-runTmuxNvim conf ribo specThunk socket = do-  nvimConf <- Internal.newConfig (pure Nothing) newRPCConfig+  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 (startSocketHandlers socket conf nvimConf) id (const $ runTest conf testCfg specThunk)+  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 ::-  (RpcHandler e env m, ReportError e) =>+  MonadIO m =>+  MonadFail m =>+  ReportError e =>+  RpcHandler e env n =>+  MonadBaseControl IO m =>   TmuxNative ->   FilePath ->   TestConfig ->   env ->-  m () ->-  IO ()-runGui api temp conf ribo specThunk = do-  void $ runExceptT @TmuxError $ runTmux api $ sendKeys (PaneId 0) [toString $ externalNvimCmdline socket]-  _ <- waitIOPredDef (pure socket) doesPathExist-  runTmuxNvim conf ribo specThunk socket-  where-    socket = temp </> "nvim-socket"+  n a ->+  m a+runGui api temp conf ribo specThunk =+  runSocketNvimHs conf ribo specThunk =<< liftIO (startNvimInTmux api temp)  unsafeGuiSpec ::-  (RpcHandler e env m, ReportError e) =>+  MonadIO m =>+  MonadFail m =>+  ReportError e =>+  RpcHandler e env n =>+  MonadBaseControl IO m =>   TmuxNative ->   FilePath ->-  Runner m ->+  Runner n ->   TestConfig ->   env ->-  m () ->-  IO ()+  n a ->+  m a unsafeGuiSpec api temp runner conf s specThunk =   runGui api temp conf s $ runner conf specThunk  unsafeGuiSpecR ::-  (RpcHandler e (Ribosome env) m, ReportError e) =>+  MonadIO m =>+  MonadFail m =>+  ReportError e =>+  MonadBaseControl IO m =>+  RpcHandler e (Ribosome env) n =>   TmuxNative ->   FilePath ->-  Runner m ->+  Runner n ->   TestConfig ->   env ->-  m () ->-  IO ()+  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 ::-  DeepPrisms e RpcError =>+  MonadIO m =>+  MonadFail m =>   ReportError e =>+  MonadBaseControl IO m =>+  DeepPrisms e RpcError =>   TestConfig ->   TmuxNative ->   s ->-  Ribo s e () ->-  IO ()+  Ribo s e a ->+  m a guiSpec conf api env specThunk = do-  socketDir <- tempDir "tmux-socket"-  withTempDirectory socketDir "spec" run+  withSystemTempDir run   where     run tempdir =       unsafeGuiSpecR api tempdir uSpec conf env specThunk  withTmux ::   DeepPrisms e RpcError =>-  Ribo s e () ->+  Ribo s e a ->   TmuxNative ->-  Ribo s e ()+  Ribo s e a withTmux thunk (TmuxNative (Just socket)) =   updateSetting tmuxSocket socket *> thunk withTmux _ _ =-  throwString "no socket in test tmux"+  throwText "no socket in test tmux"  tmuxSpec ::-  Show s =>   DeepPrisms e RpcError =>   ReportError e =>-  Default s =>   TestConfig ->   s ->-  Ribo s e () ->-  IO ()+  TestT (Ribo s e) () ->+  UnitTest tmuxSpec conf env specThunk =-  Chiasma.tmuxSpec run-  where-    run api = guiSpec conf api env (withTmux specThunk api)+  inTestT specThunk \ th ->+    Chiasma.tmuxSpec \ api -> guiSpec conf api env (withTmux th api)  tmuxSpec' ::-  Show s =>   DeepPrisms e RpcError =>   ReportError e =>-  Default s =>   TmuxTestConf ->   TestConfig ->   s ->-  Ribo s e () ->-  IO ()+  TestT (Ribo s e) () ->+  UnitTest tmuxSpec' tmuxConf conf env specThunk =-  Chiasma.tmuxSpec' tmuxConf run-  where-    run api = guiSpec conf api env (withTmux specThunk api)+  inTestT specThunk \ th ->+    Chiasma.tmuxSpec' tmuxConf \ api ->+      guiSpec conf api env (withTmux th api)  tmuxSpecDef ::-  Show s =>   DeepPrisms e RpcError =>   ReportError e =>   Default s =>-  Ribo s e () ->-  IO ()+  TestT (Ribo s e) () ->+  UnitTest tmuxSpecDef =   tmuxSpec def def  tmuxGuiSpec ::   DeepPrisms e RpcError =>   ReportError e =>-  Default s =>   TestConfig ->   s ->   Ribo s e () ->-  IO ()+  UnitTest tmuxGuiSpec conf env specThunk =   Chiasma.tmuxGuiSpec run   where@@ -175,6 +208,64 @@   ReportError e =>   Default s =>   Ribo s e () ->-  IO ()+  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)
lib/Ribosome/Test/Ui.hs view
@@ -1,9 +1,6 @@-{-# OPTIONS_GHC -F -pgmF htfpp #-}- module Ribosome.Test.Ui where -import Test.Framework-+import Hedgehog (TestT, (===)) import Ribosome.Api.Window (currentCursor, cursor) import Ribosome.Control.Monad.Ribo (NvimE) import Ribosome.Nvim.Api.Data (Window)@@ -11,28 +8,25 @@  windowCountIs ::   NvimE e m =>-  AssertM m =>   Int ->-  m ()+  TestT m () windowCountIs count = do   wins <- nvimListWins-  gassertEqual count (length wins)+  count === (length wins)  cursorIs ::   NvimE e m =>-  AssertM m =>   Int ->   Int ->   Window ->-  m ()+  TestT m () cursorIs line col =-  gassertEqual (line, col) <=< cursor+  ((line, col) ===) <=< lift . cursor  currentCursorIs ::   NvimE e m =>-  AssertM m =>   Int ->   Int ->-  m ()+  TestT m () currentCursorIs line col =-  gassertEqual (line, col) =<< currentCursor+  ((line, col) ===) =<< lift currentCursor
lib/Ribosome/Test/Unit.hs view
@@ -1,9 +1,11 @@ module Ribosome.Test.Unit where -import Control.Monad.IO.Class (MonadIO)-import Data.Default (def)+import Control.Exception.Lifted (bracket_)+import Hedgehog (TestT)+import Hedgehog.Internal.Property (mkTestT, runTestT) import System.FilePath (takeDirectory, takeFileName, (</>))-import System.Log.Logger (Priority(DEBUG), setLevel, updateGlobalLogger)+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)@@ -14,7 +16,7 @@ import Ribosome.Test.Orphans ()  uPrefix :: Text-uPrefix = "u"+uPrefix = "test"  uSpec :: (MonadIO m, NvimE e m) => Runner m uSpec conf spec = do@@ -22,26 +24,44 @@   spec  unitSpec ::-  (RpcHandler e (Ribosome env) m, ReportError e, MonadIO m, NvimE e' m) =>+  MonadIO n =>+  MonadIO m =>+  NvimE e' n =>+  MonadFail m =>+  ReportError e =>+  MonadBaseControl IO m =>+  RpcHandler e (Ribosome env) n =>   TestConfig ->   env ->-  m () ->-  IO ()-unitSpec =-  unsafeEmbeddedSpecR uSpec+  TestT n a ->+  TestT m a+unitSpec cfg env t = do+  mkTestT (unsafeEmbeddedSpecR uSpec cfg env (runTestT t))  unitSpecDef ::-  (RpcHandler e (Ribosome env) m, ReportError e, MonadIO m, NvimE e' m) =>+  MonadIO n =>+  MonadIO m =>+  NvimE e' n =>+  MonadFail m =>+  ReportError e =>+  MonadBaseControl IO m =>+  RpcHandler e (Ribosome env) n =>   env ->-  m () ->-  IO ()+  TestT n a ->+  TestT m a unitSpecDef =   unitSpec def  unitSpecDef' ::-  (RpcHandler e (Ribosome ()) m, ReportError e, MonadIO m, NvimE e' m) =>-  m () ->-  IO ()+  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 () @@ -61,15 +81,19 @@  withLogAs ::   MonadIO m =>+  MonadBaseControl IO m =>   Text ->   m a ->   m a-withLogAs name thunk = do-  liftIO $ updateGlobalLogger (toString name) (setLevel DEBUG)-  thunk+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 =
+ readme.md view
@@ -0,0 +1,91 @@+# 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
ribosome-test.cabal view
@@ -1,75 +1,281 @@-cabal-version: 1.12-name: ribosome-test-version: 0.3.0.1-license: OtherLicense-license-file: LICENSE-copyright: 2019 Torsten Schmits-maintainer: tek@tryp.io-author: Torsten Schmits-homepage: https://github.com/tek/ribosome-hs#readme-bug-reports: https://github.com/tek/ribosome-hs/issues-synopsis: test helpers for ribosome-description:-    Please see the README on GitHub at <https://github.com/tek/ribosome-hs>-category: Neovim-build-type: Simple+cabal-version: 2.2 +-- This file has been generated from package.yaml by hpack version 0.34.4.+--+-- 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>+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+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-hs+  type: git+  location: https://github.com/tek/ribosome  library-    exposed-modules:-        Ribosome.Test.Await-        Ribosome.Test.Embed-        Ribosome.Test.Exists-        Ribosome.Test.File-        Ribosome.Test.Functional-        Ribosome.Test.Input-        Ribosome.Test.Orphans-        Ribosome.Test.Screenshot-        Ribosome.Test.Tmux-        Ribosome.Test.Ui-        Ribosome.Test.Unit-    hs-source-dirs: lib-    other-modules:-        Prelude-    default-language: Haskell2010-    default-extensions: AutoDeriveTypeable BangPatterns BinaryLiterals-                        ConstraintKinds DataKinds DefaultSignatures DeriveDataTypeable-                        DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable-                        DoAndIfThenElse EmptyDataDecls ExistentialQuantification-                        FlexibleContexts FlexibleInstances FunctionalDependencies GADTs-                        GeneralizedNewtypeDeriving InstanceSigs KindSignatures LambdaCase-                        MonadFailDesugaring MultiParamTypeClasses MultiWayIf NamedFieldPuns-                        OverloadedStrings PartialTypeSignatures PatternGuards PolyKinds-                        RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving-                        TupleSections TypeApplications TypeFamilies TypeSynonymInstances-                        UnicodeSyntax ViewPatterns-    build-depends:-        HTF >=0.13.2.5 && <0.14,-        base-noprelude >=4.7 && <5,-        bytestring >=0.10.8.2 && <0.11,-        chiasma >=0.1.0.0 && <0.2,-        cornea >=0.2.2.0 && <0.3,-        data-default >=0.7.1.1 && <0.8,-        directory >=1.3.1.5 && <1.4,-        exceptions >=0.10.0 && <0.11,-        filepath >=1.4.2 && <1.5,-        free >=5.0.2 && <5.1,-        hslogger >=1.2.12 && <1.3,-        lifted-base >=0.2.3.12 && <0.3,-        messagepack >=0.5.4 && <0.6,-        monad-control >=1.0.2.3 && <1.1,-        mtl >=2.2.2 && <2.3,-        nvim-hs >=2.1.0.0 && <2.2,-        process >=1.6.3.0 && <1.7,-        relude >=0.1.1 && <0.2,-        resourcet >=1.2.2 && <1.3,-        ribosome >=0.3.0.1 && <0.4,-        text >=1.2.3.1 && <1.3,-        transformers >=0.5.5.0 && <0.6,-        typed-process >=0.2.3.0 && <0.3,-        unix >=2.7.2.2 && <2.8,-        unliftio >=0.2.9.0 && <0.3,-        unliftio-core >=0.1.2.0 && <0.2+  exposed-modules:+      Ribosome.Test.Await+      Ribosome.Test.Embed+      Ribosome.Test.Exists+      Ribosome.Test.File+      Ribosome.Test.Functional+      Ribosome.Test.Input+      Ribosome.Test.Orphans+      Ribosome.Test.Run+      Ribosome.Test.Screenshot+      Ribosome.Test.Tmux+      Ribosome.Test.Ui+      Ribosome.Test.Unit+  other-modules:+      Paths_ribosome_test+  autogen-modules:+      Paths_ribosome_test+  hs-source-dirs:+      lib+  default-extensions:+      AllowAmbiguousTypes+      ApplicativeDo+      BangPatterns+      BinaryLiterals+      BlockArguments+      ConstraintKinds+      DataKinds+      DefaultSignatures+      DeriveAnyClass+      DeriveDataTypeable+      DeriveFoldable+      DeriveFunctor+      DeriveGeneric+      DeriveLift+      DeriveTraversable+      DerivingStrategies+      DisambiguateRecordFields+      DoAndIfThenElse+      DuplicateRecordFields+      EmptyDataDecls+      ExistentialQuantification+      FlexibleContexts+      FlexibleInstances+      FunctionalDependencies+      GADTs+      GeneralizedNewtypeDeriving+      InstanceSigs+      KindSignatures+      LambdaCase+      LiberalTypeSynonyms+      MultiParamTypeClasses+      MultiWayIf+      NamedFieldPuns+      OverloadedStrings+      OverloadedLists+      PackageImports+      PartialTypeSignatures+      PatternGuards+      PatternSynonyms+      PolyKinds+      QuantifiedConstraints+      QuasiQuotes+      RankNTypes+      RecordWildCards+      RecursiveDo+      ScopedTypeVariables+      StandaloneDeriving+      TemplateHaskell+      TupleSections+      TypeApplications+      TypeFamilies+      TypeFamilyDependencies+      TypeOperators+      TypeSynonymInstances+      UndecidableInstances+      UnicodeSyntax+      ViewPatterns+  ghc-options: -Wall -Wredundant-constraints -Wsimplifiable-class-constraints+  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+    , ribosome+    , tasty+    , tasty-hedgehog+    , template-haskell+    , text+    , transformers+    , typed-process+    , unix+    , unliftio+  mixins:+      base hiding (Prelude)+    , ribosome hiding (Ribosome.Prelude)+    , ribosome (Ribosome.Prelude as Prelude)+  default-language: Haskell2010++test-suite ribosome-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.SyntaxTest+      Ribosome.Test.THTest+      Ribosome.Test.WatcherTest+      Ribosome.Test.WindowTest+      TestError+      Paths_ribosome_test+  hs-source-dirs:+      test+  default-extensions:+      AllowAmbiguousTypes+      ApplicativeDo+      BangPatterns+      BinaryLiterals+      BlockArguments+      ConstraintKinds+      DataKinds+      DefaultSignatures+      DeriveAnyClass+      DeriveDataTypeable+      DeriveFoldable+      DeriveFunctor+      DeriveGeneric+      DeriveLift+      DeriveTraversable+      DerivingStrategies+      DisambiguateRecordFields+      DoAndIfThenElse+      DuplicateRecordFields+      EmptyDataDecls+      ExistentialQuantification+      FlexibleContexts+      FlexibleInstances+      FunctionalDependencies+      GADTs+      GeneralizedNewtypeDeriving+      InstanceSigs+      KindSignatures+      LambdaCase+      LiberalTypeSynonyms+      MultiParamTypeClasses+      MultiWayIf+      NamedFieldPuns+      OverloadedStrings+      OverloadedLists+      PackageImports+      PartialTypeSignatures+      PatternGuards+      PatternSynonyms+      PolyKinds+      QuantifiedConstraints+      QuasiQuotes+      RankNTypes+      RecordWildCards+      RecursiveDo+      ScopedTypeVariables+      StandaloneDeriving+      TemplateHaskell+      TupleSections+      TypeApplications+      TypeFamilies+      TypeFamilyDependencies+      TypeOperators+      TypeSynonymInstances+      UndecidableInstances+      UnicodeSyntax+      ViewPatterns+  ghc-options: -Wall -Wredundant-constraints -Wsimplifiable-class-constraints -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+    , ribosome+    , 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)+  default-language: Haskell2010
+ test/Main.hs view
@@ -0,0 +1,42 @@+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 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+  ]++main :: IO ()+main =+  defaultMain tests
+ test/Ribosome/Test/AutocmdTest.hs view
@@ -0,0 +1,49 @@+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
+ test/Ribosome/Test/MappingTest.hs view
@@ -0,0 +1,71 @@+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
+ test/Ribosome/Test/MenuTest.hs view
@@ -0,0 +1,324 @@+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+    ]
+ test/Ribosome/Test/MsgpackTest.hs view
@@ -0,0 +1,170 @@+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+  ]
+ test/Ribosome/Test/NvimMenuTest.hs view
@@ -0,0 +1,164 @@+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+  ]
+ test/Ribosome/Test/PromptTest.hs view
@@ -0,0 +1,39 @@+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
+ test/Ribosome/Test/RpcTest.hs view
@@ -0,0 +1,154 @@+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
+ test/Ribosome/Test/ScratchTest.hs view
@@ -0,0 +1,95 @@+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
+ test/Ribosome/Test/SettingTest.hs view
@@ -0,0 +1,51 @@+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
+ test/Ribosome/Test/SyntaxTest.hs view
@@ -0,0 +1,48 @@+module Ribosome.Test.SyntaxTest where++import TestError (TestError)+import Hedgehog (TestT)+import Chiasma.Data.TmuxError (TmuxError)+import Chiasma.Test.Tmux (TmuxTestConf(..))++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.Test.Screenshot (awaitScreenshot)+import Ribosome.Test.Tmux (tmuxSpec')++data SyntaxSpecError =+  Test TestError+  |+  Tmux TmuxError++deepPrisms ''SyntaxSpecError++instance ReportError SyntaxSpecError where+  errorReport _ =+    undefined++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++test_syntax :: UnitTest+test_syntax =+  tmuxSpec' def { ttcWidth = 300, ttcHeight = 51, ttcGui = False } (defaultTestConfig "syntax") def syntaxSpec
+ test/Ribosome/Test/THTest.hs view
@@ -0,0 +1,45 @@+{-# 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)
+ test/Ribosome/Test/WatcherTest.hs view
@@ -0,0 +1,46 @@+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
+ test/Ribosome/Test/WindowTest.hs view
@@ -0,0 +1,43 @@+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
+ test/TestError.hs view
@@ -0,0 +1,28 @@+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