blob: 1c5de41de4e24e51fc26fb108821027d7f1cdf64 [file] [log] [blame] [edit]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Tests of generated code for virtual fields.
#include <stdint.h>
#include <type_traits>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "testdata/virtual_field.emb.h"
namespace emboss {
namespace test {
namespace {
// Check that the constant methods are generated as constexpr free functions in
// the type's aliased namespace, and have the appropriate values.
static_assert(StructureWithConstants::ten() == 10,
"StructureWithConstants::ten() == 10");
static_assert(StructureWithConstants::twenty() == 20,
"StructureWithConstants::twenty() == 20");
static_assert(StructureWithConstants::four_billion() == 4000000000U,
"StructureWithConstants::four_billion() == 4000000000U");
static_assert(StructureWithConstants::ten_billion() == 10000000000L,
"StructureWithConstants::ten_billion() == 10000000000L");
static_assert(StructureWithConstants::minus_ten_billion() == -10000000000L,
"StructureWithConstants::minus_ten_billion() == -10000000000L");
// Check the return types of the static Read methods.
static_assert(::std::is_same</**/ ::std::int32_t,
decltype(StructureWithConstants::ten())>::value,
"StructureWithConstants::ten() returns ::std::int8_t");
static_assert(::std::is_same</**/ ::std::int32_t,
decltype(StructureWithConstants::twenty())>::value,
"StructureWithConstants::twenty() returns ::std::int8_t");
static_assert(
::std::is_same</**/ ::std::uint32_t,
decltype(StructureWithConstants::four_billion())>::value,
"StructureWithConstants::four_billion() returns ::std::uint32_t");
static_assert(
::std::is_same</**/ ::std::int64_t,
decltype(StructureWithConstants::ten_billion())>::value,
"StructureWithConstants::ten_billion() returns ::std::int64_t");
TEST(Constants, ValuesOnView) {
::std::array<char, 4> values = {0, 0, 0, 0};
const auto view = MakeStructureWithConstantsView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_EQ(10, view.ten().Read());
EXPECT_EQ(10, view.alias_of_ten().Read());
EXPECT_EQ(10, view.alias_of_alias_of_ten().Read());
EXPECT_EQ(20, view.twenty().Read());
EXPECT_EQ(4000000000U, view.four_billion().Read());
EXPECT_EQ(10000000000L, view.ten_billion().Read());
EXPECT_EQ(0U, view.value().Read());
EXPECT_EQ(0U, view.alias_of_value().Read());
EXPECT_EQ(0U, view.alias_of_alias_of_value().Read());
view.alias_of_alias_of_value().Write(10);
EXPECT_EQ(10U, view.value().Read());
EXPECT_EQ(10U, view.alias_of_value().Read());
EXPECT_EQ(10U, view.alias_of_alias_of_value().Read());
}
TEST(Computed, Values) {
::std::array<char, 8> values = {5, 0, 0, 0, 50, 0, 0, 0};
const auto view = MakeStructureWithComputedValuesView(&values);
EXPECT_EQ(5U, view.value().Read());
EXPECT_EQ(10U, view.doubled().Read());
EXPECT_EQ(15U, view.plus_ten().Read());
EXPECT_EQ(50, view.value2().Read());
EXPECT_EQ(100, view.signed_doubled().Read());
EXPECT_EQ(60, view.signed_plus_ten().Read());
EXPECT_EQ(250, view.product().Read());
view.value2().Write(-50);
EXPECT_EQ(-100, view.signed_doubled().Read());
EXPECT_EQ(-40, view.signed_plus_ten().Read());
EXPECT_EQ(-250, view.product().Read());
}
TEST(Computed, ReadFailsWhenUnderlyingFieldIsNotOk) {
::std::array<char, 0> values = {};
const auto view = MakeStructureWithComputedValuesView(&values);
EXPECT_DEATH(view.value().Read(), "");
EXPECT_DEATH(view.doubled().Read(), "");
}
// Check the return types of nonstatic Read methods.
static_assert(
::std::is_same</**/ ::std::int64_t,
decltype(MakeStructureWithComputedValuesView("x", 1)
.doubled()
.Read())>::value,
"StructureWithComputedValuesView::doubled().Read() should return "
"::std::int64_t.");
// Check the return types of nonstatic Read methods.
static_assert(
::std::is_same</**/ ::std::int64_t,
decltype(MakeStructureWithComputedValuesView("x", 1)
.product()
.Read())>::value,
"StructureWithComputedValuesView::product().Read() should return "
"::std::int64_t.");
TEST(Constants, TextFormatWrite) {
::std::array<char, 4> values = {5, 0, 0, 0};
const auto view = MakeStructureWithConstantsView(&values);
// TODO(bolms): Provide a way of marking fields as "not for text format," so
// that end users can choose whether to use an alias or an original field or
// both in the text format.
EXPECT_EQ("{ value: 5, alias_of_value: 5, alias_of_alias_of_value: 5 }",
::emboss::WriteToString(view));
EXPECT_EQ(
"{\n"
" # ten: 10 # 0xa\n"
" # twenty: 20 # 0x14\n"
" # four_billion: 4_000_000_000 # 0xee6b_2800\n"
" # ten_billion: 10_000_000_000 # 0x2_540b_e400\n"
" # minus_ten_billion: -10_000_000_000 # -0x2_540b_e400\n"
" value: 5 # 0x5\n"
" alias_of_value: 5 # 0x5\n"
" alias_of_alias_of_value: 5 # 0x5\n"
" # alias_of_ten: 10 # 0xa\n"
" # alias_of_alias_of_ten: 10 # 0xa\n"
"}",
::emboss::WriteToString(view, ::emboss::MultilineText()));
}
TEST(Computed, TextFormatWrite) {
::std::array<char, 8> values = {5, 0, 0, 0, 50, 0, 0, 0};
const auto view = MakeStructureWithComputedValuesView(&values);
EXPECT_EQ("{ value: 5, plus_ten: 15, value2: 50, signed_plus_ten: 60 }",
::emboss::WriteToString(view));
EXPECT_EQ(
"{\n"
" value: 5 # 0x5\n"
" # doubled: 10 # 0xa\n"
" plus_ten: 15 # 0xf\n"
" value2: 50 # 0x32\n"
" # signed_doubled: 100 # 0x64\n"
" signed_plus_ten: 60 # 0x3c\n"
" # product: 250 # 0xfa\n"
"}",
::emboss::WriteToString(view, ::emboss::MultilineText()));
}
TEST(Constants, TextFormatRead) {
::std::array<char, 4> values = {5, 0, 0, 0};
const auto view = MakeStructureWithConstantsView(&values);
EXPECT_TRUE(::emboss::UpdateFromText(view, "{ value: 50 }"));
EXPECT_EQ(50, values[0]);
EXPECT_FALSE(::emboss::UpdateFromText(view, "{ ten: 50 }"));
// TODO(bolms): Should this be allowed?
EXPECT_FALSE(::emboss::UpdateFromText(view, "{ ten: 10 }"));
}
TEST(Computed, TextFormatRead) {
::std::array<char, 8> values = {5, 0, 0, 0, 50, 0, 0, 0};
const auto view = MakeStructureWithComputedValuesView(&values);
EXPECT_TRUE(::emboss::UpdateFromText(view, "{ value: 50, value2: 5 }"));
EXPECT_EQ(50, values[0]);
EXPECT_EQ(5, values[4]);
EXPECT_FALSE(::emboss::UpdateFromText(view, "{ product: 10 }"));
// TODO(bolms): Make Emboss automatically infer write_transform for
// easily-reversible cases like `field * 2`.
EXPECT_FALSE(::emboss::UpdateFromText(view, "{ doubled: 10 }"));
}
TEST(ConditionalVirtual, ConditionChecks) {
::std::array<char, 4> values = {5, 0, 0, 0};
const auto view = MakeStructureWithConditionalValueView(&values);
EXPECT_TRUE(view.has_two_x().Value());
EXPECT_TRUE(view.has_x_plus_one().Value());
EXPECT_EQ(10, view.two_x().Read());
EXPECT_EQ(6, view.x_plus_one().Read());
EXPECT_EQ(10, view.two_x().UncheckedRead());
EXPECT_EQ(6, view.x_plus_one().UncheckedRead());
view.x().Write(0x80000000U);
EXPECT_FALSE(view.has_two_x().Value());
EXPECT_DEATH(view.two_x().Read(), "");
EXPECT_TRUE(view.has_x_plus_one().Value());
EXPECT_EQ(0x80000001U, view.x_plus_one().Read());
}
TEST(ConditionalVirtual, UncheckedRead) {
::std::array<char, 4> values = {5, 0, 0, 0};
const auto view = MakeStructureWithConditionalValueView(&values[0], 1);
EXPECT_FALSE(view.Ok());
EXPECT_FALSE(view.x().Ok());
EXPECT_DEATH(view.two_x().Read(), "");
EXPECT_EQ(0, view.two_x().UncheckedRead());
}
TEST(ConditionalVirtual, TextFormatWrite) {
::std::array<unsigned char, 4> values = {0, 0, 0, 0x80};
const auto view = MakeStructureWithConditionalValueView(&values);
EXPECT_EQ("{ x: 2147483648, x_plus_one: 2147483649 }",
::emboss::WriteToString(view));
EXPECT_EQ(
"{\n"
" x: 2_147_483_648 # 0x8000_0000\n"
" x_plus_one: 2_147_483_649 # 0x8000_0001\n"
"}",
::emboss::WriteToString(view, ::emboss::MultilineText()));
view.x().Write(5);
EXPECT_EQ("{ x: 5, x_plus_one: 6 }", ::emboss::WriteToString(view));
EXPECT_EQ(
"{\n"
" x: 5 # 0x5\n"
" # two_x: 10 # 0xa\n"
" x_plus_one: 6 # 0x6\n"
"}",
::emboss::WriteToString(view, ::emboss::MultilineText()));
}
TEST(VirtualInCondition, ConditionCheck) {
::std::array<char, 8> values = {5, 0, 0, 0, 50, 0, 0, 0};
const auto view = MakeStructureWithValueInConditionView(&values);
EXPECT_TRUE(view.has_if_two_x_lt_100().Value());
view.x().Write(75);
EXPECT_FALSE(view.has_if_two_x_lt_100().Value());
}
TEST(VirtualInLocation, Offset) {
::std::array<char, 8> values = {5, 0, 0, 0, 50, 0, 0, 0};
const auto view = MakeStructureWithValuesInLocationView(&values);
EXPECT_FALSE(view.Ok());
view.x().Write(2);
EXPECT_TRUE(view.Ok());
EXPECT_EQ(50U, view.offset_two_x().Read());
EXPECT_EQ(50U, view.size_two_x().Read());
view.x().Write(1);
EXPECT_FALSE(view.Ok());
EXPECT_EQ(50 * 0x10000U, view.offset_two_x().Read());
view.x().Write(0);
EXPECT_EQ(0U, view.offset_two_x().Read());
}
TEST(BooleanVirtual, TrueAndFalse) {
::std::array<char, 4> values = {5, 0, 0, 0};
const auto view = MakeStructureWithBoolValueView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_FALSE(view.x_is_ten().Read());
view.x().Write(10);
EXPECT_TRUE(view.x_is_ten().Read());
}
TEST(EnumVirtual, SmallAndLarge) {
::std::array<char, 4> values = {5, 0, 0, 0};
const auto view = MakeStructureWithEnumValueView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_EQ(StructureWithEnumValue::Category::SMALL, view.x_size().Read());
view.x().Write(100);
EXPECT_EQ(StructureWithEnumValue::Category::LARGE, view.x_size().Read());
}
TEST(BitsVirtual, Sum) {
::std::array<char, 4> values = {5, 0, 10, 0};
const auto view = MakeStructureWithBitsWithValueView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_EQ(5, view.b().a().Read());
EXPECT_EQ(5, view.alias_of_b_a().Read());
EXPECT_EQ(10, view.b().b().Read());
EXPECT_EQ(15, view.b().sum().Read());
EXPECT_EQ(15, view.alias_of_b_sum().Read());
view.alias_of_b_a().Write(20);
EXPECT_EQ(20, view.b().a().Read());
EXPECT_EQ(20, view.alias_of_b_a().Read());
EXPECT_EQ(20, values[0]);
}
TEST(ForeignConstants, ForeignConstants) {
static_assert(StructureUsingForeignConstants::one_hundred() == 100,
"StructureUsingForeignConstants::one_hundred() == 100");
::std::array<char, 14> values = {5, 0, 0, 0, 10, 0, 0, 0, 15, 0, 20, 0, 0, 0};
const auto view = MakeStructureUsingForeignConstantsView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_EQ(20U, view.x().Read());
EXPECT_EQ(100, view.one_hundred().Read());
}
TEST(HasField, HasField) {
::std::array<char, 3> values = {0, 0, 0};
const auto view = MakeHasFieldView(&values);
EXPECT_FALSE(view.Ok()); // There is not enough information to determine if
// view.has_y(), so the view is not Ok().
view.z().Write(11);
EXPECT_TRUE(view.Ok());
EXPECT_FALSE(view.has_y().Value());
EXPECT_TRUE(view.has_x().Value());
EXPECT_FALSE(view.x().has_y().Value());
EXPECT_FALSE(view.x_has_y().Read());
EXPECT_FALSE(view.x_has_y().UncheckedRead());
view.x().v().Write(11);
EXPECT_TRUE(view.Ok());
EXPECT_TRUE(view.has_y().Value());
EXPECT_TRUE(view.has_x().Value());
EXPECT_TRUE(view.x().has_y().Value());
EXPECT_TRUE(view.x_has_y().Read());
EXPECT_TRUE(view.x_has_y().UncheckedRead());
}
TEST(RestrictedAlias, RestrictedAlias) {
::std::array<char, 5> values = {1, 2, 3, 4, 5};
const auto view = MakeRestrictedAliasView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_TRUE(view.has_a_b().Value());
EXPECT_TRUE(view.a_b().Ok());
EXPECT_FALSE(view.has_a_b_alias().Value());
EXPECT_FALSE(view.a_b_alias().Ok());
EXPECT_FALSE(view.a_b_alias().a().Ok());
EXPECT_FALSE(view.a_b_alias().b().Ok());
view.alias_switch().Write(11);
EXPECT_TRUE(view.has_a_b().Value());
EXPECT_TRUE(view.a_b().Ok());
EXPECT_TRUE(view.has_a_b_alias().Value());
EXPECT_TRUE(view.a_b_alias().Ok());
}
TEST(VirtualWithConditionalComponent, ReadWhenAllPresent) {
::std::array<char, 2> values = {0, 0};
const auto view = MakeVirtualUnconditionallyUsesConditionalView(&values);
EXPECT_TRUE(view.x_nor_xc().Read());
EXPECT_TRUE(view.x_nor_xc().UncheckedRead());
}
TEST(VirtualWithConditionalComponent, ReadWhenNotAllPresent) {
::std::array<char, 2> values = {1, 0};
const auto view = MakeVirtualUnconditionallyUsesConditionalView(&values);
EXPECT_FALSE(view.x_nor_xc().Read());
EXPECT_FALSE(view.x_nor_xc().UncheckedRead());
}
TEST(IntrinsicSize, SizeInBytes) {
::std::array<char, 1> values = {10};
const auto view = MakeUsesSizeView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_TRUE(view.IntrinsicSizeInBytes().Ok());
EXPECT_EQ(1, view.IntrinsicSizeInBytes().Read());
EXPECT_EQ(1, UsesSizeView::IntrinsicSizeInBytes().Read());
EXPECT_EQ(1, UsesSize::IntrinsicSizeInBytes());
EXPECT_TRUE(view.r().IntrinsicSizeInBits().Ok());
EXPECT_EQ(8, view.r().IntrinsicSizeInBits().Read());
EXPECT_EQ(8, UsesSize::R::IntrinsicSizeInBits());
EXPECT_EQ(values[0], view.r().q().Read());
EXPECT_EQ(values[0] + 1, view.r_q_plus_byte_size().Read());
EXPECT_EQ(values[0] + 8, view.r().q_plus_bit_size().Read());
}
TEST(VirtualFields, SizeInBytes) {
const ::std::array</**/ ::std::uint8_t, 8> values = {0x11, 0x11, 0x11, 0x11,
0x22, 0x22, 0x22, 0x22};
const auto view = MakeUsesExternalSizeView(&values);
EXPECT_TRUE(view.Ok());
EXPECT_EQ(8U, view.SizeInBytes());
EXPECT_EQ(view.x().SizeInBytes(), 4U);
EXPECT_EQ(view.y().SizeInBytes(), 4U);
EXPECT_EQ(view.x().value().Read(), 0x11111111U);
EXPECT_EQ(view.y().value().Read(), 0x22222222U);
EXPECT_TRUE(view.IntrinsicSizeInBytes().Ok());
EXPECT_EQ(8, UsesExternalSizeView::IntrinsicSizeInBytes().Read());
EXPECT_EQ(8, UsesExternalSize::MaxSizeInBytes());
}
TEST(WriteTransform, Write) {
::std::array<char, 1> values = {0};
const auto view = MakeImplicitWriteBackView(&values);
view.x_plus_ten().Write(11);
EXPECT_EQ(1, view.x().Read());
EXPECT_EQ(11, view.x_plus_ten().Read());
view.ten_plus_x().Write(12);
EXPECT_EQ(2, view.x().Read());
EXPECT_EQ(12, view.ten_plus_x().Read());
EXPECT_TRUE((::std::is_same<decltype(view.x_minus_ten())::ValueType,
::std::int32_t>::value));
view.x_minus_ten().Write(-7);
EXPECT_EQ(3, view.x().Read());
EXPECT_EQ(-7, view.x_minus_ten().Read());
view.ten_minus_x().Write(6);
EXPECT_EQ(4, view.x().Read());
EXPECT_EQ(6, view.ten_minus_x().Read());
view.ten_minus_x_plus_ten().Write(4);
EXPECT_EQ(16, view.x().Read());
EXPECT_EQ(4, view.ten_minus_x_plus_ten().Read());
}
TEST(WriteTransform, CouldWriteValue) {
::std::array<char, 1> values = {0};
const auto view = MakeImplicitWriteBackView(&values);
EXPECT_EQ(0, view.x().Read());
// x is UInt:8, so has range [0, 255].
EXPECT_FALSE(view.x_plus_ten().CouldWriteValue(9));
EXPECT_TRUE(view.x_plus_ten().CouldWriteValue(10));
EXPECT_TRUE(view.x_plus_ten().CouldWriteValue(265));
EXPECT_FALSE(view.x_plus_ten().CouldWriteValue(266));
EXPECT_FALSE(view.ten_plus_x().CouldWriteValue(9));
EXPECT_TRUE(view.ten_plus_x().CouldWriteValue(10));
EXPECT_TRUE(view.ten_plus_x().CouldWriteValue(265));
EXPECT_FALSE(view.ten_plus_x().CouldWriteValue(266));
EXPECT_FALSE(view.x_minus_ten().CouldWriteValue(-11));
EXPECT_TRUE(view.x_minus_ten().CouldWriteValue(-10));
EXPECT_TRUE(view.x_minus_ten().CouldWriteValue(245));
EXPECT_FALSE(view.x_minus_ten().CouldWriteValue(246));
EXPECT_FALSE(view.ten_minus_x().CouldWriteValue(-246));
EXPECT_TRUE(view.ten_minus_x().CouldWriteValue(-245));
EXPECT_TRUE(view.ten_minus_x().CouldWriteValue(10));
EXPECT_FALSE(view.ten_minus_x().CouldWriteValue(11));
EXPECT_FALSE(view.ten_minus_x_plus_ten().CouldWriteValue(-236));
EXPECT_TRUE(view.ten_minus_x_plus_ten().CouldWriteValue(-235));
EXPECT_TRUE(view.ten_minus_x_plus_ten().CouldWriteValue(20));
EXPECT_FALSE(view.ten_minus_x_plus_ten().CouldWriteValue(21));
}
} // namespace
} // namespace test
} // namespace emboss