blob: fdce9df8eda9963f2ab508714ccbd5e5357cb026 [file] [log] [blame]
// Copyright 2021 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.
#ifndef SRC_LIB_FIDL_C_WALKER_TESTS_CONFORMANCE_TEST_UTILS_H_
#define SRC_LIB_FIDL_C_WALKER_TESTS_CONFORMANCE_TEST_UTILS_H_
#include <lib/fidl/coding.h>
#include <lib/fidl/internal.h>
#include <lib/fidl/llcpp/message.h>
#include <lib/fit/function.h>
#include <zircon/fidl.h>
#include <zircon/status.h>
#include <zircon/types.h>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <memory>
#include <vector>
#ifdef __Fuchsia__
#include <lib/fidl/llcpp/internal/transport_channel.h>
#else
#include <lib/fidl/llcpp/internal/transport_channel_host.h>
#include "lib/fidl/llcpp/traits.h"
// The current build rules for zircon/system/ulib/zircon don't allow linking
// zx_status_get_string on host. Consider changing in the future.
#define zx_status_get_string(status) ((status))
#endif
namespace c_conformance_utils {
inline bool operator==(zx_handle_disposition_t a, zx_handle_disposition_t b) {
return a.operation == b.operation && a.handle == b.handle && a.type == b.type &&
a.rights == b.rights && a.result == b.result;
}
inline bool operator!=(zx_handle_disposition_t a, zx_handle_disposition_t b) { return !(a == b); }
inline std::ostream& operator<<(std::ostream& os, const zx_handle_disposition_t& hd) {
return os << "zx_handle_disposition_t{\n"
<< " .operation = " << hd.operation << "\n"
<< " .handle = " << hd.handle << "\n"
<< " .type = " << hd.type << "\n"
<< " .rights = " << hd.rights << "\n"
<< " .result = " << hd.result << "\n"
<< "}\n";
}
template <typename T>
bool ComparePayload(const T* actual, size_t actual_size, const T* expected, size_t expected_size) {
bool pass = true;
for (size_t i = 0; i < actual_size && i < expected_size; i++) {
if (actual[i] != expected[i]) {
pass = false;
if constexpr (std::is_same_v<T, zx_handle_disposition_t>) {
std::cout << std::dec << "element[" << i << "]: actual=" << actual[i]
<< " expected=" << expected[i];
} else {
std::cout << std::dec << "element[" << i << "]: " << std::hex << "actual=0x" << +actual[i]
<< " "
<< "expected=0x" << +expected[i] << "\n";
}
}
}
if (actual_size != expected_size) {
pass = false;
std::cout << std::dec << "element[...]: "
<< "actual.size=" << +actual_size << " "
<< "expected.size=" << +expected_size << "\n";
}
return pass;
}
template <typename FidlType>
bool EncodeSuccess(FidlWireFormatVersion wire_format_version, FidlType* value,
const std::vector<uint8_t>& expected_bytes,
const std::vector<zx_handle_disposition_t>& expected_handle_dispositions,
bool check_handle_rights) {
static_assert(!fidl::IsFidlTransactionalMessage<FidlType>::value,
"EncodeSuccess assumes non-transactional messages");
// Linearize the built objects using LLCPP encode -> decode.
fidl::unstable::OwnedEncodedMessage<FidlType> llcpp_encoded(
fidl::internal::WireFormatVersion::kV2, value);
auto& outgoing_msg = llcpp_encoded.GetOutgoingMessage();
auto copied_bytes = outgoing_msg.CopyBytes();
fidl::unstable::DecodedMessage<FidlType> llcpp_decoded(
copied_bytes.data(), static_cast<uint32_t>(copied_bytes.size()), outgoing_msg.handles(),
outgoing_msg.template handle_metadata<fidl::internal::ChannelTransport>(),
outgoing_msg.handle_actual());
if (llcpp_decoded.status() != ZX_OK) {
std::cout << "Decoding target success value failed ("
<< zx_status_get_string(llcpp_decoded.status())
<< "): " << llcpp_decoded.FormatDescription().c_str() << std::endl;
return false;
}
// Handles are now owned by |llcpp_decoded|.
outgoing_msg.ReleaseHandles();
zx_handle_disposition_t handle_dispositions[ZX_CHANNEL_MAX_MSG_HANDLES];
uint32_t actual_handles;
const char* error_msg = nullptr;
zx_status_t status =
fidl_encode_etc(fidl::DeprecatedCTypeTraits<FidlType>::kType, llcpp_decoded.PrimaryObject(),
static_cast<uint32_t>(copied_bytes.size()), handle_dispositions,
std::size(handle_dispositions), &actual_handles, &error_msg);
// The decoded message is consumed by |fidl_encode_etc|, and handles are moved
// to |handle_dispositions|.
llcpp_decoded.ReleasePrimaryObject();
if (status != ZX_OK) {
std::cout << "Encoding failed (" << zx_status_get_string(status) << "): " << error_msg
<< std::endl;
return false;
}
if (error_msg != nullptr) {
std::cout << "error message unexpectedly non-null when status is ZX_OK: " << error_msg
<< std::endl;
return false;
}
bool bytes_match = ComparePayload(copied_bytes.data(), copied_bytes.size(), expected_bytes.data(),
expected_bytes.size());
bool handles_match = false;
if (check_handle_rights) {
handles_match =
ComparePayload(handle_dispositions, actual_handles, expected_handle_dispositions.data(),
expected_handle_dispositions.size());
} else {
std::vector<zx_handle_t> handles;
std::vector<zx_handle_t> expected_handles;
for (size_t i = 0; i < actual_handles; i++) {
handles.push_back(handle_dispositions[i].handle);
}
expected_handles.reserve(expected_handle_dispositions.size());
for (const auto& handle_disposition : expected_handle_dispositions) {
expected_handles.push_back(handle_disposition.handle);
}
handles_match = ComparePayload(handles.data(), handles.size(), expected_handles.data(),
expected_handles.size());
}
FidlHandleDispositionCloseMany(handle_dispositions, actual_handles);
return bytes_match && handles_match;
}
// Verifies that |bytes| and |handles| successfully decodes.
// EqualityCheck takes a raw pointer to the input in its FIDL decoded form,
// and checks deep equality and compares handles based on koid, type and rights.
inline bool DecodeSuccess(FidlWireFormatVersion wire_format_version, const fidl_type* type,
std::vector<uint8_t> bytes, std::vector<zx_handle_info_t> handles,
fit::callback<bool(void* actual)> equality_check) {
ZX_ASSERT(wire_format_version == FIDL_WIRE_FORMAT_VERSION_V2);
const char* error_msg = nullptr;
zx_status_t status =
fidl_decode_etc(type, bytes.data(), static_cast<uint32_t>(bytes.size()), handles.data(),
static_cast<uint32_t>(handles.size()), &error_msg);
if (status != ZX_OK) {
std::cout << "Decoding failed (" << zx_status_get_string(status) << "): " << error_msg
<< std::endl;
return false;
}
if (error_msg != nullptr) {
std::cout << "error message unexpectedly non-null when status is ZX_OK: " << error_msg
<< std::endl;
return false;
}
if (!equality_check(static_cast<void*>(bytes.data()))) {
std::cout << "decode results does not equal to expected" << std::endl;
return false;
}
return true;
}
// Verifies that |bytes| and |handles| successfully validates.
inline bool ValidateSuccess(FidlWireFormatVersion wire_format_version, const fidl_type* type,
std::vector<uint8_t> bytes,
const std::vector<zx_handle_info_t>& handles) {
ZX_ASSERT(wire_format_version == FIDL_WIRE_FORMAT_VERSION_V2);
const char* error_msg = nullptr;
zx_status_t status = internal__fidl_validate__v2__may_break(
type, bytes.data(), static_cast<uint32_t>(bytes.size()),
static_cast<uint32_t>(handles.size()), &error_msg);
if (status != ZX_OK) {
std::cout << "Validating failed (" << zx_status_get_string(status) << "): " << error_msg
<< std::endl;
return false;
}
if (error_msg != nullptr) {
std::cout << "error message unexpectedly non-null when status is ZX_OK: " << error_msg
<< std::endl;
return false;
}
return true;
}
// Verifies that |bytes| and |handles| fails to decode.
inline bool DecodeFailure(FidlWireFormatVersion wire_format_version, const fidl_type* type,
std::vector<uint8_t> bytes, std::vector<zx_handle_info_t> handles,
zx_status_t expected_error_code) {
ZX_ASSERT(wire_format_version == FIDL_WIRE_FORMAT_VERSION_V2);
const char* error_msg = nullptr;
zx_status_t status =
fidl_decode_etc(type, bytes.data(), static_cast<uint32_t>(bytes.size()), handles.data(),
static_cast<uint32_t>(handles.size()), &error_msg);
if (status == ZX_OK) {
std::cout << "Decoding unexpectedly succeeded" << std::endl;
return false;
}
if (status != expected_error_code) {
std::cout << "Decoding failed with error code " << zx_status_get_string(status) << " ("
<< error_msg << "), but expected error code "
<< zx_status_get_string(expected_error_code) << std::endl;
return false;
}
return true;
}
// Verifies that |bytes| and |handles| fails to validate.
inline bool ValidateFailure(FidlWireFormatVersion wire_format_version, const fidl_type* type,
std::vector<uint8_t> bytes,
const std::vector<zx_handle_info_t>& handles,
zx_status_t expected_error_code) {
ZX_ASSERT(wire_format_version == FIDL_WIRE_FORMAT_VERSION_V2);
const char* error_msg = nullptr;
zx_status_t status = internal__fidl_validate__v2__may_break(
type, bytes.data(), static_cast<uint32_t>(bytes.size()),
static_cast<uint32_t>(handles.size()), &error_msg);
if (status == ZX_OK) {
std::cout << "Validating unexpectedly succeeded" << std::endl;
return false;
}
if (status != expected_error_code) {
std::cout << "Validating failed with error code " << zx_status_get_string(status) << " ("
<< error_msg << "), but expected error code "
<< zx_status_get_string(expected_error_code) << std::endl;
return false;
}
return true;
}
} // namespace c_conformance_utils
#endif // SRC_LIB_FIDL_C_WALKER_TESTS_CONFORMANCE_TEST_UTILS_H_