1 // Copyright 2009 Daniel Erat <dan@erat.org>
2 // All rights reserved.
3
4 #include <cassert>
5 #include <map>
6 #define __STDC_LIMIT_MACROS // needed to get MAX and MIN macros from stdint.h
7 #include <stdint.h>
8 #include <string>
9
10 #include <gtest/gtest.h>
11
12 #include "config_parser.h"
13 #include "setting.h"
14
15 using std::map;
16 using std::string;
17
18 namespace xsettingsd {
19
20 // Test the basic operation of CharStream.
TEST(CharStreamTest,Basic)21 TEST(CharStreamTest, Basic) {
22 ConfigParser::StringCharStream stream("012");
23
24 // We should die if we try to do anything before calling Init().
25 ASSERT_DEATH(stream.AtEOF(), "initialized");
26 ASSERT_DEATH(stream.GetChar(), "initialized");
27 ASSERT_DEATH(stream.UngetChar('0'), "initialized");
28
29 // Now read a character, put it back, and read it again.
30 ASSERT_TRUE(stream.Init(NULL));
31 EXPECT_FALSE(stream.AtEOF());
32 EXPECT_EQ('0', stream.GetChar());
33 stream.UngetChar('0');
34 EXPECT_EQ('0', stream.GetChar());
35
36 // Do the same thing with the second character...
37 EXPECT_FALSE(stream.AtEOF());
38 EXPECT_EQ('1', stream.GetChar());
39 stream.UngetChar('1');
40 EXPECT_EQ('1', stream.GetChar());
41
42 // ... and with the third. We should be at EOF after reading it.
43 EXPECT_EQ('2', stream.GetChar());
44 EXPECT_TRUE(stream.AtEOF());
45 stream.UngetChar('2');
46 EXPECT_FALSE(stream.AtEOF());
47 EXPECT_EQ('2', stream.GetChar());
48 EXPECT_TRUE(stream.AtEOF());
49 }
50
51 // Test that line numbers are reported correctly as we get and un-get
52 // characters.
TEST(CharStreamTest,LineNumbers)53 TEST(CharStreamTest, LineNumbers) {
54 ConfigParser::StringCharStream stream("a\nb\n\nc");
55
56 // We use line 0 to represent not having read any characters yet.
57 ASSERT_TRUE(stream.Init(NULL));
58 EXPECT_EQ(0, stream.line_num());
59
60 // Getting the first 'a' should put us on line 1. We move back to 0 when
61 // we un-get it and back to 1 when we re-get it.
62 EXPECT_EQ('a', stream.GetChar());
63 EXPECT_EQ(1, stream.line_num());
64 stream.UngetChar('a');
65 EXPECT_EQ(0, stream.line_num());
66 EXPECT_EQ('a', stream.GetChar());
67 EXPECT_EQ(1, stream.line_num());
68
69 // The first newline should show up as being on line 1 as well.
70 EXPECT_EQ('\n', stream.GetChar());
71 EXPECT_EQ(1, stream.line_num());
72 stream.UngetChar('\n');
73 EXPECT_EQ(1, stream.line_num());
74 EXPECT_EQ('\n', stream.GetChar());
75 EXPECT_EQ(1, stream.line_num());
76
77 // The 'b' is on line 2...
78 EXPECT_EQ('b', stream.GetChar());
79 EXPECT_EQ(2, stream.line_num());
80 stream.UngetChar('b');
81 EXPECT_EQ(1, stream.line_num());
82 EXPECT_EQ('b', stream.GetChar());
83 EXPECT_EQ(2, stream.line_num());
84
85 // ... as is the first newline after it.
86 EXPECT_EQ('\n', stream.GetChar());
87 EXPECT_EQ(2, stream.line_num());
88 stream.UngetChar('\n');
89 EXPECT_EQ(2, stream.line_num());
90 EXPECT_EQ('\n', stream.GetChar());
91 EXPECT_EQ(2, stream.line_num());
92
93 // The second newline should show up as being on line 3.
94 EXPECT_EQ('\n', stream.GetChar());
95 EXPECT_EQ(3, stream.line_num());
96 stream.UngetChar('\n');
97 EXPECT_EQ(2, stream.line_num());
98 EXPECT_EQ('\n', stream.GetChar());
99 EXPECT_EQ(3, stream.line_num());
100
101 // And the 'c' is on line 4.
102 EXPECT_EQ('c', stream.GetChar());
103 EXPECT_EQ(4, stream.line_num());
104 stream.UngetChar('c');
105 EXPECT_EQ(3, stream.line_num());
106 EXPECT_EQ('c', stream.GetChar());
107 EXPECT_EQ(4, stream.line_num());
108
109 EXPECT_TRUE(stream.AtEOF());
110 }
111
112 class ConfigParserTest : public testing::Test {
113 protected:
114 // Helper methods to get the return value or string from
115 // ConfigParser::ReadSettingName().
GetReadSettingNameResult(const string & input)116 bool GetReadSettingNameResult(const string& input) {
117 bool result;
118 RunReadSettingName(input, &result, NULL);
119 return result;
120 }
GetReadSettingNameData(const string & input)121 string GetReadSettingNameData(const string& input) {
122 bool result;
123 string data;
124 RunReadSettingName(input, &result, &data);
125 assert(result);
126 return data;
127 }
128
GetReadIntegerResult(const string & input)129 bool GetReadIntegerResult(const string& input) {
130 bool result;
131 RunReadInteger(input, &result, NULL);
132 return result;
133 }
GetReadIntegerData(const string & input)134 int32_t GetReadIntegerData(const string& input) {
135 bool result;
136 int32_t data;
137 RunReadInteger(input, &result, &data);
138 assert(result);
139 return data;
140 }
141
GetReadStringResult(const string & input)142 bool GetReadStringResult(const string& input) {
143 bool result;
144 RunReadString(input, &result, NULL);
145 return result;
146 }
GetReadStringData(const string & input)147 string GetReadStringData(const string& input) {
148 bool result;
149 string data;
150 RunReadString(input, &result, &data);
151 assert(result);
152 return data;
153 }
154
GetReadColorResult(const string & input)155 bool GetReadColorResult(const string& input) {
156 bool result;
157 RunReadColor(input, &result, NULL);
158 return result;
159 }
GetReadColorData(const string & input)160 string GetReadColorData(const string& input) {
161 bool result;
162 string data;
163 RunReadColor(input, &result, &data);
164 assert(result);
165 return data;
166 }
167
168 private:
RunReadSettingName(const string & input,bool * result_out,string * name_out)169 void RunReadSettingName(const string& input,
170 bool* result_out,
171 string* name_out) {
172 ConfigParser::CharStream* stream =
173 new ConfigParser::StringCharStream(input);
174 assert(stream->Init(NULL));
175 ConfigParser parser(stream);
176 string name;
177 bool result = parser.ReadSettingName(&name);
178 if (result_out)
179 *result_out = result;
180 if (name_out)
181 *name_out = name;
182 }
183
RunReadInteger(const string & input,bool * result_out,int32_t * num_out)184 void RunReadInteger(const string& input,
185 bool* result_out,
186 int32_t* num_out) {
187 ConfigParser::CharStream* stream =
188 new ConfigParser::StringCharStream(input);
189 assert(stream->Init(NULL));
190 ConfigParser parser(stream);
191 int32_t num = 0;
192 bool result = parser.ReadInteger(&num);
193 if (result_out)
194 *result_out = result;
195 if (num_out)
196 *num_out = num;
197 }
198
RunReadString(const string & input,bool * result_out,string * str_out)199 void RunReadString(const string& input,
200 bool* result_out,
201 string* str_out) {
202 ConfigParser::CharStream* stream =
203 new ConfigParser::StringCharStream(input);
204 assert(stream->Init(NULL));
205 ConfigParser parser(stream);
206 string str;
207 bool result = parser.ReadString(&str);
208 if (result_out)
209 *result_out = result;
210 if (str_out)
211 *str_out = str;
212 }
213
RunReadColor(const string & input,bool * result_out,string * str_out)214 void RunReadColor(const string& input,
215 bool* result_out,
216 string* str_out) {
217 ConfigParser::CharStream* stream =
218 new ConfigParser::StringCharStream(input);
219 assert(stream->Init(NULL));
220 ConfigParser parser(stream);
221 uint16_t red = 0, green = 0, blue = 0, alpha = 0;
222 bool result = parser.ReadColor(&red, &green, &blue, &alpha);
223 if (result_out)
224 *result_out = result;
225 if (str_out)
226 *str_out = StringPrintf("(%d,%d,%d,%d)", red, green, blue, alpha);
227 }
228 };
229
TEST_F(ConfigParserTest,ReadSettingName)230 TEST_F(ConfigParserTest, ReadSettingName) {
231 EXPECT_EQ("test", GetReadSettingNameData("test"));
232 EXPECT_EQ("First/Second", GetReadSettingNameData("First/Second"));
233 EXPECT_EQ("Has_Underscore", GetReadSettingNameData("Has_Underscore"));
234 EXPECT_EQ("trailing_space", GetReadSettingNameData("trailing_space "));
235 EXPECT_EQ("blah", GetReadSettingNameData("blah#comment"));
236 EXPECT_FALSE(GetReadSettingNameResult(" leading_space"));
237 EXPECT_FALSE(GetReadSettingNameResult("/leading_slash"));
238 EXPECT_FALSE(GetReadSettingNameResult("trailing_slash/"));
239 EXPECT_FALSE(GetReadSettingNameResult("double//slash"));
240 EXPECT_FALSE(GetReadSettingNameResult("digit_after_slash/0"));
241
242 // For good measure, test the examples of legitimate and illegitimate
243 // names from the spec.
244 EXPECT_EQ("GTK/colors/background0",
245 GetReadSettingNameData("GTK/colors/background0"));
246 EXPECT_EQ("_background", GetReadSettingNameData("_background"));
247 EXPECT_EQ("_111", GetReadSettingNameData("_111"));
248 EXPECT_FALSE(GetReadSettingNameResult("/"));
249 EXPECT_FALSE(GetReadSettingNameResult("_background/"));
250 EXPECT_FALSE(GetReadSettingNameResult("GTK//colors"));
251 EXPECT_FALSE(GetReadSettingNameResult(""));
252 }
253
TEST_F(ConfigParserTest,ReadInteger)254 TEST_F(ConfigParserTest, ReadInteger) {
255 EXPECT_EQ(0, GetReadIntegerData("0"));
256 EXPECT_EQ(10, GetReadIntegerData("10"));
257 EXPECT_EQ(12, GetReadIntegerData("0012"));
258 EXPECT_EQ(15, GetReadIntegerData("15#2 comment"));
259 EXPECT_EQ(20, GetReadIntegerData("20 "));
260 EXPECT_EQ(30, GetReadIntegerData("30\n"));
261 EXPECT_EQ(INT32_MAX, GetReadIntegerData("2147483647"));
262 EXPECT_EQ(-5, GetReadIntegerData("-5"));
263 EXPECT_EQ(INT32_MIN, GetReadIntegerData("-2147483648"));
264 EXPECT_FALSE(GetReadIntegerResult(""));
265 EXPECT_FALSE(GetReadIntegerResult("-"));
266 EXPECT_FALSE(GetReadIntegerResult("--2"));
267 EXPECT_FALSE(GetReadIntegerResult("2-3"));
268 EXPECT_FALSE(GetReadIntegerResult(" "));
269 EXPECT_FALSE(GetReadIntegerResult(" 23"));
270 }
271
TEST_F(ConfigParserTest,ReadString)272 TEST_F(ConfigParserTest, ReadString) {
273 EXPECT_EQ("test", GetReadStringData("\"test\""));
274 EXPECT_EQ(" some whitespace ", GetReadStringData("\" some whitespace \""));
275 EXPECT_EQ("a\tb\nc", GetReadStringData("\"a\\tb\\nc\""));
276 EXPECT_EQ("a\"b\\c", GetReadStringData("\"a\\\"b\\\\c\""));
277 EXPECT_EQ("ar", GetReadStringData("\"\\a\\r\""));
278 EXPECT_EQ(" ", GetReadStringData("\" \""));
279 EXPECT_EQ("", GetReadStringData("\"\""));
280 EXPECT_EQ("a", GetReadStringData("\"a\" "));
281 EXPECT_FALSE(GetReadStringResult(""));
282 EXPECT_FALSE(GetReadStringResult("a"));
283 EXPECT_FALSE(GetReadStringResult("\""));
284 EXPECT_FALSE(GetReadStringResult("\"\n\""));
285 }
286
TEST_F(ConfigParserTest,ReadColor)287 TEST_F(ConfigParserTest, ReadColor) {
288 EXPECT_EQ("(1,2,3,4)", GetReadColorData("(1,2,3,4)"));
289 EXPECT_EQ("(1,2,3,65535)", GetReadColorData("(1,2,3)"));
290 EXPECT_EQ("(32768,32769,32770,32771)",
291 GetReadColorData("( 32768 ,32769 , 32770, 32771 )"));
292 EXPECT_FALSE(GetReadColorResult(""));
293 EXPECT_FALSE(GetReadColorResult("("));
294 EXPECT_FALSE(GetReadColorResult(")"));
295 EXPECT_FALSE(GetReadColorResult("()"));
296 EXPECT_FALSE(GetReadColorResult("( )"));
297 EXPECT_FALSE(GetReadColorResult("(2)"));
298 EXPECT_FALSE(GetReadColorResult("(,2)"));
299 EXPECT_FALSE(GetReadColorResult("(2,)"));
300 EXPECT_FALSE(GetReadColorResult("(2,3)"));
301 EXPECT_FALSE(GetReadColorResult("(2,3,4,)"));
302 EXPECT_FALSE(GetReadColorResult("(2,3,,4)"));
303 EXPECT_FALSE(GetReadColorResult("(2,3,4,5,)"));
304 EXPECT_FALSE(GetReadColorResult("(2(,3,4,5)"));
305 EXPECT_FALSE(GetReadColorResult("(2,3,4,5,6)"));
306 EXPECT_FALSE(GetReadColorResult("(2a,3,4,5)"));
307 EXPECT_FALSE(GetReadColorResult("(2 1,3,4,5)"));
308 EXPECT_FALSE(GetReadColorResult("(2,3,4,5"));
309 EXPECT_FALSE(GetReadColorResult("(2,3\n,4,5)"));
310 }
311
IntegerSettingEquals(const char * expected_expr,const char * actual_expr,int32_t expected,const Setting * actual)312 testing::AssertionResult IntegerSettingEquals(
313 const char* expected_expr,
314 const char* actual_expr,
315 int32_t expected,
316 const Setting* actual) {
317 if (!actual) {
318 testing::Message msg;
319 msg << "Expected: " << expected << "\n"
320 << " Actual: " << actual_expr << " is NULL";
321 return testing::AssertionFailure(msg);
322 }
323
324 const IntegerSetting *setting = dynamic_cast<const IntegerSetting*>(actual);
325 if (!setting) {
326 testing::Message msg;
327 msg << "Expected: " << expected << "\n"
328 << " Actual: " << actual_expr << " (not an IntegerSetting)";
329 return testing::AssertionFailure(msg);
330 }
331
332 if (setting->value() != expected) {
333 testing::Message msg;
334 msg << "Expected: " << expected << "\n"
335 << " Actual: " << actual_expr << " contains " << setting->value();
336 return testing::AssertionFailure(msg);
337 }
338
339 return testing::AssertionSuccess();
340 }
341
StringSettingEquals(const char * expected_expr,const char * actual_expr,const string & expected,const Setting * actual)342 testing::AssertionResult StringSettingEquals(
343 const char* expected_expr,
344 const char* actual_expr,
345 const string& expected,
346 const Setting* actual) {
347 if (!actual) {
348 testing::Message msg;
349 msg << "Expected: " << expected << "\n"
350 << " Actual: " << actual_expr << " is NULL";
351 return testing::AssertionFailure(msg);
352 }
353
354 const StringSetting *setting = dynamic_cast<const StringSetting*>(actual);
355 if (!setting) {
356 testing::Message msg;
357 msg << "Expected: " << expected << "\n"
358 << " Actual: " << actual_expr << " (not a StringSetting)";
359 return testing::AssertionFailure(msg);
360 }
361
362 if (setting->value() != expected) {
363 testing::Message msg;
364 msg << "Expected: \"" << expected << "\"\n"
365 << " Actual: " << actual_expr << " contains \""
366 << setting->value() << "\"";
367 return testing::AssertionFailure(msg);
368 }
369
370 return testing::AssertionSuccess();
371 }
372
ColorSettingEquals(const char * expected_expr,const char * actual_expr,const string & expected_str,const Setting * actual)373 testing::AssertionResult ColorSettingEquals(
374 const char* expected_expr,
375 const char* actual_expr,
376 const string& expected_str,
377 const Setting* actual) {
378 if (!actual) {
379 testing::Message msg;
380 msg << "Expected: " << expected_str << "\n"
381 << " Actual: " << actual_expr << " is NULL";
382 return testing::AssertionFailure(msg);
383 }
384
385 const ColorSetting *setting = dynamic_cast<const ColorSetting*>(actual);
386 if (!setting) {
387 testing::Message msg;
388 msg << "Expected: " << expected_str << "\n"
389 << " Actual: " << actual_expr << " (not a ColorSetting)";
390 return testing::AssertionFailure(msg);
391 }
392
393 string actual_str = StringPrintf("(%d,%d,%d,%d)",
394 setting->red(), setting->green(),
395 setting->blue(), setting->alpha());
396 if (actual_str != expected_str) {
397 testing::Message msg;
398 msg << "Expected: \"" << expected_str << "\"\n"
399 << " Actual: " << actual_expr << " contains \""
400 << actual_str << "\"";
401 return testing::AssertionFailure(msg);
402 }
403
404 return testing::AssertionSuccess();
405 }
406
TEST_F(ConfigParserTest,Parse)407 TEST_F(ConfigParserTest, Parse) {
408 const char* good_input =
409 "Setting1 5\n"
410 "Setting2 \"this is a string\"\n"
411 "# commented line\n"
412 "\n"
413 "Setting3 2 # trailing comment\n"
414 "Setting4 \"\\\"quoted\\\"\"# comment\n"
415 "Setting5 (45,21, 5 , 8)# color";
416 ConfigParser parser(new ConfigParser::StringCharStream(good_input));
417 SettingsMap settings;
418 ASSERT_TRUE(parser.Parse(&settings, NULL, 0));
419 ASSERT_EQ(5, settings.map().size());
420 EXPECT_PRED_FORMAT2(IntegerSettingEquals, 5, settings.GetSetting("Setting1"));
421 EXPECT_PRED_FORMAT2(StringSettingEquals,
422 "this is a string",
423 settings.GetSetting("Setting2"));
424 EXPECT_PRED_FORMAT2(IntegerSettingEquals, 2, settings.GetSetting("Setting3"));
425 EXPECT_PRED_FORMAT2(StringSettingEquals,
426 "\"quoted\"",
427 settings.GetSetting("Setting4"));
428 EXPECT_PRED_FORMAT2(ColorSettingEquals,
429 "(45,21,5,8)",
430 settings.GetSetting("Setting5"));
431
432 const char* extra_name = "SettingName 3 blah";
433 parser.Reset(new ConfigParser::StringCharStream(extra_name));
434 EXPECT_FALSE(parser.Parse(&settings, NULL, 0));
435
436 const char* missing_value = "SettingName";
437 parser.Reset(new ConfigParser::StringCharStream(missing_value));
438 EXPECT_FALSE(parser.Parse(&settings, NULL, 0));
439
440 const char* comment_instead_of_value = "SettingName # test 3\n";
441 parser.Reset(new ConfigParser::StringCharStream(comment_instead_of_value));
442 EXPECT_FALSE(parser.Parse(&settings, NULL, 0));
443
444 const char* duplicate_name = "SettingName 4\nSettingName 3";
445 parser.Reset(new ConfigParser::StringCharStream(duplicate_name));
446 EXPECT_FALSE(parser.Parse(&settings, NULL, 0));
447 }
448
449 } // namespace xsettingsd
450
main(int argc,char ** argv)451 int main(int argc, char** argv) {
452 testing::InitGoogleTest(&argc, argv);
453 return RUN_ALL_TESTS();
454 }
455