Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
if_tcp_test_helper.hxx
1#include <unistd.h>
2
3#include "openlcb/IfTcp.hxx"
7#include "utils/FdUtils.hxx"
8#include "utils/HubDeviceSelect.hxx"
9#include "utils/async_if_test_helper.hxx"
11
12using ::testing::_;
13using ::testing::SaveArg;
14using ::testing::StrictMock;
15
16namespace openlcb
17{
18
20{
21public:
22 MOCK_METHOD2(send, void(const string &message, unsigned priority));
23
24 void send(Buffer<HubData> *b, unsigned priority) override
25 {
26 {
27 GenMessage actual;
28 actual.clear();
29 EXPECT_TRUE(TcpDefs::parse_tcp_message(*b->data(), &actual));
30 LOG(INFO,
31 "[sent] 0x%012" PRIx64 " -> %012" PRIx64 " MTI %03x payload %s",
32 actual.src.id, actual.dst.id, actual.mti,
33 string_to_hex(actual.payload).c_str());
34 }
35 send(*b->data(), priority);
36 b->unref();
37 }
38};
39
40class TcpIfTest : public ::testing::Test
41{
42protected:
43 static int local_node_count;
44
45 TcpIfTest()
46 {
47 device_.register_port(&listenPort_);
48 }
49
51 {
52 device_.unregister_port(&listenPort_);
53 wait();
54 }
55
56 void wait()
57 {
59 }
60
63 {
64 lastPacket_.clear();
65 EXPECT_CALL(listenPort_, send(_, _)).WillOnce(SaveArg<0>(&lastPacket_));
66 }
67
70 {
71 EXPECT_CALL(listenPort_, send(_, _)).Times(AtLeast(0));
72 }
73
76 {
77 EXPECT_CALL(listenPort_, send(_, _))
78 .WillRepeatedly(WithArg<0>(
79 Invoke([this](const string &d) { allPackets_.push_back(d); })));
80 }
81
87 template <typename... Args> void generate_input_message(Args &&... args)
88 {
89 GenMessage msg;
90 msg.reset(std::forward<Args>(args)...);
91 auto *b = device_.alloc();
92 TcpDefs::render_tcp_message(msg, INPUT_GW_NODE_ID, 0x42, b->data());
93 b->data()->skipMember_ = &listenPort_;
94 device_.send(b);
95 wait();
96 }
97
102 template <typename... Args> void generate_output_message(Args &&... args)
103 {
104 generate_output_message(&ifTcp_, std::forward<Args>(args)...);
105 }
106
111 template <typename... Args>
112 void generate_output_message(IfTcp *iface, Args &&... args)
113 {
114 auto *f = iface->global_message_write_flow();
115 auto *b = f->alloc();
116 b->data()->reset(std::forward<Args>(args)...);
117 f->send(b);
118 wait();
119 }
120
121#define expect_packet_is(pkt, x...) \
122 do \
123 { \
124 GenMessage actual; \
125 actual.clear(); \
126 EXPECT_TRUE(TcpDefs::parse_tcp_message(pkt, &actual)); \
127 GenMessage expected; \
128 expected.reset(x); \
129 EXPECT_EQ(expected.mti, actual.mti); \
130 EXPECT_EQ(expected.src, actual.src); \
131 EXPECT_EQ(expected.dst.id, actual.dst.id); \
132 EXPECT_EQ(expected.payload, actual.payload); \
133 } while (0)
134
142 template <class NodeType = DefaultNode>
144 std::unique_ptr<NodeType> *p, NodeID node_id, IfTcp *iface = nullptr)
145 {
146 if (!iface)
147 {
148 iface = &ifTcp_;
149 }
151 p->reset(new DefaultNode(iface, node_id));
152 wait();
153 Mock::VerifyAndClear(&listenPort_);
155 node_id, node_id_to_buffer(node_id));
156 return p->get();
157 }
158
159 BarrierNotifiable *get_notifiable()
160 {
161 bn_.reset(&n_);
162 return &bn_;
163 }
164
165 void wait_for_notification()
166 {
168 }
169
172 HubFlow device_{&g_service};
173 ::testing::StrictMock<MockHubPort> listenPort_;
177 vector<string> allPackets_;
178
179 static constexpr NodeID TEST_NODE_ID = 0x101212231225ULL;
180 static constexpr NodeID REMOTE_NODE_ID = 0x050902030405ULL;
181 static constexpr NodeID INPUT_GW_NODE_ID = 0x101112131415ULL;
182 static constexpr NodeID GW_NODE_ID = 0x101112131415ULL;
183
184 IfTcp ifTcp_{GW_NODE_ID, &device_, local_node_count};
185};
186
187constexpr NodeID TcpIfTest::TEST_NODE_ID;
188constexpr NodeID TcpIfTest::REMOTE_NODE_ID;
189constexpr NodeID TcpIfTest::INPUT_GW_NODE_ID;
190constexpr NodeID TcpIfTest::GW_NODE_ID;
191
192int TcpIfTest::local_node_count = 9;
193
194class TcpNodeTest : public TcpIfTest
195{
196protected:
198 {
199 node_ = create_new_node(&ownedNode_, TEST_NODE_ID);
200 }
201
202 std::unique_ptr<DefaultNode> ownedNode_;
203 Node *node_;
204};
205
207{
208protected:
210 {
211#ifdef __linux__
212 // We expect write failures to occur but we want to handle them where
213 // the error occurs rather than in a SIGPIPE handler.
214 signal(SIGPIPE, SIG_IGN);
215#endif
216 }
217
219 {
220 wait();
221 }
222
223 friend struct ClientIf;
224
225 struct ClientIf
226 {
227 ClientIf(MultiTcpIfTest *parent, NodeID node_id)
228 : parent_(parent)
229 , nodeId_(node_id)
230 {
231 ERRNOCHECK("socketpair", socketpair(AF_UNIX, SOCK_STREAM, 0, fds_));
232 ERRNOCHECK("fcntl", fcntl(fds_[0], F_SETFL,
233 fcntl(fds_[0], F_GETFL, 0) | O_NONBLOCK));
234 ERRNOCHECK("fcntl", fcntl(fds_[1], F_SETFL,
235 fcntl(fds_[1], F_GETFL, 0) | O_NONBLOCK));
236 ifTcp_.add_network_fd(fds_[1]);
237 parent->ifTcp_.add_network_fd(fds_[0]);
238 }
239 int fds_[2];
240 MultiTcpIfTest *parent_;
241 NodeID nodeId_;
242 HubFlow device_{&g_service};
243 IfTcp ifTcp_{nodeId_, &device_, parent_->local_node_count};
244 };
245
246 void add_client(NodeID node_id)
247 {
248 clients_.emplace_back(new ClientIf(this, node_id));
249 }
250
251 vector<std::unique_ptr<ClientIf>> clients_;
252};
253
254} // namespace openlcb
int fcntl(int fd, int cmd,...)
Manipulate a file descriptor.
Definition Fileio.cxx:494
ssize_t send(int socket, const void *buffer, size_t length, int flags)
Initiate transmission of a message from the specified socket.
Definition Socket.cxx:239
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
void unref()
Decrement count.
Definition Buffer.hxx:675
T * data()
get a pointer to the start of the data.
Definition Buffer.hxx:215
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
void register_port(port_type *port)
Adds a new port.
Definition Hub.hxx:167
void unregister_port(port_type *port)
Removes a previously added port.
Definition Hub.hxx:174
A Notifiable for synchronously waiting for a notification.
void wait_for_notification()
Blocks the current thread until the notification is delivered.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
Trivial implementation of a virtual Node.
Network interface class for a character stream link that speaks the (point-to-point) TcpTransfer prot...
Definition IfTcp.hxx:67
void add_network_fd(int fd, Notifiable *on_error=nullptr)
Adds a network client connection to the device.
Definition IfTcp.cxx:83
MessageHandler * global_message_write_flow()
Definition If.hxx:200
void send(Buffer< HubData > *b, unsigned priority) override
Entry point to the flow.
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
static void render_tcp_message(const GenMessage &msg, NodeID gateway_node_id, long long sequence, string *tgt)
Renders a TCP message into a single buffer, ready to transmit.
Definition IfTcpImpl.hxx:56
static bool parse_tcp_message(const string &src, GenMessage *tgt)
Parses a TCP message format (from binary payload) into a general OpenLCB message.
void ignore_all_packets()
Instructs the mock device to ignore all sent packets.
void capture_next_packet()
Instructs the mock device to save the next sent packet.
void generate_output_message(Args &&... args)
Creates a GenMessage and sends it out via the IfTcp.
string lastPacket_
Stores the data from capture_next_packet.
void generate_output_message(IfTcp *iface, Args &&... args)
Creates a GenMessage and sends it out via the IfTcp.
void generate_input_message(Args &&... args)
Creates a GenMessage, renders it to binary format, and injects it as if it came as an input from the ...
void capture_all_packets()
Instructs the mock device to ignore all sent packets.
Node * create_new_node(std::unique_ptr< NodeType > *p, NodeID node_id, IfTcp *iface=nullptr)
Helper function to create a new virtual node.
vector< string > allPackets_
Stores data from capture all packets.
#define ERRNOCHECK(where, x...)
Calls the function x, and if the return value is negative, prints errno as error message to stderr an...
Definition logging.h:174
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
string node_id_to_buffer(NodeID id)
Convenience function to render a 48-bit NMRAnet node ID into a new buffer.
Definition If.cxx:45
uint64_t NodeID
48-bit NMRAnet Node ID type
#define SOCK_STREAM
TCP Socket.
Definition socket.h:45
@ MTI_INITIALIZATION_COMPLETE
initialization complete
This class is used in the dispatching of incoming or outgoing NMRAnet messages to the message handler...
Definition If.hxx:72
NodeHandle dst
Destination node.
Definition If.hxx:106
NodeHandle src
Source node.
Definition If.hxx:104
Defs::MTI mti
OpenLCB MTI of the incoming message.
Definition If.hxx:108
string payload
Data content in the message body.
Definition If.hxx:113
NodeID id
48-bit NMRAnet Node ID
void wait_for_main_executor()
Blocks the current thread until the main executor has run out of work.