1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "runtime/cpp/emboss_enum_view.h"
16
17 #include "gtest/gtest.h"
18 #include "runtime/cpp/emboss_prelude.h"
19 #include "runtime/cpp/emboss_text_util.h"
20
21 namespace emboss {
22 namespace support {
23 namespace test {
24
25 template </**/ ::std::size_t kBits>
26 using LittleEndianBitBlockN =
27 BitBlock<LittleEndianByteOrderer<ReadWriteContiguousBuffer>, kBits>;
28
29 enum class Foo : ::std::int64_t {
30 kMin = -0x7fffffffffffffffL - 1,
31 kOne = 1,
32 kTwo = 2,
33 kBig = 0x0e0f10,
34 kBigBackwards = 0x100f0e,
35 kReallyBig = 0x090a0b0c0d0e0f10L,
36 kReallyBigBackwards = 0x100f0e0d0c0b0a09L,
37 k2to24MinusOne = (1L << 24) - 1,
38 k2to24 = 1L << 24,
39 kMax = 0x7fffffffffffffffL,
40 };
41
TryToGetEnumFromName(const char * name,Foo * result)42 static inline bool TryToGetEnumFromName(const char *name, Foo *result) {
43 if (!strcmp("kMin", name)) {
44 *result = Foo::kMin;
45 return true;
46 }
47 if (!strcmp("kOne", name)) {
48 *result = Foo::kOne;
49 return true;
50 }
51 if (!strcmp("kTwo", name)) {
52 *result = Foo::kTwo;
53 return true;
54 }
55 if (!strcmp("kBig", name)) {
56 *result = Foo::kBig;
57 return true;
58 }
59 if (!strcmp("kBigBackwards", name)) {
60 *result = Foo::kBigBackwards;
61 return true;
62 }
63 if (!strcmp("kReallyBig", name)) {
64 *result = Foo::kReallyBig;
65 return true;
66 }
67 if (!strcmp("kReallyBigBackwards", name)) {
68 *result = Foo::kReallyBigBackwards;
69 return true;
70 }
71 if (!strcmp("k2to24MinusOne", name)) {
72 *result = Foo::k2to24MinusOne;
73 return true;
74 }
75 if (!strcmp("k2to24", name)) {
76 *result = Foo::k2to24;
77 return true;
78 }
79 if (!strcmp("kMax", name)) {
80 *result = Foo::kMax;
81 return true;
82 }
83 return false;
84 }
85
TryToGetNameFromEnum(Foo value)86 static inline const char *TryToGetNameFromEnum(Foo value) {
87 switch (value) {
88 case Foo::kMin:
89 return "kMin";
90 case Foo::kOne:
91 return "kOne";
92 case Foo::kTwo:
93 return "kTwo";
94 case Foo::kBig:
95 return "kBig";
96 case Foo::kBigBackwards:
97 return "kBigBackwards";
98 case Foo::kReallyBig:
99 return "kReallyBig";
100 case Foo::kReallyBigBackwards:
101 return "kReallyBigBackwards";
102 case Foo::k2to24MinusOne:
103 return "k2to24MinusOne";
104 case Foo::k2to24:
105 return "k2to24";
106 case Foo::kMax:
107 return "kMax";
108 default:
109 return nullptr;
110 }
111 }
112
113 template </**/ ::std::size_t kBits>
114 using FooViewN = EnumView<Foo, FixedSizeViewParameters<kBits, AllValuesAreOk>,
115 LittleEndianBitBlockN<kBits>>;
116
117 template <int kMaxBits>
CheckEnumViewSizeInBits()118 void CheckEnumViewSizeInBits() {
119 const int size_in_bits =
120 EnumView<Foo, FixedSizeViewParameters<kMaxBits, AllValuesAreOk>,
121 OffsetBitBlock<LittleEndianBitBlockN<64>>>::SizeInBits();
122 EXPECT_EQ(size_in_bits, kMaxBits);
123 return CheckEnumViewSizeInBits<kMaxBits - 1>();
124 }
125
126 template <>
CheckEnumViewSizeInBits()127 void CheckEnumViewSizeInBits<0>() {
128 return;
129 }
130
TEST(EnumView,SizeInBits)131 TEST(EnumView, SizeInBits) { CheckEnumViewSizeInBits<64>(); }
132
TEST(EnumView,ValueType)133 TEST(EnumView, ValueType) {
134 using BitBlockType = OffsetBitBlock<LittleEndianBitBlockN<64>>;
135 EXPECT_TRUE(
136 (::std::is_same<Foo,
137 EnumView<Foo, FixedSizeViewParameters<8, AllValuesAreOk>,
138 BitBlockType>::ValueType>::value));
139 EXPECT_TRUE(
140 (::std::is_same<Foo,
141 EnumView<Foo, FixedSizeViewParameters<6, AllValuesAreOk>,
142 BitBlockType>::ValueType>::value));
143 EXPECT_TRUE(
144 (::std::is_same<Foo,
145 EnumView<Foo, FixedSizeViewParameters<33, AllValuesAreOk>,
146 BitBlockType>::ValueType>::value));
147 EXPECT_TRUE(
148 (::std::is_same<Foo,
149 EnumView<Foo, FixedSizeViewParameters<64, AllValuesAreOk>,
150 BitBlockType>::ValueType>::value));
151 }
152
TEST(EnumView,CouldWriteValue)153 TEST(EnumView, CouldWriteValue) {
154 EXPECT_TRUE(FooViewN<64>::CouldWriteValue(Foo::kMax));
155 EXPECT_TRUE(FooViewN<64>::CouldWriteValue(Foo::kMax));
156 EXPECT_TRUE(FooViewN<24>::CouldWriteValue(Foo::k2to24MinusOne));
157 EXPECT_FALSE(FooViewN<24>::CouldWriteValue(Foo::k2to24));
158 }
159
TEST(EnumView,ReadAndWriteWithSufficientBuffer)160 TEST(EnumView, ReadAndWriteWithSufficientBuffer) {
161 ::std::vector</**/ ::std::uint8_t> bytes = {
162 {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
163 auto enum64_view = FooViewN<64>{ReadWriteContiguousBuffer{bytes.data(), 8}};
164 EXPECT_EQ(Foo::kReallyBig, enum64_view.Read());
165 EXPECT_EQ(Foo::kReallyBig, enum64_view.UncheckedRead());
166 enum64_view.Write(Foo::kReallyBigBackwards);
167 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x09, 0x0a, 0x0b, 0x0c, 0x0d,
168 0x0e, 0x0f, 0x10, 0x08}),
169 bytes);
170 enum64_view.UncheckedWrite(Foo::kReallyBig);
171 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x10, 0x0f, 0x0e, 0x0d, 0x0c,
172 0x0b, 0x0a, 0x09, 0x08}),
173 bytes);
174 EXPECT_TRUE(enum64_view.TryToWrite(Foo::kReallyBigBackwards));
175 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x09, 0x0a, 0x0b, 0x0c, 0x0d,
176 0x0e, 0x0f, 0x10, 0x08}),
177 bytes);
178 EXPECT_TRUE(enum64_view.Ok());
179 EXPECT_TRUE(enum64_view.IsComplete());
180 }
181
TEST(EnumView,ReadAndWriteWithInsufficientBuffer)182 TEST(EnumView, ReadAndWriteWithInsufficientBuffer) {
183 ::std::vector</**/ ::std::uint8_t> bytes = {
184 {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
185 auto enum64_view = FooViewN<64>{ReadWriteContiguousBuffer{bytes.data(), 4}};
186 EXPECT_EQ(Foo::kReallyBig, enum64_view.UncheckedRead());
187 #if EMBOSS_CHECK_ABORTS
188 EXPECT_DEATH(enum64_view.Read(), "");
189 EXPECT_DEATH(enum64_view.Write(Foo::kReallyBigBackwards), "");
190 #endif // EMBOSS_CHECK_ABORTS
191 EXPECT_FALSE(enum64_view.TryToWrite(Foo::kReallyBigBackwards));
192 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x10, 0x0f, 0x0e, 0x0d, 0x0c,
193 0x0b, 0x0a, 0x09, 0x08}),
194 bytes);
195 enum64_view.UncheckedWrite(Foo::kReallyBigBackwards);
196 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x09, 0x0a, 0x0b, 0x0c, 0x0d,
197 0x0e, 0x0f, 0x10, 0x08}),
198 bytes);
199 EXPECT_FALSE(enum64_view.Ok());
200 EXPECT_FALSE(enum64_view.IsComplete());
201 }
202
TEST(EnumView,NonPowerOfTwoSize)203 TEST(EnumView, NonPowerOfTwoSize) {
204 ::std::vector</**/ ::std::uint8_t> bytes = {{0x10, 0x0f, 0x0e, 0x0d}};
205 auto enum24_view = FooViewN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}};
206 EXPECT_EQ(Foo::kBig, enum24_view.Read());
207 EXPECT_EQ(Foo::kBig, enum24_view.UncheckedRead());
208 enum24_view.Write(Foo::kBigBackwards);
209 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x0e, 0x0f, 0x10, 0x0d}),
210 bytes);
211 #if EMBOSS_CHECK_ABORTS
212 EXPECT_DEATH(enum24_view.Write(Foo::k2to24), "");
213 #endif // EMBOSS_CHECK_ABORTS
214 enum24_view.UncheckedWrite(Foo::k2to24);
215 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x00, 0x00, 0x00, 0x0d}),
216 bytes);
217 EXPECT_TRUE(enum24_view.Ok());
218 EXPECT_TRUE(enum24_view.IsComplete());
219 }
220
TEST(EnumView,NonPowerOfTwoSizeInsufficientBuffer)221 TEST(EnumView, NonPowerOfTwoSizeInsufficientBuffer) {
222 ::std::vector</**/ ::std::uint8_t> bytes = {{0x10, 0x0f, 0x0e, 0x0d}};
223 auto enum24_view = FooViewN<24>{ReadWriteContiguousBuffer{bytes.data(), 2}};
224 EXPECT_EQ(Foo::kBig, enum24_view.UncheckedRead());
225 #if EMBOSS_CHECK_ABORTS
226 EXPECT_DEATH(enum24_view.Read(), "");
227 EXPECT_DEATH(enum24_view.Write(Foo::kBigBackwards), "");
228 #endif // EMBOSS_CHECK_ABORTS
229 enum24_view.UncheckedWrite(Foo::kBigBackwards);
230 EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{0x0e, 0x0f, 0x10, 0x0d}),
231 bytes);
232 EXPECT_FALSE(enum24_view.Ok());
233 EXPECT_FALSE(enum24_view.IsComplete());
234 }
235
TEST(EnumView,UpdateFromText)236 TEST(EnumView, UpdateFromText) {
237 ::std::vector</**/ ::std::uint8_t> bytes = {
238 {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
239 const auto enum64_view =
240 FooViewN<64>{ReadWriteContiguousBuffer{bytes.data(), 8}};
241 EXPECT_TRUE(UpdateFromText(enum64_view, "kBig"));
242 EXPECT_EQ(Foo::kBig, enum64_view.Read());
243 EXPECT_TRUE(UpdateFromText(enum64_view, "k2to24"));
244 EXPECT_EQ(Foo::k2to24, enum64_view.Read());
245 EXPECT_FALSE(UpdateFromText(enum64_view, "k2to24M"));
246 EXPECT_EQ(Foo::k2to24, enum64_view.Read());
247 EXPECT_TRUE(UpdateFromText(enum64_view, "k2to24MinusOne"));
248 EXPECT_EQ(Foo::k2to24MinusOne, enum64_view.Read());
249 EXPECT_TRUE(UpdateFromText(enum64_view, "0x0e0f10"));
250 EXPECT_EQ(Foo::kBig, enum64_view.Read());
251 EXPECT_TRUE(UpdateFromText(enum64_view, "0x7654321"));
252 EXPECT_EQ(static_cast<Foo>(0x7654321), enum64_view.Read());
253 EXPECT_FALSE(UpdateFromText(enum64_view, "0y0"));
254 EXPECT_EQ(static_cast<Foo>(0x7654321), enum64_view.Read());
255 EXPECT_FALSE(UpdateFromText(enum64_view, "-x"));
256 EXPECT_EQ(static_cast<Foo>(0x7654321), enum64_view.Read());
257 EXPECT_TRUE(UpdateFromText(enum64_view, "-0x8000_0000_0000_0000"));
258 EXPECT_EQ(Foo::kMin, enum64_view.Read());
259 }
260
TEST(EnumView,WriteToText)261 TEST(EnumView, WriteToText) {
262 ::std::vector</**/ ::std::uint8_t> bytes = {
263 {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
264 const auto enum64_view =
265 FooViewN<64>{ReadWriteContiguousBuffer{bytes.data(), 8}};
266 EXPECT_EQ("kReallyBig", WriteToString(enum64_view));
267 EXPECT_EQ("kReallyBig # 651345242494996240",
268 WriteToString(enum64_view, TextOutputOptions().WithComments(true)));
269 EXPECT_EQ("kReallyBig # 0x90a0b0c0d0e0f10",
270 WriteToString(
271 enum64_view,
272 TextOutputOptions().WithComments(true).WithNumericBase(16)));
273 enum64_view.Write(static_cast<Foo>(123));
274 EXPECT_EQ("123", WriteToString(enum64_view));
275 EXPECT_EQ("123",
276 WriteToString(enum64_view, TextOutputOptions().WithComments(true)));
277 EXPECT_EQ("0x7b",
278 WriteToString(
279 enum64_view,
280 TextOutputOptions().WithComments(true).WithNumericBase(16)));
281 }
282
283 } // namespace test
284 } // namespace support
285 } // namespace emboss
286