diff --git a/Builds/VisualStudio2012/RippleD.vcxproj b/Builds/VisualStudio2012/RippleD.vcxproj index cd304a991..a553ceb50 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj +++ b/Builds/VisualStudio2012/RippleD.vcxproj @@ -778,6 +778,12 @@ true + + true + true + true + true + true true @@ -1501,6 +1507,16 @@ + + + + + + + + + + diff --git a/Builds/VisualStudio2012/RippleD.vcxproj.filters b/Builds/VisualStudio2012/RippleD.vcxproj.filters index c4b8552b5..c9578065f 100644 --- a/Builds/VisualStudio2012/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2012/RippleD.vcxproj.filters @@ -151,6 +151,9 @@ {1d9bc26d-d76e-4fd4-a737-b968e31e614b} + + {129c51dc-e885-4023-999b-e133d800fb80} + @@ -864,6 +867,9 @@ [1] Ripple\ripple_core\validator + + [1] Ripple\ripple_core\test + @@ -1685,6 +1691,36 @@ [1] Ripple\ripple_core\validator + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + + + [1] Ripple\ripple_core\test + diff --git a/modules/ripple_core/ripple_core.cpp b/modules/ripple_core/ripple_core.cpp index e01ad2999..3299d9d62 100644 --- a/modules/ripple_core/ripple_core.cpp +++ b/modules/ripple_core/ripple_core.cpp @@ -13,6 +13,7 @@ #include "beast/modules/beast_core/system/BeforeBoost.h" // must come first #include #include +#include // For NodeStore backends #include "beast/modules/beast_db/beast_db.h" @@ -46,6 +47,18 @@ namespace ripple #include "node/NodeStore.cpp" #include "node/NodeObject.cpp" +# include "test/Results.h" +# include "test/SimplePayload.h" +# include "test/MessageType.h" +# include "test/ConnectionType.h" +# include "test/PeerType.h" +# include "test/NetworkType.h" +# include "test/StateBase.h" +# include "test/PeerLogicBase.h" +# include "test/InitPolicy.h" +# include "test/ConfigType.h" +#include "test/TestOverlay.cpp" + # include "validator/Validator.h" #include "validator/Validator.cpp" # include "validator/ValidatorSourceStrings.h" diff --git a/modules/ripple_core/test/ConfigType.h b/modules/ripple_core/test/ConfigType.h new file mode 100644 index 000000000..088e9b232 --- /dev/null +++ b/modules/ripple_core/test/ConfigType.h @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_CONFIGTYPE_H_INCLUDED +#define RIPPLE_CORE_TEST_CONFIGTYPE_H_INCLUDED + +/** A simulated peer to peer network for unit tests. */ +namespace TestOverlay +{ + +/** Combines Params with standard Config requirements for test objects. */ +template < + class Params, + template class StateType = StateBase, + template class PeerLogicType = PeerLogicBase +> +class ConfigType +{ +public: + // These defaults can be overridden in + // Params simply by adding declarations to it. + + static int64 const randomSeedValue = 42; + + typedef std::size_t SizeType; + + typedef SimplePayload Payload; + + typedef StateType State; + typedef MessageType Message; + typedef NetworkType Network; + + typedef PeerType Peer; + typedef PeerLogicType PeerLogic; + + typedef NoInitPolicy InitPolicy; +}; + +} + +#endif diff --git a/modules/ripple_core/test/ConnectionType.h b/modules/ripple_core/test/ConnectionType.h new file mode 100644 index 000000000..78156a151 --- /dev/null +++ b/modules/ripple_core/test/ConnectionType.h @@ -0,0 +1,173 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_CONNECTIONTYPE_H_INCLUDED +#define RIPPLE_CORE_TEST_CONNECTIONTYPE_H_INCLUDED + +namespace TestOverlay +{ + +/** A connection between two nodes. */ +template +class ConnectionType : public Config +{ +public: + using typename Config::Peer; + using typename Config::Message; + + typedef std::vector Messages; + typedef typename Config::State::UniqueID UniqueID; + typedef boost::unordered_set MessageTable; + + /** Create the 'no connection' object. */ + ConnectionType () + : m_peer (nullptr) + { + } + + ConnectionType (Peer& peer, bool inbound) + : m_peer (&peer) + , m_inbound (inbound) + { + } + + ConnectionType (ConnectionType const& other) + : m_peer (other.m_peer) + , m_inbound (other.m_inbound) + { + } + + ConnectionType& operator= (ConnectionType const& other) + { + m_peer = other.m_peer; + m_inbound = other.m_inbound; + } + + /** Returns `true` if there is no connection. */ + bool empty () const + { + return m_peer == nullptr; + } + + /** Returns `true` if this is an inbound connection. + If there is no connection, the return value is undefined. + */ + bool inbound () const + { + return m_inbound; + } + + /** Returns the peer on the other end. + If there is no connection, the return value is undefined. + */ + /** @{ */ + Peer& peer () + { + return *m_peer; + } + + Peer const& peer () const + { + return *m_peer; + } + /** @} */ + + /** Returns a container with the current step's incoming messages. */ + /** @{ */ + Messages& messages () + { + return m_messages; + } + + Messages const& messages () const + { + return m_messages; + } + /** @} */ + + /** Returns a container with the next step's incoming messages. + During each step, peers process the current step's message + list, but post new messages to the pending messages list. + This way, new messages will always process in the next step + and not the current one. + */ + /** @{ */ + Messages& pending () + { + return m_pending; + } + + Messages const& pending () const + { + return m_pending; + } + /** @} */ + + + //-------------------------------------------------------------------------- + + /** A UnaryPredicate that always returns true. */ + class Any + { + public: + bool operator() (ConnectionType const&) const + { + return true; + } + }; + + //-------------------------------------------------------------------------- + + /** A UnaryPredicate that returns `true` if the peer matches. */ + class IsPeer + { + public: + explicit IsPeer (Peer const& peer) + : m_peer (&peer) + { + } + + bool operator() (ConnectionType const& connection) const + { + return &connection.peer () == m_peer; + } + + private: + Peer const* m_peer; + }; + + //-------------------------------------------------------------------------- + + /** A UnaryPredicate that returns `true` if the peer does not match. */ + class IsNotPeer + { + public: + explicit IsNotPeer (Peer const& peer) + : m_peer (&peer) + { + } + + bool operator() (ConnectionType const& connection) const + { + return &connection.peer () != m_peer; + } + + private: + Peer const* m_peer; + }; + + //-------------------------------------------------------------------------- + +private: + Peer* m_peer; + bool m_inbound; + Messages m_messages; + Messages m_pending; +}; + +} + +#endif diff --git a/modules/ripple_core/test/InitPolicy.h b/modules/ripple_core/test/InitPolicy.h new file mode 100644 index 000000000..a57c57ecb --- /dev/null +++ b/modules/ripple_core/test/InitPolicy.h @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_INITPOLICY_H_INCLUDED +#define RIPPLE_CORE_TEST_INITPOLICY_H_INCLUDED + +/** A simulated peer to peer network for unit tests. */ +namespace TestOverlay +{ + +//------------------------------------------------------------------------------ +// +// InitPolicy +// +// This is called during construction to form the network. +// + +/** InitPolicy which does nothing. */ +class NoInitPolicy +{ +public: + template + void operator() (Network& network) + { + } +}; + +//------------------------------------------------------------------------------ + +/** Init policy for a pre-built connected network. */ +template +class PremadeInitPolicy +{ +public: + static int const numberOfPeers = NumberOfPeers; + static int const outgoingConnectionsPerPeer = OutgoingConnectionsPerPeer; + + template + void operator() (Network& network) + { + typedef typename Network::Peer Peer; + typedef typename Network::Peers Peers; + typedef typename Network::Config Config; + typedef typename Config::SizeType SizeType; + + for (SizeType i = 0; i < numberOfPeers; ++i) + network.createPeer (); + + Peers& peers (network.peers ()); + for (SizeType i = 0; i < numberOfPeers; ++i) + { + Peer& peer (*peers [i]); + for (SizeType j = 0; j < outgoingConnectionsPerPeer; ++j) + { + for (;;) + { + SizeType k (network.state ().random ().nextInt (numberOfPeers)); + if (peer.connect_to (*peers [k])) + break; + } + } + } + } +}; + +} + +#endif diff --git a/modules/ripple_core/test/MessageType.h b/modules/ripple_core/test/MessageType.h new file mode 100644 index 000000000..c724c5d6d --- /dev/null +++ b/modules/ripple_core/test/MessageType.h @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_MESSAGETYPE_H_INCLUDED +#define RIPPLE_CORE_TEST_MESSAGETYPE_H_INCLUDED + +namespace TestOverlay +{ + +/** A message sent between peers. */ +template +class MessageType : public Config +{ +public: + typedef typename Config::State::UniqueID UniqueID; + using typename Config::Payload; + + MessageType () + : m_id (0) + { + } + + MessageType (UniqueID id, Payload payload) + : m_id (id) + , m_payload (payload) + { + } + + MessageType (MessageType const& other) + : m_id (other.m_id) + , m_payload (other.m_payload) + { + } + + MessageType& operator= (MessageType const& other) + { + m_id = other.m_id; + m_payload = other.m_payload; + return *this; + } + + UniqueID id () const + { + return m_id; + } + + Payload payload () const + { + return m_payload; + } + +private: + UniqueID m_id; + Payload m_payload; +}; + +} + +#endif diff --git a/modules/ripple_core/test/NetworkType.h b/modules/ripple_core/test/NetworkType.h new file mode 100644 index 000000000..a92e3b678 --- /dev/null +++ b/modules/ripple_core/test/NetworkType.h @@ -0,0 +1,130 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_NETWORKTYPE_H_INCLUDED +#define RIPPLE_CORE_TEST_NETWORKTYPE_H_INCLUDED + +namespace TestOverlay +{ + +template +class NetworkType : public ConfigParam +{ +public: + typedef ConfigParam Config; + + using typename Config::SizeType; + using typename Config::State; + using typename Config::Peer; + + typedef std::vector > Peers; + + NetworkType () + : m_steps (0) + { + typename Config::InitPolicy () (*this); + } + + /** Return the number of steps taken in the simulation. */ + SizeType steps () const + { + return m_steps; + } + + /** Return the size of the network measured in peers. */ + SizeType size () const + { + return m_peers.size (); + } + + /** Retrieve the state information associated with the Config. */ + State& state () + { + return m_state; + } + + /** Create new Peer. */ + Peer& createPeer () + { + Peer* peer (new Peer (*this)); + m_peers.push_back (peer); + return *peer; + } + + /** Retrieve the container holding the set of peers. */ + Peers& peers () + { + return m_peers; + } + + /** Run the network for 1 iteration. */ + Results step () + { + Results results; + for (typename Peers::iterator iter = m_peers.begin (); + iter!= m_peers.end (); ++iter) + (*iter)->pre_step (); + for (typename Peers::iterator iter = m_peers.begin (); + iter!= m_peers.end (); ++iter) + (*iter)->step (); + ++results.steps; + ++m_steps; + for (typename Peers::iterator iter = m_peers.begin (); + iter!= m_peers.end (); ++iter) + { + Peer& peer (**iter); + peer.post_step (); + results = results + peer.results(); + peer.results() = Results(); + } + return results; + } + + /** Run the network until a condition is met. + Requirements: + p (*this) is well-formed and returns bool. + */ + template + Results step_until (Predicate p) + { + Results results; + while (! p (*this)) + results += step (); + return results; + } + + //-------------------------------------------------------------------------- + + /** A UnaryPredicate that returns true after # steps have passed. */ + class Steps + { + public: + explicit Steps (SizeType steps) + : m_steps (steps) + { + } + + bool operator() (NetworkType const&) + { + if (m_steps == 0) + return true; + --m_steps; + return false; + } + + private: + SizeType m_steps; + }; + +private: + State m_state; + SizeType m_steps; + Peers m_peers; +}; + +} + +#endif diff --git a/modules/ripple_core/test/PeerLogicBase.h b/modules/ripple_core/test/PeerLogicBase.h new file mode 100644 index 000000000..bfed1fce8 --- /dev/null +++ b/modules/ripple_core/test/PeerLogicBase.h @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_PEERLOGICBASE_H_INCLUDED +#define RIPPLE_CORE_TEST_PEERLOGICBASE_H_INCLUDED + +namespace TestOverlay +{ + +/** Base class for all PeerLogic implementations. + This provides stubs for all necessary functions, although + they don't actually do anything. +*/ +template +class PeerLogicBase : public Config +{ +public: + typedef typename Config::Peer Peer; + typedef typename Peer::Connection Connection; + typedef typename Connection::Message Message; + + explicit PeerLogicBase (Peer& peer) + : m_peer (peer) + { + } + + /** Return the Peer associated with this logic. */ + /** @{ */ + Peer& peer () + { + return m_peer; + } + + Peer const& peer () const + { + return m_peer; + } + /** @} */ + + // Called to process a message + void receive (Connection const& c, Message const& m) + { + } + + // Called before taking a step + void pre_step () + { + } + + // Called during a step + void step () + { + } + + // Called after a step is taken + void post_step () + { + } + +private: + Peer& m_peer; +}; + +} + +#endif diff --git a/modules/ripple_core/test/PeerType.h b/modules/ripple_core/test/PeerType.h new file mode 100644 index 000000000..99023ca07 --- /dev/null +++ b/modules/ripple_core/test/PeerType.h @@ -0,0 +1,283 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_PEERTYPE_H_INCLUDED +#define RIPPLE_CORE_TEST_PEERTYPE_H_INCLUDED + +namespace TestOverlay +{ + +/** A peer in the overlay network. */ +template +class PeerType + : public Config + , public Uncopyable +{ +public: + using typename Config::Peer; + using typename Config::Payload; + using typename Config::PeerLogic; + using typename Config::Message; + using typename Config::Network; + typedef typename Config::State::UniqueID UniqueID; + typedef ConnectionType Connection; + typedef std::vector Connections; + typedef boost::unordered_set MessageTable; + + explicit PeerType (Network& network) + : m_network (network) + , m_id (network.state().nextPeerID()) + , m_logic (*this) + { + } + + /** Return the pending Results data associated with this peer. */ + /** @{ */ + Results& results () + { + return m_results; + } + + Results const& results () const + { + return m_results; + } + /** @} */ + + /** Return the unique ID associated with this peer. */ + UniqueID id () const + { + return m_id; + } + + /** Return the network this peer belongs to. */ + /** @{ */ + Network& network () + { + return m_network; + } + + Network const& network () const + { + return m_network; + } + /** @} */ + + /** Return the container holding active connections. */ + /** @{ */ + Connections& connections () + { + return m_connections; + } + + Connections const& connections () const + { + return m_connections; + } + /** @} */ + + /** Return the container holding the message ids seen by this peer. */ + /** @{ */ + MessageTable& msg_table () + { + return m_msg_table; + } + + MessageTable const& msg_table () const + { + return m_msg_table; + } + /** @} */ + + /** Establish an outgoing connection to peer. + @return `true` if the peer is not us and not connected already. + */ + bool connect_to (Peer& peer) + { + if (&peer == this) + return false; + typename Connections::iterator const iter (std::find_if ( + connections().begin(), connections().end (), + typename Connection::IsPeer (peer))); + if (iter != connections().end()) + return false; + check_postcondition (std::find_if (peer.connections().begin(), + peer.connections().end(), + typename Connection::IsPeer (*this)) + == peer.connections().end ()); + connections().push_back (Connection (peer, false)); + peer.connections().push_back (Connection (*this, true)); + return true; + } + + /** Disconnect from a peer. + @return `true` if the peer was found and disconnected. + */ + bool disconnect (Peer& peer) + { + if (&peer == this) + return false; + typename Connections::iterator const iter1 (std::find_if ( + connections().begin(), connections().end (), + typename Connection::IsPeer (peer))); + if (iter1 == connections().end()) + return false; + typename Connections::iterator const iter2 (std::find_if ( + peer.connections().begin(), peer.connections().end (), + typename Connection::IsPeer (*this))); + check_postcondition (iter2 != peer.connections().end()); + connections().erase (iter1); + peer.connections().erase (iter2); + return true; + } + + //-------------------------------------------------------------------------- + + /** Send a new message to a specific connection. + A new message with an unused id is created with the given payload. + */ + void send (Peer& peer, Payload const& payload) + { + Message const m (network().state().nextMessageID(), payload); + check_postcondition (msg_table().insert (m.id()).second); + check_postcondition (send_to (peer, + Message (network().state().nextMessageID(), + payload))); + } + + /** Send a message to a specific connection. + The message already has an id and associated payload. + */ + bool send (Peer& peer, Message const& m) + { + return send_to (peer, m); + } + + /** Send a new message to all connections. + A new message with an unused id is created with the given payload. + */ + void send_all (Payload const& payload) + { + Message const m (network().state().nextMessageID(), payload); + check_postcondition (msg_table().insert (m.id()).second); + check_postcondition (send_all_if (m, + typename Connection::Any ())); + }; + + /** Send a message to all connections. + The message already has an id and associated payload. + */ + bool send_all (Message const& m) + { + return send_all_if (m, + typename Connection::Any ()); + }; + + /** Create a new message and send it to each connection that passes the predicate. + Predicate is a UnaryPredicate that takes a Connection parameter. + A new message with an unused id is created with the given payload. + */ + template + void send_all_if (Payload const& payload, Predicate p) + { + Message const m (network().state().nextMessageID(), payload); + check_postcondition (msg_table().insert (m.id()).second); + check_postcondition (send_all_if (m, p)); + } + + /** Send an existing message to all connections that pass the predicate. + @return `true` if at least one message was sent. + */ + template + bool send_all_if (Message const& m, Predicate p) + { + bool sent = false; + for (typename Connections::iterator iter (connections().begin()); + iter != connections().end(); ++iter) + if (p (*iter)) + sent = send_to (iter->peer(), m) || sent; + return sent; + } + +private: + // Low level send function, everything goes through this. + // Returns true if the message was sent. + // + bool send_to (Peer& peer, Message const& m) + { + // already seen it? + if (peer.msg_table().count(m.id()) != 0) + { + ++results().dropped; + return false; + } + typename Connections::iterator const iter (std::find_if ( + peer.connections().begin(), peer.connections().end (), + typename Connection::IsPeer (*this))); + check_postcondition (iter != peer.connections().end()); + check_postcondition (peer.msg_table().insert(m.id()).second); + iter->pending().push_back (m); + ++results().sent; + return true; + } + +public: + //-------------------------------------------------------------------------- + + /** Called once on each Peer object before every iteration. */ + void pre_step () + { + m_logic.pre_step (); + } + + /** Called once on each Peer object during every iteration. */ + void step () + { + // Call logic with current messages + for (typename Connections::iterator iter (connections().begin()); + iter != connections().end(); ++iter) + { + Connection& c (*iter); + for (typename Connection::Messages::iterator iter ( + c.messages().begin()); iter != c.messages().end(); ++iter) + { + Message const& m (*iter); + check_precondition (msg_table().count (m.id()) == 1); + m_logic.receive (c, m); + ++results().received; + } + } + + m_logic.step (); + } + + /** Called once on each Peer object after every iteration. */ + void post_step () + { + // Move pending messages to current messages + for (typename Connections::iterator iter (connections().begin()); + iter != connections().end(); ++iter) + { + Connection& c (*iter); + c.messages().clear (); + c.messages().swap (c.pending()); + } + + m_logic.post_step (); + } + +private: + Results m_results; + Network& m_network; + UniqueID const m_id; + Connections m_connections; + MessageTable m_msg_table; + PeerLogic m_logic; // must come last +}; + +} + +#endif diff --git a/modules/ripple_core/test/Results.h b/modules/ripple_core/test/Results.h new file mode 100644 index 000000000..64b4d429d --- /dev/null +++ b/modules/ripple_core/test/Results.h @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_RESULTS_H_INCLUDED +#define RIPPLE_CORE_TEST_RESULTS_H_INCLUDED + +namespace TestOverlay +{ + +/** Accumulates statistics on one or more simulation steps. */ +struct Results +{ + Results () + : steps (0) + , sent (0) + , received (0) + , dropped (0) + { + } + + Results (Results const& other) + : steps (other.steps) + , sent (other.sent) + , received (other.received) + , dropped (other.dropped) + { + } + + Results& operator= (Results const& other) + { + steps = other.steps; + sent = other.sent; + received = other.received; + dropped = other.dropped; + return *this; + } + + String toString () const + { + String s; + s = "steps(" + String::fromNumber (steps) + ")" + + ", sent(" + String::fromNumber (sent) + ")" + + ", received(" + String::fromNumber (received) + ")" + + ", dropped(" + String::fromNumber (dropped) + ")"; + return s; + } + + Results& operator+= (Results const& other) + { + steps += other.steps; + sent += other.sent; + received += other.received; + dropped += other.dropped; + return *this; + } + + Results operator+ (Results const& other) + { + Results results; + results.steps = steps + other.steps; + results.sent = sent + other.sent; + results.received = received + other.received; + results.dropped = dropped + other.dropped; + return results; + } + + int steps; + int sent; + int received; + int dropped; +}; + +} + +#endif diff --git a/modules/ripple_core/test/SimplePayload.h b/modules/ripple_core/test/SimplePayload.h new file mode 100644 index 000000000..e4148349e --- /dev/null +++ b/modules/ripple_core/test/SimplePayload.h @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_SIMPLEPAYLOAD_H_INCLUDED +#define RIPPLE_CORE_TEST_SIMPLEPAYLOAD_H_INCLUDED + +namespace TestOverlay +{ + +/** A simple message payload. */ +class SimplePayload +{ +public: + SimplePayload () + { + } + + SimplePayload (int what, String data = String::empty, int hops = 0) + : m_hops (hops) + , m_what (what) + , m_data (data) + { + } + + SimplePayload (SimplePayload const& other) + : m_hops (other.m_hops) + , m_what (other.m_what) + , m_data (other.m_data) + { + } + + SimplePayload& operator= (SimplePayload const& other) + { + m_hops = other.m_hops; + m_what = other.m_what; + m_data = other.m_data; + return *this; + } + + SimplePayload withHop () const + { + return SimplePayload (m_what, m_data, m_hops + 1); + } + + int hops () const + { + return m_hops; + } + + int what () const + { + return m_what; + } + + String data () const + { + return m_data; + } + +private: + int m_hops; + int m_what; + String m_data; +}; + +} + +#endif diff --git a/modules/ripple_core/test/StateBase.h b/modules/ripple_core/test/StateBase.h new file mode 100644 index 000000000..3fec6d9b2 --- /dev/null +++ b/modules/ripple_core/test/StateBase.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +#ifndef RIPPLE_CORE_TEST_STATEBASE_H_INCLUDED +#define RIPPLE_CORE_TEST_STATEBASE_H_INCLUDED + +namespace TestOverlay +{ + +/* Base class for state information used by test objects. */ +template +class StateBase +{ +public: + // Identifies messages and peers. + // Always starts at 1 and increases incrementally. + // + typedef uint64 UniqueID; + + StateBase () + : m_random (Params::randomSeedValue) + , m_peerID (0) + , m_messageID (0) + { + } + + Random& random () + { + return m_random; + } + + UniqueID nextPeerID () + { + return ++m_peerID; + } + + UniqueID nextMessageID () + { + return ++m_messageID; + } + +private: + Random m_random; + UniqueID m_peerID; + UniqueID m_messageID; +}; + +} + +#endif diff --git a/modules/ripple_core/test/TestOverlay.cpp b/modules/ripple_core/test/TestOverlay.cpp new file mode 100644 index 000000000..b1fa48fd1 --- /dev/null +++ b/modules/ripple_core/test/TestOverlay.cpp @@ -0,0 +1,133 @@ +//------------------------------------------------------------------------------ +/* + Copyright (c) 2011-2013, OpenCoin, Inc. +*/ +//============================================================================== + +namespace TestOverlay +{ + +class Tests : public UnitTest +{ +public: + template + class SeenState : public StateBase + { + public: + SeenState () + : m_seen (0) + { + } + + void increment () + { + ++m_seen; + } + + int seen () const + { + return m_seen; + } + + private: + int m_seen; + }; + + //-------------------------------------------------------------------------- + + template + class PeerLogic : public PeerLogicBase + { + public: + typedef PeerLogicBase Base; + typedef typename Config::Payload Payload; + typedef typename Base::Connection Connection; + typedef typename Base::Peer Peer; + typedef typename Base::Message Message; + typedef typename Config::SizeType SizeType; + + explicit PeerLogic (Peer& peer) + : PeerLogicBase (peer) + { + } + + ~PeerLogic () + { + } + + void step () + { + if (this->peer().id () == 1) + { + if (this->peer().network().steps() == 0) + { + this->peer().network().state().increment(); + this->peer().send_all (Payload (1)); + } + } + } + + void receive (Connection const& c, Message const& m) + { + if (this->peer().id () != 1) + { + this->peer().network().state().increment(); + this->peer().send_all_if (Message (m.id(), + m.payload().withHop ()), + typename Connection::IsNotPeer (c.peer())); + } + } + }; + + //-------------------------------------------------------------------------- + + struct Params : ConfigType < + Params, + SeenState, + PeerLogic + > + { + typedef PremadeInitPolicy <250, 3> InitPolicy; + }; + + typedef Params::Network Network; + + //-------------------------------------------------------------------------- + + void testCreation () + { + beginTestCase ("create"); + + Network network; + + Results result; + for (int i = 0; result.received < 249 && i < 100; ++i) + { + String s = + String ("step #") + String::fromNumber ( + network.steps()) + " "; + result += network.step (); + s << result.toString (); + logMessage (s); + } + + int const seen (network.state().seen()); + + String s = "Seen = " + String::fromNumber (seen); + logMessage (s); + pass (); + } + + void runTest () + { + testCreation (); + } + + Tests () : UnitTest ("TestOverlay", "ripple", runManual) + { + } +}; + +static Tests tests; + +}