| Copyright | (c) 2019 Athan Clark |
|---|---|
| License | BSD-3-Style |
| Maintainer | athan.clark@gmail.com |
| Portability | GHC |
| Safe Haskell | None |
| Language | Haskell2010 |
Test.Serialization.Symbiote
Description
As an example, say you have some data type TypeA, and some encoding / decoding instance with Aeson
for that data type. Now, you've also got a few functions that work with that data type - f :: TypeA -> TypeA
and g :: TypeA -> TypeA -> TypeA, and you've also taken the time to write a proper Arbitrary instance for TypeA.
Your first order of business in making TypeA a symbiote, is to first demonstrate what operations are supported by it:
instance SymbioteOperation TypeA TypeA where
data Operation TypeA
= F
| G TypeA
perform op x = case op of
F -> f x
G y -> g y xYou're also going to need to make sure your new data-family has appropriate serialization instances, as well:
instance ToJSON (Operation TypeA) where
toJSON op = case op of
F -> toJSON "f"
G x -> "g" .: x
instance FromJSON (Operation TypeA) where
parseJSON json = getF <|> getG
where
getF = do
s <- parseJSON json
if s == "f"
then pure F
else typeMismatch "Operation TypeA" json
getG = do
o <- parseJSON json
G <$> o .: "g"Next, let's make TypeA an instance of Symbiote:
instance Symbiote TypeA TypeA Value where encode = Aeson.toJSON decode = Aeson.parseMaybe Aeson.parseJSON encodeOut _ = Aeson.toJSON decodeOut _ = Aeson.parseMaybe Aeson.parseJSON encodeOp = Aeson.toJSON decodeOp = Aeson.parseMaybe Aeson.parseJSON
this instance above actually works for any type that implements ToJSON and FromJSON - there's an orphan
definition in Test.Serialization.Symbiote.Aeson.
Next, you're going to need to actually use this, by registering the type in a test suite:
myFancyTestSuite :: SymbioteT Value IO () myFancyTestSuite = register "TypeA" 100 (Proxy :: Proxy TypeA)
Lastly, you're going to need to actually run the test suite by attaching it to a network. The best way to
do that, is decide whether this peer will be the first or second peer to start the protocol, then use the
respective firstPeer and secondPeer functions - they take as arguments functions that define "how to send"
and "how to receive" messages, and likewise how to report status.
Synopsis
- class SymbioteOperation a o | a -> o where
- class SymbioteOperation a o => Symbiote a o s | a -> o where
- data SimpleSerialization a o
- = SimpleValue a
- | SimpleOutput o
- | SimpleOperation (Operation a)
- data Topic
- type SymbioteT s m = ReaderT Bool (StateT (Map Topic (ExistsSymbiote s)) m)
- register :: forall a o s m. Arbitrary a => Arbitrary (Operation a) => Symbiote a o s => Eq o => MonadIO m => Topic -> Int -> Proxy a -> SymbioteT s m ()
- firstPeer :: forall m s. MonadIO m => Show s => (First s -> m ()) -> m (Second s) -> (Topic -> m ()) -> (Failure Second s -> m ()) -> (Topic -> Float -> m ()) -> SymbioteT s m () -> m ()
- secondPeer :: forall s m. MonadIO m => Show s => (Second s -> m ()) -> m (First s) -> (Topic -> m ()) -> (Failure First s -> m ()) -> (Topic -> Float -> m ()) -> SymbioteT s m () -> m ()
- data First s
- = AvailableTopics (Map Topic Int)
- | FirstGenerating { }
- | FirstOperating { }
- data Second s
- = BadTopics (Map Topic Int)
- | Start
- | SecondOperating { }
- | SecondGenerating { }
- data Generating s
- = Generated {
- genValue :: s
- genOperation :: s
- | BadResult s
- | YourTurn
- | ImFinished
- | GeneratingNoParseOperated s
- = Generated {
- data Operating s
- data Failure them s
- = BadTopicsFailure { }
- | OutOfSyncFirst (First s)
- | OutOfSyncSecond (Second s)
- | TopicNonexistent Topic
- | WrongTopic { }
- | CantParseOperated Topic s
- | CantParseGeneratedValue Topic s
- | CantParseGeneratedOperation Topic s
- | CantParseLocalValue Topic s
- | CantParseLocalOperation Topic s
- | BadOperating Topic (Operating s)
- | BadGenerating Topic (Generating s)
- | BadThem Topic (them s)
- | SafeFailure {
- safeFailureTopic :: Topic
- safeFailureExpected :: s
- safeFailureGot :: s
- defaultSuccess :: Topic -> IO ()
- defaultFailure :: Show (them s) => Show s => Failure them s -> IO ()
- defaultProgress :: Topic -> Float -> IO ()
- nullProgress :: Applicative m => Topic -> Float -> m ()
- simpleTest :: MonadBaseControl IO m => MonadIO m => Show s => SymbioteT s m () -> m ()
- simpleTest' :: MonadBaseControl IO m => MonadIO m => Show s => (Topic -> m ()) -> (Failure Second s -> m ()) -> (Failure First s -> m ()) -> (Topic -> Float -> m ()) -> SymbioteT s m () -> m ()
Documentation
class SymbioteOperation a o | a -> o where Source #
A type-level relation between a type and appropriate, testable operations on that type.
Instances
class SymbioteOperation a o => Symbiote a o s | a -> o where Source #
A serialization format for a particular type, and serialized data type.
Methods
decode :: s -> Maybe a Source #
encodeOut :: Proxy a -> o -> s Source #
decodeOut :: Proxy a -> s -> Maybe o Source #
Instances
| (Serialize a, Serialize o, Serialize (Operation a), SymbioteOperation a o) => Symbiote a o ByteString Source # | |
Defined in Test.Serialization.Symbiote.Cereal.Lazy | |
| (Serialize a, Serialize o, Serialize (Operation a), SymbioteOperation a o) => Symbiote a o ByteString Source # | |
Defined in Test.Serialization.Symbiote.Cereal | |
| (ToJSON a, FromJSON a, ToJSON o, FromJSON o, ToJSON (Operation a), FromJSON (Operation a), SymbioteOperation a o) => Symbiote a o Value Source # | |
| SymbioteOperation a o => Symbiote a o (SimpleSerialization a o) Source # | |
Defined in Test.Serialization.Symbiote Methods encode :: a -> SimpleSerialization a o Source # decode :: SimpleSerialization a o -> Maybe a Source # encodeOut :: Proxy a -> o -> SimpleSerialization a o Source # decodeOut :: Proxy a -> SimpleSerialization a o -> Maybe o Source # encodeOp :: Operation a -> SimpleSerialization a o Source # decodeOp :: SimpleSerialization a o -> Maybe (Operation a) Source # | |
data SimpleSerialization a o Source #
The most trivial serialization medium for any a and o.
Constructors
| SimpleValue a | |
| SimpleOutput o | |
| SimpleOperation (Operation a) |
Instances
Unique name of a type, for a suite of tests
Instances
| Eq Topic Source # | |
| Ord Topic Source # | |
| Show Topic Source # | |
| IsString Topic Source # | |
Defined in Test.Serialization.Symbiote.Core Methods fromString :: String -> Topic # | |
| Arbitrary Topic Source # | |
| ToJSON Topic Source # | |
Defined in Test.Serialization.Symbiote.Core | |
| ToJSONKey Topic Source # | |
Defined in Test.Serialization.Symbiote.Core | |
| FromJSON Topic Source # | |
| FromJSONKey Topic Source # | |
Defined in Test.Serialization.Symbiote.Core | |
| Serialize Topic Source # | |
Arguments
| :: Arbitrary a | |
| => Arbitrary (Operation a) | |
| => Symbiote a o s | |
| => Eq o | |
| => MonadIO m | |
| => Topic | |
| -> Int | Max size |
| -> Proxy a | Reference to datatype |
| -> SymbioteT s m () |
Register a topic in the test suite
Arguments
| :: MonadIO m | |
| => Show s | |
| => (First s -> m ()) | Encode and send first messages |
| -> m (Second s) | Receive and decode second messages |
| -> (Topic -> m ()) | Report when Successful |
| -> (Failure Second s -> m ()) | Report when Failed |
| -> (Topic -> Float -> m ()) | Report on Progress |
| -> SymbioteT s m () | |
| -> m () |
Run the test suite as the first peer
Arguments
| :: MonadIO m | |
| => Show s | |
| => (Second s -> m ()) | Encode and send second messages |
| -> m (First s) | Receive and decode first messages |
| -> (Topic -> m ()) | Report when Successful |
| -> (Failure First s -> m ()) | Report when Failed |
| -> (Topic -> Float -> m ()) | Report on Progress |
| -> SymbioteT s m () | |
| -> m () |
Run the test suite as the second peer
Messages sent by the first peer
Constructors
| AvailableTopics (Map Topic Int) | Mapping of topics to their gen size |
| FirstGenerating | |
Fields | |
| FirstOperating | |
Fields | |
Instances
Messages sent by the second peer
Constructors
| BadTopics (Map Topic Int) | |
| Start | |
| SecondOperating | |
Fields | |
| SecondGenerating | |
Fields | |
Instances
data Generating s Source #
Messages sent by a peer during their generating phase
Constructors
| Generated | |
Fields
| |
| BadResult s | Expected value |
| YourTurn | |
| ImFinished | |
| GeneratingNoParseOperated s | |
Instances
Messages sent by a peer during their operating phase
Constructors
| Operated s | Serialized value after operation |
| OperatingNoParseValue s | |
| OperatingNoParseOperation s |
Instances
| Eq s => Eq (Operating s) Source # | |
| Show s => Show (Operating s) Source # | |
| Generic (Operating s) Source # | |
| Arbitrary s => Arbitrary (Operating s) Source # | |
| ToJSON s => ToJSON (Operating s) Source # | |
Defined in Test.Serialization.Symbiote | |
| FromJSON s => FromJSON (Operating s) Source # | |
| Serialize s => Serialize (Operating s) Source # | |
| type Rep (Operating s) Source # | |
Defined in Test.Serialization.Symbiote type Rep (Operating s) = D1 (MetaData "Operating" "Test.Serialization.Symbiote" "symbiote-0.0.1-EtovEXTKXxf7IiyzPiGrIq" False) (C1 (MetaCons "Operated" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 s)) :+: (C1 (MetaCons "OperatingNoParseValue" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 s)) :+: C1 (MetaCons "OperatingNoParseOperation" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 s)))) | |
Constructors
| BadTopicsFailure | |
Fields
| |
| OutOfSyncFirst (First s) | |
| OutOfSyncSecond (Second s) | |
| TopicNonexistent Topic | |
| WrongTopic | |
Fields | |
| CantParseOperated Topic s | |
| CantParseGeneratedValue Topic s | |
| CantParseGeneratedOperation Topic s | |
| CantParseLocalValue Topic s | |
| CantParseLocalOperation Topic s | |
| BadOperating Topic (Operating s) | |
| BadGenerating Topic (Generating s) | |
| BadThem Topic (them s) | |
| SafeFailure | |
Fields
| |
defaultSuccess :: Topic -> IO () Source #
Via putStrLn
nullProgress :: Applicative m => Topic -> Float -> m () Source #
Do nothing
simpleTest :: MonadBaseControl IO m => MonadIO m => Show s => SymbioteT s m () -> m () Source #
Prints to stdout and uses a local channel for a sanity-check - doesn't serialize.
Arguments
| :: MonadBaseControl IO m | |
| => MonadIO m | |
| => Show s | |
| => (Topic -> m ()) | report topic success |
| -> (Failure Second s -> m ()) | report topic failure from first (sees second) |
| -> (Failure First s -> m ()) | report topic failure from second (sees first) |
| -> (Topic -> Float -> m ()) | report topic progress |
| -> SymbioteT s m () | |
| -> m () |