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