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