Skip to content

Commit 9ebc3e1

Browse files
committed
Add more unit tests
1 parent 14dfa49 commit 9ebc3e1

File tree

4 files changed

+376
-15
lines changed

4 files changed

+376
-15
lines changed

tests/UnitTest/JsonFormatTest.cpp

Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
#include <gtest/gtest.h>
2+
3+
#include <string>
4+
#include <unordered_map>
5+
6+
#include "JsonHandler.h"
7+
8+
namespace JsonFormat
9+
{
10+
class JsonFormatTest : public ::testing::Test
11+
{
12+
protected:
13+
JsonHandler m_jsonFormatter {{}};
14+
15+
protected:
16+
void SetUp() override {}
17+
void TearDown() override {}
18+
19+
void setParseOptions(const ParseOptions &opt)
20+
{
21+
m_jsonFormatter = JsonHandler(opt);
22+
}
23+
};
24+
25+
// Test 1: Test valid JSON with default formatting options
26+
TEST_F(JsonFormatTest, FormatJson_ValidJson_DefaultOptions)
27+
{
28+
std::string inputJson = R"({"name": "John", "age": 30})";
29+
LE lineEndingOption = rj::kLf;
30+
LF formatOptions = rj::kFormatDefault;
31+
char indentChar = ' ';
32+
unsigned indentLen = 4;
33+
34+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
35+
36+
EXPECT_TRUE(result.success);
37+
EXPECT_EQ(result.error_code, -1);
38+
EXPECT_EQ(result.error_pos, -1);
39+
EXPECT_EQ(result.error_str, "");
40+
EXPECT_EQ(result.response, "{\n \"name\": \"John\",\n \"age\": 30\n}");
41+
}
42+
43+
// Test 2: Test invalid JSON input
44+
TEST_F(JsonFormatTest, FormatJson_InvalidJson)
45+
{
46+
std::string inputJson = R"({"name": "John", "age": })"; // Invalid JSON (missing age value)
47+
LE lineEndingOption = rj::kLf;
48+
LF formatOptions = rj::kFormatDefault;
49+
char indentChar = ' ';
50+
unsigned indentLen = 4;
51+
52+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
53+
54+
EXPECT_FALSE(result.success);
55+
EXPECT_NE(result.error_code, -1);
56+
EXPECT_NE(result.error_pos, -1);
57+
EXPECT_FALSE(result.error_str.empty());
58+
EXPECT_EQ(result.response, "");
59+
}
60+
61+
// Test 3: Test valid JSON with custom indentation (tabs instead of spaces)
62+
TEST_F(JsonFormatTest, FormatJson_ValidJson_TabIndentation)
63+
{
64+
std::string inputJson = R"({"name": "John", "age": 30})";
65+
LE lineEndingOption = rj::kLf;
66+
LF formatOptions = rj::kFormatDefault; // Default
67+
char indentChar = '\t'; // Tab instead of space
68+
unsigned indentLen = 1; // Single tab indentation
69+
70+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
71+
72+
EXPECT_TRUE(result.success);
73+
EXPECT_EQ(result.error_code, -1);
74+
EXPECT_EQ(result.error_pos, -1);
75+
EXPECT_EQ(result.error_str, "");
76+
EXPECT_EQ(result.response, "{\n\t\"name\": \"John\",\n\t\"age\": 30\n}");
77+
}
78+
79+
// Test 4: Test valid JSON with Windows line endings
80+
TEST_F(JsonFormatTest, FormatJson_ValidJson_WindowsLineEndings)
81+
{
82+
std::string inputJson = R"({"name": "John", "age": 30})";
83+
LE lineEndingOption = rj::kCrLf; // Windows line ending
84+
LF formatOptions = rj::kFormatDefault; // Default
85+
char indentChar = ' ';
86+
unsigned indentLen = 4;
87+
88+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
89+
90+
EXPECT_TRUE(result.success);
91+
EXPECT_EQ(result.error_code, -1);
92+
EXPECT_EQ(result.error_pos, -1);
93+
EXPECT_EQ(result.error_str, "");
94+
EXPECT_EQ(result.response, "{\r\n \"name\": \"John\",\r\n \"age\": 30\r\n}");
95+
}
96+
97+
// Test 5: Test valid JSON with no indentation (compact format)
98+
TEST_F(JsonFormatTest, FormatJson_ValidJson_NoIndentation)
99+
{
100+
std::string inputJson = R"({"name": "John", "age": 30})";
101+
LE lineEndingOption = rj::kCrLf; // Windows line ending
102+
LF formatOptions = rj::kFormatSingleLineArray; // Custom option for no pretty formatting
103+
char indentChar = ' ';
104+
unsigned indentLen = 0; // No indentation
105+
106+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
107+
108+
EXPECT_TRUE(result.success);
109+
EXPECT_EQ(result.error_code, -1);
110+
EXPECT_EQ(result.error_pos, -1);
111+
EXPECT_EQ(result.error_str, "");
112+
EXPECT_EQ(result.response, "{\r\n\"name\": \"John\",\r\n\"age\": 30\r\n}");
113+
}
114+
115+
// Test 6: Test JSON with comments ignored
116+
TEST_F(JsonFormatTest, FormatJson_IgnoreComments)
117+
{
118+
std::string inputJson = R"({
119+
// Comment here
120+
"name": "John",
121+
"age": 30
122+
})";
123+
LE lineEndingOption = rj::kCrLf; // Windows line ending
124+
LF formatOptions = rj::kFormatDefault; // Default
125+
char indentChar = ' ';
126+
unsigned indentLen = 4;
127+
128+
// Set parse options to ignore comments
129+
ParseOptions parseOptions {.bIgnoreComment = true, .bIgnoreTrailingComma = false, .bReplaceUndefined = false};
130+
setParseOptions(parseOptions);
131+
132+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
133+
134+
EXPECT_TRUE(result.success);
135+
EXPECT_EQ(result.error_code, -1);
136+
EXPECT_EQ(result.error_pos, -1);
137+
EXPECT_EQ(result.error_str, "");
138+
EXPECT_EQ(result.response, "{\r\n \"name\": \"John\",\r\n \"age\": 30\r\n}");
139+
}
140+
141+
// Test 7: Test JSON with trailing commas ignored
142+
TEST_F(JsonFormatTest, FormatJson_IgnoreTrailingCommas)
143+
{
144+
std::string inputJson = R"({
145+
"name": "John",
146+
"age": 30,
147+
})"; // Trailing comma is invalid in standard JSON
148+
LE lineEndingOption = rj::kCrLf; // Windows line ending
149+
LF formatOptions = rj::kFormatDefault; // Default
150+
char indentChar = ' ';
151+
unsigned indentLen = 2;
152+
153+
// Set parse options to ignore trailing commas
154+
ParseOptions parseOptions {.bIgnoreComment = false, .bIgnoreTrailingComma = true, .bReplaceUndefined = false};
155+
setParseOptions(parseOptions);
156+
157+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
158+
159+
EXPECT_TRUE(result.success);
160+
EXPECT_EQ(result.error_code, -1);
161+
EXPECT_EQ(result.error_pos, -1);
162+
EXPECT_EQ(result.error_str, "");
163+
EXPECT_EQ(result.response, "{\r\n \"name\": \"John\",\r\n \"age\": 30\r\n}");
164+
}
165+
166+
// Test 8: Test with infinity NaN inf
167+
TEST_F(JsonFormatTest, FormatJson_Infinity_NaN_Null)
168+
{
169+
std::unordered_map<std::string, std::string> testData {
170+
{R"({"NaN":NaN})", "{\r\n \"NaN\": NaN\r\n}"},
171+
{R"({"Mixed":[null,null,"power"]})", "{\r\n \"Mixed\": [\r\n null,\r\n null,\r\n \"power\"\r\n ]\r\n}"},
172+
{R"({"Inf":[-Infinity, Infinity, -Inf, Inf]})",
173+
"{\r\n \"Inf\": [\r\n -Infinity,\r\n Infinity,\r\n -Infinity,\r\n Infinity\r\n ]\r\n}"},
174+
};
175+
176+
LE lineEndingOption = rj::kCrLf; // Windows line ending
177+
LF formatOptions = rj::kFormatDefault; // Custom option for no pretty formatting
178+
char indentChar = ' ';
179+
unsigned indentLen = 4; // No indentation
180+
181+
for (const auto &[input, output] : testData)
182+
{
183+
auto result = m_jsonFormatter.FormatJson(input, lineEndingOption, formatOptions, indentChar, indentLen);
184+
185+
EXPECT_TRUE(result.success);
186+
EXPECT_EQ(result.error_code, -1);
187+
EXPECT_EQ(result.error_pos, -1);
188+
EXPECT_EQ(result.error_str, "");
189+
EXPECT_EQ(result.response, output);
190+
}
191+
}
192+
193+
// Test 9: Test with infinity NaN inf , single line on array
194+
TEST_F(JsonFormatTest, FormatJson_Infinity_NaN_Null_SingleLineOnArray)
195+
{
196+
std::unordered_map<std::string, std::string> testData {
197+
{R"({"NaN":NaN})", "{\r\n \"NaN\": NaN\r\n}"},
198+
{R"({"Mixed":[null,null,"power"]})", "{\r\n \"Mixed\": [null, null, \"power\"]\r\n}"},
199+
{R"({"Inf":[-Infinity, Infinity, -Inf, Inf]})", "{\r\n \"Inf\": [-Infinity, Infinity, -Infinity, Infinity]\r\n}"},
200+
};
201+
202+
LE lineEndingOption = rj::kCrLf; // Windows line ending
203+
LF formatOptions = rj::kFormatSingleLineArray; // Custom option for no pretty formatting
204+
char indentChar = ' ';
205+
unsigned indentLen = 4; // No indentation
206+
207+
for (const auto &[input, output] : testData)
208+
{
209+
auto result = m_jsonFormatter.FormatJson(input, lineEndingOption, formatOptions, indentChar, indentLen);
210+
211+
EXPECT_TRUE(result.success);
212+
EXPECT_EQ(result.error_code, -1);
213+
EXPECT_EQ(result.error_pos, -1);
214+
EXPECT_EQ(result.error_str, "");
215+
EXPECT_EQ(result.response, output);
216+
}
217+
}
218+
219+
// Test 10: Test valid JSON with unicode sequence
220+
TEST_F(JsonFormatTest, FormatJson_UnicodeSequence)
221+
{
222+
std::string inputJson = R"({"FreeTextInput":"\u003Cscript\u003Ealert(\u0022links\u0022);\u003C/script\u003E"})";
223+
LE lineEndingOption = rj::kCrLf; // Windows line ending
224+
LF formatOptions = rj::kFormatDefault; // Custom option for no pretty formatting
225+
char indentChar = ' '; // space indented
226+
unsigned indentLen = 0; // No indentation
227+
228+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
229+
230+
EXPECT_TRUE(result.success);
231+
EXPECT_EQ(result.error_code, -1);
232+
EXPECT_EQ(result.error_pos, -1);
233+
EXPECT_EQ(result.error_str, "");
234+
EXPECT_EQ(result.response, "{\r\n\"FreeTextInput\": \"<script>alert(\\\"links\\\");</script>\"\r\n}");
235+
}
236+
237+
// Test 11: Test with numbers (with and without precision)
238+
TEST_F(JsonFormatTest, FormatJson_numbers)
239+
{
240+
LE lineEndingOption = rj::kCrLf; // Windows line ending
241+
LF formatOptions = rj::kFormatDefault; // Custom option for no pretty formatting
242+
char indentChar = ' '; // space indented
243+
unsigned indentLen = 4; // No indentation
244+
245+
std::unordered_map<std::string, std::string> testData {
246+
{R"({"num": 12.148681171238422})", "12.148681171238422"}, // All good
247+
{R"({"num": 42.835353759876654})", "42.835353759876654"}, // All good
248+
{R"({"num": 5.107091491635510056019771245})", "5.10709149163551"}, // Fine: Rounded up
249+
{R"({"num": 100000000302052988.0})", "100000000302052990.0"}, // Fine: Rounded up
250+
{R"({"num": 12.148681171238427111})", "12.148681171238428"}, // Fine: Rounded down
251+
{R"({"num": 42.8353537598766541666})", "42.835353759876654"}, // Fine: Rounded up
252+
{R"({"num": -1722.1864265316147})", "-1722.1864265316146"}, // This is interesting. Why last digit changed.
253+
{R"({"num": -1722.1864265316148})", "-1722.1864265316149"}, // This is interesting. Why last digit changed.
254+
{R"({"num": -172345.18642653167979})", "-172345.18642653167"}, // This is interesting. Why not rounded up.
255+
{R"({"num": 1.234e5})", "123400.0"}, // Don't know how to fix.
256+
{R"({"num": 0.0000001000})", "1e-7"}, // Don't know how to fix.
257+
};
258+
259+
for (const auto &[input, output] : testData)
260+
{
261+
auto result = m_jsonFormatter.FormatJson(input, lineEndingOption, formatOptions, indentChar, indentLen);
262+
bool foundNumber = result.response.find(output) != std::string::npos;
263+
EXPECT_TRUE(result.success);
264+
EXPECT_EQ(result.error_code, -1);
265+
EXPECT_EQ(result.error_pos, -1);
266+
EXPECT_EQ(result.error_str, "");
267+
EXPECT_EQ(foundNumber, true) << "Number: '" << output.c_str() << "' not found in '" << result.response.c_str() << "'.\n";
268+
}
269+
}
270+
271+
// Test 12: Test with different type of data
272+
TEST_F(JsonFormatTest, FormatJson_MultipleDataType)
273+
{
274+
const std::string inputJson = R"({
275+
"name": "npp-pluginList",
276+
"version": "1.5.3",
277+
"List": {
278+
"defaultTimeInterval": 123400.0
279+
},
280+
"Array": [
281+
{
282+
"Text": "I am text",
283+
"Bool": true,
284+
"Number": 123456
285+
},
286+
{
287+
"Text": "",
288+
"Bool": false,
289+
"Number": 0,
290+
"null": null
291+
}
292+
]
293+
})";
294+
const std::string outputJson = "{\r\n \"name\": \"npp-pluginList\",\r\n \"version\": \"1.5.3\",\r\n \"List\": {\r\n \"defaultTimeInterval\": 123400.0\r\n },\r\n "
295+
"\"Array\": [\r\n {\r\n \"Text\": \"I am text\",\r\n \"Bool\": true,\r\n \"Number\": 123456\r\n },\r\n {\r\n "
296+
"\"Text\": \"\",\r\n \"Bool\": false,\r\n \"Number\": 0,\r\n \"null\": null\r\n }\r\n ]\r\n}";
297+
298+
LE lineEndingOption = rj::kCrLf; // Windows line ending
299+
LF formatOptions = rj::kFormatDefault; // Default
300+
char indentChar = ' ';
301+
unsigned indentLen = 2;
302+
303+
// Set parse options to ignore trailing commas
304+
ParseOptions parseOptions {.bIgnoreComment = false, .bIgnoreTrailingComma = true, .bReplaceUndefined = false};
305+
setParseOptions(parseOptions);
306+
307+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
308+
309+
EXPECT_TRUE(result.success);
310+
EXPECT_EQ(result.error_code, -1);
311+
EXPECT_EQ(result.error_pos, -1);
312+
EXPECT_EQ(result.error_str, "");
313+
EXPECT_EQ(result.response, outputJson);
314+
}
315+
316+
// Test 13: Test with different type of data in array on single line
317+
TEST_F(JsonFormatTest, FormatJson_MultipleDataType_SingleLine)
318+
{
319+
const std::string inputJson = R"({
320+
"name": "npp-pluginList",
321+
"version": "1.5.3",
322+
"List": {
323+
"defaultTimeInterval": 123400.0
324+
},
325+
"Array": [
326+
{
327+
"Text": "I am text",
328+
"Bool": true,
329+
"Number": 123456
330+
},
331+
{
332+
"Text": "",
333+
"Bool": false,
334+
"Number": 0,
335+
"null": null
336+
}
337+
]
338+
})";
339+
const std::string outputJson = "{\r\n \"name\": \"npp-pluginList\",\r\n \"version\": \"1.5.3\",\r\n \"List\": {\r\n \"defaultTimeInterval\": 123400.0\r\n },\r\n "
340+
"\"Array\": [{\r\n \"Text\": \"I am text\",\r\n \"Bool\": true,\r\n \"Number\": 123456\r\n }, {\r\n \"Text\": "
341+
"\"\",\r\n \"Bool\": false,\r\n \"Number\": 0,\r\n \"null\": null\r\n }]\r\n}";
342+
343+
344+
LE lineEndingOption = rj::kCrLf; // Windows line ending
345+
LF formatOptions = rj::kFormatSingleLineArray; // Default
346+
char indentChar = ' ';
347+
unsigned indentLen = 2;
348+
349+
// Set parse options to ignore trailing commas
350+
ParseOptions parseOptions {.bIgnoreComment = false, .bIgnoreTrailingComma = true, .bReplaceUndefined = false};
351+
setParseOptions(parseOptions);
352+
353+
auto result = m_jsonFormatter.FormatJson(inputJson, lineEndingOption, formatOptions, indentChar, indentLen);
354+
355+
EXPECT_TRUE(result.success);
356+
EXPECT_EQ(result.error_code, -1);
357+
EXPECT_EQ(result.error_pos, -1);
358+
EXPECT_EQ(result.error_str, "");
359+
EXPECT_EQ(result.response, outputJson);
360+
}
361+
362+
} // namespace JsonFormat

0 commit comments

Comments
 (0)