blob: c52e8eb253f8701df551a67088848ab8ba35b305 [file] [log] [blame]
#include "glog/logging.h"
#include "gtest/gtest.h"
#include "aos/events/ping_lib.h"
#include "aos/events/pong_lib.h"
#include "aos/events/simulated_event_loop.h"
#include "aos/json_to_flatbuffer.h"
#include "aos/testing/path.h"
namespace aos::testing {
using aos::testing::ArtifactPath;
namespace chrono = std::chrono;
class PingPongTest : public ::testing::Test {
public:
PingPongTest()
: config_(aos::configuration::ReadConfig(
ArtifactPath("aos/events/pingpong_config.json"))),
event_loop_factory_(&config_.message()),
ping_event_loop_(event_loop_factory_.MakeEventLoop("ping")),
ping_(ping_event_loop_.get()),
pong_event_loop_(event_loop_factory_.MakeEventLoop("pong")),
pong_(pong_event_loop_.get()) {}
// Config and factory.
// The factory is a factory for connected event loops. Each application needs
// a separate event loop (because you can't send a message to yourself in a
// single event loop). The created event loops can then send messages to each
// other and trigger callbacks to be called, or fetchers to receive data.
aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
SimulatedEventLoopFactory event_loop_factory_;
// Event loop and app for Ping
std::unique_ptr<EventLoop> ping_event_loop_;
Ping ping_;
// Event loop and app for Pong
std::unique_ptr<EventLoop> pong_event_loop_;
Pong pong_;
};
// Tests that we can startup at all. This confirms that the channels are all in
// the config.
TEST_F(PingPongTest, Starts) {
// RunFor lives in the factory because all the loops need to run together, and
// the factory is the only object that conceptually knows about all of the
// loops at once.
event_loop_factory_.RunFor(chrono::seconds(10));
}
// Tests that the number of pong messages matches the number of ping messages.
TEST_F(PingPongTest, AlwaysReplies) {
std::unique_ptr<EventLoop> test_event_loop =
event_loop_factory_.MakeEventLoop("test");
int ping_count = 0;
int pong_count = 0;
// Confirm that the ping value matches.
test_event_loop->MakeWatcher("/test",
[&ping_count](const examples::Ping &ping) {
EXPECT_EQ(ping.value(), ping_count + 1);
++ping_count;
});
// Confirm that the ping and pong counts both match, and the value also
// matches.
test_event_loop->MakeWatcher(
"/test", [&pong_count, &ping_count](const examples::Pong &pong) {
EXPECT_EQ(pong.value(), pong_count + 1);
++pong_count;
EXPECT_EQ(ping_count, pong_count);
});
event_loop_factory_.RunFor(chrono::seconds(10));
// We run at t=0 and t=10 seconds, which means we run 1 extra time.
EXPECT_EQ(ping_count, 1001);
}
// Multi-node ping pong test. This test carefully mirrors the structure of the
// single node test above to help highlight the similarities and differences.
class MultiNodePingPongTest : public ::testing::Test {
public:
MultiNodePingPongTest()
: config_(aos::configuration::ReadConfig(ArtifactPath(
"aos/events/multinode_pingpong_test_split_config.json"))),
event_loop_factory_(&config_.message()),
pi1_(
configuration::GetNode(event_loop_factory_.configuration(), "pi1")),
pi2_(
configuration::GetNode(event_loop_factory_.configuration(), "pi2")),
ping_event_loop_(event_loop_factory_.MakeEventLoop("ping", pi1_)),
ping_(ping_event_loop_.get()),
pong_event_loop_(event_loop_factory_.MakeEventLoop("pong", pi2_)),
pong_(pong_event_loop_.get()) {}
// Config and factory.
// The factory is a factory for connected event loops. Each application needs
// a separate event loop (because you can't send a message to yourself in a
// single event loop). The created event loops can then send messages to each
// other and trigger callbacks to be called, or fetchers to receive data.
aos::FlatbufferDetachedBuffer<aos::Configuration> config_;
SimulatedEventLoopFactory event_loop_factory_;
// Convenience pointers for each Node.
const Node *pi1_;
const Node *pi2_;
// Event loop and app for Ping
std::unique_ptr<EventLoop> ping_event_loop_;
Ping ping_;
// Event loop and app for Pong
std::unique_ptr<EventLoop> pong_event_loop_;
Pong pong_;
};
// Tests that the number of pong messages matches the number of ping messages
// (on both nodes this time)
TEST_F(MultiNodePingPongTest, AlwaysReplies) {
// For grins, test that ping and pong appear on both nodes and match.
std::unique_ptr<EventLoop> pi1_test_event_loop =
event_loop_factory_.MakeEventLoop("test", pi1_);
std::unique_ptr<EventLoop> pi2_test_event_loop =
event_loop_factory_.MakeEventLoop("test", pi2_);
int pi1_ping_count = 0;
int pi2_ping_count = 0;
int pi1_pong_count = 0;
int pi2_pong_count = 0;
// Confirm that the ping value matches on both nodes.
pi1_test_event_loop->MakeWatcher(
"/test", [&pi1_ping_count](const examples::Ping &ping) {
EXPECT_EQ(ping.value(), pi1_ping_count + 1);
++pi1_ping_count;
});
pi2_test_event_loop->MakeWatcher(
"/test", [&pi2_ping_count](const examples::Ping &ping) {
EXPECT_EQ(ping.value(), pi2_ping_count + 1);
++pi2_ping_count;
});
// Confirm that the ping and pong counts both match, and the value also
// matches.
pi2_test_event_loop->MakeWatcher(
"/test", [&pi2_pong_count, &pi2_ping_count](const examples::Pong &pong) {
EXPECT_EQ(pong.value(), pi2_pong_count + 1);
++pi2_pong_count;
EXPECT_EQ(pi2_ping_count, pi2_pong_count);
});
pi1_test_event_loop->MakeWatcher(
"/test", [&pi1_pong_count, &pi1_ping_count](const examples::Pong &pong) {
EXPECT_EQ(pong.value(), pi1_pong_count + 1);
++pi1_pong_count;
EXPECT_EQ(pi1_ping_count, pi1_pong_count);
});
// Since forwarding takes "time", we need to run a bit longer to let pong
// finish the last cycle.
event_loop_factory_.RunFor(chrono::seconds(10) + chrono::milliseconds(5));
// We run at t=0 and t=10 seconds, which means we run 1 extra time.
EXPECT_EQ(pi1_ping_count, 1001);
EXPECT_EQ(pi2_ping_count, 1001);
}
} // namespace aos::testing