1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "test.h"
4#include <gtest/gtest.h>
5
6#include <base/system.h>
7#include <engine/shared/jsonwriter.h>
8
9#include <limits>
10
11class JsonFileWriter
12{
13public:
14 CTestInfo m_Info;
15 CJsonFileWriter *m_pJson;
16 char m_aOutputFilename[IO_MAX_PATH_LENGTH];
17
18 JsonFileWriter() :
19 m_pJson(nullptr)
20 {
21 m_Info.Filename(pBuffer: m_aOutputFilename, BufferLength: sizeof(m_aOutputFilename), pSuffix: "-got.json");
22 IOHANDLE File = io_open(filename: m_aOutputFilename, flags: IOFLAG_WRITE);
23 EXPECT_TRUE(File);
24 m_pJson = new CJsonFileWriter(File);
25 }
26
27 void Expect(const char *pExpected)
28 {
29 ASSERT_TRUE(m_pJson);
30 delete m_pJson;
31 m_pJson = nullptr;
32
33 IOHANDLE GotFile = io_open(filename: m_aOutputFilename, flags: IOFLAG_READ);
34 ASSERT_TRUE(GotFile);
35 char *pOutput = io_read_all_str(io: GotFile);
36 io_close(io: GotFile);
37 ASSERT_TRUE(pOutput);
38 EXPECT_STREQ(pOutput, pExpected);
39 bool Correct = str_comp(a: pOutput, b: pExpected) == 0;
40 free(ptr: pOutput);
41
42 if(!Correct)
43 {
44 char aFilename[IO_MAX_PATH_LENGTH];
45 m_Info.Filename(pBuffer: aFilename, BufferLength: sizeof(aFilename), pSuffix: "-expected.json");
46 IOHANDLE ExpectedFile = io_open(filename: aFilename, flags: IOFLAG_WRITE);
47 ASSERT_TRUE(ExpectedFile);
48 io_write(io: ExpectedFile, buffer: pExpected, size: str_length(str: pExpected));
49 io_close(io: ExpectedFile);
50 }
51 else
52 {
53 fs_remove(filename: m_aOutputFilename);
54 }
55 }
56};
57
58class JsonStringWriter
59{
60public:
61 CJsonStringWriter *m_pJson;
62
63 JsonStringWriter() :
64 m_pJson(nullptr)
65 {
66 m_pJson = new CJsonStringWriter();
67 }
68
69 void Expect(const char *pExpected)
70 {
71 ASSERT_TRUE(m_pJson);
72 const std::string OutputString = m_pJson->GetOutputString();
73 EXPECT_STREQ(OutputString.c_str(), pExpected);
74 delete m_pJson;
75 m_pJson = nullptr;
76 }
77};
78
79template<typename T>
80class JsonWriters : public testing::Test
81{
82public:
83 T Impl;
84};
85
86using JsonWriterTestFixures = ::testing::Types<JsonFileWriter, JsonStringWriter>;
87TYPED_TEST_SUITE(JsonWriters, JsonWriterTestFixures);
88
89TYPED_TEST(JsonWriters, Empty)
90{
91 this->Impl.Expect("\n");
92}
93
94TYPED_TEST(JsonWriters, EmptyObject)
95{
96 this->Impl.m_pJson->BeginObject();
97 this->Impl.m_pJson->EndObject();
98 this->Impl.Expect("{\n}\n");
99}
100
101TYPED_TEST(JsonWriters, EmptyArray)
102{
103 this->Impl.m_pJson->BeginArray();
104 this->Impl.m_pJson->EndArray();
105 this->Impl.Expect("[\n]\n");
106}
107
108TYPED_TEST(JsonWriters, SpecialCharacters)
109{
110 this->Impl.m_pJson->BeginObject();
111 this->Impl.m_pJson->WriteAttribute("\x01\"'\r\n\t");
112 this->Impl.m_pJson->BeginArray();
113 this->Impl.m_pJson->WriteStrValue(" \"'abc\x01\n");
114 this->Impl.m_pJson->EndArray();
115 this->Impl.m_pJson->EndObject();
116 this->Impl.Expect(
117 "{\n"
118 "\t\"\\u0001\\\"'\\r\\n\\t\": [\n"
119 "\t\t\" \\\"'abc\\u0001\\n\"\n"
120 "\t]\n"
121 "}\n");
122}
123
124TYPED_TEST(JsonWriters, HelloWorld)
125{
126 this->Impl.m_pJson->WriteStrValue("hello world");
127 this->Impl.Expect("\"hello world\"\n");
128}
129
130TYPED_TEST(JsonWriters, Unicode)
131{
132 this->Impl.m_pJson->WriteStrValue("Heizölrückstoßabdämpfung");
133 this->Impl.Expect("\"Heizölrückstoßabdämpfung\"\n");
134}
135
136TYPED_TEST(JsonWriters, True)
137{
138 this->Impl.m_pJson->WriteBoolValue(true);
139 this->Impl.Expect("true\n");
140}
141
142TYPED_TEST(JsonWriters, False)
143{
144 this->Impl.m_pJson->WriteBoolValue(false);
145 this->Impl.Expect("false\n");
146}
147
148TYPED_TEST(JsonWriters, Null)
149{
150 this->Impl.m_pJson->WriteNullValue();
151 this->Impl.Expect("null\n");
152}
153
154TYPED_TEST(JsonWriters, EmptyString)
155{
156 this->Impl.m_pJson->WriteStrValue("");
157 this->Impl.Expect("\"\"\n");
158}
159
160TYPED_TEST(JsonWriters, EscapeNewline)
161{
162 this->Impl.m_pJson->WriteStrValue("\n");
163 this->Impl.Expect("\"\\n\"\n");
164}
165
166TYPED_TEST(JsonWriters, EscapeBackslash)
167{
168 this->Impl.m_pJson->WriteStrValue("\\");
169 this->Impl.Expect("\"\\\\\"\n"); // https://www.xkcd.com/1638/
170}
171
172TYPED_TEST(JsonWriters, EscapeControl)
173{
174 this->Impl.m_pJson->WriteStrValue("\x1b");
175 this->Impl.Expect("\"\\u001b\"\n");
176}
177
178TYPED_TEST(JsonWriters, EscapeUnicode)
179{
180 this->Impl.m_pJson->WriteStrValue("愛😂");
181 this->Impl.Expect("\"愛😂\"\n");
182}
183
184TYPED_TEST(JsonWriters, Zero)
185{
186 this->Impl.m_pJson->WriteIntValue(0);
187 this->Impl.Expect("0\n");
188}
189
190TYPED_TEST(JsonWriters, One)
191{
192 this->Impl.m_pJson->WriteIntValue(1);
193 this->Impl.Expect("1\n");
194}
195
196TYPED_TEST(JsonWriters, MinusOne)
197{
198 this->Impl.m_pJson->WriteIntValue(-1);
199 this->Impl.Expect("-1\n");
200}
201
202TYPED_TEST(JsonWriters, Large)
203{
204 this->Impl.m_pJson->WriteIntValue(std::numeric_limits<int>::max());
205 this->Impl.Expect("2147483647\n");
206}
207
208TYPED_TEST(JsonWriters, Small)
209{
210 this->Impl.m_pJson->WriteIntValue(std::numeric_limits<int>::min());
211 this->Impl.Expect("-2147483648\n");
212}
213