| 1 | //===-- RegisterFlagsTest.cpp ---------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/Target/RegisterFlags.h" |
| 10 | #include "lldb/Utility/StreamString.h" |
| 11 | #include "gmock/gmock.h" |
| 12 | #include "gtest/gtest.h" |
| 13 | |
| 14 | using namespace lldb_private; |
| 15 | using namespace lldb; |
| 16 | |
| 17 | TEST(RegisterFlagsTest, Field) { |
| 18 | // We assume that start <= end is always true, so that is not tested here. |
| 19 | |
| 20 | RegisterFlags::Field f1("abc" , 0); |
| 21 | ASSERT_EQ(f1.GetName(), "abc" ); |
| 22 | // start == end means a 1 bit field. |
| 23 | ASSERT_EQ(f1.GetSizeInBits(), (unsigned)1); |
| 24 | ASSERT_EQ(f1.GetMask(), (uint64_t)1); |
| 25 | ASSERT_EQ(f1.GetValue(0), (uint64_t)0); |
| 26 | ASSERT_EQ(f1.GetValue(3), (uint64_t)1); |
| 27 | |
| 28 | // End is inclusive meaning that start 0 to end 1 includes bit 1 |
| 29 | // to make a 2 bit field. |
| 30 | RegisterFlags::Field f2("" , 0, 1); |
| 31 | ASSERT_EQ(f2.GetSizeInBits(), (unsigned)2); |
| 32 | ASSERT_EQ(f2.GetMask(), (uint64_t)3); |
| 33 | ASSERT_EQ(f2.GetValue(UINT64_MAX), (uint64_t)3); |
| 34 | ASSERT_EQ(f2.GetValue(UINT64_MAX & ~(uint64_t)3), (uint64_t)0); |
| 35 | |
| 36 | // If the field doesn't start at 0 we need to shift up/down |
| 37 | // to account for it. |
| 38 | RegisterFlags::Field f3("" , 2, 5); |
| 39 | ASSERT_EQ(f3.GetSizeInBits(), (unsigned)4); |
| 40 | ASSERT_EQ(f3.GetMask(), (uint64_t)0x3c); |
| 41 | ASSERT_EQ(f3.GetValue(UINT64_MAX), (uint64_t)0xf); |
| 42 | ASSERT_EQ(f3.GetValue(UINT64_MAX & ~(uint64_t)0x3c), (uint64_t)0); |
| 43 | |
| 44 | // Fields are sorted lowest starting bit first. |
| 45 | ASSERT_TRUE(f2 < f3); |
| 46 | ASSERT_FALSE(f3 < f1); |
| 47 | ASSERT_FALSE(f1 < f2); |
| 48 | ASSERT_FALSE(f1 < f1); |
| 49 | } |
| 50 | |
| 51 | static RegisterFlags::Field make_field(unsigned start, unsigned end) { |
| 52 | return RegisterFlags::Field("" , start, end); |
| 53 | } |
| 54 | |
| 55 | static RegisterFlags::Field make_field(unsigned bit) { |
| 56 | return RegisterFlags::Field("" , bit); |
| 57 | } |
| 58 | |
| 59 | TEST(RegisterFlagsTest, FieldOverlaps) { |
| 60 | // Single bit fields |
| 61 | ASSERT_FALSE(make_field(0, 0).Overlaps(make_field(1))); |
| 62 | ASSERT_TRUE(make_field(1, 1).Overlaps(make_field(1))); |
| 63 | ASSERT_FALSE(make_field(1, 1).Overlaps(make_field(3))); |
| 64 | |
| 65 | ASSERT_TRUE(make_field(0, 1).Overlaps(make_field(1, 2))); |
| 66 | ASSERT_TRUE(make_field(1, 2).Overlaps(make_field(0, 1))); |
| 67 | ASSERT_FALSE(make_field(0, 1).Overlaps(make_field(2, 3))); |
| 68 | ASSERT_FALSE(make_field(2, 3).Overlaps(make_field(0, 1))); |
| 69 | |
| 70 | ASSERT_FALSE(make_field(1, 5).Overlaps(make_field(10, 20))); |
| 71 | ASSERT_FALSE(make_field(15, 30).Overlaps(make_field(7, 12))); |
| 72 | } |
| 73 | |
| 74 | TEST(RegisterFlagsTest, PaddingDistance) { |
| 75 | // We assume that this method is always called with a more significant |
| 76 | // (start bit is higher) field first and that they do not overlap. |
| 77 | |
| 78 | // [field 1][field 2] |
| 79 | ASSERT_EQ(make_field(1, 1).PaddingDistance(make_field(0)), 0ULL); |
| 80 | // [field 1][..][field 2] |
| 81 | ASSERT_EQ(make_field(2, 2).PaddingDistance(make_field(0)), 1ULL); |
| 82 | // [field 1][field 1][field 2] |
| 83 | ASSERT_EQ(make_field(1, 2).PaddingDistance(make_field(0)), 0ULL); |
| 84 | // [field 1][30 bits free][field 2] |
| 85 | ASSERT_EQ(make_field(31, 31).PaddingDistance(make_field(0)), 30ULL); |
| 86 | } |
| 87 | |
| 88 | static void test_padding(const std::vector<RegisterFlags::Field> &fields, |
| 89 | const std::vector<RegisterFlags::Field> &expected) { |
| 90 | RegisterFlags rf("" , 4, fields); |
| 91 | EXPECT_THAT(expected, ::testing::ContainerEq(rf.GetFields())); |
| 92 | } |
| 93 | |
| 94 | TEST(RegisterFlagsTest, RegisterFlagsPadding) { |
| 95 | // When creating a set of flags we assume that: |
| 96 | // * There are >= 1 fields. |
| 97 | // * They are sorted in descending order. |
| 98 | // * There may be gaps between each field. |
| 99 | |
| 100 | // Needs no padding |
| 101 | auto fields = |
| 102 | std::vector<RegisterFlags::Field>{make_field(start: 16, end: 31), make_field(start: 0, end: 15)}; |
| 103 | test_padding(fields, expected: fields); |
| 104 | |
| 105 | // Needs padding in between the fields, single bit. |
| 106 | test_padding(fields: {make_field(start: 17, end: 31), make_field(start: 0, end: 15)}, |
| 107 | expected: {make_field(start: 17, end: 31), make_field(bit: 16), make_field(start: 0, end: 15)}); |
| 108 | // Multiple bits of padding. |
| 109 | test_padding(fields: {make_field(start: 17, end: 31), make_field(start: 0, end: 14)}, |
| 110 | expected: {make_field(start: 17, end: 31), make_field(start: 15, end: 16), make_field(start: 0, end: 14)}); |
| 111 | |
| 112 | // Padding before first field, single bit. |
| 113 | test_padding(fields: {make_field(start: 0, end: 30)}, expected: {make_field(bit: 31), make_field(start: 0, end: 30)}); |
| 114 | // Multiple bits. |
| 115 | test_padding(fields: {make_field(start: 0, end: 15)}, expected: {make_field(start: 16, end: 31), make_field(start: 0, end: 15)}); |
| 116 | |
| 117 | // Padding after last field, single bit. |
| 118 | test_padding(fields: {make_field(start: 1, end: 31)}, expected: {make_field(start: 1, end: 31), make_field(bit: 0)}); |
| 119 | // Multiple bits. |
| 120 | test_padding(fields: {make_field(start: 2, end: 31)}, expected: {make_field(start: 2, end: 31), make_field(start: 0, end: 1)}); |
| 121 | |
| 122 | // Fields need padding before, in between and after. |
| 123 | // [31-28][field 27-24][23-22][field 21-20][19-12][field 11-8][7-0] |
| 124 | test_padding(fields: {make_field(start: 24, end: 27), make_field(start: 20, end: 21), make_field(start: 8, end: 11)}, |
| 125 | expected: {make_field(start: 28, end: 31), make_field(start: 24, end: 27), make_field(start: 22, end: 23), |
| 126 | make_field(start: 20, end: 21), make_field(start: 12, end: 19), make_field(start: 8, end: 11), |
| 127 | make_field(start: 0, end: 7)}); |
| 128 | } |
| 129 | |
| 130 | TEST(RegisterFieldsTest, ReverseFieldOrder) { |
| 131 | // Unchanged |
| 132 | RegisterFlags rf("" , 4, {make_field(start: 0, end: 31)}); |
| 133 | ASSERT_EQ(0x12345678ULL, (unsigned long long)rf.ReverseFieldOrder(0x12345678)); |
| 134 | |
| 135 | // Swap the two halves around. |
| 136 | RegisterFlags rf2("" , 4, {make_field(start: 16, end: 31), make_field(start: 0, end: 15)}); |
| 137 | ASSERT_EQ(0x56781234ULL, (unsigned long long)rf2.ReverseFieldOrder(0x12345678)); |
| 138 | |
| 139 | // Many small fields. |
| 140 | RegisterFlags rf3( |
| 141 | "" , 4, {make_field(bit: 31), make_field(bit: 30), make_field(bit: 29), make_field(bit: 28)}); |
| 142 | ASSERT_EQ(0x00000005ULL, rf3.ReverseFieldOrder(0xA0000000)); |
| 143 | } |
| 144 | |
| 145 | TEST(RegisterFlagsTest, AsTable) { |
| 146 | // Anonymous fields are shown with an empty name cell, |
| 147 | // whether they are known up front or added during construction. |
| 148 | RegisterFlags anon_field("" , 4, {make_field(start: 0, end: 31)}); |
| 149 | ASSERT_EQ("| 31-0 |\n" |
| 150 | "|------|\n" |
| 151 | "| |" , |
| 152 | anon_field.AsTable(100)); |
| 153 | |
| 154 | RegisterFlags anon_with_pad("" , 4, {make_field(start: 16, end: 31)}); |
| 155 | ASSERT_EQ("| 31-16 | 15-0 |\n" |
| 156 | "|-------|------|\n" |
| 157 | "| | |" , |
| 158 | anon_with_pad.AsTable(100)); |
| 159 | |
| 160 | // Use the wider of position and name to set the column width. |
| 161 | RegisterFlags name_wider("" , 4, {RegisterFlags::Field("aardvark" , 0, 31)}); |
| 162 | ASSERT_EQ("| 31-0 |\n" |
| 163 | "|----------|\n" |
| 164 | "| aardvark |" , |
| 165 | name_wider.AsTable(100)); |
| 166 | // When the padding is an odd number, put the remaining 1 on the right. |
| 167 | RegisterFlags pos_wider("" , 4, {RegisterFlags::Field("?" , 0, 31)}); |
| 168 | ASSERT_EQ("| 31-0 |\n" |
| 169 | "|------|\n" |
| 170 | "| ? |" , |
| 171 | pos_wider.AsTable(100)); |
| 172 | |
| 173 | // Single bit fields don't need to show start and end, just one of them. |
| 174 | RegisterFlags single_bit("" , 4, {make_field(bit: 31)}); |
| 175 | ASSERT_EQ("| 31 | 30-0 |\n" |
| 176 | "|----|------|\n" |
| 177 | "| | |" , |
| 178 | single_bit.AsTable(100)); |
| 179 | |
| 180 | // Columns are printed horizontally if max width allows. |
| 181 | RegisterFlags many_fields("" , 4, |
| 182 | {RegisterFlags::Field("cat" , 28, 31), |
| 183 | RegisterFlags::Field("pigeon" , 20, 23), |
| 184 | RegisterFlags::Field("wolf" , 12), |
| 185 | RegisterFlags::Field("x" , 0, 4)}); |
| 186 | ASSERT_EQ("| 31-28 | 27-24 | 23-20 | 19-13 | 12 | 11-5 | 4-0 |\n" |
| 187 | "|-------|-------|--------|-------|------|------|-----|\n" |
| 188 | "| cat | | pigeon | | wolf | | x |" , |
| 189 | many_fields.AsTable(100)); |
| 190 | |
| 191 | // max_width tells us when we need to split into further tables. |
| 192 | // Here no split is needed. |
| 193 | RegisterFlags exact_max_single_col("" , 4, {RegisterFlags::Field("?" , 0, 31)}); |
| 194 | ASSERT_EQ("| 31-0 |\n" |
| 195 | "|------|\n" |
| 196 | "| ? |" , |
| 197 | exact_max_single_col.AsTable(9)); |
| 198 | RegisterFlags exact_max_two_col( |
| 199 | "" , 4, |
| 200 | {RegisterFlags::Field("?" , 16, 31), RegisterFlags::Field("#" , 0, 15)}); |
| 201 | ASSERT_EQ("| 31-16 | 15-0 |\n" |
| 202 | "|-------|------|\n" |
| 203 | "| ? | # |" , |
| 204 | exact_max_two_col.AsTable(16)); |
| 205 | |
| 206 | // If max is less than a single column, just print the single column. The user |
| 207 | // will have to put up with some wrapping in this niche case. |
| 208 | RegisterFlags zero_max_single_col("" , 4, {RegisterFlags::Field("?" , 0, 31)}); |
| 209 | ASSERT_EQ("| 31-0 |\n" |
| 210 | "|------|\n" |
| 211 | "| ? |" , |
| 212 | zero_max_single_col.AsTable(0)); |
| 213 | // Same logic for any following columns. Effectively making a "vertical" |
| 214 | // table, just with more grid lines. |
| 215 | RegisterFlags zero_max_two_col( |
| 216 | "" , 4, |
| 217 | {RegisterFlags::Field("?" , 16, 31), RegisterFlags::Field("#" , 0, 15)}); |
| 218 | ASSERT_EQ("| 31-16 |\n" |
| 219 | "|-------|\n" |
| 220 | "| ? |\n" |
| 221 | "\n" |
| 222 | "| 15-0 |\n" |
| 223 | "|------|\n" |
| 224 | "| # |" , |
| 225 | zero_max_two_col.AsTable(0)); |
| 226 | |
| 227 | RegisterFlags max_less_than_single_col("" , 4, |
| 228 | {RegisterFlags::Field("?" , 0, 31)}); |
| 229 | ASSERT_EQ("| 31-0 |\n" |
| 230 | "|------|\n" |
| 231 | "| ? |" , |
| 232 | max_less_than_single_col.AsTable(3)); |
| 233 | RegisterFlags max_less_than_two_col( |
| 234 | "" , 4, |
| 235 | {RegisterFlags::Field("?" , 16, 31), RegisterFlags::Field("#" , 0, 15)}); |
| 236 | ASSERT_EQ("| 31-16 |\n" |
| 237 | "|-------|\n" |
| 238 | "| ? |\n" |
| 239 | "\n" |
| 240 | "| 15-0 |\n" |
| 241 | "|------|\n" |
| 242 | "| # |" , |
| 243 | max_less_than_two_col.AsTable(9)); |
| 244 | RegisterFlags max_many_columns( |
| 245 | "" , 4, |
| 246 | {RegisterFlags::Field("A" , 24, 31), RegisterFlags::Field("B" , 16, 23), |
| 247 | RegisterFlags::Field("C" , 8, 15), |
| 248 | RegisterFlags::Field("really long name" , 0, 7)}); |
| 249 | ASSERT_EQ("| 31-24 | 23-16 |\n" |
| 250 | "|-------|-------|\n" |
| 251 | "| A | B |\n" |
| 252 | "\n" |
| 253 | "| 15-8 |\n" |
| 254 | "|------|\n" |
| 255 | "| C |\n" |
| 256 | "\n" |
| 257 | "| 7-0 |\n" |
| 258 | "|------------------|\n" |
| 259 | "| really long name |" , |
| 260 | max_many_columns.AsTable(23)); |
| 261 | } |
| 262 | |
| 263 | TEST(RegisterFlagsTest, DumpEnums) { |
| 264 | ASSERT_EQ(RegisterFlags("" , 8, {RegisterFlags::Field{"A" , 0}}).DumpEnums(80), |
| 265 | "" ); |
| 266 | |
| 267 | FieldEnum basic_enum("test" , {{0, "an_enumerator" }}); |
| 268 | ASSERT_EQ(RegisterFlags("" , 8, {RegisterFlags::Field{"A" , 0, 0, &basic_enum}}) |
| 269 | .DumpEnums(80), |
| 270 | "A: 0 = an_enumerator" ); |
| 271 | |
| 272 | // If width is smaller than the enumerator name, print it anyway. |
| 273 | ASSERT_EQ(RegisterFlags("" , 8, {RegisterFlags::Field{"A" , 0, 0, &basic_enum}}) |
| 274 | .DumpEnums(5), |
| 275 | "A: 0 = an_enumerator" ); |
| 276 | |
| 277 | // Multiple values can go on the same line, up to the width. |
| 278 | FieldEnum more_enum("long_enum" , |
| 279 | {{0, "an_enumerator" }, |
| 280 | {1, "another_enumerator" }, |
| 281 | {2, "a_very_very_long_enumerator_has_its_own_line" }, |
| 282 | {3, "small" }, |
| 283 | {4, "small2" }}); |
| 284 | ASSERT_EQ(RegisterFlags("" , 8, {RegisterFlags::Field{"A" , 0, 2, &more_enum}}) |
| 285 | // Width is chosen to be exactly enough to allow 0 and 1 |
| 286 | // enumerators on the first line. |
| 287 | .DumpEnums(45), |
| 288 | "A: 0 = an_enumerator, 1 = another_enumerator,\n" |
| 289 | " 2 = a_very_very_long_enumerator_has_its_own_line,\n" |
| 290 | " 3 = small, 4 = small2" ); |
| 291 | |
| 292 | // If they all exceed width, one per line. |
| 293 | FieldEnum another_enum("another_enum" , {{0, "an_enumerator" }, |
| 294 | {1, "another_enumerator" }, |
| 295 | {2, "a_longer_enumerator" }}); |
| 296 | ASSERT_EQ( |
| 297 | RegisterFlags("" , 8, {RegisterFlags::Field{"A" , 0, 1, &another_enum}}) |
| 298 | .DumpEnums(5), |
| 299 | "A: 0 = an_enumerator,\n" |
| 300 | " 1 = another_enumerator,\n" |
| 301 | " 2 = a_longer_enumerator" ); |
| 302 | |
| 303 | // If the name is already > the width, put one value per line. |
| 304 | FieldEnum short_enum("short_enum" , {{0, "a" }, {1, "b" }, {2, "c" }}); |
| 305 | ASSERT_EQ(RegisterFlags("" , 8, |
| 306 | {RegisterFlags::Field{"AReallyLongFieldName" , 0, 1, |
| 307 | &short_enum}}) |
| 308 | .DumpEnums(10), |
| 309 | "AReallyLongFieldName: 0 = a,\n" |
| 310 | " 1 = b,\n" |
| 311 | " 2 = c" ); |
| 312 | |
| 313 | // Fields are separated by a blank line. Indentation of lines split by width |
| 314 | // is set by the size of the fields name (as opposed to some max of all field |
| 315 | // names). |
| 316 | FieldEnum enum_1("enum_1" , {{0, "an_enumerator" }, {1, "another_enumerator" }}); |
| 317 | FieldEnum enum_2("enum_2" , |
| 318 | {{0, "Cdef_enumerator_1" }, {1, "Cdef_enumerator_2" }}); |
| 319 | ASSERT_EQ(RegisterFlags("" , 8, |
| 320 | {RegisterFlags::Field{"Ab" , 1, 1, &enum_1}, |
| 321 | RegisterFlags::Field{"Cdef" , 0, 0, &enum_2}}) |
| 322 | .DumpEnums(10), |
| 323 | "Ab: 0 = an_enumerator,\n" |
| 324 | " 1 = another_enumerator\n" |
| 325 | "\n" |
| 326 | "Cdef: 0 = Cdef_enumerator_1,\n" |
| 327 | " 1 = Cdef_enumerator_2" ); |
| 328 | |
| 329 | // Having fields without enumerators shouldn't produce any extra newlines. |
| 330 | ASSERT_EQ(RegisterFlags("" , 8, |
| 331 | { |
| 332 | RegisterFlags::Field{"A" , 4, 4}, |
| 333 | RegisterFlags::Field{"B" , 3, 3, &enum_1}, |
| 334 | RegisterFlags::Field{"C" , 2, 2}, |
| 335 | RegisterFlags::Field{"D" , 1, 1, &enum_1}, |
| 336 | RegisterFlags::Field{"E" , 0, 0}, |
| 337 | }) |
| 338 | .DumpEnums(80), |
| 339 | "B: 0 = an_enumerator, 1 = another_enumerator\n" |
| 340 | "\n" |
| 341 | "D: 0 = an_enumerator, 1 = another_enumerator" ); |
| 342 | } |
| 343 | |
| 344 | TEST(RegisterFieldsTest, FlagsToXML) { |
| 345 | StreamString strm; |
| 346 | |
| 347 | // RegisterFlags requires that some fields be given, so no testing of empty |
| 348 | // input. |
| 349 | |
| 350 | // Unnamed fields are padding that are ignored. This applies to fields passed |
| 351 | // in, and those generated to fill the other bits (31-1 here). |
| 352 | RegisterFlags("Foo" , 4, {RegisterFlags::Field("" , 0, 0)}).ToXML(strm); |
| 353 | ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n" |
| 354 | "</flags>\n" ); |
| 355 | |
| 356 | strm.Clear(); |
| 357 | RegisterFlags("Foo" , 4, {RegisterFlags::Field("abc" , 0, 0)}).ToXML(strm); |
| 358 | ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n" |
| 359 | " <field name=\"abc\" start=\"0\" end=\"0\"/>\n" |
| 360 | "</flags>\n" ); |
| 361 | |
| 362 | strm.Clear(); |
| 363 | // Should use the current indentation level as a starting point. |
| 364 | strm.IndentMore(); |
| 365 | RegisterFlags( |
| 366 | "Bar" , 5, |
| 367 | {RegisterFlags::Field("f1" , 25, 32), RegisterFlags::Field("f2" , 10, 24)}) |
| 368 | .ToXML(strm); |
| 369 | ASSERT_EQ(strm.GetString(), |
| 370 | " <flags id=\"Bar\" size=\"5\">\n" |
| 371 | " <field name=\"f1\" start=\"25\" end=\"32\"/>\n" |
| 372 | " <field name=\"f2\" start=\"10\" end=\"24\"/>\n" |
| 373 | " </flags>\n" ); |
| 374 | |
| 375 | strm.Clear(); |
| 376 | strm.IndentLess(); |
| 377 | // Should replace any XML unsafe characters in field names. |
| 378 | RegisterFlags("Safe" , 8, |
| 379 | {RegisterFlags::Field("A<" , 4), RegisterFlags::Field("B>" , 3), |
| 380 | RegisterFlags::Field("C'" , 2), RegisterFlags::Field("D\"" , 1), |
| 381 | RegisterFlags::Field("E&" , 0)}) |
| 382 | .ToXML(strm); |
| 383 | ASSERT_EQ(strm.GetString(), |
| 384 | "<flags id=\"Safe\" size=\"8\">\n" |
| 385 | " <field name=\"A<\" start=\"4\" end=\"4\"/>\n" |
| 386 | " <field name=\"B>\" start=\"3\" end=\"3\"/>\n" |
| 387 | " <field name=\"C'\" start=\"2\" end=\"2\"/>\n" |
| 388 | " <field name=\"D"\" start=\"1\" end=\"1\"/>\n" |
| 389 | " <field name=\"E&\" start=\"0\" end=\"0\"/>\n" |
| 390 | "</flags>\n" ); |
| 391 | |
| 392 | // Should include enumerators as the "type". |
| 393 | strm.Clear(); |
| 394 | FieldEnum enum_single("enum_single" , {{0, "a" }}); |
| 395 | RegisterFlags("Enumerators" , 8, |
| 396 | {RegisterFlags::Field("NoEnumerators" , 4), |
| 397 | RegisterFlags::Field("OneEnumerator" , 3, 3, &enum_single)}) |
| 398 | .ToXML(strm); |
| 399 | ASSERT_EQ(strm.GetString(), |
| 400 | "<flags id=\"Enumerators\" size=\"8\">\n" |
| 401 | " <field name=\"NoEnumerators\" start=\"4\" end=\"4\"/>\n" |
| 402 | " <field name=\"OneEnumerator\" start=\"3\" end=\"3\" " |
| 403 | "type=\"enum_single\"/>\n" |
| 404 | "</flags>\n" ); |
| 405 | } |
| 406 | |
| 407 | TEST(RegisterFlagsTest, EnumeratorToXML) { |
| 408 | StreamString strm; |
| 409 | |
| 410 | FieldEnum::Enumerator(1234, "test" ).ToXML(strm); |
| 411 | ASSERT_EQ(strm.GetString(), "<evalue name=\"test\" value=\"1234\"/>" ); |
| 412 | |
| 413 | // Special XML chars in names must be escaped. |
| 414 | std::array special_names = { |
| 415 | std::make_pair(x: FieldEnum::Enumerator(0, "A<" ), |
| 416 | y: "<evalue name=\"A<\" value=\"0\"/>" ), |
| 417 | std::make_pair(x: FieldEnum::Enumerator(1, "B>" ), |
| 418 | y: "<evalue name=\"B>\" value=\"1\"/>" ), |
| 419 | std::make_pair(x: FieldEnum::Enumerator(2, "C'" ), |
| 420 | y: "<evalue name=\"C'\" value=\"2\"/>" ), |
| 421 | std::make_pair(x: FieldEnum::Enumerator(3, "D\"" ), |
| 422 | y: "<evalue name=\"D"\" value=\"3\"/>" ), |
| 423 | std::make_pair(x: FieldEnum::Enumerator(4, "E&" ), |
| 424 | y: "<evalue name=\"E&\" value=\"4\"/>" ), |
| 425 | }; |
| 426 | |
| 427 | for (const auto &[enumerator, expected] : special_names) { |
| 428 | strm.Clear(); |
| 429 | enumerator.ToXML(strm); |
| 430 | ASSERT_EQ(strm.GetString(), expected); |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | TEST(RegisterFlagsTest, EnumToXML) { |
| 435 | StreamString strm; |
| 436 | |
| 437 | FieldEnum("empty_enum" , {}).ToXML(strm, size: 4); |
| 438 | ASSERT_EQ(strm.GetString(), "<enum id=\"empty_enum\" size=\"4\"/>\n" ); |
| 439 | |
| 440 | strm.Clear(); |
| 441 | FieldEnum("single_enumerator" , {FieldEnum::Enumerator(0, "zero" )}) |
| 442 | .ToXML(strm, size: 5); |
| 443 | ASSERT_EQ(strm.GetString(), "<enum id=\"single_enumerator\" size=\"5\">\n" |
| 444 | " <evalue name=\"zero\" value=\"0\"/>\n" |
| 445 | "</enum>\n" ); |
| 446 | |
| 447 | strm.Clear(); |
| 448 | FieldEnum("multiple_enumerator" , |
| 449 | {FieldEnum::Enumerator(0, "zero" ), FieldEnum::Enumerator(1, "one" )}) |
| 450 | .ToXML(strm, size: 8); |
| 451 | ASSERT_EQ(strm.GetString(), "<enum id=\"multiple_enumerator\" size=\"8\">\n" |
| 452 | " <evalue name=\"zero\" value=\"0\"/>\n" |
| 453 | " <evalue name=\"one\" value=\"1\"/>\n" |
| 454 | "</enum>\n" ); |
| 455 | } |
| 456 | |
| 457 | TEST(RegisterFlagsTest, EnumsToXML) { |
| 458 | // This method should output all the enums used by the register flag set, |
| 459 | // only once. |
| 460 | |
| 461 | StreamString strm; |
| 462 | FieldEnum enum_a("enum_a" , {FieldEnum::Enumerator(0, "zero" )}); |
| 463 | FieldEnum enum_b("enum_b" , {FieldEnum::Enumerator(1, "one" )}); |
| 464 | FieldEnum enum_c("enum_c" , {FieldEnum::Enumerator(2, "two" )}); |
| 465 | llvm::StringSet<> seen; |
| 466 | // Pretend that enum_c was already emitted for a different flag set. |
| 467 | seen.insert(key: "enum_c" ); |
| 468 | |
| 469 | RegisterFlags("Test" , 4, |
| 470 | { |
| 471 | RegisterFlags::Field("f1" , 31, 31, &enum_a), |
| 472 | RegisterFlags::Field("f2" , 30, 30, &enum_a), |
| 473 | RegisterFlags::Field("f3" , 29, 29, &enum_b), |
| 474 | RegisterFlags::Field("f4" , 27, 28, &enum_c), |
| 475 | }) |
| 476 | .EnumsToXML(strm, seen); |
| 477 | ASSERT_EQ(strm.GetString(), "<enum id=\"enum_a\" size=\"4\">\n" |
| 478 | " <evalue name=\"zero\" value=\"0\"/>\n" |
| 479 | "</enum>\n" |
| 480 | "<enum id=\"enum_b\" size=\"4\">\n" |
| 481 | " <evalue name=\"one\" value=\"1\"/>\n" |
| 482 | "</enum>\n" ); |
| 483 | } |