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