1 #include <boost/container/flat_map.hpp>
2 #include <sdbusplus/unpack_properties.hpp>
3 
4 #include <gmock/gmock.h>
5 
6 namespace sdbusplus
7 {
8 
9 using VariantType = std::variant<std::string, uint32_t, float, double>;
10 using ContainerTypes =
11     testing::Types<std::vector<std::pair<std::string, VariantType>>,
12                    boost::container::flat_map<std::string, VariantType>,
13                    std::map<std::string, VariantType>>;
14 
15 template <typename Exception, typename F>
16 std::optional<Exception> captureException(F&& code)
17 {
18     try
19     {
20         code();
21     }
22     catch (const Exception& e)
23     {
24         return e;
25     }
26 
27     return std::nullopt;
28 }
29 
30 template <typename Container>
31 struct UnpackPropertiesTest : public testing::Test
32 {
33     void SetUp() override
34     {
35         using namespace std::string_literals;
36 
37         data.insert(data.end(),
38                     std::make_pair("Key-1"s, VariantType("string"s)));
39         data.insert(data.end(), std::make_pair("Key-2"s, VariantType(42.f)));
40         data.insert(data.end(), std::make_pair("Key-3"s, VariantType(15.)));
41     }
42 
43     Container data;
44 };
45 
46 TYPED_TEST_SUITE(UnpackPropertiesTest, ContainerTypes);
47 
48 TYPED_TEST(UnpackPropertiesTest, returnsValueWhenKeyIsPresentAndTypeMatches)
49 {
50     using namespace testing;
51 
52     std::string val1;
53     float val2 = 0.f;
54     double val3 = 0.;
55 
56     unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3", val3);
57 
58     ASSERT_THAT(val1, Eq("string"));
59     ASSERT_THAT(val2, FloatEq(42.f));
60     ASSERT_THAT(val3, DoubleEq(15.));
61 }
62 
63 TYPED_TEST(UnpackPropertiesTest,
64            unpackChangesOriginalDataWhenPassedAsNonConstReference)
65 {
66     using namespace testing;
67 
68     std::string val1, val2;
69 
70     unpackProperties(this->data, "Key-1", val1);
71     unpackProperties(this->data, "Key-1", val2);
72 
73     ASSERT_THAT(val1, Eq("string"));
74     ASSERT_THAT(val2, Not(Eq("string")));
75 }
76 
77 TYPED_TEST(UnpackPropertiesTest,
78            unpackDoesntChangeOriginalDataWhenPassesAsConstReference)
79 {
80     using namespace testing;
81 
82     std::string val1, val2;
83 
84     unpackProperties(Const(this->data), "Key-1", val1);
85     unpackProperties(Const(this->data), "Key-1", val2);
86 
87     ASSERT_THAT(val1, Eq("string"));
88     ASSERT_THAT(val2, Eq("string"));
89 }
90 
91 TYPED_TEST(UnpackPropertiesTest, throwsErrorWhenKeyIsMissing)
92 {
93     using namespace testing;
94 
95     std::string val1;
96     float val2 = 0.f;
97     double val3 = 0.;
98 
99     auto error = captureException<exception::UnpackPropertyError>([&] {
100         unpackProperties(this->data, "Key-1", val1, "Key-4", val2, "Key-3",
101                          val3);
102     });
103 
104     ASSERT_TRUE(error);
105     ASSERT_THAT(error->reason,
106                 Eq(exception::UnpackPropertyError::reasonMissingProperty));
107     ASSERT_THAT(error->propertyName, Eq("Key-4"));
108 }
109 
110 TYPED_TEST(UnpackPropertiesTest, throwsErrorWhenTypeDoesntMatch)
111 {
112     using namespace testing;
113 
114     std::string val1;
115     std::string val2;
116     double val3 = 0.;
117 
118     auto error = captureException<exception::UnpackPropertyError>([&] {
119         unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3",
120                          val3);
121     });
122 
123     ASSERT_TRUE(error);
124     ASSERT_THAT(error->reason,
125                 Eq(exception::UnpackPropertyError::reasonTypeNotMatched));
126     ASSERT_THAT(error->propertyName, Eq("Key-2"));
127 }
128 
129 TYPED_TEST(UnpackPropertiesTest,
130            returnsUndefinedValueForDuplicatedKeysWhenDataIsNonConstReference)
131 {
132     using namespace testing;
133     using namespace std::string_literals;
134 
135     std::string val1;
136     float val2 = 0.f;
137     double val3 = 0.;
138     std::string val4;
139 
140     unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3", val3,
141                      "Key-1", val4);
142 
143     ASSERT_THAT(val1, Eq("string"));
144     ASSERT_THAT(val2, FloatEq(42.f));
145     ASSERT_THAT(val3, DoubleEq(15.));
146     ASSERT_THAT(val4, Not(Eq("string")));
147 }
148 
149 TYPED_TEST(UnpackPropertiesTest,
150            returnsValueForDuplicatedKeysWhenDataIsConstReference)
151 {
152     using namespace testing;
153     using namespace std::string_literals;
154 
155     std::string val1;
156     float val2 = 0.f;
157     double val3 = 0.;
158     std::string val4;
159 
160     unpackProperties(Const(this->data), "Key-1", val1, "Key-2", val2, "Key-3",
161                      val3, "Key-1", val4);
162 
163     ASSERT_THAT(val1, Eq("string"));
164     ASSERT_THAT(val2, FloatEq(42.f));
165     ASSERT_THAT(val3, DoubleEq(15.));
166     ASSERT_THAT(val4, Eq("string"));
167 }
168 
169 struct UnpackPropertiesTest_ForVector :
170     public UnpackPropertiesTest<
171         std::vector<std::pair<std::string, VariantType>>>
172 {};
173 
174 TEST_F(UnpackPropertiesTest_ForVector, silentlyDiscardsDuplicatedKeyInData)
175 {
176     using namespace testing;
177     using namespace std::string_literals;
178 
179     std::string val1;
180     float val2 = 0.f;
181     double val3 = 0.;
182 
183     this->data.insert(this->data.end(),
184                       std::make_pair("Key-1"s, VariantType("string2"s)));
185 
186     unpackProperties(this->data, "Key-1", val1, "Key-2", val2, "Key-3", val3);
187 
188     ASSERT_THAT(val1, Eq("string"));
189     ASSERT_THAT(val2, FloatEq(42.f));
190     ASSERT_THAT(val3, DoubleEq(15.));
191 }
192 
193 } // namespace sdbusplus
194