blob: e33367ab38a9b96b3f4140340d934f9faa449e0a [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/component/cpp/environment_services_helper.h>
#include <lib/component/cpp/testing/test_util.h>
#include <lib/component/cpp/testing/test_with_environment.h>
#include "bus_manager.h"
#define ASSERT_OK(st) ASSERT_EQ(ZX_OK, (st))
#define ASSERT_NOK(st) ASSERT_NE(ZX_OK, (st))
#define WAIT_FOR_OK(ok) \
ASSERT_TRUE(RunLoopWithTimeoutOrUntil([&ok]() { return ok; }, zx::sec(2)))
#define WAIT_FOR_OK_AND_RESET(ok) \
WAIT_FOR_OK(ok); \
ok = false
namespace netemul {
namespace testing {
using component::testing::EnclosingEnvironment;
using component::testing::EnvironmentServices;
using component::testing::TestWithEnvironment;
static const char* kMainTestBus = "test-bus";
static const char* kAltTestBus = "alt-bus";
class BusTest : public TestWithEnvironment {
public:
using BusManagerSync = fidl::SynchronousInterfacePtr<BusManager::FBusManager>;
using BusSync = fidl::SynchronousInterfacePtr<Bus::FBus>;
using BusAsync = fidl::InterfacePtr<Bus::FBus>;
protected:
void SetUp() override {
fuchsia::sys::EnvironmentPtr parent_env;
real_services()->ConnectToService(parent_env.NewRequest());
svc_loop_ =
std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToThread);
ASSERT_OK(svc_loop_->StartThread("testloop"));
svc_ = std::make_unique<BusManager>(svc_loop_->dispatcher());
auto services =
EnvironmentServices::Create(parent_env, svc_loop_->dispatcher());
services->AddService(svc_->GetHandler());
test_env_ = CreateNewEnclosingEnvironment("env", std::move(services));
ASSERT_TRUE(WaitForEnclosingEnvToStart(test_env_.get()));
}
void TearDown() override {
async::PostTask(svc_loop_->dispatcher(), [this]() {
svc_.reset();
svc_loop_->Quit();
});
svc_loop_->JoinThreads();
}
void GetBusManager(fidl::InterfaceRequest<BusManager::FBusManager> manager) {
test_env_->ConnectToService(std::move(manager));
}
void FillEventData(Bus::FEvent* event, int32_t code,
const std::string& name = "", size_t args_size = 0) {
event->code = code;
if (!name.empty()) {
event->message = name;
}
event->arguments->clear();
while (args_size > 0) {
event->arguments->push_back(static_cast<uint8_t>(args_size--));
}
}
static bool EventEquals(const Bus::FEvent& e1, const Bus::FEvent& e2) {
return (e1.code == e2.code) && (e1.message == e2.message) &&
(e2.arguments->size() == e1.arguments->size()) &&
(memcmp(&e2.arguments.get()[0], &e1.arguments.get()[0],
e1.arguments->size()) == 0);
}
void DataExchangeTest(const Bus::FEvent& ref_event_1,
const Bus::FEvent& ref_event_2, bool ensure = false) {
BusManagerSync bm;
GetBusManager(bm.NewRequest());
BusAsync cli1;
ASSERT_OK(bm->Subscribe(kMainTestBus, "cli1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
BusAsync cli2;
ASSERT_OK(bm->Subscribe(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;
}
std::unique_ptr<EnclosingEnvironment> test_env_;
std::unique_ptr<async::Loop> svc_loop_;
std::unique_ptr<BusManager> svc_;
};
TEST_F(BusTest, CreateBusAndClient) {
BusManagerSync bm;
GetBusManager(bm.NewRequest());
BusSync cli1;
ASSERT_OK(bm->Subscribe(kMainTestBus, "cli1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
BusSync cli2;
ASSERT_OK(bm->Subscribe(kMainTestBus, "cli2", cli2.NewRequest()));
ASSERT_TRUE(cli2.is_bound());
// client with name cli2 on same bus should be disallowed:
BusSync cli3;
ASSERT_OK(bm->Subscribe(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", 10);
FillEventData(&ref_event_2, 2, "Hello evt 2", 20);
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", 10);
FillEventData(&ref_event_2, 2, "Hello evt 2", 20);
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) {
BusManagerSync bm;
GetBusManager(bm.NewRequest());
bool received_data = false;
// attach a client to an alternate test bus
BusAsync cli1;
ASSERT_OK(bm->Subscribe(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) {
BusManagerSync bm;
GetBusManager(bm.NewRequest());
BusSync cli3;
ASSERT_OK(bm->Subscribe(kMainTestBus, "c3", cli3.NewRequest()));
std::vector<std::string> clients;
ASSERT_OK(cli3->GetClients(&clients));
ASSERT_TRUE(VectorSetEquals(clients, {"c3"}));
BusAsync cli1;
ASSERT_OK(bm->Subscribe(kMainTestBus, "c1", cli1.NewRequest()));
ASSERT_TRUE(cli1.is_bound());
bool ok = false;
cli1.events().OnClientAttached = [&ok](fidl::StringPtr client) {
ASSERT_EQ(client, "c2");
ok = true;
};
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->Subscribe(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
cli1.Unbind();
}
} // namespace testing
} // namespace netemul