Skip to content

Commit 6d73edb

Browse files
committed
ini: add unit test
1 parent 548688e commit 6d73edb

14 files changed

Lines changed: 97 additions & 37 deletions

File tree

include/chen/data/ini.hpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
* -----------------------------------------------------------------------------
88
* Ini file consists of two parts: section and property
99
* -) Case sensitive: e.g: key=val, Key=val are two different properties
10-
* -) Comments: support semicolons ';', comment can appear anywhere
11-
* -) Line break: using '\n' in file
1210
* -) Duplicate key: will trigger a error, don't allow this
13-
* -) Escape: '\\', '\0', '\a', '\b', '\t', '\r', '\n', '\;', '\#', '\=', '\:', '\x????', '\"'
1411
* -) Global key: allowed this, its section name will be empty
12+
* -) Comments: support semicolons ';', comment can appear anywhere
1513
* -) Hierarchy: now allowed
14+
* -) Line break: using '\n' in file
15+
* -) Escape: '\\', '\0', '\a', '\b', '\t', '\r', '\n', '\;', '\#', '\=', '\:', '\x????', '\"'
1616
* -) Quoted values: allow double quotes, will remove it automatically
1717
* -) Whitespace: left and right whitespaces are removed automatically, add double quote if you want preserved
1818
* -----------------------------------------------------------------------------
@@ -129,12 +129,14 @@ void chen::ini::exception(const InputIterator &beg, InputIterator &cur, InputIte
129129
{
130130
if (cur == end)
131131
{
132-
throw error("ini: unexpected end of input");
132+
throw chen::ini::error("ini: unexpected end of input");
133133
}
134134
else
135135
{
136136
auto pos = chen::num::str(std::distance(beg, cur));
137-
throw error(chen::str::format("ini: unexpected token '%c' at position %s", *cur, pos.c_str()));
137+
auto tok = std::isprint(*cur) ? std::string(1, *cur) : chen::str::format("\\x%02x", static_cast<int>(*cur));
138+
139+
throw chen::ini::error(chen::str::format("ini: unexpected token '%s' at position %s", tok.c_str(), pos.c_str()));
138140
}
139141
}
140142

@@ -181,7 +183,7 @@ void chen::ini::decode(chen::ini::value_type &out, const InputIterator &beg, Inp
181183
if (out.find(s.first) == out.end())
182184
out.emplace(std::move(s));
183185
else
184-
chen::ini::exception(beg, cur, end);
186+
throw chen::ini::error(chen::str::format("ini: duplicate section '%s' found", s.first.c_str()));
185187
}
186188
break;
187189

@@ -255,6 +257,12 @@ void chen::ini::decode(chen::ini::property_type &out, const InputIterator &beg,
255257
if (key.empty())
256258
chen::ini::exception(beg, cur, end);
257259

260+
if (out.find(key) != out.end())
261+
{
262+
auto pos = chen::num::str(std::distance(beg, cur) - key.size());
263+
throw chen::ini::error(chen::str::format("ini: duplicate key '%s' found at position %s", key.c_str(), pos.c_str()));
264+
}
265+
258266
// equal
259267
if ((cur == end) || (*cur != '='))
260268
chen::ini::exception(beg, cur, end);
@@ -273,15 +281,7 @@ void chen::ini::decode(chen::ini::property_type &out, const InputIterator &beg,
273281
}
274282

275283
// store
276-
if (out.find(key) == out.end())
277-
{
278-
out.emplace(std::move(key), std::move(val));
279-
}
280-
else
281-
{
282-
auto pos = chen::num::str(std::distance(beg, cur) - 4);
283-
throw error(chen::str::format("ini: duplicate key %s found at position %s", key.c_str(), pos.c_str()));
284-
}
284+
out.emplace(std::move(key), std::move(val));
285285

286286
// skip
287287
if ((cur != end) && (*cur == '\n'))
@@ -396,7 +396,7 @@ void chen::ini::decode(std::string &out, const InputIterator &beg, InputIterator
396396
{
397397
// e.g: \xD83D\xDE00, it's a emoji character
398398
auto pos = chen::num::str(std::distance(beg, cur) - 4);
399-
throw error(chen::str::format("ini: invalid unicode char \\u%s at position %s", unicode, pos.c_str()));
399+
throw chen::ini::error(chen::str::format("ini: invalid unicode char '\\u%s' at position %s", unicode, pos.c_str()));
400400
}
401401
}
402402
break;

include/chen/data/json.hpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,14 @@ void chen::json::exception(const InputIterator &beg, InputIterator &cur, InputIt
299299
{
300300
if (cur == end)
301301
{
302-
throw error("json: unexpected end of input");
302+
throw chen::json::error("json: unexpected end of input");
303303
}
304304
else
305305
{
306306
auto pos = chen::num::str(std::distance(beg, cur));
307-
throw error(chen::str::format("json: unexpected token '%c' at position %s", *cur, pos.c_str()));
307+
auto tok = std::isprint(*cur) ? std::string(1, *cur) : chen::str::format("\\x%02x", static_cast<int>(*cur));
308+
309+
throw chen::json::error(chen::str::format("json: unexpected token '%s' at position %s", tok.c_str(), pos.c_str()));
308310
}
309311
}
310312

@@ -584,12 +586,12 @@ void chen::json::decode(double &out, const InputIterator &beg, InputIterator &cu
584586
if (std::isinf(d))
585587
{
586588
auto pos = chen::num::str(std::distance(beg, cur) - str.size());
587-
throw error(chen::str::format("json: number %s is overflow at position %s", str.c_str(), pos.c_str()));
589+
throw chen::json::error(chen::str::format("json: number '%s' is overflow at position %s", str.c_str(), pos.c_str()));
588590
}
589591
else if (std::isnan(d))
590592
{
591593
auto pos = chen::num::str(std::distance(beg, cur) - str.size());
592-
throw error(chen::str::format("json: number %s is invalid at position %s", str.c_str(), pos.c_str()));
594+
throw chen::json::error(chen::str::format("json: number '%s' is invalid at position %s", str.c_str(), pos.c_str()));
593595
}
594596

595597
out = d;
@@ -609,7 +611,7 @@ void chen::json::decode(std::string &out, const InputIterator &beg, InputIterato
609611
if ((ch >= 0) && (ch <= 31)) // see ASCII
610612
{
611613
auto pos = chen::num::str(std::distance(beg, cur));
612-
throw error(chen::str::format("json: control character code '%d' is not escaped at position %s", static_cast<int>(ch), pos.c_str()));
614+
throw chen::json::error(chen::str::format("json: control character code '%d' is not escaped at position %s", static_cast<int>(ch), pos.c_str()));
613615
}
614616

615617
// unescape characters
@@ -676,7 +678,7 @@ void chen::json::decode(std::string &out, const InputIterator &beg, InputIterato
676678
{
677679
// e.g: \uD83D\uDE00, it's a emoji character
678680
auto pos = chen::num::str(std::distance(beg, cur) - 4);
679-
throw error(chen::str::format("json: invalid unicode char \\u%s at position %s", unicode, pos.c_str()));
681+
throw chen::json::error(chen::str::format("json: invalid unicode char '\\u%s' at position %s", unicode, pos.c_str()));
680682
}
681683
}
682684
break;

src/data/ini.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ chen::ini::value_type chen::ini::parse(const std::string &text, bool file)
2323
stream.open(text.c_str(), std::ios_base::binary);
2424

2525
std::istreambuf_iterator<char> cur(stream);
26-
return chen::ini::parse(cur, std::istreambuf_iterator<char>());
26+
return ini::parse(cur, std::istreambuf_iterator<char>());
2727
}
2828
catch (const std::ios_base::failure&)
2929
{
30-
throw error(str::format("ini: decode %s: %s", text.c_str(), chen::sys::error().c_str()));
30+
throw ini::error(str::format("ini: decode %s: %s", text.c_str(), chen::sys::error().c_str()));
3131
}
3232
}
3333
else
3434
{
35-
return chen::ini::parse(text.begin(), text.end());
35+
return ini::parse(text.begin(), text.end());
3636
}
3737
}
3838

src/data/json.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ chen::json json::parse(const std::string &text, bool file)
310310
}
311311
catch (const std::ios_base::failure&)
312312
{
313-
throw error(str::format("json: decode %s: %s", text.c_str(), chen::sys::error().c_str()));
313+
throw json::error(str::format("json: decode %s: %s", text.c_str(), chen::sys::error().c_str()));
314314
}
315315
}
316316
else
@@ -343,7 +343,7 @@ void json::validate(const std::string &text, bool file)
343343
}
344344
catch (const std::ios_base::failure&)
345345
{
346-
throw error(str::format("json: decode %s: %s", text.c_str(), chen::sys::error().c_str()));
346+
throw json::error(str::format("json: decode %s: %s", text.c_str(), chen::sys::error().c_str()));
347347
}
348348
}
349349
else
@@ -410,39 +410,39 @@ const chen::json::object& json::getObject() const
410410
if (this->_type == Type::Object)
411411
return *this->_data.o;
412412
else
413-
throw error("json: type is not object");
413+
throw json::error("json: type is not object");
414414
}
415415

416416
const chen::json::array& json::getArray() const
417417
{
418418
if (this->_type == Type::Array)
419419
return *this->_data.a;
420420
else
421-
throw error("json: type is not array");
421+
throw json::error("json: type is not array");
422422
}
423423

424424
double json::getNumber() const
425425
{
426426
if (this->_type == Type::Number)
427427
return this->_data.d;
428428
else
429-
throw error("json: type is not number");
429+
throw json::error("json: type is not number");
430430
}
431431

432432
int json::getInteger() const
433433
{
434434
if (this->_type == Type::Number)
435435
return static_cast<int>(this->_data.d);
436436
else
437-
throw error("json: type is not number");
437+
throw json::error("json: type is not number");
438438
}
439439

440440
const std::string& json::getString() const
441441
{
442442
if (this->_type == Type::String)
443443
return *this->_data.s;
444444
else
445-
throw error("json: type is not string");
445+
throw json::error("json: type is not string");
446446
}
447447

448448
bool json::getBool() const
@@ -452,39 +452,39 @@ bool json::getBool() const
452452
else if (this->_type == Type::False)
453453
return false;
454454
else
455-
throw error("json: type is not bool");
455+
throw json::error("json: type is not bool");
456456
}
457457

458458
chen::json::object& json::getObject()
459459
{
460460
if (this->_type == Type::Object)
461461
return *this->_data.o;
462462
else
463-
throw error("json: type is not object");
463+
throw json::error("json: type is not object");
464464
}
465465

466466
chen::json::array& json::getArray()
467467
{
468468
if (this->_type == Type::Array)
469469
return *this->_data.a;
470470
else
471-
throw error("json: type is not array");
471+
throw json::error("json: type is not array");
472472
}
473473

474474
double& json::getNumber()
475475
{
476476
if (this->_type == Type::Number)
477477
return this->_data.d;
478478
else
479-
throw error("json: type is not number");
479+
throw json::error("json: type is not number");
480480
}
481481

482482
std::string& json::getString()
483483
{
484484
if (this->_type == Type::String)
485485
return *this->_data.s;
486486
else
487-
throw error("json: type is not string");
487+
throw json::error("json: type is not string");
488488
}
489489

490490
// convert value

test/data/ini/fail1.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[section
2+
key="section is not enclosed"

test/data/ini/fail2.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[section]
2+
key="duplicate key is not allowed"
3+
key="duplicate key is not allowed"

test/data/ini/fail3.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[section]
2+
key="simple value"
3+
4+
[section]
5+
key="duplicate section is not allowed"

test/data/ini/fail4.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
; no '=' following key
2+
[section]
3+
key

test/data/ini/fail5.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[section]
2+
key="value is not enclosed

test/data/ini/pass1.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[section]
2+
key="simple value"
3+
4+
[Section]
5+
key="two sections with different case is valid"

0 commit comments

Comments
 (0)