1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 /**
28 * Unit tests for Kfg interface
29 */
30 
31 #include <kfg/extern.h>
32 
33 #include <map>
34 #include <string>
35 #include <cstring>
36 #include <sstream>
37 
38 #include <ktst/unit_test.hpp>
39 
40 #include <klib/namelist.h>
41 
42 using namespace std;
43 
44 TEST_SUITE(KfgWbTestSuite);
45 
46 //////////////////////////////////////////// tests for flex-generated scanner
47 extern "C" {
48 #include "../libs/kfg/kfg-parse.h"
49 #include "../libs/kfg/config-tokens.h"
50 }
51 
52 // simple symbol table
53 class SymbolTable : public map<string, string>
54 {
55 protected:
Add(const char * name,const char * value)56     void Add(const char* name, const char* value)
57     {
58         (*this)[name]=value;
59     }
60 
lookup_var(void * self,KFGToken * pb)61     static bool lookup_var(void * self, KFGToken* pb)
62     {
63         const SymbolTable* tab=(const SymbolTable*)self;
64         map<string, string>::const_iterator var=tab->find(string(pb->tokenText+2, pb->tokenLength-3));
65         if (var == tab->end())
66         {
67             return false;
68         }
69         pb->tokenText = var->second.c_str();
70         pb->tokenLength = var->second.length();
71         pb->tokenId = kfgVAR_REF;
72         return true;
73     }
74 };
75 
76 // test fixture for scanner tests
77 class KfgScanFixture : public SymbolTable
78 {
79 public:
KfgScanFixture()80     KfgScanFixture()
81     {
82         sb.self=this;
83         sb.file="test";
84         sb.look_up_var=lookup_var;
85         sb.buffer=0;
86         sb.scanner=0;
87         sb.report_error=report_error;
88     }
~KfgScanFixture()89     ~KfgScanFixture()
90     {
91         KFGScan_yylex_destroy(&sb);
92     }
InitScan(const char * input,bool trace=false)93     void InitScan(const char* input, bool trace=false)
94     {
95         KFGScan_yylex_init(&sb, input);
96         KFGScan_set_debug(trace, &sb);
97     }
Scan()98     int Scan()
99     {
100         errorMsg.clear();
101 
102         KFGScan_yylex(&sym, &sb);
103 
104         const char* token=sb.lastToken->tokenText;
105         if (sb.lastToken->tokenId != kfgEND_INPUT && token != 0)
106         {
107             tokenText=string(token, sb.lastToken->tokenLength);
108         }
109         else
110         {
111             tokenText.clear();
112         }
113         return sym.pb.tokenId;
114     }
report_error(KFGScanBlock * sb,const char * msg)115     static void report_error(KFGScanBlock* sb, const char* msg)
116     {
117         ((KfgScanFixture*)sb->self)->errorMsg=msg;
118     }
119 
MatchToken(const char * token,int expectedId,const char * expectedText,int nextToken=kfgEND_INPUT,bool trace=false,bool keepError=true)120     string MatchToken(const char* token, int expectedId, const char* expectedText, int nextToken=kfgEND_INPUT, bool trace=false, bool keepError=true)
121     {
122         InitScan(token, trace);
123         int id=Scan();
124         if (id != expectedId)
125         {
126             ostringstream os;
127             os << "Scan() returned " << id << ", expected" << expectedId;
128             return os.str();
129         }
130         if (expectedText == 0)
131         {
132             expectedText=token;
133         }
134         if (tokenText != expectedText)
135         {
136             ostringstream os;
137             os << "Scan() returned " << tokenText.c_str() << ", expected" << expectedText;
138             return os.str();
139         }
140 
141         // save the errorMsg returned by Scan() from being overwritten, in case the test case is interested in checking it rather than the next one.
142         string errorMsgSaved=errorMsg;
143         id=Scan();
144         if (id != nextToken)
145         {
146             ostringstream os;
147             os << "post-Scan() returned " << id << ", expected" << nextToken;
148             return os.str();
149         }
150         if (keepError)
151         {
152             errorMsg=errorMsgSaved;
153         }
154         return "";
155     }
MatchToken(const char * token,int expectedId,bool trace=false)156     string MatchToken(const char* token, int expectedId, bool trace=false)
157     {
158         return MatchToken(token, expectedId, 0, kfgEND_INPUT, trace);
159     }
160 
161     KFGSymbol sym;
162     KFGScanBlock sb;
163     string tokenText;
164     string errorMsg;
165 };
166 
FIXTURE_TEST_CASE(KfgScanMultiLineComments,KfgScanFixture)167 FIXTURE_TEST_CASE(KfgScanMultiLineComments, KfgScanFixture)
168 {   // C comments with an \n inside are recognized as EOL
169     InitScan("/***\n\n\n*/");
170     REQUIRE_EQUAL(Scan(), (int)kfgEND_LINE);
171     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
172 }
173 
FIXTURE_TEST_CASE(KfgScanLineComments,KfgScanFixture)174 FIXTURE_TEST_CASE(KfgScanLineComments, KfgScanFixture)
175 {
176     InitScan(
177         "/*********/\n"
178         "#some more stuff\n"
179         "/*========*/");
180     REQUIRE_EQUAL(Scan(), (int)kfgEND_LINE); // C comments without \n inside are skipped
181     REQUIRE_EQUAL(Scan(), (int)kfgEND_LINE); // line comments skipped
182     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);// not folowed by /n
183 }
184 
FIXTURE_TEST_CASE(KfgScanNestedMultiLineComments,KfgScanFixture)185 FIXTURE_TEST_CASE(KfgScanNestedMultiLineComments, KfgScanFixture)
186 {
187     InitScan("/***\n"
188              "/**/\n" // the comment ends here
189              "*/");
190     REQUIRE_EQUAL(Scan(), (int)kfgEND_LINE);
191     REQUIRE_EQUAL(Scan(), (int)kfgEND_LINE);
192     REQUIRE_EQUAL(Scan(), (int)kfgUNRECOGNIZED);
193 }
194 
FIXTURE_TEST_CASE(KfgScanIgnoredSymbols,KfgScanFixture)195 FIXTURE_TEST_CASE(KfgScanIgnoredSymbols, KfgScanFixture)
196 {
197     InitScan("\v\r\t\f");
198     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
199 }
200 
FIXTURE_TEST_CASE(KfgScanEndOfLine,KfgScanFixture)201 FIXTURE_TEST_CASE(KfgScanEndOfLine, KfgScanFixture)
202 {
203     InitScan("\v\r\t\f\n");
204     REQUIRE_EQUAL(Scan(), (int)kfgEND_LINE);
205     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
206 }
207 
FIXTURE_TEST_CASE(KfgScanStrings,KfgScanFixture)208 FIXTURE_TEST_CASE(KfgScanStrings, KfgScanFixture)
209 {   // strings with white space in between
210     InitScan("'str1' \"str2\"");
211     REQUIRE_EQUAL(Scan(), (int)kfgSTRING);          REQUIRE_EQUAL(tokenText, std::string("str1"));
212     REQUIRE_EQUAL(Scan(), (int)kfgSTRING);          REQUIRE_EQUAL(tokenText, std::string("str2"));
213     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
214 }
215 
FIXTURE_TEST_CASE(KfgScanString1,KfgScanFixture)216 FIXTURE_TEST_CASE(KfgScanString1, KfgScanFixture)
217 {   // ( in string
218     REQUIRE_EQ(MatchToken("'str1('", kfgSTRING, "str1("), string());
219 }
FIXTURE_TEST_CASE(KfgScanString2,KfgScanFixture)220 FIXTURE_TEST_CASE(KfgScanString2, KfgScanFixture)
221 {   // $ in string
222     REQUIRE_EQ(MatchToken("'a$str2\\\"'", kfgESCAPED_STRING, "a$str2\\\""), string());
223 }
FIXTURE_TEST_CASE(KfgScanString3,KfgScanFixture)224 FIXTURE_TEST_CASE(KfgScanString3, KfgScanFixture)
225 {   // ) in string
226     REQUIRE_EQ(MatchToken("\"str)3\"", kfgSTRING, "str)3"), string());
227 }
FIXTURE_TEST_CASE(KfgScanString4,KfgScanFixture)228 FIXTURE_TEST_CASE(KfgScanString4, KfgScanFixture)
229 {   // escaped \n in string
230     REQUIRE_EQ(MatchToken("\"str4\\n'\"", kfgESCAPED_STRING, "str4\\n'"), string());
231 }
FIXTURE_TEST_CASE(KfgScanString5,KfgScanFixture)232 FIXTURE_TEST_CASE(KfgScanString5, KfgScanFixture)
233 {   // escaped \" in string
234     REQUIRE_EQ(MatchToken("\"\\\"\"", kfgESCAPED_STRING, "\\\""), string());
235 }
236 
FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes1,KfgScanFixture)237 FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes1, KfgScanFixture)
238 {   // escaped $ in string, case 1
239     REQUIRE_EQ(MatchToken("'\\$'", kfgESCAPED_STRING, "\\$"), string());
240 }
FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes2,KfgScanFixture)241 FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes2, KfgScanFixture)
242 {   // escaped $ in string, case 2
243     REQUIRE_EQ(MatchToken("\"$\\$1\"", kfgESCAPED_STRING, "$\\$1"), string());
244 }
FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes3,KfgScanFixture)245 FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes3, KfgScanFixture)
246 {   // escaped $( in string
247     REQUIRE_EQ(MatchToken("\"$\\$(ref)\"", kfgESCAPED_STRING, "$\\$(ref)"), string());
248 }
FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes4,KfgScanFixture)249 FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes4, KfgScanFixture)
250 {   // unterminated $( in double quoted string
251     REQUIRE_EQ(MatchToken("\"$(qq\"qq", kfgSTRING, "", kfgSTRING), string());
252     REQUIRE_EQUAL(tokenText, string("$(qq\"qq"));
253     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
254 }
FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes5,KfgScanFixture)255 FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes5, KfgScanFixture)
256 {   // unterminated $( in single quoted string
257     REQUIRE_EQ(MatchToken("'$(qq'qq", kfgSTRING, "", kfgSTRING), string());
258     REQUIRE_EQUAL(tokenText, string("$(qq\'qq"));
259     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
260 }
FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes6,KfgScanFixture)261 FIXTURE_TEST_CASE(KfgScanVarRefInStringEscapes6, KfgScanFixture)
262 {   // unterminated $( eats up the rest of the line
263     REQUIRE_EQ(MatchToken("'$(qq zzz", kfgSTRING, "", kfgSTRING), string());
264     REQUIRE_EQUAL(tokenText, string("$(qq zzz"));
265     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
266 }
267 
268 // EOL in strings
FIXTURE_TEST_CASE(KfgScanUnterminatedString1,KfgScanFixture)269 FIXTURE_TEST_CASE(KfgScanUnterminatedString1, KfgScanFixture)
270 {
271     REQUIRE_EQ(MatchToken("'st\n", kfgUNTERM_STRING, "st", kfgEND_LINE), string(""));
272     REQUIRE(errorMsg.length() > 0);
273 }
FIXTURE_TEST_CASE(KfgScanUnterminatedString2,KfgScanFixture)274 FIXTURE_TEST_CASE(KfgScanUnterminatedString2, KfgScanFixture)
275 {
276     REQUIRE_EQ(MatchToken("\"st\n", kfgUNTERM_STRING, "st", kfgEND_LINE), string(""));
277     REQUIRE(errorMsg.length() > 0);
278 }
FIXTURE_TEST_CASE(KfgScanUnterminatedString3,KfgScanFixture)279 FIXTURE_TEST_CASE(KfgScanUnterminatedString3, KfgScanFixture)
280 {
281     REQUIRE_EQ(MatchToken("'s\\t\n", kfgUNTERM_ESCAPED_STRING, "s\\t", kfgEND_LINE), string(""));
282     REQUIRE(errorMsg.length() > 0);
283 }
FIXTURE_TEST_CASE(KfgScanUnterminatedString4,KfgScanFixture)284 FIXTURE_TEST_CASE(KfgScanUnterminatedString4, KfgScanFixture)
285 {
286     REQUIRE_EQ(MatchToken("\"s\\t\n", kfgUNTERM_ESCAPED_STRING, "s\\t", kfgEND_LINE), string(""));
287     REQUIRE(errorMsg.length() > 0);
288 }
289 // EOF in strings
FIXTURE_TEST_CASE(KfgScanUnterminatedString5,KfgScanFixture)290 FIXTURE_TEST_CASE(KfgScanUnterminatedString5, KfgScanFixture)
291 {
292     REQUIRE_EQ(MatchToken("'st", kfgUNTERM_STRING, "st"), string(""));
293     REQUIRE(errorMsg.length() > 0);
294 }
FIXTURE_TEST_CASE(KfgScanUnterminatedString6,KfgScanFixture)295 FIXTURE_TEST_CASE(KfgScanUnterminatedString6, KfgScanFixture)
296 {
297     REQUIRE_EQ(MatchToken("\"st", kfgUNTERM_STRING, "st"), string(""));
298     REQUIRE(errorMsg.length() > 0);
299 }
FIXTURE_TEST_CASE(KfgScanUnterminatedString7,KfgScanFixture)300 FIXTURE_TEST_CASE(KfgScanUnterminatedString7, KfgScanFixture)
301 {
302     REQUIRE_EQ(MatchToken("'s\\t", kfgUNTERM_ESCAPED_STRING, "s\\t"), string(""));
303     REQUIRE(errorMsg.length() > 0);
304 }
FIXTURE_TEST_CASE(KfgScanUnterminatedString8,KfgScanFixture)305 FIXTURE_TEST_CASE(KfgScanUnterminatedString8, KfgScanFixture)
306 {
307     REQUIRE_EQ(MatchToken("\"s\\t", kfgUNTERM_ESCAPED_STRING, "s\\t"), string(""));
308     REQUIRE(errorMsg.length() > 0);
309 }
310 
FIXTURE_TEST_CASE(KfgScanSimplePathNames,KfgScanFixture)311 FIXTURE_TEST_CASE(KfgScanSimplePathNames, KfgScanFixture)
312 {
313     InitScan("a _aBc 9-9 _--.* ");
314     REQUIRE_EQUAL(Scan(), (int)kfgREL_PATH); REQUIRE_EQUAL(tokenText, string("a"));
315     REQUIRE_EQUAL(Scan(), (int)kfgREL_PATH); REQUIRE_EQUAL(tokenText, string("_aBc"));
316     REQUIRE_EQUAL(Scan(), (int)kfgREL_PATH); REQUIRE_EQUAL(tokenText, string("9-9"));
317     REQUIRE_EQUAL(Scan(), (int)kfgREL_PATH); REQUIRE_EQUAL(tokenText, string("_--."));
318     REQUIRE_EQUAL(Scan(), (int)kfgUNRECOGNIZED);
319     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
320 }
321 
FIXTURE_TEST_CASE(KfgScanLongPathNames,KfgScanFixture)322 FIXTURE_TEST_CASE(KfgScanLongPathNames, KfgScanFixture)
323 {
324     InitScan("/a/b/c a/_.- ");
325     REQUIRE_EQUAL(Scan(), (int)kfgABS_PATH); REQUIRE_EQUAL(tokenText, string("/a/b/c"));
326     REQUIRE_EQUAL(Scan(), (int)kfgREL_PATH); REQUIRE_EQUAL(tokenText, string("a/_.-"));
327     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
328 }
329 
FIXTURE_TEST_CASE(KfgScanLiterals,KfgScanFixture)330 FIXTURE_TEST_CASE(KfgScanLiterals, KfgScanFixture)
331 {
332     REQUIRE_EQ(MatchToken("=", kfgASSIGN), string());
333 }
334 
FIXTURE_TEST_CASE(KfgScanVarRefSimple,KfgScanFixture)335 FIXTURE_TEST_CASE(KfgScanVarRefSimple, KfgScanFixture)
336 {
337     InitScan("$(ref)");
338     Add("ref", "value");
339     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value"));
340     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
341 }
342 
FIXTURE_TEST_CASE(KfgScanVarRefUndefined,KfgScanFixture)343 FIXTURE_TEST_CASE(KfgScanVarRefUndefined, KfgScanFixture)
344 {
345     InitScan("$(reff)");
346     Add("ref", "value");
347     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE(errorMsg.length() == 0); // undefined vars not reported
348     REQUIRE_EQUAL(tokenText, string(""));
349     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
350 }
351 
FIXTURE_TEST_CASE(KfgScanVarRefInDoubleString,KfgScanFixture)352 FIXTURE_TEST_CASE(KfgScanVarRefInDoubleString, KfgScanFixture)
353 {
354     InitScan("\"-$(ref)+\"");
355     Add("ref", "value");
356     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("-"));
357     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value"));
358     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("+"));
359     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
360 }
361 
FIXTURE_TEST_CASE(KfgScanVarRefInSingleString,KfgScanFixture)362 FIXTURE_TEST_CASE(KfgScanVarRefInSingleString, KfgScanFixture)
363 {
364     InitScan("'-$(ref)+'");
365     Add("ref", "value");
366     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("-"));
367     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value"));
368     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("+"));
369     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
370 }
371 
FIXTURE_TEST_CASE(KfgScanMultipleVarRefsInString1,KfgScanFixture)372 FIXTURE_TEST_CASE(KfgScanMultipleVarRefsInString1, KfgScanFixture)
373 {
374     InitScan("'-$(ref1)+$(ref2)-'");
375     Add("ref1", "value1");
376     Add("ref2", "value2");
377     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("-"));
378     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value1"));
379     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("+"));
380     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value2"));
381     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("-"));
382     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
383 }
384 
FIXTURE_TEST_CASE(KfgScanMultipleVarRefsInString2,KfgScanFixture)385 FIXTURE_TEST_CASE(KfgScanMultipleVarRefsInString2, KfgScanFixture)
386 {
387     InitScan("'$(ref1)$(ref2)'");
388     Add("ref1", "value1");
389     Add("ref2", "value2");
390     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string(""));
391     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value1"));
392     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string("value2"));
393     REQUIRE_EQUAL(Scan(), (int)kfgSTRING); REQUIRE_EQUAL(tokenText, string(""));
394     REQUIRE_EQUAL(Scan(), (int)kfgEND_INPUT);
395 }
396 
397 //// bad token cases
FIXTURE_TEST_CASE(KfgScanVarRefEndFile,KfgScanFixture)398 FIXTURE_TEST_CASE(KfgScanVarRefEndFile, KfgScanFixture)
399 {
400     InitScan("$(ref");
401     REQUIRE_EQUAL(Scan(), (int)kfgUNRECOGNIZED);
402 }
403 
FIXTURE_TEST_CASE(KfgScanVarRefInStringEndFile,KfgScanFixture)404 FIXTURE_TEST_CASE(KfgScanVarRefInStringEndFile, KfgScanFixture)
405 {   // broken var refs inside strings are converted into strings
406     REQUIRE_EQ(MatchToken("'$(ref", kfgSTRING, "", kfgSTRING, false, false), string(""));
407     REQUIRE_EQUAL(tokenText, string("$(ref"));
408     REQUIRE(errorMsg.length() > 0);
409 }
410 
FIXTURE_TEST_CASE(KfgScanVarRefInStringEndLine,KfgScanFixture)411 FIXTURE_TEST_CASE(KfgScanVarRefInStringEndLine, KfgScanFixture)
412 {   // broken var refs inside strings are converted into strings
413     REQUIRE_EQ(MatchToken("\"$(ref\n", kfgSTRING, "", kfgSTRING, false, false), string(""));
414     REQUIRE_EQUAL(tokenText, string("$(ref"));
415     REQUIRE(errorMsg.length() > 0);
416 }
417 
418 //////////////////////////////////////////// tests for bison-generated parser
419 
420 // test fixture for parser tests
421 class KfgParseFixture : public SymbolTable
422 {
423 public:
KfgParseFixture()424     KfgParseFixture()
425     {
426     }
~KfgParseFixture()427     ~KfgParseFixture()
428     {
429     }
Parse(const char * input)430     rc_t Parse(const char* input)
431     {
432         KFGParseBlock pb;
433         KFGScanBlock sb;
434 
435         KFGScan_yylex_init(&sb, input);
436 
437         pb.write_nvp=write_nvp;
438 
439         sb.self=this;
440         sb.file="test";
441         sb.look_up_var=lookup_var;
442         sb.report_error=report_error;
443         errorMsg.clear();
444         KFG_parse(&pb, &sb);
445 
446         KFGScan_yylex_destroy(&sb);
447 
448         return 0;
449     }
450 
write_nvp(void * cfg,const char * name,size_t nameLen,VNamelist * values)451     static rc_t write_nvp(void * cfg, const char* name, size_t nameLen, VNamelist* values)
452     {
453         uint32_t count;
454         rc_t rc=VNameListCount(values, &count);
455         if (rc != 0)
456         {
457             return rc;
458         }
459         string value;
460         for (uint32_t i=0; i < count; ++i)
461         {
462             const char* val;
463             rc=VNameListGet(values, i, &val);
464             if (rc != 0)
465             {
466                 return rc;
467             }
468             value+=string(val, strlen(val));
469         }
470 
471         KfgParseFixture* config=(KfgParseFixture*)cfg;
472         (*config)[string(name, nameLen)] = value;
473 
474         return 0;
475     }
476 
report_error(KFGScanBlock * sb,const char * msg)477     static void report_error(KFGScanBlock* sb, const char* msg)
478     {
479         KfgParseFixture* obj=((KfgParseFixture*)sb->self);
480         obj->errorMsg   = msg;
481         obj->errorLine  = sb->lastToken->line_no;
482         obj->errorCol   = sb->lastToken->column_no;
483         const char* token=sb->lastToken->tokenText;
484         obj->errorToken =string(token, sb->lastToken->tokenLength);
485     }
486 
487     size_t errorLine;
488     size_t errorCol;
489     string errorToken;
490     string errorMsg;
491 };
492 
FIXTURE_TEST_CASE(KfgParseEmptyFile,KfgParseFixture)493 FIXTURE_TEST_CASE(KfgParseEmptyFile, KfgParseFixture)
494 {
495     REQUIRE_RC(Parse(""));
496     REQUIRE_EQ(size(), (size_t)0);
497     REQUIRE_RC(Parse("\n\n"));
498     REQUIRE_EQ(size(), (size_t)0);
499 }
500 
FIXTURE_TEST_CASE(KfgParseOneNameValue,KfgParseFixture)501 FIXTURE_TEST_CASE(KfgParseOneNameValue, KfgParseFixture)
502 {
503     REQUIRE_RC(Parse("name = \"value\""));  // no \n before EOF
504     REQUIRE_EQ(size(), (size_t)1);
505     REQUIRE_EQ((*this)["name"], string("value"));
506 }
507 
FIXTURE_TEST_CASE(KfgParseMultNameValue,KfgParseFixture)508 FIXTURE_TEST_CASE(KfgParseMultNameValue, KfgParseFixture)
509 {
510     REQUIRE_RC(Parse(
511         "name1 = \"value1\"\n"
512         "name2 = \"value2\"/**\n\n**/"    // comment with \n inside serves as a line end
513         "name3 = \"value3\"\n"
514         ));
515     REQUIRE_EQ(size(), (size_t)3);
516     REQUIRE_EQ((*this)["name1"], string("value1"));
517     REQUIRE_EQ((*this)["name2"], string("value2"));
518     REQUIRE_EQ((*this)["name3"], string("value3"));
519 }
520 
FIXTURE_TEST_CASE(KfgParseAbsPath,KfgParseFixture)521 FIXTURE_TEST_CASE(KfgParseAbsPath, KfgParseFixture)
522 {
523     REQUIRE_RC(Parse("/id/1name/1 = \"value\"\n"));
524     REQUIRE_EQ(size(), (size_t)1);
525     REQUIRE_EQ((*this)["/id/1name/1"], string("value"));
526 }
527 
FIXTURE_TEST_CASE(KfgParseRelPath,KfgParseFixture)528 FIXTURE_TEST_CASE(KfgParseRelPath, KfgParseFixture)
529 {
530     REQUIRE_RC(Parse("1/name1/id = \"value\"\n"));
531     REQUIRE_EQ(size(), (size_t)1);
532     REQUIRE_EQ((*this)["1/name1/id"], string("value"));
533 }
534 
FIXTURE_TEST_CASE(KfgParseEscString,KfgParseFixture)535 FIXTURE_TEST_CASE(KfgParseEscString, KfgParseFixture)
536 {
537     REQUIRE_RC(Parse("1/name1/id = \"v\\tlue\"\n"));
538     REQUIRE_EQ(size(), (size_t)1);
539     REQUIRE_EQ((*this)["1/name1/id"], string("v\tlue"));
540 }
541 
FIXTURE_TEST_CASE(KfgParseVarRefSimple,KfgParseFixture)542 FIXTURE_TEST_CASE(KfgParseVarRefSimple, KfgParseFixture)
543 {
544     REQUIRE_RC(Parse(
545         "ref='value'\n"
546         "var = $(ref)"
547         ));
548     REQUIRE_EQ((*this)["var"], string("value"));
549 }
550 
FIXTURE_TEST_CASE(KfgParseVarRefSimpleUndefined,KfgParseFixture)551 FIXTURE_TEST_CASE(KfgParseVarRefSimpleUndefined, KfgParseFixture)
552 {
553     REQUIRE_RC(Parse(
554         "ref='value'\n"
555         "var1 = $(reff)\n"
556         "var2 = $(ref)\n"
557         "var3 = $(var1)"
558         ));
559     REQUIRE_EQ((*this)["var1"], string(""));
560     REQUIRE_EQ((*this)["var2"], string("value")); // and recovered from
561     REQUIRE_EQ((*this)["var3"], string("")); // var1 is usable
562 }
563 
FIXTURE_TEST_CASE(KfgParseSyntaxRecovery1,KfgParseFixture)564 FIXTURE_TEST_CASE(KfgParseSyntaxRecovery1, KfgParseFixture)
565 {
566     REQUIRE_RC(Parse(
567         "a='q'nn\n" // syntax error (name) and skip to eol
568         "name='val'" // this should parse
569         ));
570     REQUIRE_EQ(errorLine, (size_t)1);
571     REQUIRE_EQ(errorCol, (size_t)6);
572     REQUIRE_EQ(errorToken, string("nn"));
573     REQUIRE_EQ(errorMsg, string("syntax error"));
574     REQUIRE_EQ((*this)["name"], string("val"));
575 }
576 
FIXTURE_TEST_CASE(KfgParseSyntaxRecovery2,KfgParseFixture)577 FIXTURE_TEST_CASE(KfgParseSyntaxRecovery2, KfgParseFixture)
578 {
579     REQUIRE_RC(Parse(
580         "a='q'%\n"  // syntax error (unrecognized character) and skip to eol
581         "name='val'" // this should parse
582         ));
583     REQUIRE_EQ(errorLine, (size_t)1);
584     REQUIRE_EQ(errorCol, (size_t)6);
585     REQUIRE_EQ(errorToken, string("%"));
586     REQUIRE_EQ(errorMsg, string("syntax error"));
587     REQUIRE_EQ((*this)["name"], string("val"));
588 }
589 
FIXTURE_TEST_CASE(KfgParseVarRefPath,KfgParseFixture)590 FIXTURE_TEST_CASE(KfgParseVarRefPath, KfgParseFixture)
591 {
592     REQUIRE_RC(Parse(
593         "ref/sub='value'\n"
594         "var = $(ref/sub)"
595         ));
596     REQUIRE_EQ((*this)["var"], string("value"));
597 }
598 
599 //////////////////////////////////////////// Main
600 extern "C"
601 {
602 #include <kapp/args.h>
603 #include <kfg/config.h>
604 
KAppVersion(void)605 ver_t CC KAppVersion ( void )
606 {
607     return 0x1000000;
608 }
UsageSummary(const char * progname)609 rc_t CC UsageSummary (const char * progname)
610 {
611     return 0;
612 }
613 
Usage(const Args * args)614 rc_t CC Usage ( const Args * args )
615 {
616     return 0;
617 }
618 
619 const char UsageDefaultName[] = "wb-test-kfg";
620 
KMain(int argc,char * argv[])621 rc_t CC KMain ( int argc, char *argv [] )
622 {
623     KConfigDisableUserSettings();
624     rc_t rc = KfgWbTestSuite(argc, argv);
625     return rc;
626 }
627 
628 }
629