symbiote-0.0.0.1: Data serialization, communication, and operation verification implementation

Copyright(c) 2019 Athan Clark
LicenseBSD-3-Style
Maintainerathan.clark@gmail.com
PortabilityGHC
Safe HaskellNone
LanguageHaskell2010

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 where
  data Operation TypeA
    = F
    | G TypeA
  perform op x = case op of
    F -> f x
    G y -> g y x

You'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 fail "Not F"
      getG = do
        x <- json .: "g"
        pure (G x)

Next, let's make TypeA an instance of Symbiote:

instance Symbiote TypeA Value where
  encode = Aeson.toJSON
  decode = 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

Documentation

class SymbioteOperation a where Source #

A type-level relation between a type and appropriate, testable operations on that type.

Associated Types

data Operation a :: * Source #

Methods

perform :: Operation a -> a -> a Source #

class SymbioteOperation a => Symbiote a s where Source #

A serialization format for a particular type, and serialized data type.

Methods

encode :: a -> s Source #

decode :: s -> Maybe a Source #

encodeOp :: Operation a -> s Source #

decodeOp :: s -> Maybe (Operation a) Source #

newtype EitherOp a Source #

The most trivial serialization medium for any a.

Constructors

EitherOp (Either a (Operation a)) 
Instances
SymbioteOperation a => Symbiote a (EitherOp a) Source # 
Instance details

Defined in Test.Serialization.Symbiote

(Eq a, Eq (Operation a)) => Eq (EitherOp a) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

(==) :: EitherOp a -> EitherOp a -> Bool #

(/=) :: EitherOp a -> EitherOp a -> Bool #

(Show a, Show (Operation a)) => Show (EitherOp a) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

showsPrec :: Int -> EitherOp a -> ShowS #

show :: EitherOp a -> String #

showList :: [EitherOp a] -> ShowS #

data Topic Source #

Unique name of a type, for a suite of tests

Instances
Eq Topic Source # 
Instance details

Defined in Test.Serialization.Symbiote.Core

Methods

(==) :: Topic -> Topic -> Bool #

(/=) :: Topic -> Topic -> Bool #

Ord Topic Source # 
Instance details

Defined in Test.Serialization.Symbiote.Core

Methods

compare :: Topic -> Topic -> Ordering #

(<) :: Topic -> Topic -> Bool #

(<=) :: Topic -> Topic -> Bool #

(>) :: Topic -> Topic -> Bool #

(>=) :: Topic -> Topic -> Bool #

max :: Topic -> Topic -> Topic #

min :: Topic -> Topic -> Topic #

Show Topic Source # 
Instance details

Defined in Test.Serialization.Symbiote.Core

Methods

showsPrec :: Int -> Topic -> ShowS #

show :: Topic -> String #

showList :: [Topic] -> ShowS #

IsString Topic Source # 
Instance details

Defined in Test.Serialization.Symbiote.Core

Methods

fromString :: String -> Topic #

register Source #

Arguments

:: Arbitrary a 
=> Arbitrary (Operation a) 
=> Symbiote a s 
=> Eq a 
=> MonadIO m 
=> Topic 
-> Int

Max size

-> Proxy a 
-> SymbioteT s m () 

Register a topic in the test suite

firstPeer Source #

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

secondPeer Source #

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

data First s Source #

Messages sent by the first peer

Instances
Eq s => Eq (First s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

(==) :: First s -> First s -> Bool #

(/=) :: First s -> First s -> Bool #

Show s => Show (First s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

showsPrec :: Int -> First s -> ShowS #

show :: First s -> String #

showList :: [First s] -> ShowS #

data Second s Source #

Messages sent by the second peer

Instances
Eq s => Eq (Second s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

(==) :: Second s -> Second s -> Bool #

(/=) :: Second s -> Second s -> Bool #

Show s => Show (Second s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

showsPrec :: Int -> Second s -> ShowS #

show :: Second s -> String #

showList :: [Second s] -> ShowS #

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
Eq s => Eq (Generating s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

(==) :: Generating s -> Generating s -> Bool #

(/=) :: Generating s -> Generating s -> Bool #

Show s => Show (Generating s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

data Operating s Source #

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 # 
Instance details

Defined in Test.Serialization.Symbiote

Methods

(==) :: Operating s -> Operating s -> Bool #

(/=) :: Operating s -> Operating s -> Bool #

Show s => Show (Operating s) Source # 
Instance details

Defined in Test.Serialization.Symbiote

defaultSuccess :: Topic -> IO () Source #

Via putStrLn

defaultFailure :: Show (them s) => Show s => Failure them s -> IO () Source #

Via putStrLn

defaultProgress :: Topic -> Float -> IO () Source #

Via putStrLn

nullProgress :: Topic -> Float -> IO () 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.