xref: /llvm-project/lldb/unittests/Target/RegisterFlagsTest.cpp (revision e87f94a6a806a941242506680f88573d6a87a828)
16ea45e30SDavid Spickett //===-- RegisterFlagsTest.cpp ---------------------------------------------===//
26ea45e30SDavid Spickett //
36ea45e30SDavid Spickett // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46ea45e30SDavid Spickett // See https://llvm.org/LICENSE.txt for license information.
56ea45e30SDavid Spickett // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66ea45e30SDavid Spickett //
76ea45e30SDavid Spickett //===----------------------------------------------------------------------===//
86ea45e30SDavid Spickett 
96ea45e30SDavid Spickett #include "lldb/Target/RegisterFlags.h"
10d1556e5eSDavid Spickett #include "lldb/Utility/StreamString.h"
116ea45e30SDavid Spickett #include "gmock/gmock.h"
126ea45e30SDavid Spickett #include "gtest/gtest.h"
136ea45e30SDavid Spickett 
146ea45e30SDavid Spickett using namespace lldb_private;
156ea45e30SDavid Spickett using namespace lldb;
166ea45e30SDavid Spickett 
176ea45e30SDavid Spickett TEST(RegisterFlagsTest, Field) {
186ea45e30SDavid Spickett   // We assume that start <= end is always true, so that is not tested here.
196ea45e30SDavid Spickett 
20fe929770SDavid Spickett   RegisterFlags::Field f1("abc", 0);
216ea45e30SDavid Spickett   ASSERT_EQ(f1.GetName(), "abc");
226ea45e30SDavid Spickett   // start == end means a 1 bit field.
236ea45e30SDavid Spickett   ASSERT_EQ(f1.GetSizeInBits(), (unsigned)1);
246ea45e30SDavid Spickett   ASSERT_EQ(f1.GetMask(), (uint64_t)1);
256ea45e30SDavid Spickett   ASSERT_EQ(f1.GetValue(0), (uint64_t)0);
266ea45e30SDavid Spickett   ASSERT_EQ(f1.GetValue(3), (uint64_t)1);
276ea45e30SDavid Spickett 
286ea45e30SDavid Spickett   // End is inclusive meaning that start 0 to end 1 includes bit 1
296ea45e30SDavid Spickett   // to make a 2 bit field.
306ea45e30SDavid Spickett   RegisterFlags::Field f2("", 0, 1);
316ea45e30SDavid Spickett   ASSERT_EQ(f2.GetSizeInBits(), (unsigned)2);
326ea45e30SDavid Spickett   ASSERT_EQ(f2.GetMask(), (uint64_t)3);
336ea45e30SDavid Spickett   ASSERT_EQ(f2.GetValue(UINT64_MAX), (uint64_t)3);
346ea45e30SDavid Spickett   ASSERT_EQ(f2.GetValue(UINT64_MAX & ~(uint64_t)3), (uint64_t)0);
356ea45e30SDavid Spickett 
366ea45e30SDavid Spickett   // If the field doesn't start at 0 we need to shift up/down
376ea45e30SDavid Spickett   // to account for it.
386ea45e30SDavid Spickett   RegisterFlags::Field f3("", 2, 5);
396ea45e30SDavid Spickett   ASSERT_EQ(f3.GetSizeInBits(), (unsigned)4);
406ea45e30SDavid Spickett   ASSERT_EQ(f3.GetMask(), (uint64_t)0x3c);
416ea45e30SDavid Spickett   ASSERT_EQ(f3.GetValue(UINT64_MAX), (uint64_t)0xf);
426ea45e30SDavid Spickett   ASSERT_EQ(f3.GetValue(UINT64_MAX & ~(uint64_t)0x3c), (uint64_t)0);
436ea45e30SDavid Spickett 
446ea45e30SDavid Spickett   // Fields are sorted lowest starting bit first.
456ea45e30SDavid Spickett   ASSERT_TRUE(f2 < f3);
466ea45e30SDavid Spickett   ASSERT_FALSE(f3 < f1);
476ea45e30SDavid Spickett   ASSERT_FALSE(f1 < f2);
486ea45e30SDavid Spickett   ASSERT_FALSE(f1 < f1);
496ea45e30SDavid Spickett }
506ea45e30SDavid Spickett 
516ea45e30SDavid Spickett static RegisterFlags::Field make_field(unsigned start, unsigned end) {
526ea45e30SDavid Spickett   return RegisterFlags::Field("", start, end);
536ea45e30SDavid Spickett }
546ea45e30SDavid Spickett 
55fe929770SDavid Spickett static RegisterFlags::Field make_field(unsigned bit) {
56fe929770SDavid Spickett   return RegisterFlags::Field("", bit);
57fe929770SDavid Spickett }
58fe929770SDavid Spickett 
596ea45e30SDavid Spickett TEST(RegisterFlagsTest, FieldOverlaps) {
606ea45e30SDavid Spickett   // Single bit fields
61fe929770SDavid Spickett   ASSERT_FALSE(make_field(0, 0).Overlaps(make_field(1)));
62fe929770SDavid Spickett   ASSERT_TRUE(make_field(1, 1).Overlaps(make_field(1)));
63fe929770SDavid Spickett   ASSERT_FALSE(make_field(1, 1).Overlaps(make_field(3)));
646ea45e30SDavid Spickett 
656ea45e30SDavid Spickett   ASSERT_TRUE(make_field(0, 1).Overlaps(make_field(1, 2)));
666ea45e30SDavid Spickett   ASSERT_TRUE(make_field(1, 2).Overlaps(make_field(0, 1)));
676ea45e30SDavid Spickett   ASSERT_FALSE(make_field(0, 1).Overlaps(make_field(2, 3)));
686ea45e30SDavid Spickett   ASSERT_FALSE(make_field(2, 3).Overlaps(make_field(0, 1)));
696ea45e30SDavid Spickett 
706ea45e30SDavid Spickett   ASSERT_FALSE(make_field(1, 5).Overlaps(make_field(10, 20)));
716ea45e30SDavid Spickett   ASSERT_FALSE(make_field(15, 30).Overlaps(make_field(7, 12)));
726ea45e30SDavid Spickett }
736ea45e30SDavid Spickett 
746ea45e30SDavid Spickett TEST(RegisterFlagsTest, PaddingDistance) {
756ea45e30SDavid Spickett   // We assume that this method is always called with a more significant
766ea45e30SDavid Spickett   // (start bit is higher) field first and that they do not overlap.
776ea45e30SDavid Spickett 
786ea45e30SDavid Spickett   // [field 1][field 2]
79fe929770SDavid Spickett   ASSERT_EQ(make_field(1, 1).PaddingDistance(make_field(0)), 0ULL);
806ea45e30SDavid Spickett   // [field 1][..][field 2]
81fe929770SDavid Spickett   ASSERT_EQ(make_field(2, 2).PaddingDistance(make_field(0)), 1ULL);
826ea45e30SDavid Spickett   // [field 1][field 1][field 2]
83fe929770SDavid Spickett   ASSERT_EQ(make_field(1, 2).PaddingDistance(make_field(0)), 0ULL);
846ea45e30SDavid Spickett   // [field 1][30 bits free][field 2]
85fe929770SDavid Spickett   ASSERT_EQ(make_field(31, 31).PaddingDistance(make_field(0)), 30ULL);
866ea45e30SDavid Spickett }
876ea45e30SDavid Spickett 
886ea45e30SDavid Spickett static void test_padding(const std::vector<RegisterFlags::Field> &fields,
896ea45e30SDavid Spickett                          const std::vector<RegisterFlags::Field> &expected) {
906ea45e30SDavid Spickett   RegisterFlags rf("", 4, fields);
916ea45e30SDavid Spickett   EXPECT_THAT(expected, ::testing::ContainerEq(rf.GetFields()));
926ea45e30SDavid Spickett }
936ea45e30SDavid Spickett 
946ea45e30SDavid Spickett TEST(RegisterFlagsTest, RegisterFlagsPadding) {
956ea45e30SDavid Spickett   // When creating a set of flags we assume that:
966ea45e30SDavid Spickett   // * There are >= 1 fields.
976ea45e30SDavid Spickett   // * They are sorted in descending order.
986ea45e30SDavid Spickett   // * There may be gaps between each field.
996ea45e30SDavid Spickett 
1006ea45e30SDavid Spickett   // Needs no padding
1016ea45e30SDavid Spickett   auto fields =
1026ea45e30SDavid Spickett       std::vector<RegisterFlags::Field>{make_field(16, 31), make_field(0, 15)};
1036ea45e30SDavid Spickett   test_padding(fields, fields);
1046ea45e30SDavid Spickett 
1056ea45e30SDavid Spickett   // Needs padding in between the fields, single bit.
1066ea45e30SDavid Spickett   test_padding({make_field(17, 31), make_field(0, 15)},
107fe929770SDavid Spickett                {make_field(17, 31), make_field(16), make_field(0, 15)});
1086ea45e30SDavid Spickett   // Multiple bits of padding.
1096ea45e30SDavid Spickett   test_padding({make_field(17, 31), make_field(0, 14)},
1106ea45e30SDavid Spickett                {make_field(17, 31), make_field(15, 16), make_field(0, 14)});
1116ea45e30SDavid Spickett 
1126ea45e30SDavid Spickett   // Padding before first field, single bit.
113fe929770SDavid Spickett   test_padding({make_field(0, 30)}, {make_field(31), make_field(0, 30)});
1146ea45e30SDavid Spickett   // Multiple bits.
1156ea45e30SDavid Spickett   test_padding({make_field(0, 15)}, {make_field(16, 31), make_field(0, 15)});
1166ea45e30SDavid Spickett 
1176ea45e30SDavid Spickett   // Padding after last field, single bit.
118fe929770SDavid Spickett   test_padding({make_field(1, 31)}, {make_field(1, 31), make_field(0)});
1196ea45e30SDavid Spickett   // Multiple bits.
1206ea45e30SDavid Spickett   test_padding({make_field(2, 31)}, {make_field(2, 31), make_field(0, 1)});
1216ea45e30SDavid Spickett 
1226ea45e30SDavid Spickett   // Fields need padding before, in between and after.
1236ea45e30SDavid Spickett   // [31-28][field 27-24][23-22][field 21-20][19-12][field 11-8][7-0]
1246ea45e30SDavid Spickett   test_padding({make_field(24, 27), make_field(20, 21), make_field(8, 11)},
1256ea45e30SDavid Spickett                {make_field(28, 31), make_field(24, 27), make_field(22, 23),
1266ea45e30SDavid Spickett                 make_field(20, 21), make_field(12, 19), make_field(8, 11),
1276ea45e30SDavid Spickett                 make_field(0, 7)});
1286ea45e30SDavid Spickett }
129e07a421dSDavid Spickett 
130e07a421dSDavid Spickett TEST(RegisterFieldsTest, ReverseFieldOrder) {
131e07a421dSDavid Spickett   // Unchanged
132e07a421dSDavid Spickett   RegisterFlags rf("", 4, {make_field(0, 31)});
1330ae342f4SJie Fu   ASSERT_EQ(0x12345678ULL, (unsigned long long)rf.ReverseFieldOrder(0x12345678));
134e07a421dSDavid Spickett 
135e07a421dSDavid Spickett   // Swap the two halves around.
136e07a421dSDavid Spickett   RegisterFlags rf2("", 4, {make_field(16, 31), make_field(0, 15)});
1370ae342f4SJie Fu   ASSERT_EQ(0x56781234ULL, (unsigned long long)rf2.ReverseFieldOrder(0x12345678));
138e07a421dSDavid Spickett 
139e07a421dSDavid Spickett   // Many small fields.
140fe929770SDavid Spickett   RegisterFlags rf3(
141fe929770SDavid Spickett       "", 4, {make_field(31), make_field(30), make_field(29), make_field(28)});
142e07a421dSDavid Spickett   ASSERT_EQ(0x00000005ULL, rf3.ReverseFieldOrder(0xA0000000));
143e07a421dSDavid Spickett }
1448b73a2e8SDavid Spickett 
1458b73a2e8SDavid Spickett TEST(RegisterFlagsTest, AsTable) {
1468b73a2e8SDavid Spickett   // Anonymous fields are shown with an empty name cell,
1478b73a2e8SDavid Spickett   // whether they are known up front or added during construction.
1488b73a2e8SDavid Spickett   RegisterFlags anon_field("", 4, {make_field(0, 31)});
1498b73a2e8SDavid Spickett   ASSERT_EQ("| 31-0 |\n"
1508b73a2e8SDavid Spickett             "|------|\n"
1518b73a2e8SDavid Spickett             "|      |",
1528b73a2e8SDavid Spickett             anon_field.AsTable(100));
1538b73a2e8SDavid Spickett 
1548b73a2e8SDavid Spickett   RegisterFlags anon_with_pad("", 4, {make_field(16, 31)});
1558b73a2e8SDavid Spickett   ASSERT_EQ("| 31-16 | 15-0 |\n"
1568b73a2e8SDavid Spickett             "|-------|------|\n"
1578b73a2e8SDavid Spickett             "|       |      |",
1588b73a2e8SDavid Spickett             anon_with_pad.AsTable(100));
1598b73a2e8SDavid Spickett 
1608b73a2e8SDavid Spickett   // Use the wider of position and name to set the column width.
1618b73a2e8SDavid Spickett   RegisterFlags name_wider("", 4, {RegisterFlags::Field("aardvark", 0, 31)});
1628b73a2e8SDavid Spickett   ASSERT_EQ("|   31-0   |\n"
1638b73a2e8SDavid Spickett             "|----------|\n"
1648b73a2e8SDavid Spickett             "| aardvark |",
1658b73a2e8SDavid Spickett             name_wider.AsTable(100));
1668b73a2e8SDavid Spickett   // When the padding is an odd number, put the remaining 1 on the right.
1678b73a2e8SDavid Spickett   RegisterFlags pos_wider("", 4, {RegisterFlags::Field("?", 0, 31)});
1688b73a2e8SDavid Spickett   ASSERT_EQ("| 31-0 |\n"
1698b73a2e8SDavid Spickett             "|------|\n"
1708b73a2e8SDavid Spickett             "|  ?   |",
1718b73a2e8SDavid Spickett             pos_wider.AsTable(100));
1728b73a2e8SDavid Spickett 
1738b73a2e8SDavid Spickett   // Single bit fields don't need to show start and end, just one of them.
174fe929770SDavid Spickett   RegisterFlags single_bit("", 4, {make_field(31)});
1758b73a2e8SDavid Spickett   ASSERT_EQ("| 31 | 30-0 |\n"
1768b73a2e8SDavid Spickett             "|----|------|\n"
1778b73a2e8SDavid Spickett             "|    |      |",
1788b73a2e8SDavid Spickett             single_bit.AsTable(100));
1798b73a2e8SDavid Spickett 
1808b73a2e8SDavid Spickett   // Columns are printed horizontally if max width allows.
1818b73a2e8SDavid Spickett   RegisterFlags many_fields("", 4,
1828b73a2e8SDavid Spickett                             {RegisterFlags::Field("cat", 28, 31),
1838b73a2e8SDavid Spickett                              RegisterFlags::Field("pigeon", 20, 23),
184fe929770SDavid Spickett                              RegisterFlags::Field("wolf", 12),
1858b73a2e8SDavid Spickett                              RegisterFlags::Field("x", 0, 4)});
1868b73a2e8SDavid Spickett   ASSERT_EQ("| 31-28 | 27-24 | 23-20  | 19-13 |  12  | 11-5 | 4-0 |\n"
1878b73a2e8SDavid Spickett             "|-------|-------|--------|-------|------|------|-----|\n"
1888b73a2e8SDavid Spickett             "|  cat  |       | pigeon |       | wolf |      |  x  |",
1898b73a2e8SDavid Spickett             many_fields.AsTable(100));
1908b73a2e8SDavid Spickett 
1918b73a2e8SDavid Spickett   // max_width tells us when we need to split into further tables.
1928b73a2e8SDavid Spickett   // Here no split is needed.
1938b73a2e8SDavid Spickett   RegisterFlags exact_max_single_col("", 4, {RegisterFlags::Field("?", 0, 31)});
1948b73a2e8SDavid Spickett   ASSERT_EQ("| 31-0 |\n"
1958b73a2e8SDavid Spickett             "|------|\n"
1968b73a2e8SDavid Spickett             "|  ?   |",
1978b73a2e8SDavid Spickett             exact_max_single_col.AsTable(9));
1988b73a2e8SDavid Spickett   RegisterFlags exact_max_two_col(
1998b73a2e8SDavid Spickett       "", 4,
2008b73a2e8SDavid Spickett       {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});
2018b73a2e8SDavid Spickett   ASSERT_EQ("| 31-16 | 15-0 |\n"
2028b73a2e8SDavid Spickett             "|-------|------|\n"
2038b73a2e8SDavid Spickett             "|   ?   |  #   |",
2048b73a2e8SDavid Spickett             exact_max_two_col.AsTable(16));
2058b73a2e8SDavid Spickett 
2068b73a2e8SDavid Spickett   // If max is less than a single column, just print the single column. The user
2078b73a2e8SDavid Spickett   // will have to put up with some wrapping in this niche case.
2088b73a2e8SDavid Spickett   RegisterFlags zero_max_single_col("", 4, {RegisterFlags::Field("?", 0, 31)});
2098b73a2e8SDavid Spickett   ASSERT_EQ("| 31-0 |\n"
2108b73a2e8SDavid Spickett             "|------|\n"
2118b73a2e8SDavid Spickett             "|  ?   |",
2128b73a2e8SDavid Spickett             zero_max_single_col.AsTable(0));
2138b73a2e8SDavid Spickett   // Same logic for any following columns. Effectively making a "vertical"
2148b73a2e8SDavid Spickett   // table, just with more grid lines.
2158b73a2e8SDavid Spickett   RegisterFlags zero_max_two_col(
2168b73a2e8SDavid Spickett       "", 4,
2178b73a2e8SDavid Spickett       {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});
2188b73a2e8SDavid Spickett   ASSERT_EQ("| 31-16 |\n"
2198b73a2e8SDavid Spickett             "|-------|\n"
2208b73a2e8SDavid Spickett             "|   ?   |\n"
2218b73a2e8SDavid Spickett             "\n"
2228b73a2e8SDavid Spickett             "| 15-0 |\n"
2238b73a2e8SDavid Spickett             "|------|\n"
2248b73a2e8SDavid Spickett             "|  #   |",
2258b73a2e8SDavid Spickett             zero_max_two_col.AsTable(0));
2268b73a2e8SDavid Spickett 
2278b73a2e8SDavid Spickett   RegisterFlags max_less_than_single_col("", 4,
2288b73a2e8SDavid Spickett                                          {RegisterFlags::Field("?", 0, 31)});
2298b73a2e8SDavid Spickett   ASSERT_EQ("| 31-0 |\n"
2308b73a2e8SDavid Spickett             "|------|\n"
2318b73a2e8SDavid Spickett             "|  ?   |",
2328b73a2e8SDavid Spickett             max_less_than_single_col.AsTable(3));
2338b73a2e8SDavid Spickett   RegisterFlags max_less_than_two_col(
2348b73a2e8SDavid Spickett       "", 4,
2358b73a2e8SDavid Spickett       {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});
2368b73a2e8SDavid Spickett   ASSERT_EQ("| 31-16 |\n"
2378b73a2e8SDavid Spickett             "|-------|\n"
2388b73a2e8SDavid Spickett             "|   ?   |\n"
2398b73a2e8SDavid Spickett             "\n"
2408b73a2e8SDavid Spickett             "| 15-0 |\n"
2418b73a2e8SDavid Spickett             "|------|\n"
2428b73a2e8SDavid Spickett             "|  #   |",
2438b73a2e8SDavid Spickett             max_less_than_two_col.AsTable(9));
2448b73a2e8SDavid Spickett   RegisterFlags max_many_columns(
2458b73a2e8SDavid Spickett       "", 4,
2468b73a2e8SDavid Spickett       {RegisterFlags::Field("A", 24, 31), RegisterFlags::Field("B", 16, 23),
2478b73a2e8SDavid Spickett        RegisterFlags::Field("C", 8, 15),
2488b73a2e8SDavid Spickett        RegisterFlags::Field("really long name", 0, 7)});
2498b73a2e8SDavid Spickett   ASSERT_EQ("| 31-24 | 23-16 |\n"
2508b73a2e8SDavid Spickett             "|-------|-------|\n"
2518b73a2e8SDavid Spickett             "|   A   |   B   |\n"
2528b73a2e8SDavid Spickett             "\n"
2538b73a2e8SDavid Spickett             "| 15-8 |\n"
2548b73a2e8SDavid Spickett             "|------|\n"
2558b73a2e8SDavid Spickett             "|  C   |\n"
2568b73a2e8SDavid Spickett             "\n"
2578b73a2e8SDavid Spickett             "|       7-0        |\n"
2588b73a2e8SDavid Spickett             "|------------------|\n"
2598b73a2e8SDavid Spickett             "| really long name |",
2608b73a2e8SDavid Spickett             max_many_columns.AsTable(23));
2618b73a2e8SDavid Spickett }
262d1556e5eSDavid Spickett 
263f838f08cSDavid Spickett TEST(RegisterFlagsTest, DumpEnums) {
264f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0}}).DumpEnums(80),
265f838f08cSDavid Spickett             "");
266f838f08cSDavid Spickett 
267f838f08cSDavid Spickett   FieldEnum basic_enum("test", {{0, "an_enumerator"}});
268f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 0, &basic_enum}})
269f838f08cSDavid Spickett                 .DumpEnums(80),
270f838f08cSDavid Spickett             "A: 0 = an_enumerator");
271f838f08cSDavid Spickett 
272f838f08cSDavid Spickett   // If width is smaller than the enumerator name, print it anyway.
273f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 0, &basic_enum}})
274f838f08cSDavid Spickett                 .DumpEnums(5),
275f838f08cSDavid Spickett             "A: 0 = an_enumerator");
276f838f08cSDavid Spickett 
277*e87f94a6SJay Foad   // Multiple values can go on the same line, up to the width.
278f838f08cSDavid Spickett   FieldEnum more_enum("long_enum",
279f838f08cSDavid Spickett                       {{0, "an_enumerator"},
280f838f08cSDavid Spickett                        {1, "another_enumerator"},
281f838f08cSDavid Spickett                        {2, "a_very_very_long_enumerator_has_its_own_line"},
282f838f08cSDavid Spickett                        {3, "small"},
283f838f08cSDavid Spickett                        {4, "small2"}});
284f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 2, &more_enum}})
285f838f08cSDavid Spickett                 // Width is chosen to be exactly enough to allow 0 and 1
286f838f08cSDavid Spickett                 // enumerators on the first line.
287f838f08cSDavid Spickett                 .DumpEnums(45),
288f838f08cSDavid Spickett             "A: 0 = an_enumerator, 1 = another_enumerator,\n"
289f838f08cSDavid Spickett             "   2 = a_very_very_long_enumerator_has_its_own_line,\n"
290f838f08cSDavid Spickett             "   3 = small, 4 = small2");
291f838f08cSDavid Spickett 
292f838f08cSDavid Spickett   // If they all exceed width, one per line.
293f838f08cSDavid Spickett   FieldEnum another_enum("another_enum", {{0, "an_enumerator"},
294f838f08cSDavid Spickett                                           {1, "another_enumerator"},
295f838f08cSDavid Spickett                                           {2, "a_longer_enumerator"}});
296f838f08cSDavid Spickett   ASSERT_EQ(
297f838f08cSDavid Spickett       RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 1, &another_enum}})
298f838f08cSDavid Spickett           .DumpEnums(5),
299f838f08cSDavid Spickett       "A: 0 = an_enumerator,\n"
300f838f08cSDavid Spickett       "   1 = another_enumerator,\n"
301f838f08cSDavid Spickett       "   2 = a_longer_enumerator");
302f838f08cSDavid Spickett 
303f838f08cSDavid Spickett   // If the name is already > the width, put one value per line.
304f838f08cSDavid Spickett   FieldEnum short_enum("short_enum", {{0, "a"}, {1, "b"}, {2, "c"}});
305f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8,
306f838f08cSDavid Spickett                           {RegisterFlags::Field{"AReallyLongFieldName", 0, 1,
307f838f08cSDavid Spickett                                                 &short_enum}})
308f838f08cSDavid Spickett                 .DumpEnums(10),
309f838f08cSDavid Spickett             "AReallyLongFieldName: 0 = a,\n"
310f838f08cSDavid Spickett             "                      1 = b,\n"
311f838f08cSDavid Spickett             "                      2 = c");
312f838f08cSDavid Spickett 
313f838f08cSDavid Spickett   // Fields are separated by a blank line. Indentation of lines split by width
314f838f08cSDavid Spickett   // is set by the size of the fields name (as opposed to some max of all field
315f838f08cSDavid Spickett   // names).
316f838f08cSDavid Spickett   FieldEnum enum_1("enum_1", {{0, "an_enumerator"}, {1, "another_enumerator"}});
317f838f08cSDavid Spickett   FieldEnum enum_2("enum_2",
318f838f08cSDavid Spickett                    {{0, "Cdef_enumerator_1"}, {1, "Cdef_enumerator_2"}});
319f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8,
320f838f08cSDavid Spickett                           {RegisterFlags::Field{"Ab", 1, 1, &enum_1},
321f838f08cSDavid Spickett                            RegisterFlags::Field{"Cdef", 0, 0, &enum_2}})
322f838f08cSDavid Spickett                 .DumpEnums(10),
323f838f08cSDavid Spickett             "Ab: 0 = an_enumerator,\n"
324f838f08cSDavid Spickett             "    1 = another_enumerator\n"
325f838f08cSDavid Spickett             "\n"
326f838f08cSDavid Spickett             "Cdef: 0 = Cdef_enumerator_1,\n"
327f838f08cSDavid Spickett             "      1 = Cdef_enumerator_2");
328f838f08cSDavid Spickett 
329f838f08cSDavid Spickett   // Having fields without enumerators shouldn't produce any extra newlines.
330f838f08cSDavid Spickett   ASSERT_EQ(RegisterFlags("", 8,
331f838f08cSDavid Spickett                           {
332f838f08cSDavid Spickett                               RegisterFlags::Field{"A", 4, 4},
333f838f08cSDavid Spickett                               RegisterFlags::Field{"B", 3, 3, &enum_1},
334f838f08cSDavid Spickett                               RegisterFlags::Field{"C", 2, 2},
335f838f08cSDavid Spickett                               RegisterFlags::Field{"D", 1, 1, &enum_1},
336f838f08cSDavid Spickett                               RegisterFlags::Field{"E", 0, 0},
337f838f08cSDavid Spickett                           })
338f838f08cSDavid Spickett                 .DumpEnums(80),
339f838f08cSDavid Spickett             "B: 0 = an_enumerator, 1 = another_enumerator\n"
340f838f08cSDavid Spickett             "\n"
341f838f08cSDavid Spickett             "D: 0 = an_enumerator, 1 = another_enumerator");
342f838f08cSDavid Spickett }
343f838f08cSDavid Spickett 
344f838f08cSDavid Spickett TEST(RegisterFieldsTest, FlagsToXML) {
345d1556e5eSDavid Spickett   StreamString strm;
346d1556e5eSDavid Spickett 
347d1556e5eSDavid Spickett   // RegisterFlags requires that some fields be given, so no testing of empty
348d1556e5eSDavid Spickett   // input.
349d1556e5eSDavid Spickett 
350d1556e5eSDavid Spickett   // Unnamed fields are padding that are ignored. This applies to fields passed
351d1556e5eSDavid Spickett   // in, and those generated to fill the other bits (31-1 here).
352d1556e5eSDavid Spickett   RegisterFlags("Foo", 4, {RegisterFlags::Field("", 0, 0)}).ToXML(strm);
353d1556e5eSDavid Spickett   ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"
354d1556e5eSDavid Spickett                               "</flags>\n");
355d1556e5eSDavid Spickett 
356d1556e5eSDavid Spickett   strm.Clear();
357d1556e5eSDavid Spickett   RegisterFlags("Foo", 4, {RegisterFlags::Field("abc", 0, 0)}).ToXML(strm);
358d1556e5eSDavid Spickett   ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"
359d1556e5eSDavid Spickett                               "  <field name=\"abc\" start=\"0\" end=\"0\"/>\n"
360d1556e5eSDavid Spickett                               "</flags>\n");
361d1556e5eSDavid Spickett 
362d1556e5eSDavid Spickett   strm.Clear();
363d1556e5eSDavid Spickett   // Should use the current indentation level as a starting point.
364d1556e5eSDavid Spickett   strm.IndentMore();
365d1556e5eSDavid Spickett   RegisterFlags(
366d1556e5eSDavid Spickett       "Bar", 5,
367d1556e5eSDavid Spickett       {RegisterFlags::Field("f1", 25, 32), RegisterFlags::Field("f2", 10, 24)})
368d1556e5eSDavid Spickett       .ToXML(strm);
369d1556e5eSDavid Spickett   ASSERT_EQ(strm.GetString(),
370d1556e5eSDavid Spickett             "  <flags id=\"Bar\" size=\"5\">\n"
371d1556e5eSDavid Spickett             "    <field name=\"f1\" start=\"25\" end=\"32\"/>\n"
372d1556e5eSDavid Spickett             "    <field name=\"f2\" start=\"10\" end=\"24\"/>\n"
373d1556e5eSDavid Spickett             "  </flags>\n");
374d1556e5eSDavid Spickett 
375d1556e5eSDavid Spickett   strm.Clear();
376d1556e5eSDavid Spickett   strm.IndentLess();
377d1556e5eSDavid Spickett   // Should replace any XML unsafe characters in field names.
378d1556e5eSDavid Spickett   RegisterFlags("Safe", 8,
379d1556e5eSDavid Spickett                 {RegisterFlags::Field("A<", 4), RegisterFlags::Field("B>", 3),
380d1556e5eSDavid Spickett                  RegisterFlags::Field("C'", 2), RegisterFlags::Field("D\"", 1),
381d1556e5eSDavid Spickett                  RegisterFlags::Field("E&", 0)})
382d1556e5eSDavid Spickett       .ToXML(strm);
383d1556e5eSDavid Spickett   ASSERT_EQ(strm.GetString(),
384d1556e5eSDavid Spickett             "<flags id=\"Safe\" size=\"8\">\n"
385d1556e5eSDavid Spickett             "  <field name=\"A&lt;\" start=\"4\" end=\"4\"/>\n"
386d1556e5eSDavid Spickett             "  <field name=\"B&gt;\" start=\"3\" end=\"3\"/>\n"
387d1556e5eSDavid Spickett             "  <field name=\"C&apos;\" start=\"2\" end=\"2\"/>\n"
388d1556e5eSDavid Spickett             "  <field name=\"D&quot;\" start=\"1\" end=\"1\"/>\n"
389d1556e5eSDavid Spickett             "  <field name=\"E&amp;\" start=\"0\" end=\"0\"/>\n"
390d1556e5eSDavid Spickett             "</flags>\n");
391f838f08cSDavid Spickett 
392f838f08cSDavid Spickett   // Should include enumerators as the "type".
393f838f08cSDavid Spickett   strm.Clear();
394f838f08cSDavid Spickett   FieldEnum enum_single("enum_single", {{0, "a"}});
395f838f08cSDavid Spickett   RegisterFlags("Enumerators", 8,
396f838f08cSDavid Spickett                 {RegisterFlags::Field("NoEnumerators", 4),
397f838f08cSDavid Spickett                  RegisterFlags::Field("OneEnumerator", 3, 3, &enum_single)})
398f838f08cSDavid Spickett       .ToXML(strm);
399f838f08cSDavid Spickett   ASSERT_EQ(strm.GetString(),
400f838f08cSDavid Spickett             "<flags id=\"Enumerators\" size=\"8\">\n"
401f838f08cSDavid Spickett             "  <field name=\"NoEnumerators\" start=\"4\" end=\"4\"/>\n"
402f838f08cSDavid Spickett             "  <field name=\"OneEnumerator\" start=\"3\" end=\"3\" "
403f838f08cSDavid Spickett             "type=\"enum_single\"/>\n"
404f838f08cSDavid Spickett             "</flags>\n");
405f838f08cSDavid Spickett }
406f838f08cSDavid Spickett 
407f838f08cSDavid Spickett TEST(RegisterFlagsTest, EnumeratorToXML) {
408f838f08cSDavid Spickett   StreamString strm;
409f838f08cSDavid Spickett 
410f838f08cSDavid Spickett   FieldEnum::Enumerator(1234, "test").ToXML(strm);
411f838f08cSDavid Spickett   ASSERT_EQ(strm.GetString(), "<evalue name=\"test\" value=\"1234\"/>");
412f838f08cSDavid Spickett 
413f838f08cSDavid Spickett   // Special XML chars in names must be escaped.
414f838f08cSDavid Spickett   std::array special_names = {
415f838f08cSDavid Spickett       std::make_pair(FieldEnum::Enumerator(0, "A<"),
416f838f08cSDavid Spickett                      "<evalue name=\"A&lt;\" value=\"0\"/>"),
417f838f08cSDavid Spickett       std::make_pair(FieldEnum::Enumerator(1, "B>"),
418f838f08cSDavid Spickett                      "<evalue name=\"B&gt;\" value=\"1\"/>"),
419f838f08cSDavid Spickett       std::make_pair(FieldEnum::Enumerator(2, "C'"),
420f838f08cSDavid Spickett                      "<evalue name=\"C&apos;\" value=\"2\"/>"),
421f838f08cSDavid Spickett       std::make_pair(FieldEnum::Enumerator(3, "D\""),
422f838f08cSDavid Spickett                      "<evalue name=\"D&quot;\" value=\"3\"/>"),
423f838f08cSDavid Spickett       std::make_pair(FieldEnum::Enumerator(4, "E&"),
424f838f08cSDavid Spickett                      "<evalue name=\"E&amp;\" value=\"4\"/>"),
425f838f08cSDavid Spickett   };
426f838f08cSDavid Spickett 
427f838f08cSDavid Spickett   for (const auto &[enumerator, expected] : special_names) {
428f838f08cSDavid Spickett     strm.Clear();
429f838f08cSDavid Spickett     enumerator.ToXML(strm);
430f838f08cSDavid Spickett     ASSERT_EQ(strm.GetString(), expected);
431f838f08cSDavid Spickett   }
432f838f08cSDavid Spickett }
433f838f08cSDavid Spickett 
434f838f08cSDavid Spickett TEST(RegisterFlagsTest, EnumToXML) {
435f838f08cSDavid Spickett   StreamString strm;
436f838f08cSDavid Spickett 
437f838f08cSDavid Spickett   FieldEnum("empty_enum", {}).ToXML(strm, 4);
438f838f08cSDavid Spickett   ASSERT_EQ(strm.GetString(), "<enum id=\"empty_enum\" size=\"4\"/>\n");
439f838f08cSDavid Spickett 
440f838f08cSDavid Spickett   strm.Clear();
441f838f08cSDavid Spickett   FieldEnum("single_enumerator", {FieldEnum::Enumerator(0, "zero")})
442f838f08cSDavid Spickett       .ToXML(strm, 5);
443f838f08cSDavid Spickett   ASSERT_EQ(strm.GetString(), "<enum id=\"single_enumerator\" size=\"5\">\n"
444f838f08cSDavid Spickett                               "  <evalue name=\"zero\" value=\"0\"/>\n"
445f838f08cSDavid Spickett                               "</enum>\n");
446f838f08cSDavid Spickett 
447f838f08cSDavid Spickett   strm.Clear();
448f838f08cSDavid Spickett   FieldEnum("multiple_enumerator",
449f838f08cSDavid Spickett             {FieldEnum::Enumerator(0, "zero"), FieldEnum::Enumerator(1, "one")})
450f838f08cSDavid Spickett       .ToXML(strm, 8);
451f838f08cSDavid Spickett   ASSERT_EQ(strm.GetString(), "<enum id=\"multiple_enumerator\" size=\"8\">\n"
452f838f08cSDavid Spickett                               "  <evalue name=\"zero\" value=\"0\"/>\n"
453f838f08cSDavid Spickett                               "  <evalue name=\"one\" value=\"1\"/>\n"
454f838f08cSDavid Spickett                               "</enum>\n");
455f838f08cSDavid Spickett }
456f838f08cSDavid Spickett 
457f838f08cSDavid Spickett TEST(RegisterFlagsTest, EnumsToXML) {
458f838f08cSDavid Spickett   // This method should output all the enums used by the register flag set,
459f838f08cSDavid Spickett   // only once.
460f838f08cSDavid Spickett 
461f838f08cSDavid Spickett   StreamString strm;
462f838f08cSDavid Spickett   FieldEnum enum_a("enum_a", {FieldEnum::Enumerator(0, "zero")});
463f838f08cSDavid Spickett   FieldEnum enum_b("enum_b", {FieldEnum::Enumerator(1, "one")});
464f838f08cSDavid Spickett   FieldEnum enum_c("enum_c", {FieldEnum::Enumerator(2, "two")});
465f838f08cSDavid Spickett   llvm::StringSet<> seen;
466f838f08cSDavid Spickett   // Pretend that enum_c was already emitted for a different flag set.
467f838f08cSDavid Spickett   seen.insert("enum_c");
468f838f08cSDavid Spickett 
469f838f08cSDavid Spickett   RegisterFlags("Test", 4,
470f838f08cSDavid Spickett                 {
471f838f08cSDavid Spickett                     RegisterFlags::Field("f1", 31, 31, &enum_a),
472f838f08cSDavid Spickett                     RegisterFlags::Field("f2", 30, 30, &enum_a),
473f838f08cSDavid Spickett                     RegisterFlags::Field("f3", 29, 29, &enum_b),
474f838f08cSDavid Spickett                     RegisterFlags::Field("f4", 27, 28, &enum_c),
475f838f08cSDavid Spickett                 })
476f838f08cSDavid Spickett       .EnumsToXML(strm, seen);
477f838f08cSDavid Spickett   ASSERT_EQ(strm.GetString(), "<enum id=\"enum_a\" size=\"4\">\n"
478f838f08cSDavid Spickett                               "  <evalue name=\"zero\" value=\"0\"/>\n"
479f838f08cSDavid Spickett                               "</enum>\n"
480f838f08cSDavid Spickett                               "<enum id=\"enum_b\" size=\"4\">\n"
481f838f08cSDavid Spickett                               "  <evalue name=\"one\" value=\"1\"/>\n"
482f838f08cSDavid Spickett                               "</enum>\n");
483d1556e5eSDavid Spickett }