-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
POC for polysemy #771
POC for polysemy #771
Conversation
expectedTx <- noteInputFile "test/cardano-cli-golden/files/input/byron/tx/normal.tx" | ||
createdTx <- noteTempFile tempDir "tx" | ||
void $ execCardanoCLI | ||
hprop_byronTx = propertyOnce $ localWorkspace $ do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tempDir
argument is no longer needed because that information now propagates into our tests via Reader ProjectRoot
.
hprop_byronTx = propertyOnce $ localWorkspace $ do | ||
signingKey <- jotPkgInputFile "test/cardano-cli-golden/files/input/byron/keys/byron.skey" | ||
expectedTx <- jotPkgInputFile "test/cardano-cli-golden/files/input/byron/tx/normal.tx" | ||
createdTx <- jotTempFile "tx" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can ask for a temp file without supplying the tempDir
because it can get it from Reader ProjectRoot
.
2ae181c
to
92ff831
Compare
compareByronTxs :: () | ||
=> HasCallStack | ||
=> Member (Embed IO) r | ||
=> Member Hedgehog r |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test functions have a Member Hedgehog r
constraint.
=> Member Hedgehog r | ||
=> FilePath | ||
-> FilePath | ||
-> Sem r () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though getTxByteString
is not a test function we can use it seamlessly below.
f & moduleWorkspace "cardano-cli" | ||
& runReader (ProjectRoot cabalProjectDir) | ||
& runReader (PackagePath "cardano-cli") | ||
& runResource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The localWorkspace
function sets up all the effects for the tests. For example readers for ProjectRoot
and PackagePath
are supplied. Additional moduleWorkspace
provides a reader for Workspace
which is used to create temporary files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can have multiple readers - very nice!
=> HasCallStack | ||
=> Member (Embed IO) r | ||
=> Member Hedgehog r | ||
=> Member Log r |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can log in test and non-test functions and those will get annotated in the test report.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where does this Log effect come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Log
effect comes from polysemy-log
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interpreters for the Log
effect are registered by propertyOnce
here:
See the lines:
& interpretLogDataLog
& setLogLevel (Just Info)
& interpretDataLogHedgehog formatLogEntry getLogEntryCallStack
& interpretDataLogHedgehog id (const GHC.callStack)
& interpretTimeGhc
7203b38
to
7805ddc
Compare
-> Sem r () | ||
checkVrfFilePermissions vrfSignKey = | ||
embed (runExceptT (Api.checkVrfFilePermissions vrfSignKey)) | ||
& onLeftM throw |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's very easy to create polysemy
wrappers for functions.
|
||
checkVrfFilePermissions (File vrfSignKey) | ||
-- key-gen-VRF cli command created a VRF signing key file with the wrong permissions | ||
& trapFail @VRFPrivateKeyFilePermissionError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error handling in tests is much more succinct. If an error is thrown, we just trap it here and fail with minimal ceremony. Error handling supports multiple errors and it is always clear which error it is that occured.
7805ddc
to
1107902
Compare
@@ -0,0 +1,36 @@ | |||
{-# LANGUAGE DataKinds #-} | |||
{-# LANGUAGE FlexibleContexts #-} | |||
{-# LANGUAGE OverloadedStrings #-} |
Check warning
Code scanning / HLint
Unused LANGUAGE pragma Warning
Found:
{-# LANGUAGE OverloadedStrings #-}
Perhaps you should remove it.
{-# LANGUAGE DataKinds #-} | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE TypeApplications #-} |
Check warning
Code scanning / HLint
Unused LANGUAGE pragma Warning
Found:
{-# LANGUAGE TypeApplications #-}
Perhaps you should remove it.
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE TypeApplications #-} | ||
{-# LANGUAGE TypeOperators #-} |
Check warning
Code scanning / HLint
Unused LANGUAGE pragma Warning
Found:
{-# LANGUAGE TypeOperators #-}
Perhaps you should remove it.
Note: may require {-# LANGUAGE ExplicitNamespaces #-} adding to the top of the file
(embed $ runExceptT $ Cli.readByronSigningKey bKeyFormat fp) | ||
& onLeftM throw |
Check notice
Code scanning / HLint
Move brackets to avoid $ Note
Found:
(embed $ runExceptT $ Cli.readByronSigningKey bKeyFormat fp)
& onLeftM throw
Perhaps:
embed (runExceptT $ Cli.readByronSigningKey bKeyFormat fp)
& onLeftM throw
=> Member Log r | ||
=> Member (Embed IO) r | ||
=> Sem | ||
( Reader Workspace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where does Workspace
type come from?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice! I love the idea of using of inversion of control for our tests! We should do it for cardano-cli at some point too.
I haven't used polysemy much, so I'm wondering why polysemy and not some other effect system?
Sould we be worried about its peformance? I guess not much, becase it's just for tests, but it's something to be mindful of.
I'm not approving yet, I'm wondering what rest of the team thinks about it. Definitely there's some learning curve here.
cabal.project
Outdated
|
||
source-repository-package | ||
type: git | ||
location: https://github.com/newhoggy/polysemy-conc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this SRP? I don't see a difference between your fork and @tek's version.
And also I don't see a dependency to polysemy-conc
in the modified cabal files!?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use it directly. The SRP is because polysemy-conc
is a transitive dependency and it has a dependency that doesn't compile on Windows.
See: tek/polysemy-conc#4
createdATxAuxBS <- getTxByteString createdTx & trapFail @ByronTxError | ||
expectedATxAuxBS <- getTxByteString expectedTx & trapFail @ByronTxError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we don't write those trapFail
(where is this function defined btw? I could not find it)?
I'm worried we're not saving on boilerplate if for every IO call, we have to annotate it with trapFail theExceptionThatItCanThrow
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What trapFail
gives to is a quick way to catch the error and annotate it at the point of failure. The failure annotation will attach to the specific trapFail
that failed so you can tell which one it was that failed very easily.
It is possible to trapFail
once for the entire test, however what you get if you do that is the at the failure annotation will be at the trapFail
at the catch-all level not at the operation that failed.
If you don't care for the usefulness of getting the annotation at the point of failure you can reduce boilerplate further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The trapFail
function is defined here:
The library hw-polysemy
is like hedgehog-extras
by written using polysemy
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we don't write those trapFail?
If you don't call trapFail
you get a compile error. For example:
test/cardano-cli-golden/Test/Golden/Byron/Tx.hs:74:23: error: [GHC-39999]
• Could not deduce ‘Member (Error ByronTxError) r’
arising from a use of ‘getTxByteString’
from the context: (HasCallStack, Member (Embed IO) r,
Member Hedgehog r)
bound by the type signature for:
compareByronTxs :: forall (r :: Polysemy.Internal.Kind.EffectRow).
(HasCallStack, Member (Embed IO) r, Member Hedgehog r) =>
FilePath -> FilePath -> Sem r ()
at test/cardano-cli-golden/Test/Golden/Byron/Tx.hs:(65,1)-(71,13)
• In a stmt of a 'do' block:
expectedATxAuxBS <- getTxByteString expectedTx
In the expression:
do createdATxAuxBS <- getTxByteString createdTx
& trapFail @ByronTxError
expectedATxAuxBS <- getTxByteString expectedTx
normalByronTxToGenTx expectedATxAuxBS
=== normalByronTxToGenTx createdATxAuxBS
In an equation for ‘compareByronTxs’:
compareByronTxs createdTx expectedTx
= do createdATxAuxBS <- getTxByteString createdTx
& trapFail @ByronTxError
expectedATxAuxBS <- getTxByteString expectedTx
normalByronTxToGenTx expectedATxAuxBS
=== normalByronTxToGenTx createdATxAuxBS
|
74 | expectedATxAuxBS <- getTxByteString expectedTx
| ^^^^^^^^^^^^^^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you get an error that says Could not deduce ‘Member (Error MyError) r’
, you have two choices. You can handle it right there use trap
, which is like catch
, or trapFail
which is like trap
by fails immediately with a hedgehog annotation or you can propagate the error by adding Member (Error MyError) r
to the function signature in which case you punt the responsibility of handling the error to the caller.
Note that a function can declare that it throws multiple errors so you don't have to create a new sum type with many constructors to propagate multiple errors.
In the current state, I am neutral:
|
One advantage is that you could log in the functions being tested and then capture those logs in a test annotation like this:
The line:
Is logging from the |
Mainly due to familiarity and ecosystem - in particular the logging library |
5ad1e8c
to
8f77407
Compare
All in all, I'm not convinced:
If we are convinced this is important, I would wait a few months to see if this part of the ecosystem gains traction. |
b11d193
to
e06c483
Compare
e06c483
to
9a46ab0
Compare
9a46ab0
to
63e629b
Compare
This PR is stale because it has been open 45 days with no activity. |
This issue was closed because it has been stalled for 60 days with no activity. |
Changelog
Context
Using
polysemy
to simplify tests.How to trust this PR
Highlight important bits of the PR that will make the review faster. If there are commands the reviewer can run to observe the new behavior, describe them.
Checklist