[llcpp] fidl::LinearizeAndEncode + passing conformance tests Will switch over to LinearizeAndEncode in next CL. Change Ia83987d7995066d9a82c8d347cd0f34cc6803b75 is needed for this to work with empty requests / responses. Change-Id: Ibe04b261d169039495ef28e2143c1e83b4282757 Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/396713 Commit-Queue: Benjamin Prosnitz <bprosnitz@google.com> Reviewed-by: Yifei Teng <yifeit@google.com> Testability-Review: Yifei Teng <yifeit@google.com>
diff --git a/src/lib/fidl/llcpp/tests/test_utils.h b/src/lib/fidl/llcpp/tests/test_utils.h index 9efd3ce..29a6778 100644 --- a/src/lib/fidl/llcpp/tests/test_utils.h +++ b/src/lib/fidl/llcpp/tests/test_utils.h
@@ -5,7 +5,9 @@ #ifndef SRC_LIB_FIDL_LLCPP_TESTS_TEST_UTILS_H_ #define SRC_LIB_FIDL_LLCPP_TESTS_TEST_UTILS_H_ +#include <lib/fidl/llcpp/aligned.h> #include <lib/fidl/llcpp/coding.h> +#include <lib/fidl/llcpp/linearized.h> #include <zircon/status.h> #include <zircon/types.h> @@ -14,56 +16,21 @@ #include <iostream> #include <vector> +// Testing utilities indended for GIDL-generated conformance tests. namespace llcpp_conformance_utils { bool ComparePayload(const uint8_t* actual, size_t actual_size, const uint8_t* expected, size_t expected_size); -// A wrapper around fidl::LinearizeResult that owns the memory. -template <typename FidlType> -struct OwnedLinearizeResult { - // The result, whose message field points to either inline_buffer or - // linearized_buffer (or neither in error conditions). - fidl::LinearizeResult<FidlType> result; - // Used for types with no out-of-line parts, which do not need linearization. - FIDL_ALIGNDECL char inline_buffer[FidlAlign(sizeof(FidlType))]; - // Used in the general case where linearization is needed. - std::vector<uint8_t> linearized_buffer; -}; - -// Linearizes |value|, producing an |OwnedLinearizeResult|. -// Note: This is destructive to |value|. -template <typename FidlType> -OwnedLinearizeResult<FidlType> Linearize(FidlType* value) { - static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required"); - - OwnedLinearizeResult<FidlType> owned_result; - FidlType* aligned_value = new (owned_result.inline_buffer) FidlType(std::move(*value)); - - if constexpr (FidlType::HasPointer) { - owned_result.linearized_buffer.resize(ZX_CHANNEL_MAX_MSG_BYTES); - memset(owned_result.linearized_buffer.data(), 0xcd, owned_result.linearized_buffer.size()); - owned_result.result = fidl::Linearize( - aligned_value, - fidl::BytePart(&owned_result.linearized_buffer[0], ZX_CHANNEL_MAX_MSG_BYTES)); - } else { - owned_result.result = - fidl::LinearizeResult(ZX_OK, nullptr, - fidl::DecodedMessage<FidlType>(fidl::BytePart( - reinterpret_cast<uint8_t*>(aligned_value), - FidlAlign(sizeof(FidlType)), FidlAlign(sizeof(FidlType))))); - } - return owned_result; -} - // Verifies that |value| encodes to |bytes|. -// Note: This is destructive to |value|. +// Note: This is destructive to |value| - a new value must be created with each call. template <typename FidlType> -bool EncodeSuccess(FidlType* value, const std::vector<uint8_t>& bytes) { +bool LinearizeThenEncodeSuccess(FidlType* value, const std::vector<uint8_t>& bytes) { static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required"); - auto owned_linearize_result = Linearize(value); - auto& linearize_result = owned_linearize_result.result; + fidl::aligned<FidlType> aligned_value = std::move(*value); + auto linearized = fidl::internal::Linearized<FidlType>(&aligned_value.value); + auto& linearize_result = linearized.result(); if (linearize_result.status != ZX_OK || linearize_result.error != nullptr) { std::cout << "Linearization failed (" << zx_status_get_string(linearize_result.status) << "): " << linearize_result.error << std::endl; @@ -81,13 +48,14 @@ } // Verifies that |value| fails to encode, with the expected error code. -// Note: This is destructive to |value|. +// Note: This is destructive to |value| - a new value must be created with each call. template <typename FidlType> -bool EncodeFailure(FidlType* value, zx_status_t expected_error_code) { +bool LinearizeThenEncodeFailure(FidlType* value, zx_status_t expected_error_code) { static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required"); - auto owned_linearize_result = Linearize(value); - auto& linearize_result = owned_linearize_result.result; + fidl::aligned<FidlType> aligned_value = std::move(*value); + auto linearized = fidl::internal::Linearized<FidlType>(&aligned_value.value); + auto& linearize_result = linearized.result(); if (linearize_result.status != ZX_OK && linearize_result.status != expected_error_code) { std::cout << "Linearization failed with error code " << zx_status_get_string(linearize_result.status) << " (" << linearize_result.error @@ -110,6 +78,44 @@ return true; } +// Verifies that |value| encodes to |bytes|. +// Note: This is destructive to |value| - a new value must be created with each call. +template <typename FidlType> +bool CombinedLinearizeAndEncodeSuccess(FidlType* value, const std::vector<uint8_t>& bytes) { + static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required"); + + ::fidl::internal::LinearizeBuffer<FidlType> buffer; + auto encode_result = fidl::LinearizeAndEncode(value, buffer.buffer()); + if (encode_result.status != ZX_OK || encode_result.error != nullptr) { + std::cout << "Encoding failed (" << zx_status_get_string(encode_result.status) + << "): " << encode_result.error << std::endl; + return false; + } + return ComparePayload(encode_result.message.bytes().data(), + encode_result.message.bytes().actual(), &bytes[0], bytes.size()); +} + +// Verifies that |value| fails to encode, with the expected error code. +// Note: This is destructive to |value| - a new value must be created with each call. +template <typename FidlType> +bool CombinedLinearizeAndEncodeFailure(FidlType* value, zx_status_t expected_error_code) { + static_assert(fidl::IsFidlType<FidlType>::value, "FIDL type required"); + + ::fidl::internal::LinearizeBuffer<FidlType> buffer; + auto encode_result = fidl::LinearizeAndEncode(value, buffer.buffer()); + if (encode_result.status == ZX_OK) { + std::cout << "Encoding unexpectedly succeeded" << std::endl; + return false; + } + if (encode_result.status != expected_error_code) { + std::cout << "Encoding failed with error code " << zx_status_get_string(encode_result.status) + << " (" << encode_result.error << "), but expected error code " + << zx_status_get_string(expected_error_code) << std::endl; + return false; + } + return true; +} + // Verifies that |bytes| decodes to an object that is the same as |value|. template <typename FidlType> bool DecodeSuccess(FidlType* value, std::vector<uint8_t> bytes) {
diff --git a/tools/fidl/gidl/llcpp/conformance.go b/tools/fidl/gidl/llcpp/conformance.go index 3636890..bcccca1 100644 --- a/tools/fidl/gidl/llcpp/conformance.go +++ b/tools/fidl/gidl/llcpp/conformance.go
@@ -28,13 +28,21 @@ #include "src/lib/fidl/llcpp/tests/test_utils.h" {{ range .EncodeSuccessCases }} -TEST(Conformance, {{ .Name }}_Encode) { +TEST(Conformance, {{ .Name }}_LinearizeThenEncode) { fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator; fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator; {{ .ValueBuild }} const auto expected = {{ .Bytes }}; auto obj = {{ .ValueVar }}; - EXPECT_TRUE(llcpp_conformance_utils::EncodeSuccess(&obj, expected)); + EXPECT_TRUE(llcpp_conformance_utils::LinearizeThenEncodeSuccess(&obj, expected)); +} +TEST(Conformance, {{ .Name }}_CombinedLinearizeAndEncode) { + fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator; + fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator; + {{ .ValueBuild }} + const auto expected = {{ .Bytes }}; + auto obj = {{ .ValueVar }}; + EXPECT_TRUE(llcpp_conformance_utils::CombinedLinearizeAndEncodeSuccess(&obj, expected)); } {{ end }} @@ -50,12 +58,19 @@ {{ end }} {{ range .EncodeFailureCases }} -TEST(Conformance, {{ .Name }}_Encode_Failure) { +TEST(Conformance, {{ .Name }}_LinearizeThenEncode_Failure) { fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator; fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator; {{ .ValueBuild }} auto obj = {{ .ValueVar }}; - EXPECT_TRUE(llcpp_conformance_utils::EncodeFailure(&obj, {{ .ErrorCode }})); + EXPECT_TRUE(llcpp_conformance_utils::LinearizeThenEncodeFailure(&obj, {{ .ErrorCode }})); +} +TEST(Conformance, {{ .Name }}_CombinedLinearizeAndEncode_Failure) { + fidl::UnsafeBufferAllocator<ZX_CHANNEL_MAX_MSG_BYTES> allocator; + fidl::Allocator* allocator_ptr __attribute__((unused)) = &allocator; + {{ .ValueBuild }} + auto obj = {{ .ValueVar }}; + EXPECT_TRUE(llcpp_conformance_utils::CombinedLinearizeAndEncodeFailure(&obj, {{ .ErrorCode }})); } {{ end }}
diff --git a/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h b/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h index 0106106..c831748 100644 --- a/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h +++ b/zircon/system/ulib/fidl/include/lib/fidl/llcpp/coding.h
@@ -197,6 +197,26 @@ return result; } +template <typename FidlType> +EncodeResult<FidlType> LinearizeAndEncode(FidlType* value, BytePart bytes) { + static_assert(IsFidlType<FidlType>::value, "FIDL type required"); + static_assert(FidlType::Type != nullptr, "FidlType should have a coding table"); + EncodeResult<FidlType> result; + uint32_t num_bytes_actual; + uint32_t num_handles_actual; + result.message.bytes() = std::move(bytes); + result.status = fidl_linearize_and_encode( + FidlType::Type, value, result.message.bytes().data(), result.message.bytes().capacity(), + result.message.handles().data(), result.message.handles().capacity(), &num_bytes_actual, + &num_handles_actual, &result.error); + if (result.status != ZX_OK) { + return result; + } + result.message.bytes().set_actual(num_bytes_actual); + result.message.handles().set_actual(num_handles_actual); + return result; +} + #ifdef __Fuchsia__ namespace {