blob: 97f3147551a104d0b2686c045172338e52368ca9 [file] [log] [blame]
// Copyright 2019 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/builder.h>
#include <lib/fidl/cpp/message.h>
#include <lib/fidl/cpp/message_builder.h>
#include <lib/fidl/llcpp/string_view.h>
#include <lib/fidl/llcpp/transaction.h>
#include <lib/sync/completion.h>
#include <limits.h>
#include <zircon/syscalls.h>
#include <thread>
#include <type_traits>
#include <fidl/test/coding/fuchsia/llcpp/fidl.h>
#include <fidl/test/coding/llcpp/fidl.h>
#include <zxtest/zxtest.h>
#include "fidl_coded_types.h"
namespace {
class Transaction : public fidl::Transaction {
public:
Transaction() = default;
explicit Transaction(sync_completion_t* wait, sync_completion_t* signal)
: wait_(wait), signal_(signal) {}
std::unique_ptr<fidl::Transaction> TakeOwnership() override { ZX_ASSERT(false); }
zx_status_t Reply(fidl::OutgoingMessage* message) override {
if (wait_ && signal_) {
sync_completion_signal(signal_);
sync_completion_wait(wait_, ZX_TIME_INFINITE);
}
return ZX_OK;
}
void Close(zx_status_t epitaph) override {}
~Transaction() override = default;
private:
sync_completion_t* wait_;
sync_completion_t* signal_;
};
using Completer = ::llcpp::fidl::test::coding::fuchsia::Llcpp::Interface::ActionCompleter::Sync;
// A completer being destroyed without replying (but needing one) should crash
TEST(LlcppTransaction, no_reply_asserts) {
Transaction txn{};
ASSERT_DEATH([&] { Completer completer(&txn); }, "no reply should crash");
}
// A completer being destroyed without replying (but needing one) should crash
TEST(LlcppTransaction, no_expected_reply_doesnt_assert) {
Transaction txn{};
fidl::Completer<fidl::CompleterBase>::Sync completer(&txn);
}
// A completer replying twice should crash
TEST(LlcppTransaction, double_reply_asserts) {
Transaction txn{};
Completer completer(&txn);
completer.Reply(0);
ASSERT_DEATH([&] { completer.Reply(1); }, "second reply should crash");
}
// It is allowed to reply and then close
TEST(LlcppTransaction, reply_then_close_doesnt_assert) {
Transaction txn{};
Completer completer(&txn);
completer.Reply(0);
completer.Close(ZX_ERR_INVALID_ARGS);
}
// It is not allowed to close then reply
TEST(LlcppTransaction, close_then_reply_asserts) {
Transaction txn{};
Completer completer(&txn);
completer.Close(ZX_ERR_INVALID_ARGS);
ASSERT_DEATH([&] { completer.Reply(1); }, "reply after close should crash");
}
// It is not allowed to be accessed from multiple threads simultaneously
TEST(LlcppTransaction, concurrent_access_asserts) {
sync_completion_t signal, wait;
Transaction txn{&signal, &wait};
Completer completer(&txn);
std::thread t([&] { completer.Reply(1); });
sync_completion_wait(&wait, ZX_TIME_INFINITE);
// TODO(fxbug.dev/54499): Hide assertion failed messages from output - they are confusing.
ASSERT_DEATH([&] { completer.Reply(1); }, "concurrent access should crash");
ASSERT_DEATH([&] { completer.Close(ZX_OK); }, "concurrent access should crash");
ASSERT_DEATH([&] { completer.EnableNextDispatch(); }, "concurrent access should crash");
ASSERT_DEATH([&] { completer.ToAsync(); }, "concurrent access should crash");
sync_completion_signal(&signal);
t.join(); // Don't accidentally invoke ~Completer() while `t` is still in Reply().
}
// If there is a serialization error, it does not need to be closed or replied to.
TEST(LlcppTransaction, transaction_error) {
Transaction txn{};
::llcpp::fidl::test::coding::fuchsia::Llcpp::Interface::EnumActionCompleter::Sync completer(&txn);
// We are using the fact that 2 isn't a valid enum value to cause an error.
fidl::Result result =
completer.Reply(static_cast<llcpp::fidl::test::coding::fuchsia::TestEnum>(2));
ASSERT_FALSE(result.ok());
}
namespace test_async_completer_deleted_methods {
template <typename T, typename = void>
struct test : std::false_type {};
template <typename T>
struct test<T, std::void_t<decltype(std::declval<T>().EnableNextDispatch())>> : std::true_type {};
// Invoking `FooCompleter::Async::EnableNextDispatch` should be a compile-time error.
TEST(LlcppCompleter, AsyncCompleterCannotEnableNextDispatch) {
Transaction txn{};
Completer completer(&txn);
static_assert(test<decltype(completer)>::value);
static_assert(!test<decltype(completer.ToAsync())>::value);
// Not relevant to the test, but required to neutralize the completer.
completer.Close(ZX_OK);
}
} // namespace test_async_completer_deleted_methods
namespace test_sync_completer_deleted_methods {
template <typename T>
T TryToMove(T t) {
return std::move(t);
}
template <typename T, typename = void>
struct test : std::false_type {};
template <typename T>
struct test<T, std::void_t<decltype(TryToMove<T>(std::declval<T>()))>> : std::true_type {};
// Invoking move construction on `FooCompleter::Sync` should be a compile-time error.
TEST(LlcppCompleter, SyncCompleterCannotBeMoved) {
Transaction txn{};
Completer completer(&txn);
// Sync one cannot be moved.
static_assert(!test<decltype(completer)>::value);
// Async one can be moved.
static_assert(test<decltype(completer.ToAsync())>::value);
// Not relevant to the test, but required to neutralize the completer.
completer.Close(ZX_OK);
}
} // namespace test_sync_completer_deleted_methods
} // namespace