Skip to content

resolve #107 IO module out outLn etc#120

Open
martinhelmer wants to merge 4 commits intochshersh:mainfrom
martinhelmer:main
Open

resolve #107 IO module out outLn etc#120
martinhelmer wants to merge 4 commits intochshersh:mainfrom
martinhelmer:main

Conversation

@martinhelmer
Copy link
Copy Markdown
Collaborator

[#107] Implement 'out', 'outLn', 'err' and 'errLn' functions for outputting 'Text' to corresponding handlers

functions added in IO module
refactored existing calls to use new functions

Additional tasks

  • Documentation for changes provided/changed
  • Tests added
  • Updated CHANGELOG.md

@martinhelmer martinhelmer requested a review from chshersh as a code owner March 26, 2023 17:56
@chshersh chshersh added test api:new New function or type in the exported API labels Mar 27, 2023
Copy link
Copy Markdown
Owner

@chshersh chshersh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks neat 👍🏻

A few suggestions to make the API more convenient, and also some improvements to tests.

Comment thread src/Iris/IO.hs Outdated
@
@since x.x.x.x
-}
out :: Text -> IO ()
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's actually make this function work in a polymoprhic MonadIO, so people won't need to write liftIO. And you actually remove liftIO $ ... in all places where you use out, outLn and etc.

Suggested change
out :: Text -> IO ()
out :: MonadIO m => Text -> m ()

Comment thread src/Iris/IO.hs Outdated
@@ -0,0 +1,95 @@
{- |
Module : Iris.IO
Copyright : (c) 2022 Dmitrii Kovanikov
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this was copy-pasted from another module, so we can update the year 😅

Suggested change
Copyright : (c) 2022 Dmitrii Kovanikov
Copyright : (c) 2023 Dmitrii Kovanikov

Comment thread test/Test/Iris/IO.hs
import qualified Iris.IO as IO (err, errLn, out, outLn)

checkStdErr :: IO a -> IO String
checkStdErr = hCapture_ [stderr] . hSilence [stdout]
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the hSilence part should be removed:

Suggested change
checkStdErr = hCapture_ [stderr] . hSilence [stdout]
checkStdErr = hCapture_ [stderr]

Otherwise, the test it "does not write to stderr " $ checkStdErr (IO.out "TEXT") >>= flip shouldBe "" doesn't really check that the function doesn't output anything to stderr. Because stderr was explicitly silenced.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to silence the handle we're not checking. otherwise erroneous behaviour will cause "TEXT" to leak into the console, messing up the test progress report.

I believe the implementation is correct as it is. checkStdErr does not silence stderr. it silences stdout.

i can also confirm that the tests do seem to work; hacking IO.out to print to both stdout and stderr gives ths following result:

   IO
    out
      writes to stdout, no LF  [✔]
      does not write to stderr  [✘]
    outLn
      writes to stdout, LF  [✔]
      does not write to stderr  [✔]
    err
      writes to sterr, no LF  [✔]
      does not write to stdout  [✔]
    errLn
      writes to stderr, LF  [✔]
      does not write to stdout  [✔]

Failures:

  test/Test/Iris/IO.hs:25:83:
  1) Iris.IO.out does not write to stderr
       expected: ""
        but got: "TEXT"

but maybe i'm still missing something...

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now. I was confused by silence but I can see that both functions checkStdOut and checkStdErr are used in a pair to test the output to both stdout and stderr. A single test is not enough to make sure that the function doesn't print somewhere else, but two tests together make everything work perfectly 👍🏻

No need to change these helper functions 😌

Comment thread test/Test/Iris/IO.hs Outdated
describe "IO" $ do
describe "out" $ do
-- we need `flip` to get the expectation reported correctly in case of a failure
it "writes to stdout, no LF " $ checkStdOut (IO.out "TEXT") >>= flip shouldBe "TEXT"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use shouldReturn from hspec to avoid >>= flip

Suggested change
it "writes to stdout, no LF " $ checkStdOut (IO.out "TEXT") >>= flip shouldBe "TEXT"
it "writes to stdout, no LF " $ checkStdOut (IO.out "TEXT") `shouldReturn` "TEXT"

Copy link
Copy Markdown
Owner

@chshersh chshersh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! A few minor nitpicks but this is almost done 🏁

Comment thread src/Iris/IO.hs
Comment on lines +55 to +56
out = do
liftIO . Text.hPutStr stdout
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This do seem redundant

Suggested change
out = do
liftIO . Text.hPutStr stdout
out = liftIO . Text.hPutStr stdout

Comment on lines +117 to +118
IO.out $ "I don't understand your answer: '" <> input <> "'"
IO.out "Please, answer yes or no (or y, or n)"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be outLn and not just out.

Ideally, if we could fully test this function, the tests could've caught this error. But I don't see a good way to test it. I was thinking about mocking stdin somehow to provide input. But this looks impossible (or maybe I just don't know how to do this). Tricks like silently allow to capture only stdout and stderr but they don't allow to substitute stdin which is a pity.

A proper test for this function would be to write a separate executable and use some scripting-scenario framework where you can specify input and expected output. But this is a lot of work for this small test, but maybe we'll add it later eventually anyway when things stabilise.

P.S. I remember already writing this comment but I don't see it in the review. Maybe I forgot to submit it 🤔

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered a future improvement which would be "take interactive input from file instead of stdin"? (Useful for batch installation scripts etc)

If we were to support such an improvement we'd need to store the input handle in the environment. And that would also allow for unit testing this part easier.

Thoughts?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote multiple CLI tools but I never had the need to test in batch mode from a file 🤔

You don't need to mock stdin in such cases because you can always pipe the content of the file into your program:

cat my-commands | my-program

For full E2E testing of CLI tools, one can use an external solution like this one:

I can see how storing stdin in the environment can make testing easier but it can also make the architecture of a CLI app more complicated, confusing and potentially introduce errors when you read not from the environment stdin but from the global one

So I propose just changing IO.out to IO.outLn here and invest if full integration testing later.

Comment thread test/Test/Iris/IO.hs Outdated
Co-authored-by: Dmitrii Kovanikov <kovanikov@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api:new New function or type in the exported API test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants