blob: 3db0a153e1e3b458ca305232e313b66172b4f3d5 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async/cpp/task.h>
#include "src/lib/testing/loop_fixture/real_loop_fixture.h"
#include "src/lib/testing/predicates/status.h"
#include "sync_manager.h"
#define WAIT_FOR_OK(ok) RunLoopUntil([&ok]() { return ok; })
#define WAIT_FOR_OK_AND_RESET(ok) \
WAIT_FOR_OK(ok); \
ok = false
namespace netemul {
namespace testing {
static const char* kMainTestBus = "test-bus";
static const char* kAltTestBus = "alt-bus";
class BusTest : public gtest::RealLoopFixture {
public:
using SyncManagerSync = fidl::SynchronousInterfacePtr<SyncManager::FSyncManager>;
using BusSync = fidl::SynchronousInterfacePtr<Bus::FBus>;
using BusAsync = fidl::InterfacePtr<Bus::FBus>;
protected:
void SetUp() override {
svc_loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(svc_loop_->StartThread("testloop"));
svc_ = std::make_unique<SyncManager>(svc_loop_->dispatcher());
}
void TearDown() override {
async::PostTask(svc_loop_->dispatcher(), [this]() {
svc_.reset();
svc_loop_->Quit();
});
svc_loop_->JoinThreads();
}
void GetSyncManager(fidl::InterfaceRequest<SyncManager::FSyncManager> manager) {
svc_->GetHandler()(std::move(manager));
}
void FillEventData(Bus::FEvent* event, int32_t code = 0, const std::string& name = "",
std::vector<uint8_t> args = {}) {
if (code != 0) {
event->set_code(code);
} else {
event->clear_code();
}
if (!name.empty()) {
event->set_message(name);
} else {
event->clear_message();
}
if (!args.empty()) {
event->set_arguments(std::move(args));
} else {
event->clear_arguments();
}
}
static bool EventEquals(const netemul::Bus::FEvent& e1, const netemul::Bus::FEvent& e2) {
return (e1.has_code() == e2.has_code() && (!e1.has_code() || e1.code() == e2.code())) &&
(e1.has_message() == e2.has_message() &&
(!e1.has_message() || e1.message() == e2.message())) &&
(e1.has_arguments() == e2.has_arguments() &&
(!e1.has_arguments() ||
(e1.arguments().size() == e2.arguments().size() &&
memcmp(e1.arguments().data(), e2.arguments().data(), e1.arguments().size()) == 0)));
}
void DataExchangeTest(const Bus::FEvent& ref_event_1, const Bus::FEvent& ref_event_2,
bool ensure = false) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusAsync cli1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "cli1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
BusAsync cli2;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "cli2", cli2.NewRequest()));
ASSERT_TRUE(cli2.is_bound());
bool ok1 = false;
bool ok2 = false;
cli1.events().OnBusData = [&ok1, &ref_event_1](Bus::FEvent event) {
ASSERT_TRUE(EventEquals(ref_event_1, event));
ok1 = true;
};
cli2.events().OnBusData = [&ok2, &ref_event_2](Bus::FEvent event) {
ASSERT_TRUE(EventEquals(ref_event_2, event));
ok2 = true;
};
Bus::FEvent snd;
ASSERT_OK(ref_event_1.Clone(&snd));
if (ensure) {
bool published = false;
cli2->EnsurePublish(std::move(snd), [&published]() { published = true; });
WAIT_FOR_OK(published);
} else {
cli2->Publish(std::move(snd));
}
// wait for client 1 to receive data
WAIT_FOR_OK_AND_RESET(ok1);
// client2 mustn't have received anything
ASSERT_FALSE(ok2);
ASSERT_OK(ref_event_2.Clone(&snd));
if (ensure) {
bool published = false;
cli1->EnsurePublish(std::move(snd), [&published]() { published = true; });
WAIT_FOR_OK(published);
} else {
cli1->Publish(std::move(snd));
}
// wait for client 2 to receive data
WAIT_FOR_OK_AND_RESET(ok2);
// client1 mustn't have received anything
ASSERT_FALSE(ok1);
}
bool VectorSetEquals(const std::vector<std::string>& s1, const std::vector<std::string>& s2) {
if (s1.size() != s2.size()) {
return false;
}
// these vectors are small enough that the O(n2) search here is not too
// problematic shouldn't use larger vectors here, performance will suffer
for (const auto& x1 : s1) {
bool found = false;
for (const auto& x2 : s2) {
if (x2 == x1) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
void TestEventWaiting(BusSync* c1, BusAsync* c2, bool expect_result, Bus::FEvent wait,
Bus::FEvent publish, const std::string& test_caller) {
int64_t timeout = zx::msec(expect_result ? 0 : 15).to_nsecs();
bool got_callback = false;
(*c2)->WaitForEvent(std::move(wait), timeout,
[&got_callback, expect_result, test_caller](bool result) {
EXPECT_EQ(result, expect_result) << test_caller;
got_callback = true;
});
bool published = false;
// send whatever on the bus to make sure wait is in effect.
(*c2)->EnsurePublish(Bus::FEvent(), [&published]() { published = true; });
WAIT_FOR_OK(published);
ASSERT_OK((*c1)->EnsurePublish(std::move(publish)));
WAIT_FOR_OK(got_callback);
}
std::unique_ptr<async::Loop> svc_loop_;
std::unique_ptr<SyncManager> svc_;
};
TEST_F(BusTest, CreateBusAndClient) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusSync cli1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "cli1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
BusSync cli2;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "cli2", cli2.NewRequest()));
ASSERT_TRUE(cli2.is_bound());
// client with name cli2 on same bus should be disallowed:
BusSync cli3;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "cli2", cli2.NewRequest()));
RunLoopUntilIdle();
ASSERT_FALSE(cli3.is_bound());
}
TEST_F(BusTest, ExchangeFullData) {
Bus::FEvent ref_event_1, ref_event_2;
FillEventData(&ref_event_1, 1, "Hello evt 1", {1, 2, 3, 4});
FillEventData(&ref_event_2, 2, "Hello evt 2", {1, 2, 3, 4, 5, 6, 7, 8});
DataExchangeTest(ref_event_1, ref_event_2);
}
TEST_F(BusTest, ExchangeFullDataEnsured) {
Bus::FEvent ref_event_1, ref_event_2;
FillEventData(&ref_event_1, 1, "Hello evt 1", {1, 2, 3, 4});
FillEventData(&ref_event_2, 2, "Hello evt 2", {1, 2, 3, 4, 5, 6, 7, 8});
DataExchangeTest(ref_event_1, ref_event_2, true);
}
TEST_F(BusTest, ExchangeCodeOnlyData) {
Bus::FEvent ref_event_1, ref_event_2;
FillEventData(&ref_event_1, 1);
FillEventData(&ref_event_2, 2);
DataExchangeTest(ref_event_1, ref_event_2);
}
TEST_F(BusTest, CrossTalk) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
bool received_data = false;
// attach a client to an alternate test bus
BusAsync cli1;
ASSERT_OK(bm->BusSubscribe(kAltTestBus, "cli1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
cli1.events().OnBusData = [&received_data](Bus::FEvent event) { received_data = true; };
// run a regular data exchange test:
Bus::FEvent ref_event_1, ref_event_2;
FillEventData(&ref_event_1, 1);
FillEventData(&ref_event_2, 2);
DataExchangeTest(ref_event_1, ref_event_2);
// ensure that client on opposite bus is still bound and data was not
// received:
ASSERT_FALSE(received_data);
ASSERT_TRUE(cli1.is_bound());
}
TEST_F(BusTest, ClientObservation) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusSync cli3;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c3", cli3.NewRequest()));
std::vector<std::string> clients;
ASSERT_OK(cli3->GetClients(&clients));
ASSERT_TRUE(VectorSetEquals(clients, {"c3"}));
BusAsync cli1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
bool ok = false;
cli1.events().OnClientAttached = [&ok](fidl::StringPtr client) {
if (client == "c2") {
ok = true;
} else {
ASSERT_EQ(client, "c3");
}
};
cli1.events().OnClientDetached = [&ok](fidl::StringPtr client) {
ASSERT_EQ(client, "c2");
ok = true;
};
ASSERT_OK(cli3->GetClients(&clients));
ASSERT_TRUE(VectorSetEquals(clients, {"c1", "c3"}));
{
BusAsync cli2;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c2", cli2.NewRequest()));
ASSERT_TRUE(cli2.is_bound());
// wait for OnClientAttached event to fire
WAIT_FOR_OK_AND_RESET(ok);
ASSERT_OK(cli3->GetClients(&clients));
ASSERT_TRUE(VectorSetEquals(clients, {"c1", "c2", "c3"}));
}
// cli2 went away, wait for client detached event
WAIT_FOR_OK_AND_RESET(ok);
// check again that it went away
ASSERT_OK(cli3->GetClients(&clients));
ASSERT_TRUE(VectorSetEquals(clients, {"c1", "c3"}));
// make sure to unbind cli1 first so we don't get the detaching event of cli3
cli1.Unbind();
}
TEST_F(BusTest, WaitForClients) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusSync cli1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c1", cli1.NewRequest()));
BusAsync cli_async;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "async", cli_async.NewRequest()));
bool got_callback = false;
cli_async->WaitForClients({"c1"}, zx::msec(0).to_nsecs(),
[&got_callback](bool result, fidl::VectorPtr<std::string> absent) {
EXPECT_TRUE(result);
EXPECT_FALSE(absent.has_value());
got_callback = true;
});
WAIT_FOR_OK_AND_RESET(got_callback);
cli_async->WaitForClients({"doesn't exist"}, zx::msec(10).to_nsecs(),
[&got_callback](bool result, fidl::VectorPtr<std::string> absent) {
EXPECT_FALSE(result);
ASSERT_TRUE(absent.has_value());
ASSERT_EQ(absent->size(), 1ul);
EXPECT_EQ(absent->at(0), "doesn't exist");
got_callback = true;
});
WAIT_FOR_OK_AND_RESET(got_callback);
cli_async->WaitForClients({"c1", "c2"}, zx::msec(1000).to_nsecs(),
[&got_callback](bool result, fidl::VectorPtr<std::string> absent) {
EXPECT_TRUE(result);
EXPECT_FALSE(absent.has_value());
got_callback = true;
});
RunLoopUntilIdle();
EXPECT_FALSE(got_callback);
BusSync cli2;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c2", cli2.NewRequest()));
WAIT_FOR_OK_AND_RESET(got_callback);
}
TEST_F(BusTest, DestroyWithClientWaiting) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusAsync cli_async;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "async", cli_async.NewRequest()));
cli_async->WaitForClients(
{"c1"}, zx::msec(0).to_nsecs(),
[](bool result, fidl::VectorPtr<std::string> absent) { FAIL() << "Mustn't reach callback"; });
}
TEST_F(BusTest, WaitForEvent) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusSync cli1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c1", cli1.NewRequest()));
BusAsync cli_async;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "async", cli_async.NewRequest()));
bool got_callback = false;
bool published = false;
Bus::FEvent e1;
// check that timeout fires
cli_async->WaitForEvent(std::move(e1), zx::msec(1).to_nsecs(), [&got_callback](bool result) {
EXPECT_FALSE(result);
got_callback = true;
});
WAIT_FOR_OK_AND_RESET(got_callback);
int evt_counter = 0;
cli_async.events().OnBusData = [&evt_counter](Bus::FEvent data) { evt_counter++; };
// check that callback fires with simple event with code
Bus::FEvent evt_wait, evt_snd;
FillEventData(&evt_wait, 1);
evt_wait.Clone(&evt_snd);
cli_async->WaitForEvent(std::move(evt_wait), zx::msec(0).to_nsecs(),
[&got_callback](bool result) {
EXPECT_TRUE(result);
got_callback = true;
});
// just send whatever on the bus to guarantee that wait is already in effect
cli_async->EnsurePublish(Bus::FEvent(), [&published]() { published = true; });
WAIT_FOR_OK_AND_RESET(published);
cli1->Publish(std::move(evt_snd));
WAIT_FOR_OK_AND_RESET(got_callback);
// check that callback fires on second event
FillEventData(&evt_wait, 1);
FillEventData(&evt_snd, 2);
cli_async->WaitForEvent(std::move(evt_wait), zx::msec(0).to_nsecs(),
[&got_callback](bool result) {
EXPECT_TRUE(result);
got_callback = true;
});
// just send whatever on the bus to guarantee that wait is already in effect
cli_async->EnsurePublish(Bus::FEvent(), [&published]() { published = true; });
WAIT_FOR_OK_AND_RESET(published);
cli1->EnsurePublish(std::move(evt_snd));
EXPECT_FALSE(got_callback);
FillEventData(&evt_snd, 1);
cli1->EnsurePublish(std::move(evt_snd));
WAIT_FOR_OK_AND_RESET(got_callback);
// check that event sent by same client doesn't trigger callback
FillEventData(&evt_wait, 1);
FillEventData(&evt_snd, 2);
cli_async->WaitForEvent(std::move(evt_wait), zx::msec(15).to_nsecs(),
[&got_callback](bool result) {
EXPECT_FALSE(result);
got_callback = true;
});
cli_async->EnsurePublish(std::move(evt_snd), [&published]() { published = true; });
WAIT_FOR_OK_AND_RESET(published);
WAIT_FOR_OK_AND_RESET(got_callback);
EXPECT_EQ(evt_counter,
3); // check that all events actually made into the event callback
}
TEST_F(BusTest, EventWaitingEquality) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusSync cli1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "c1", cli1.NewRequest()));
BusAsync cli_async;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "async", cli_async.NewRequest()));
int evt_counter = 0;
cli_async.events().OnBusData = [&evt_counter](Bus::FEvent data) { evt_counter++; };
Bus::FEvent wait, publish;
FillEventData(&wait, 1);
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, true, std::move(wait), std::move(publish), "code match");
FillEventData(&wait, 0, "msg");
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, true, std::move(wait), std::move(publish), "message match");
FillEventData(&wait, 0, "", {1, 2, 3});
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, true, std::move(wait), std::move(publish), "arg match");
FillEventData(&wait, 2);
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, false, std::move(wait), std::move(publish), "code mismatch");
FillEventData(&wait, 0, "bla");
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, false, std::move(wait), std::move(publish),
"message mismatch");
FillEventData(&wait, 0, "", {4, 5, 6});
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, false, std::move(wait), std::move(publish), "arg mismatch");
FillEventData(&wait, 1, "msg", {4, 5, 6});
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, false, std::move(wait), std::move(publish),
"all match/arg mismatch");
FillEventData(&wait, 1, "msg", {1, 2, 3});
FillEventData(&publish, 1, "msg", {1, 2, 3});
TestEventWaiting(&cli1, &cli_async, true, std::move(wait), std::move(publish), "all match");
// check that all events actually made into the event callback
ASSERT_TRUE(
RunLoopWithTimeoutOrUntil([&evt_counter]() { return evt_counter == 8; }, zx::msec(100)));
}
TEST_F(BusTest, OnClientAttachedFiresForPreviousClients) {
SyncManagerSync bm;
GetSyncManager(bm.NewRequest());
BusSync b1;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "b1", b1.NewRequest()));
BusSync b2;
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "b2", b2.NewRequest()));
bool saw_b1 = false;
bool saw_b2 = false;
bool ok = false;
BusAsync cli;
cli.events().OnClientAttached = [&saw_b1, &saw_b2, &ok](fidl::StringPtr client) {
if (client == "b1") {
ASSERT_FALSE(saw_b1);
saw_b1 = true;
} else if (client == "b2") {
ASSERT_FALSE(saw_b2);
saw_b2 = true;
} else {
FAIL() << "Unexpected client " << client;
}
ok = saw_b1 && saw_b2;
};
ASSERT_OK(bm->BusSubscribe(kMainTestBus, "cli", cli.NewRequest()));
WAIT_FOR_OK(ok);
}
} // namespace testing
} // namespace netemul