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<\" start=\"4\" end=\"4\"/>\n" 386d1556e5eSDavid Spickett " <field name=\"B>\" start=\"3\" end=\"3\"/>\n" 387d1556e5eSDavid Spickett " <field name=\"C'\" start=\"2\" end=\"2\"/>\n" 388d1556e5eSDavid Spickett " <field name=\"D"\" start=\"1\" end=\"1\"/>\n" 389d1556e5eSDavid Spickett " <field name=\"E&\" 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<\" value=\"0\"/>"), 417f838f08cSDavid Spickett std::make_pair(FieldEnum::Enumerator(1, "B>"), 418f838f08cSDavid Spickett "<evalue name=\"B>\" value=\"1\"/>"), 419f838f08cSDavid Spickett std::make_pair(FieldEnum::Enumerator(2, "C'"), 420f838f08cSDavid Spickett "<evalue name=\"C'\" value=\"2\"/>"), 421f838f08cSDavid Spickett std::make_pair(FieldEnum::Enumerator(3, "D\""), 422f838f08cSDavid Spickett "<evalue name=\"D"\" value=\"3\"/>"), 423f838f08cSDavid Spickett std::make_pair(FieldEnum::Enumerator(4, "E&"), 424f838f08cSDavid Spickett "<evalue name=\"E&\" 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 }