Skip to content

Commit

Permalink
Merge pull request #80 from Yexiaoxing/master
Browse files Browse the repository at this point in the history
I hate C++. Add some death tests to JSON.
  • Loading branch information
alesiong authored May 21, 2017
2 parents f1aa999 + 276c502 commit a50053e
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 33 deletions.
4 changes: 4 additions & 0 deletions src/include/algorithm/Zip.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <vector>
namespace cuhksz {

/**
* Base class for Zip function. Refer to CUHKSZ::zip function for usage.
* @tparam T container type
*/
template <typename T>
class Zip {
public:
Expand Down
41 changes: 23 additions & 18 deletions src/json/Json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ JSONObject parse_array(const std::string &str, size_t &offset) {
break;
} else {
error("Array: Expected ',' or ']', found '" +
std::string(1, str[offset]) + "'");
std::string(1, str[offset]) + "'");
}
}

Expand Down Expand Up @@ -125,11 +125,12 @@ JSONObject parse_string(const std::string &str, size_t &offset) {
} else {
error(
"String: Expected hex character in unicode escape, found '" +
std::string(1, c) + "'");
std::string(1, c) + "'");
}
}
offset += 4;
} break;
}
break;
default:
val += '\\';
break;
Expand Down Expand Up @@ -161,32 +162,36 @@ JSONObject parse_number(const std::string &str, size_t &offset) {
}
}
if (c == 'E' || c == 'e') {
if (offset == str.length()) {
error("Number: Expected a number for exponent, nothing remaining.");
}
c = str[offset++];
if (c == '-') {
++offset;
exp_str += '-';
}
while (true) {
c = str[offset++];
c = str[offset];

if (c >= '0' && c <= '9') {
exp_str += c;
} else if (!isspace(c) && c != ',' && c != ']' && c != '}') {
} else if (!isspace(c) && c != ',' && c != ']' && c != '}' && c != 0) {
error("Number: Expected a number for exponent, found '" +
std::string(1, c) + "'");
std::string(1, c) + "'");
} else {
break;
}
offset++;
}
exp = cuhksz::stringCast<int>(exp_str);
} else if (!isspace(c) && c != ',' && c != ']' && c != '}') {
exp = cuhksz::stringCast<double>(exp_str);
} else if (!isspace(c) && c != ',' && c != ']' && c != '}' && c != 0) {
error("Number: unexpected character '" + std::string(1, c) + "'");
}
--offset;
if (c != 0) --offset;

if (isDouble)
Number = (int)cuhksz::stringCast<int>(val) * std::pow(10, exp);
Number = (double)cuhksz::stringCast<double>(val) * std::pow(10, exp);
else if (!exp_str.empty())
Number = (int)cuhksz::stringCast<int>(val) * std::pow(10, exp);
Number = (double)cuhksz::stringCast<double>(val) * std::pow(10, exp);
else
Number = (int)cuhksz::stringCast<int>(val);
return Number;
Expand All @@ -200,16 +205,15 @@ JSONObject parse_bool(const std::string &str, size_t &offset) {
Bool = false;
} else {
error("Bool: Expected 'true' or 'false', found '" + str.substr(offset, 5) +
"'");
"'");
}
offset += (Bool.toBool() ? 4 : 5);
return Bool;
}

JSONObject parse_null(const std::string &str, size_t &offset) {
JSONObject Null;
if (str.substr(offset, 4) != "null")
error("Null: Expected 'null', found '" + str.substr(offset, 4) + "'");
if (str.substr(offset, 4) != "null") error("Null: Expected 'null', found '" + str.substr(offset, 4) + "'");
offset += 4;
return Null;
}
Expand Down Expand Up @@ -295,7 +299,8 @@ void JSONObject::clearData() {
case Type::String:
delete Data.String;
break;
default: {}
default: {
}
}
}

Expand Down Expand Up @@ -388,8 +393,8 @@ bool JSONObject::hasKey(const std::string &key) const {

int JSONObject::size() const {
return (int)(objType == Type::Object
? Data.Map->size()
: objType == Type::Array ? Data.List->size() : -1);
? Data.Map->size()
: objType == Type::Array ? Data.List->size() : -1);
}

std::string JSONObject::toString() const {
Expand Down
112 changes: 99 additions & 13 deletions tests/json/JsonTest.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#include <string>

#include "json/Json.h"
#include "string_utils/StringCast.h"

#include "gtest/gtest.h"

TEST(Json, DumpJson) {
cuhksz::JSONObject obj;
EXPECT_EQ(obj.dump(), "null");

// Create a new Array as a field of an Object.
obj["array"] = cuhksz::JSONObject::Array(true, "Two", 3, 4.0);

Expand Down Expand Up @@ -57,14 +60,15 @@ TEST(Json, LoadJson) {
}
},
"obj" : {
"inner" : "Inside"
"inner" : "Inside\""
},
"parsed" : [{
"Key" : "Value"
}, false]
})";
auto jsonObject = cuhksz::loadJSON(json);
EXPECT_EQ((std::string)jsonObject["new"]["some"]["deep"]["key"], "Value");
EXPECT_EQ((std::string)jsonObject["obj"]["inner"], "Inside\\\"");
}

TEST(Json, loadWithInitList) {
Expand Down Expand Up @@ -98,13 +102,48 @@ TEST(Json, arrayTest) {
EXPECT_EQ((std::string)array[1], "Test1");
EXPECT_EQ((std::string)array[2], "Test2");
EXPECT_EQ((std::string)array[3], "Test4");
EXPECT_EQ((std::string)array.at(3), "Test4");

// Arrays can be nested:
cuhksz::JSONObject Array2;

Array2[2][0][1] = true;

EXPECT_EQ((bool)Array2[2][0][1], true);
EXPECT_EQ(array.length(), 4);

array = cuhksz::loadJSON("[]");
EXPECT_EQ(array.length(), 0);

}

TEST(Json, mapTest) {
cuhksz::JSONObject obj =
cuhksz::JSONObject::Build(cuhksz::JSONObject::Type::Object);

obj["Key0"] = "Value0";
obj["Key1"] = "Value1";
obj["Key2"] = "Value2";

EXPECT_TRUE(obj.hasKey("Key0"));
EXPECT_EQ((std::string)obj.at("Key2"), "Value2");
EXPECT_EQ((std::string)obj.at("Key1"), "Value1");
EXPECT_EQ(obj.size(), 3);
}

TEST(Json, copyTest) {
cuhksz::JSONObject obj =
cuhksz::JSONObject::Build(cuhksz::JSONObject::Type::Object);

obj["Key0"] = "Value0";
obj["Key1"] = "Value1";
obj["Key2"] = "Value2";

auto obj_copied = cuhksz::JSONObject(obj);
EXPECT_TRUE(obj_copied.hasKey("Key0"));
EXPECT_EQ((std::string)obj_copied.at("Key2"), "Value2");
EXPECT_EQ((std::string)obj_copied.at("Key1"), "Value1");
EXPECT_EQ(obj_copied.size(), 3);
}

TEST(Json, primTest) {
Expand All @@ -115,13 +154,18 @@ TEST(Json, primTest) {
EXPECT_EQ((std::string)obj, "Test String");
obj = 2.2;
EXPECT_NEAR((float)obj, (float)2.2, 0.0001);
EXPECT_NEAR(obj.toFloat(), (float)2.2, 0.0001);
EXPECT_NEAR((double)obj, (double)2.2, 0.0001);
EXPECT_NEAR(obj.toDouble(), (double)2.2, 0.0001);
obj = 3;
EXPECT_EQ((int)obj, 3);
EXPECT_EQ(obj.toInt(), 3);
}

TEST(Json, iterTest) {
cuhksz::JSONObject array =
cuhksz::JSONObject::Build(cuhksz::JSONObject::Type::Array);
array = cuhksz::JSONObject::Array();
cuhksz::JSONObject obj =
cuhksz::JSONObject::Build(cuhksz::JSONObject::Type::Object);

Expand All @@ -147,28 +191,70 @@ TEST(Json, iterTest) {
}
}

TEST(Json, loadJSONFailure) {
TEST(JsonDeathTest, loadJSONFailure) {
std::string json = R"({"array" : ][true, "Two", 3, 4.000000],)";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
".* Parse: Unknown starting character .*");
" Parse: Unknown starting character ");

json = R"({"array" : [true, "Two["3, 4.000000]})";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
"Expected");

json = R"({"array", [true, "Two["3, 4.000000]})";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
"Object: Expected colon");

json = R"({"array": [true, "Two[", 3, 4.000000]/})";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
"Object: Expected comma");

json = R"({"array" : [true, "Two[】"3, 4.000000]})";
json = R"({"array": [true, "Two[\u%9P", 3, 4.000000]})";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
".*Array: Expected.*");
"String: Expected hex character in unicode escape");

json = R"({"array", [true, "Two[】"3, 4.000000]})";
json = R"({"array": [trlue, "Two[\u%9P", 3, 4.000000]})";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
".*Object: Expected colon.*");
"Bool: Expected 'true' or 'false', found");

json = R"({"array": [true, "Two[】", 3, 4.000000]/})";
json = R"(nuli)";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
".*Object: Expected comma.*");
"Null: Expected 'null',");

json = R"({"array": [true, "Two[】\u%9P", 3, 4.000000]})";
json = R"(1.20E)";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
".*String: Expected hex character in unicode escape.*");
"Number: Expected a number for exponent");

json = R"({"array": [trlue, "Two[】\u%9P", 3, 4.000000]})";
json = R"(1.20f)";
EXPECT_EXIT(cuhksz::loadJSON(json), ::testing::ExitedWithCode(1),
".*Bool: Expected 'true' or 'false', found.*");
"Number: unexpected character*");
}

TEST(Json, loadJSONString) {
std::string json = R"({"array": "\\ \t \r \n \f \\\ \/ \b \u0A18 \l" })";
std::string expected = "{\n \"array\" : \"\\\\ \\t \\r \\n \\f \\\\\\\\/ \\b \\\\u0A18 \\\\\"\n}";
EXPECT_EQ(cuhksz::loadJSON(json).dump(), expected);

json = R"(null)";
expected = "null";
EXPECT_EQ(cuhksz::loadJSON(json).dump(), expected);


EXPECT_EQ(cuhksz::loadJSON("{}").dump(), "{\n\n}");
EXPECT_EQ(cuhksz::loadJSON("[]").dump(), "[]");
}


TEST(Json, loadJSONNumber) {
std::vector<std::string> jsonVector = {
"1.00",
"100",
"1.234"
};

for (std::string json: jsonVector) {
EXPECT_NEAR(cuhksz::stringCast<double>(cuhksz::loadJSON(json).dump())(), cuhksz::stringCast<double>(json), 0.0001);
}

EXPECT_NEAR(cuhksz::stringCast<double>(cuhksz::loadJSON("1.20E+2").dump())(), 120, 0.0001);
EXPECT_NEAR(cuhksz::stringCast<double>(cuhksz::loadJSON("1.20E-2").dump())(), 0.012, 0.0001);
}
2 changes: 1 addition & 1 deletion tests/json/json_functions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
#include "gtest/gtest.h"

TEST(JsonFunctions, jsonEscape) {
ASSERT_EQ(cuhksz::jsonEscape("\\test\b\f\n\r\t"), "\\\\test\\b\\f\\n\\r\\t");
ASSERT_EQ(cuhksz::jsonEscape("\\\"\\test\b\f\n\r\t"), "\\\\\\\"\\\\test\\b\\f\\n\\r\\t");
}
2 changes: 1 addition & 1 deletion tests/test_main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "gtest/gtest.h"

int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit a50053e

Please sign in to comment.