blob: 362cbeadb1fd0dca7f95de29b729056765e30655 [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/fidl/cpp/binding.h"
#include <string>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "lib/fidl/cpp/interface_ptr.h"
#include "lib/fidl/cpp/test/async_loop_for_test.h"
#include "lib/fidl/cpp/test/frobinator_impl.h"
namespace fidl {
namespace {
TEST(Binding, Trivial) {
Binding<fidl::test::frobinator::Frobinator> binding(nullptr);
}
TEST(Binding, Control) {
fidl::test::AsyncLoopForTest loop;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl);
fidl::test::frobinator::FrobinatorPtr ptr;
EXPECT_EQ(nullptr, binding.dispatcher());
EXPECT_EQ(ZX_OK, binding.Bind(ptr.NewRequest()));
EXPECT_EQ(loop.dispatcher(), binding.dispatcher());
ptr->Frob("hello");
EXPECT_TRUE(impl.frobs.empty());
EXPECT_TRUE(impl.grobs.empty());
EXPECT_EQ(ZX_OK, binding.WaitForMessage());
EXPECT_EQ(1u, impl.frobs.size());
EXPECT_EQ("hello", impl.frobs[0]);
EXPECT_TRUE(impl.grobs.empty());
impl.frobs.clear();
std::vector<std::string> responses;
ptr->Grob("world", [&responses](StringPtr value) {
EXPECT_FALSE(value.is_null());
responses.push_back(std::move(*value));
});
EXPECT_TRUE(impl.frobs.empty());
EXPECT_TRUE(impl.grobs.empty());
EXPECT_TRUE(responses.empty());
loop.RunUntilIdle();
EXPECT_TRUE(impl.frobs.empty());
EXPECT_EQ(1u, impl.grobs.size());
EXPECT_EQ("world", impl.grobs[0]);
EXPECT_EQ(1u, responses.size());
EXPECT_EQ("response", responses[0]);
}
TEST(Binding, Bind) {
fidl::test::AsyncLoopForTest loop;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl);
EXPECT_FALSE(binding.is_bound());
EXPECT_EQ(&impl, binding.impl());
auto handle = binding.NewBinding();
EXPECT_TRUE(handle.is_valid());
EXPECT_TRUE(binding.is_bound());
EXPECT_EQ(&impl, binding.impl());
auto request = binding.Unbind();
EXPECT_TRUE(request.is_valid());
EXPECT_FALSE(binding.is_bound());
EXPECT_EQ(&impl, binding.impl());
EXPECT_EQ(ZX_OK, binding.Bind(std::move(request)));
EXPECT_FALSE(request.is_valid());
EXPECT_TRUE(binding.is_bound());
EXPECT_EQ(&impl, binding.impl());
}
TEST(Binding, ConstructBound) {
fidl::test::AsyncLoopForTest loop;
InterfaceHandle<fidl::test::frobinator::Frobinator> handle;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl,
handle.NewRequest());
EXPECT_TRUE(handle.is_valid());
EXPECT_TRUE(binding.is_bound());
EXPECT_EQ(&impl, binding.impl());
}
TEST(Binding, ErrorHandler) {
fidl::test::AsyncLoopForTest loop;
InterfaceHandle<fidl::test::frobinator::Frobinator> handle;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl,
handle.NewRequest());
EXPECT_TRUE(handle.is_valid());
EXPECT_TRUE(binding.is_bound());
EXPECT_EQ(&impl, binding.impl());
int error_count = 0;
binding.set_error_handler([&error_count, &binding](zx_status_t status) {
EXPECT_EQ(ZX_ERR_INVALID_ARGS, status);
++error_count;
EXPECT_FALSE(binding.is_bound());
});
EXPECT_EQ(ZX_OK, handle.channel().write(0, "a", 1, nullptr, 0));
EXPECT_EQ(0, error_count);
loop.RunUntilIdle();
EXPECT_EQ(1, error_count);
}
TEST(Binding, DestructDuringErrorHandler) {
fidl::test::AsyncLoopForTest loop;
InterfaceHandle<fidl::test::frobinator::Frobinator> handle;
test::FrobinatorImpl impl;
auto binding =
std::make_unique<Binding<fidl::test::frobinator::Frobinator>>(&impl);
binding->Bind(handle.NewRequest());
EXPECT_TRUE(handle.is_valid());
EXPECT_TRUE(binding->is_bound());
EXPECT_EQ(&impl, binding->impl());
int error_count = 0;
binding->set_error_handler([&error_count, &binding](zx_status_t status) {
EXPECT_EQ(ZX_ERR_INVALID_ARGS, status);
++error_count;
EXPECT_FALSE(binding->is_bound());
binding.reset();
});
EXPECT_EQ(ZX_OK, handle.channel().write(0, "a", 1, nullptr, 0));
EXPECT_EQ(0, error_count);
loop.RunUntilIdle();
EXPECT_EQ(1, error_count);
}
TEST(Binding, PeerClosedTriggersErrorHandler) {
fidl::test::AsyncLoopForTest loop;
InterfaceHandle<fidl::test::frobinator::Frobinator> handle;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl,
handle.NewRequest());
int error_count = 0;
binding.set_error_handler([&error_count, &binding](zx_status_t status) {
EXPECT_EQ(ZX_ERR_PEER_CLOSED, status);
++error_count;
EXPECT_FALSE(binding.is_bound());
});
handle = nullptr;
EXPECT_EQ(0, error_count);
loop.RunUntilIdle();
EXPECT_EQ(1, error_count);
}
TEST(Binding, UnbindDoesNotTriggerErrorHandler) {
fidl::test::AsyncLoopForTest loop;
InterfaceHandle<fidl::test::frobinator::Frobinator> handle;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl,
handle.NewRequest());
int error_count = 0;
binding.set_error_handler([&error_count, &binding](zx_status_t status) {
EXPECT_EQ(ZX_ERR_INVALID_ARGS, status);
++error_count;
});
binding.Unbind();
EXPECT_EQ(0, error_count);
loop.RunUntilIdle();
EXPECT_EQ(0, error_count);
}
TEST(Binding, EpitaphReceivedWhenBindingClosed) {
fidl::test::AsyncLoopForTest loop;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl);
constexpr zx_status_t kSysError = 0xabDECADE;
fidl::test::frobinator::FrobinatorPtr ptr;
EXPECT_EQ(ZX_OK, binding.Bind(ptr.NewRequest()));
zx_status_t error = 0;
ptr.set_error_handler(
[&error](zx_status_t remote_error) { error = remote_error; });
EXPECT_EQ(ZX_OK, binding.Close(kSysError));
// Check that you can only call Close once...
EXPECT_EQ(ZX_ERR_BAD_STATE, binding.Close(kSysError));
loop.RunUntilIdle();
EXPECT_EQ(kSysError, error);
ptr->Frob("This should break");
EXPECT_EQ(ZX_ERR_BAD_STATE, binding.WaitForMessage());
}
TEST(Binding, ErrorHandlerCalledWhenInterfacePtrClosed) {
fidl::test::AsyncLoopForTest loop;
test::FrobinatorImpl impl;
Binding<fidl::test::frobinator::Frobinator> binding(&impl);
fidl::test::frobinator::FrobinatorPtr ptr;
EXPECT_EQ(ZX_OK, binding.Bind(ptr.NewRequest()));
zx_status_t error = 0;
binding.set_error_handler(
[&error](zx_status_t remote_error) { error = remote_error; });
ptr.Unbind();
loop.RunUntilIdle();
EXPECT_EQ(ZX_ERR_PEER_CLOSED, error);
ptr->Frob("This should break");
EXPECT_EQ(ZX_ERR_BAD_STATE, binding.WaitForMessage());
}
} // namespace
} // namespace fidl