Skip to content

Commit e32516a

Browse files
committed
typed-protocols:test - added prop_anncodec for ReqResp mini-protocol
1 parent 59ad700 commit e32516a

File tree

6 files changed

+94
-4
lines changed

6 files changed

+94
-4
lines changed

typed-protocols/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
The `Codec` type evolved into a new `CodecF` data type, and two type aliases
99
`AnnotatedCodec`, `Codec`.
1010
* `prop_codec` properties moved to `typed-protocols:codec-properties` library
11-
(`Network.TypedProtocol.Codec.Properties` module).
11+
(`Network.TypedProtocol.Codec.Properties` module). They now return the
12+
`QuickCheck`'s `Property` rather than a `Bool`.
1213

1314
### Non-breaking changes
1415

typed-protocols/examples/Network/TypedProtocol/ReqResp/Codec.hs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{-# LANGUAGE BlockArguments #-}
2+
13
module Network.TypedProtocol.ReqResp.Codec where
24

35
import Network.TypedProtocol.Codec
@@ -43,6 +45,68 @@ codecReqResp =
4345
where failure = CodecFailure ("unexpected server message: " ++ str)
4446

4547

48+
data WithBytes a = WithBytes {
49+
bytes :: String,
50+
message :: a
51+
}
52+
deriving (Show, Eq)
53+
54+
mkWithBytes :: Show a => a -> WithBytes a
55+
mkWithBytes message = WithBytes { bytes = show message, message }
56+
57+
58+
anncodecReqResp ::
59+
forall req resp m
60+
. (Monad m, Show req, Show resp, Read req, Read resp)
61+
=> AnnotatedCodec (ReqResp (WithBytes req) (WithBytes resp)) CodecFailure m String
62+
anncodecReqResp =
63+
Codec{encode, decode}
64+
where
65+
encode :: forall req' resp'
66+
(st :: ReqResp (WithBytes req') (WithBytes resp'))
67+
(st' :: ReqResp (WithBytes req') (WithBytes resp'))
68+
. ( Show req'
69+
, Show resp'
70+
)
71+
=> Message (ReqResp (WithBytes req') (WithBytes resp')) st st'
72+
-> String
73+
-- NOTE: we're not using 'Show (Message ...)' instance. If `req ~ Int`,
74+
-- then negative numbers will be surrounded with braces (e.g. @"(-1)"@) and
75+
-- the `Read` type class doesn't have a way to see that brackets were consumed
76+
-- from the input string.
77+
encode (MsgReq WithBytes { message })
78+
= "MsgReq " ++ show message ++ "\n"
79+
encode (MsgResp WithBytes { message })
80+
= "MsgResp " ++ show message ++ "\n"
81+
encode MsgDone
82+
= "MsgDone" ++ "\n"
83+
84+
decode :: forall req' resp' m'
85+
(st :: ReqResp (WithBytes req') (WithBytes resp'))
86+
. (Monad m', Read req', Read resp', ActiveState st)
87+
=> StateToken st
88+
-> m' (DecodeStep String CodecFailure m' (Annotator String st))
89+
decode stok =
90+
decodeTerminatedFrame '\n' $ \str trailing ->
91+
case (stok, break (==' ') str) of
92+
(SingIdle, ("MsgReq", str'))
93+
| Just req <- readMaybe @req' str'
94+
-> DecodeDone (Annotator \str'' ->
95+
let used = init $ drop 7 str'' in
96+
SomeMessage (MsgReq (WithBytes used req))) trailing
97+
(SingIdle, ("MsgDone", ""))
98+
-> DecodeDone (Annotator \_str'' -> SomeMessage MsgDone) trailing
99+
(SingBusy, ("MsgResp", str'))
100+
| Just resp <- readMaybe @resp' str'
101+
-> DecodeDone (Annotator \str'' ->
102+
let used = init $ drop 8 str'' in
103+
SomeMessage (MsgResp (WithBytes used resp))) trailing
104+
105+
(_ , _ ) -> DecodeFail failure
106+
where failure = CodecFailure ("unexpected server message: " ++ str)
107+
108+
109+
46110
codecReqRespId ::
47111
forall req resp m
48112
. (Monad m, Show req, Show resp)

typed-protocols/examples/Network/TypedProtocol/ReqResp/Type.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ instance StateTokenI StDone where
2828
instance Protocol (ReqResp req resp) where
2929

3030
data Message (ReqResp req resp) from to where
31-
MsgReq :: req -> Message (ReqResp req resp) StIdle StBusy
32-
MsgResp :: resp -> Message (ReqResp req resp) StBusy StIdle
33-
MsgDone :: Message (ReqResp req resp) StIdle StDone
31+
MsgReq :: forall req resp. req -> Message (ReqResp req resp) StIdle StBusy
32+
MsgResp :: forall req resp. resp -> Message (ReqResp req resp) StBusy StIdle
33+
MsgDone :: forall req resp. Message (ReqResp req resp) StIdle StDone
3434

3535
type StateAgency StIdle = ClientAgency
3636
type StateAgency StBusy = ServerAgency

typed-protocols/properties/Network/TypedProtocol/Codec/Properties.hs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,14 @@ prop_codec = prop_codecF const
136136

137137
-- | The 'Codec' round-trip property in a pure monad.
138138
--
139+
-- NOTE: when a message is annotated with bytes (e.g. `WithBytes` in
140+
-- `Network.TypedProtocol.ReqResp.Codec.anncodecReqResp`), this property will
141+
-- assess that the decoded bytes are equal to the supplied bytes with
142+
-- `msg :: AnyMessage ps`. It is important to use the bytes in `WithBytes` when
143+
-- encoding the `msg`. Verifying this property is especially important if the
144+
-- bytes are used to check a cryptographic signature, when the exact same bytes
145+
-- received from the network must be used.
146+
--
139147
prop_anncodec
140148
:: forall ps failure m bytes.
141149
( Monad m

typed-protocols/test/Network/TypedProtocol/ReqResp/Tests.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ tests = testGroup "Network.TypedProtocol.ReqResp"
7373
, testProperty "codec 3-splits" $ withMaxSuccess 30 prop_codec_cbor_splits3_ReqResp
7474
]
7575
]
76+
, testGroup "AnnotatedCodec"
77+
[ testProperty "codec" prop_anncodec_ReqResp
78+
]
7679
]
7780

7881

@@ -360,3 +363,16 @@ prop_codec_cbor_splits3_ReqResp msg =
360363
splits3BS
361364
CBOR.codecReqResp
362365
msg
366+
367+
368+
instance (Show a, Arbitrary a) => Arbitrary (WithBytes a) where
369+
arbitrary = mkWithBytes <$> arbitrary
370+
shrink WithBytes { message } = mkWithBytes <$> shrink message
371+
372+
prop_anncodec_ReqResp
373+
:: AnyMessage (ReqResp (WithBytes Int) (WithBytes Int))
374+
-> Property
375+
prop_anncodec_ReqResp =
376+
prop_anncodec
377+
runIdentity
378+
anncodecReqResp

typed-protocols/typed-protocols.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ library
5353

5454
library codec-properties
5555
import: GHC
56+
visibility: public
5657
exposed-modules: Network.TypedProtocol.Codec.Properties
5758
Network.TypedProtocol.Stateful.Codec.Properties
5859
build-depends: base >=4.12 && <4.22,

0 commit comments

Comments
 (0)