7#ifndef _UTILS_ASYNC_IF_TEST_HELPER_HXX_
8#define _UTILS_ASYNC_IF_TEST_HELPER_HXX_
14#include "openlcb/NodeInitializeFlow.hxx"
20using ::testing::AtLeast;
21using ::testing::AtMost;
23using ::testing::Field;
24using ::testing::Invoke;
25using ::testing::IsNull;
27using ::testing::NiceMock;
28using ::testing::NotNull;
29using ::testing::Pointee;
30using ::testing::Return;
31using ::testing::StrCaseEq;
32using ::testing::StrictMock;
33using ::testing::WithArg;
34using ::testing::SaveArg;
62 MOCK_METHOD1(mwrite,
void(
const string &s));
77static void print_packet(
const string &pkt)
79 fprintf(stderr,
"%s\n", pkt.c_str());
107 gc_hub0.register_port(&
canBus_);
112 gc_hub0.unregister_port(&
canBus_);
115 gc_hub0.unregister_port(
printer_.get());
134 void usleep(
unsigned long usecs) {
135 long long deadline = usecs;
139 os_emscripten_yield();
152#define expect_packet(gc_packet) \
153 EXPECT_CALL(canBus_, mwrite(StrCaseEq(gc_packet)))
170#define expect_packet_and_send_response(gc_packet, resp_packet) \
171 EXPECT_CALL(canBus_, mwrite(StrCaseEq(gc_packet))) \
172 .WillOnce(::testing::InvokeWithoutArgs( \
173 [this]() { send_packet(resp_packet); }))
183 EXPECT_CALL(
canBus_, mwrite(_)).Times(AtLeast(0));
192 HASSERT(!
printer_ &&
"cannot have more than one print_all_packets call");
193 NiceMock<MockSend> *m =
new NiceMock<MockSend>();
194 EXPECT_CALL(*m, mwrite(_)).Times(AtLeast(0)).WillRepeatedly(
195 WithArg<0>(Invoke(print_packet)));
196 gc_hub0.register_port(m);
213 packet->
data()->assign(gc_packet);
215 gc_hub0.send(packet);
228 Mock::VerifyAndClear(&
canBus_);
230 EXPECT_CALL(
canBus_, mwrite(_)).Times(0);
246#define send_packet_and_expect_response(pkt, resp) \
249 expect_packet(resp); \
250 send_packet_and_flush_expect(pkt); \
262 Mock::VerifyAndClear(&
canBus_);
292 delete g_gc_adapter1;
295#define expect_packet1(gc_packet) \
296 EXPECT_CALL(canBus1_, mwrite(StrCaseEq(gc_packet)))
305 packet->
data()->assign(gc_packet);
307 gc_hub1.send(packet);
317static const NodeID TEST_NODE_ID = 0x02010d000003ULL;
324 , gatewayNodeID_(gateway_node_id)
330 ownedFlows_.emplace_back(e);
335 remove_local_node_from_map(node);
340 if (expected.
id && actual.
id)
342 return expected.
id == actual.
id;
345 LOG(
VERBOSE,
"Cannot reconcile expected and actual NodeHandles for "
346 "equality testing.");
352 return gatewayNodeID_;
356 std::vector<std::unique_ptr<Destructable>> ownedFlows_;
370 static int local_alias_cache_size;
371 static int local_node_count;
372 static int remote_alias_cache_size;
377 ifCan_.reset(
new IfCan(&
g_executor, &can_hub0, local_alias_cache_size, remote_alias_cache_size, local_node_count));
378 run_x([
this]() {
ifCan_->local_aliases()->add(TEST_NODE_ID, 0x22A); });
379 ifCan_->set_alias_allocator(
390 ifCan_->alias_allocator()->TEST_finish_pending_allocation();
396 friend class ::TrainTestHelper;
402 inject_allocated_alias(0x33A);
407 void inject_allocated_alias(
NodeAlias alias)
409 if (!
ifCan_->alias_allocator()) {
410 ifCan_->set_alias_allocator(
413 run_x([
this, alias]() {
414 ifCan_->alias_allocator()->TEST_add_allocated_alias(alias);
418 void expect_next_alias_allocation(
NodeAlias a = 0)
427 EXPECT_CALL(
canBus_, mwrite(StringPrintf(
":X17020%03XN;", a)))
429 .RetiresOnSaturation();
430 EXPECT_CALL(
canBus_, mwrite(StringPrintf(
":X1610D%03XN;", a)))
432 .RetiresOnSaturation();
433 EXPECT_CALL(
canBus_, mwrite(StringPrintf(
":X15000%03XN;", a)))
435 .RetiresOnSaturation();
436 EXPECT_CALL(
canBus_, mwrite(StringPrintf(
":X14003%03XN;", a)))
438 .RetiresOnSaturation();
440 EXPECT_CALL(
canBus_, mwrite(StringPrintf(
":X10700%03XN;", a)))
442 .RetiresOnSaturation();
452 void wait_for_notification()
471int AsyncIfTest::local_alias_cache_size = 10;
472int AsyncIfTest::local_node_count = 9;
473int AsyncIfTest::remote_alias_cache_size = 10;
480 : eventService_(
ifCan_.get())
482 EXPECT_CALL(
canBus_, mwrite(
":X1910022AN02010D000003;")).Times(1);
484 node_ = ownedNode_.get();
485 ifCan_->add_addressed_message_support();
487 Mock::VerifyAndClear(&
canBus_);
493 wait_for_event_thread();
496 void wait_for_event_thread()
498 while (EventService::instance->event_processing_pending())
501 os_emscripten_yield();
510 std::unique_ptr<DefaultNode> ownedNode_;
521 MOCK_METHOD2(handle_message,
522 void(
GenMessage *message,
unsigned priority));
525 handle_message(message->data(), priority);
534 uint64_t value = htobe64(
id);
537 if (memcmp(&value, arg.data(), 8))
554 uint64_t value = htobe64(
id);
555 if (arg->used() != 6)
557 const uint8_t *expected =
reinterpret_cast<const uint8_t *
>(&value) + 2;
558 const uint8_t *actual =
static_cast<const uint8_t *
>(arg->start());
559 if (memcmp(expected, actual, 6))
561 for (
int i = 0; i < 6; ++i)
563 if (expected[i] != actual[i])
565 LOG(
INFO,
"mismatch at position %d, expected %02x actual %02x",
566 i, expected[i], actual[i]);
577 uint64_t value = htobe64(
id);
580 const uint8_t *expected =
reinterpret_cast<const uint8_t *
>(&value) + 2;
581 const uint8_t *actual =
reinterpret_cast<const uint8_t *
>(arg.data());
582 if (memcmp(expected, actual, 6))
584 for (
int i = 0; i < 6; ++i)
586 if (expected[i] != actual[i])
588 LOG(
INFO,
"mismatch at position %d, expected %02x actual %02x",
589 i, expected[i], actual[i]);
DynamicPool * mainBufferPool
main buffer pool instance
StateFlow< Buffer< HubData >, QList< 1 > > HubPort
Base class for a port to an ascii hub that is implemented as a stateflow.
Test fixture base class for a second CAN-bus.
static void TearDownTestCase()
De-Initializes test case with CAN1.
void send_packet1(const string &gc_packet)
Send a gridconnect packet to the second CAN port.
static void SetUpTestCase()
Initializes test case with CAN1.
StrictMock< MockSend > canBus1_
Helper object for setting expectations on the packets sent on the bus.
Test fixture base class for tests that need a CAN-based interface(but not necessary specific to OpenL...
std::unique_ptr< HubPort > printer_
Object for debug-printing every packet (if requested).
void expect_any_packet()
Ignores all produced packets.
void twait()
Delays the current thread until all asynchronous processing and all pending timers have completed.
void send_packet_and_flush_expect(const string &pkt)
Sends a packet to the canbus, waits for the executor to clear, and then verifies all previous expecta...
static void TearDownTestCase()
De-Initializes test case with CAN0.
static void SetUpTestCase()
Initializes test case with CAN0.
void print_all_packets()
Prints all packets sent to the canbus until the end of the current test function.
NiceMock< MockSend > canBus_
Helper object for setting expectations on the packets sent on the bus.
void clear_expect(bool strict=false)
Clears all existing expectations on the CAN-bus packets.
void send_packet(const string &gc_packet)
Injects a packet to the interface.
void wait()
Delays the current thread until we are certain that all asynchrnous processing has completed.
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.
T * data()
get a pointer to the start of the data.
An object that can be scheduled on an executor to run.
Publicly visible API for the gridconnect-to-CAN bridge.
static GCAdapterBase * CreateGridConnectAdapter(HubFlow *gc_side, CanHubFlow *can_side, bool double_bytes)
This function connects an ASCII (GridConnect-format) CAN adapter to a binary CAN adapter,...
Helper class for setting expectation on the CANbus traffic in unit tests.
virtual Action entry()
Entry into the StateFlow activity.
An object that can schedule itself on an executor to run.
virtual void notify()=0
Generic callback.
void alloc(Buffer< BufferType > **result, Executable *flow=NULL)
Get a free item out of the pool.
Return type for a state flow callback.
Action release_and_exit()
Terminates the processing of the current message.
State flow with a given typed input queue.
A Notifiable for synchronously waiting for a notification.
void wait_for_notification()
Blocks the current thread until the notification is delivered.
This state flow is responsible for reserving node ID aliases.
Test fixture base class with helper methods for exercising the asynchronous interface code.
std::unique_ptr< IfCan > ifCan_
The interface under test.
AliasInfo testAlias_
Temporary object used to send aliases around in the alias allocator flow.
bool pendingAliasAllocation_
true if we have a pending async alias allocation task.
void create_allocated_alias()
Creates an alias allocator flow, and injects an already allocated alias.
NodeAlias aliasSeed_
The next alias we will make the allocator create.
Base class for test cases with one virtual node on a CANbus interface.
Trivial implementation of a virtual Node.
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Abstract class representing an OpenLCB Interface.
Performs upon-startup initialization of virtual nodes.
bool matching_node(NodeHandle expected, NodeHandle actual) override
NodeID get_default_node_id() override
void add_owned_flow(Executable *e) override
Transfers ownership of a module to the interface.
void delete_local_node(Node *node) override
Removes a local node from this interface.
Test handler for receiving incoming openlcb Message objects from a bus.
Base class for NMRAnet nodes conforming to the asynchronous interface.
#define LOG(level, message...)
Conditionally write a message to the logging output.
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
static const int INFO
Loglevel that is printed by default, reporting some status information.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
uint64_t NodeID
48-bit NMRAnet Node ID type
MATCHER_P(IsBufferValue, id, "")
GoogleMock matcher on a Payload being equal to a given 64-bit value in network byte order.
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
long long os_get_time_monotonic(void)
Get the monotonic time since the system started.
Information we know locally about an NMRAnet CAN alias.
This class is used in the dispatching of incoming or outgoing NMRAnet messages to the message handler...
Container of both a NodeID and NodeAlias.
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.
Executor< 1 > g_executor
Global executor thread for tests.
void run_x(std::function< void()> fn)
Synchronously runs a function in the main executor.
void wait_for_main_timers()
Delays the current thread until all asynchronous processing and all pending timers have completed.