blob: db51f5b8a49829d1289e602b85caa6f4b3d7d8ae [file] [log] [blame]
// Copyright 2020 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 CTS_TESTS_PKG_FIDL_CPP_TEST_TEST_UTIL_H_
#define CTS_TESTS_PKG_FIDL_CPP_TEST_TEST_UTIL_H_
#include <lib/fidl/cpp/wire_format_metadata.h>
#include <lib/fidl/internal.h>
#include <cstdint>
#include <ios>
#include <iostream>
#include <zxtest/zxtest.h>
#include "lib/fidl/cpp/clone.h"
#include "sdk/lib/fidl/cpp/message.h"
namespace fidl {
namespace test {
namespace util {
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 cmp_payload(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 <class Output, class Input>
Output RoundTrip(const Input& input) {
fidl::BodyEncoder encoder(::fidl::internal::WireFormatVersion::kV2);
auto offset = encoder.Alloc(EncodingInlineSize<Input, fidl::BodyEncoder>(&encoder));
fidl::Clone(input).Encode(&encoder, offset);
auto oubgoing_body = encoder.GetBody();
const char* err_msg = nullptr;
EXPECT_EQ(
ZX_OK,
oubgoing_body.Validate(::fidl::internal::WireFormatVersion::kV2, Output::FidlType, &err_msg),
"%s", err_msg);
std::vector<zx_handle_info_t> handle_infos(oubgoing_body.handles().actual());
EXPECT_EQ(ZX_OK,
FidlHandleDispositionsToHandleInfos(oubgoing_body.handles().data(), handle_infos.data(),
oubgoing_body.handles().actual()));
fidl::HLCPPIncomingBody incoming_body(
BytePart(oubgoing_body.bytes(), 0),
HandleInfoPart(handle_infos.data(), static_cast<uint32_t>(handle_infos.size())));
oubgoing_body.ClearHandlesUnsafe();
EXPECT_EQ(ZX_OK,
incoming_body.Decode(fidl::internal::WireFormatMetadataForVersion(
fidl::internal::WireFormatVersion::kV2),
Output::FidlType, &err_msg),
"%s", err_msg);
fidl::Decoder decoder(std::move(incoming_body));
Output output;
Output::Decode(&decoder, &output, 0);
return output;
}
template <class Output>
Output DecodedBytes(std::vector<uint8_t> input) {
HLCPPIncomingBody body(BytePart(input.data(), static_cast<uint32_t>(input.capacity()),
static_cast<uint32_t>(input.size())),
HandleInfoPart());
const char* error = nullptr;
EXPECT_EQ(ZX_OK,
body.Decode(fidl::internal::WireFormatMetadataForVersion(
fidl::internal::WireFormatVersion::kV2),
Output::FidlType, &error),
"%s", error);
fidl::Decoder decoder(std::move(body));
Output output;
Output::Decode(&decoder, &output, 0);
return output;
}
template <class Output>
Output DecodedBytes(internal::WireFormatVersion wire_format, std::vector<uint8_t> bytes,
std::vector<zx_handle_info_t> handle_infos) {
HLCPPIncomingBody body(
BytePart(bytes.data(), static_cast<uint32_t>(bytes.capacity()),
static_cast<uint32_t>(bytes.size())),
HandleInfoPart(handle_infos.data(), static_cast<uint32_t>(handle_infos.capacity()),
static_cast<uint32_t>(handle_infos.size())));
bytes.resize(bytes.capacity()); // To avoid container overflow during V2 -> V1 transform
const char* error = nullptr;
EXPECT_EQ(ZX_OK,
body.Decode(fidl::internal::WireFormatMetadataForVersion(wire_format), Output::FidlType,
&error),
"%s", error);
fidl::Decoder decoder(std::move(body));
Output output;
Output::Decode(&decoder, &output, 0);
return output;
}
template <class Input>
void ForgetHandles(internal::WireFormatVersion wire_format, Input input) {
// Encode purely for the side effect of linearizing the handles.
fidl::BodyEncoder enc(wire_format);
auto offset = enc.Alloc(EncodingInlineSize<Input, fidl::BodyEncoder>(&enc));
input.Encode(&enc, offset);
enc.GetBody().ClearHandlesUnsafe();
}
template <class Input>
bool ValueToBytes(const Input& input, const std::vector<uint8_t>& expected) {
fidl::BodyEncoder enc(::fidl::internal::WireFormatVersion::kV2);
auto offset = enc.Alloc(EncodingInlineSize<Input, fidl::BodyEncoder>(&enc));
fidl::Clone(input).Encode(&enc, offset);
auto msg = enc.GetBody();
return cmp_payload(msg.bytes().data(), msg.bytes().actual(), expected.data(), expected.size());
}
template <class Input>
bool ValueToBytes(internal::WireFormatVersion wire_format, Input input,
const std::vector<uint8_t>& bytes,
const std::vector<zx_handle_disposition_t>& handles, bool check_rights = true) {
fidl::BodyEncoder enc(wire_format);
auto offset = enc.Alloc(EncodingInlineSize<Input, fidl::BodyEncoder>(&enc));
input.Encode(&enc, offset);
HLCPPOutgoingBody body = enc.GetBody();
auto bytes_match =
cmp_payload(body.bytes().data(), body.bytes().actual(), bytes.data(), bytes.size());
bool handles_match = false;
if (check_rights) {
handles_match =
cmp_payload(body.handles().data(), body.handles().actual(), handles.data(), handles.size());
} else {
zx_handle_t body_handles[ZX_CHANNEL_MAX_MSG_HANDLES];
for (uint32_t i = 0; i < body.handles().actual(); i++) {
body_handles[i] = body.handles().data()[i].handle;
}
zx_handle_t expected_handles[ZX_CHANNEL_MAX_MSG_HANDLES];
for (uint32_t i = 0; i < handles.size(); i++) {
expected_handles[i] = handles.data()[i].handle;
}
handles_match =
cmp_payload(body_handles, body.handles().actual(), expected_handles, handles.size());
}
const char* validation_error = nullptr;
zx_status_t validation_status = body.Validate(wire_format, Input::FidlType, &validation_error);
if (validation_status != ZX_OK) {
std::cout << "Validator exited with status " << validation_status << std::endl;
}
if (validation_error) {
std::cout << "Validator error " << validation_error << std::endl;
}
return bytes_match && handles_match && (validation_status == ZX_OK);
}
template <class Output>
void CheckDecodeFailure(internal::WireFormatVersion wire_format, std::vector<uint8_t> input,
std::vector<zx_handle_info_t> handle_infos,
const zx_status_t expected_failure_code) {
HLCPPIncomingBody body(
BytePart(input.data(), static_cast<uint32_t>(input.capacity()),
static_cast<uint32_t>(input.size())),
HandleInfoPart(handle_infos.data(), static_cast<uint32_t>(handle_infos.capacity()),
static_cast<uint32_t>(handle_infos.size())));
input.resize(input.capacity()); // To avoid container overflow during V2 -> V1 transform
const char* error = nullptr;
EXPECT_EQ(expected_failure_code,
body.Decode(fidl::internal::WireFormatMetadataForVersion(wire_format), Output::FidlType,
&error),
"%s", error);
}
template <class Input>
void CheckEncodeFailure(internal::WireFormatVersion wire_format, const Input& input,
const zx_status_t expected_failure_code) {
fidl::BodyEncoder enc(wire_format);
auto offset = enc.Alloc(EncodingInlineSize<Input, fidl::BodyEncoder>(&enc));
fidl::Clone(input).Encode(&enc, offset);
auto msg = enc.GetBody();
const char* error = nullptr;
EXPECT_EQ(expected_failure_code, msg.Validate(wire_format, Input::FidlType, &error), "%s", error);
}
} // namespace util
} // namespace test
} // namespace fidl
#endif // CTS_TESTS_PKG_FIDL_CPP_TEST_TEST_UTIL_H_