Skip to content

Parse serialized test cases in shrink viewer tool - #22

Open
nbloomf wants to merge 1 commit into
conformance-testingfrom
add/parse-test-cases-in-viewer
Open

Parse serialized test cases in shrink viewer tool#22
nbloomf wants to merge 1 commit into
conformance-testingfrom
add/parse-test-cases-in-viewer

Conversation

@nbloomf

@nbloomf nbloomf commented Feb 17, 2026

Copy link
Copy Markdown
Member

Description

Depends on tweag/ouroboros-consensus-testing#31 and tweag/ouroboros-consensus-testing#56

This patch adds to the shrink viewer tool added in #17 the ability to parse a serialized test case file. It also updates the test runner to generate compatible JSON.

Serialized test cases do not directly represent minimal failing examples. Because it is super important that the viewer is accurate and creating values of type GenesisTest is pretty intricate, we instead store a QCGen (seed) and a shrink index and reuse the real generator of test cases.

There's a handful of todos in the test case runner that are waiting on the testgen tool; these are marked with my handle.

@nbloomf nbloomf self-assigned this Feb 17, 2026
nbloomf added a commit to tweag/ouroboros-consensus-testing that referenced this pull request Mar 3, 2026
Depends on #28,
closes
tweag/cardano-conformance-testing-of-consensus#79.

One necessary feature of our testing strategy is the ability to
represent individual test cases in a form that can be stored,
transmitted, and parsed. This PR adds the ability to render test cases
as JSON.

Most instances are handwritten (rather than derived via Generic) to
avoid leaking details about the internal representation, since
serialized test cases may persist.

See also tweag/cardano-node#22

```
cabal test ouroboros-consensus-diffusion:consensus-test  --test-options='-p "/JSON Serialization/"'
```
@dpulls

dpulls Bot commented Mar 3, 2026

Copy link
Copy Markdown

🎉 All dependencies have been resolved !

@nbloomf
nbloomf force-pushed the add/parse-test-cases-in-viewer branch from 7ff5c82 to f5dbed7 Compare March 20, 2026 00:31
@nbloomf
nbloomf changed the base branch from conformance-testing to ninioArtillero/list-classes March 20, 2026 04:35
@nbloomf
nbloomf force-pushed the add/parse-test-cases-in-viewer branch from c7bd2df to c7219d5 Compare March 20, 2026 04:40
@nbloomf
nbloomf marked this pull request as ready for review March 20, 2026 04:45
@ninioArtillero ninioArtillero added the enhancement New feature or request label Mar 20, 2026

@isovector isovector left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Great start, thanks! Left some comments

Comment thread cardano-node/app/conformance-test-runner.hs
Comment thread cardano-node/app/conformance-test-runner.hs Outdated
let isGlobalSuccess =
testRes == TestSuccess &&
(isNothing inputIndex || inputIndex == Just mempty)
testVersion = Serialize.TestVersion 1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's pull this off the test suite too, and verify that the one in our testfile agrees with it

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wonderig, you mean adding a TestSuiteData field for it?

In general, how should we go about when the work depends on upstream changes? My take would be to just make then in a new branch on the other package and work it out locally, but then the question remains of how reviewers could attempt to compile. For example we could change the source-repository-package to point to the corresponding revision. Or just trust the submitter managed to compile locally and the changes the PR depends on will eventually land.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like it didn't happen, but there was supposed to be a test version attached to each conformance test, which we could cross reference to ensure that the serialized version is talking to the same version. Maybe sync with @ninioArtillero about the best place to get this data to line up.

Comment thread cardano-node/app/conformance-test-viewer.hs Outdated
Comment on lines +153 to +165
rawKey <- case rawJson of
Aeson.Object o -> case Aeson.lookup "key" o of
Just (Aeson.String k) -> pure k
_ -> throwError "Malformed JSON: missing string field \"key\""
_ -> throwError "Malformed JSON: must be an object"
testKey <- case parseKeyName (T.unpack rawKey) of
Just (k :: GenesisTestKey) -> pure k
Nothing -> throwError $ "Unrecognized test case key: " <> T.unpack rawKey

reifiedTestCase :: Serialize.ReifiedTestCase GenesisTestKey Serialize.BlockRep
<- case eitherDecode input of
Right ok -> pure ok
Left err -> throwError $ "Input decoding error: " <> err

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we put this logic into a canonical FromJSON instance?

Comment thread cardano-node/app/conformance-test-viewer.hs Outdated
, errors
, mtl
, optparse-applicative
, ouroboros-consensus-cardano

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm pretty sure we need this one.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm in favor of keeping redundant imports in general... just for convenience. This could easily be cleaned-up when editing for upstream submission.

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeOperators #-}

{-# OPTIONS_GHC -Wno-orphans #-}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we have orphans?

Comment on lines +186 to +189
-- QuickCheck's default size is 30, which we adjust to get the initial test case.
genesisTest = QC.unGen generator seed (ctMaxSize conformanceTest 30)

pure $ makeTestCase genesisTest

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't understand what's happening here. I think you can just expand out the shrink index.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We store the block tree and point schedule in the json, but the full GenesisTest has a bunch of other parameters that aren't included. I think the two options are to either store the extra data and build the genesis test, or store the seed and regenerate it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In that case I really don't understand what's happening. Please write a big comment describing the situation.

@isovector

Copy link
Copy Markdown
Member

Does this actually parse any test files? Would be nice instead of hardcoding in a call to genchains.

@nbloomf

nbloomf commented Mar 20, 2026

Copy link
Copy Markdown
Member Author

Does this actually parse any test files? Would be nice instead of hardcoding in a call to genchains.

Yes; getInputTestCase reads/decodes the test case and is used in main. I touched on this in another reply but the reason why genChains makes an appearance is that GenesisTest has more stuff in it than the blockchain and point schedule, and to ensure the shrunk test case is correct (and because who knows how all those parameters affect test case generation) this deterministically regenerates it just like the tests do. I figure it's less fragile to recreate from the seed and test key than from 10 genesis test parameters, although that is arguable.

@ninioArtillero ninioArtillero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is coming nicely! Left some comments.

shrinkGenesisTest _ = []

data TestResult = TestSuccess | TestFailure deriving (Eq, Ord, Show, Enum, Bounded)
data TestResult

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think now that we are storing the seed in a test failure a docstring is called for

let isGlobalSuccess =
testRes == TestSuccess &&
(isNothing inputIndex || inputIndex == Just mempty)
testVersion = Serialize.TestVersion 1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wonderig, you mean adding a TestSuiteData field for it?

In general, how should we go about when the work depends on upstream changes? My take would be to just make then in a new branch on the other package and work it out locally, but then the question remains of how reviewers could attempt to compile. For example we could change the source-repository-package to point to the corresponding revision. Or just trust the submitter managed to compile locally and the changes the PR depends on will eventually land.

, errors
, mtl
, optparse-applicative
, ouroboros-consensus-cardano

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm in favor of keeping redundant imports in general... just for convenience. This could easily be cleaned-up when editing for upstream submission.

let bytes = encodePretty testCase
writeOutputTestCase outputPath (TestCase reifiedTestCase testCase _) = do
let
Serialize.ReifiedTestCase {..} = reifiedTestCase

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Avoid using RecordWildCards per the style guide (Damian also explicitly asked for this on our upstream PR). NamedFieldPuns is kosher BTW.

Comment thread cardano-node/app/conformance-test-viewer.hs Outdated
import System.Environment (getArgs)
import System.IO (hPutStr, hPutStrLn, stderr)
import Test.Consensus.Genesis.Setup (ConformanceTest(..))
import Test.Consensus.Genesis.Tests (GenesisTestKey, testSuite)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Just flagging that GenesisTestKey does not contain all the tests: we are missing the SmokeTestKey. This is not relevant to this PR though.

As a note, we are still to define another Key type to gather all keys.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

BTW, now such key is found under TestSuite.All

, optMode :: Mode
} deriving (Eq, Show)

data Mode

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please add some documentation to this and the following type. Alternatively, in this case, it might suffice to give a more specific name.

Comment thread cardano-node/app/conformance-test-viewer.hs Outdated
@ninioArtillero
ninioArtillero force-pushed the ninioArtillero/list-classes branch 2 times, most recently from 0abdc51 to 7135d6f Compare March 24, 2026 18:59
@nbloomf
nbloomf force-pushed the add/parse-test-cases-in-viewer branch from 6a04c07 to a6805d5 Compare March 30, 2026 15:30
@nbloomf
nbloomf changed the base branch from ninioArtillero/list-classes to conformance-testing March 30, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants