1 /*****************************************************************************
2  * Copyright (c) 2014-2018 OpenRCT2 developers
3  *
4  * For a complete list of all authors, please refer to contributors.md
5  * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6  *
7  * OpenRCT2 is licensed under the GNU General Public License version 3.
8  *****************************************************************************/
9 
10 #include "openrct2/config/IniWriter.hpp"
11 
12 #include "openrct2/config/ConfigEnum.hpp"
13 #include "openrct2/core/MemoryStream.h"
14 #include "openrct2/platform/platform.h"
15 
16 #include <gtest/gtest.h>
17 #include <limits>
18 
19 class IniWriterTest : public testing::Test
20 {
21 };
22 
23 static auto Enum_Currency = ConfigEnum<int32_t>({
24     ConfigEnumEntry<int32_t>("GBP", 1),
25 });
26 
TEST_F(IniWriterTest,create_empty)27 TEST_F(IniWriterTest, create_empty)
28 {
29     OpenRCT2::MemoryStream ms(0);
30     ASSERT_EQ(ms.CanRead(), true);
31     ASSERT_EQ(ms.CanWrite(), true);
32     auto iw = CreateIniWriter(&ms);
33     ASSERT_NE(iw, nullptr);
34 }
35 
TEST_F(IniWriterTest,create_one_section)36 TEST_F(IniWriterTest, create_one_section)
37 {
38     OpenRCT2::MemoryStream ms(1000);
39     auto iw = CreateIniWriter(&ms);
40     ASSERT_NE(iw, nullptr);
41     iw->WriteSection("OpenRCT2");
42     uint8_t null_terminator = 0;
43     ms.Write(&null_terminator, 1);
44     ASSERT_GE(ms.GetPosition(), 12);
45     ASSERT_LE(ms.GetPosition(), 13); // Accommodate for varying-sized newline (Windows)
46     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
47     ms.SetPosition(0);
48     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
49     ASSERT_STREQ(ini, "[OpenRCT2]" PLATFORM_NEWLINE);
50     Memory::Free(ini);
51 }
52 
TEST_F(IniWriterTest,create_multiple_sections)53 TEST_F(IniWriterTest, create_multiple_sections)
54 {
55     OpenRCT2::MemoryStream ms(1000);
56     auto iw = CreateIniWriter(&ms);
57     ASSERT_NE(iw, nullptr);
58     iw->WriteSection("OpenRCT1");
59     iw->WriteSection("OpenRCT2");
60     iw->WriteSection("OpenRCT3");
61     iw->WriteSection("OpenRCT4");
62     uint8_t null_terminator = 0;
63     ms.Write(&null_terminator, 1);
64     ASSERT_GE(ms.GetPosition(), 48);
65     ASSERT_LE(ms.GetPosition(), 55); // Accommodate for varying-sized newline (Windows)
66     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
67     ms.SetPosition(0);
68     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
69     ASSERT_STREQ(
70         ini,
71         "[OpenRCT1]" PLATFORM_NEWLINE PLATFORM_NEWLINE "[OpenRCT2]" PLATFORM_NEWLINE PLATFORM_NEWLINE
72         "[OpenRCT3]" PLATFORM_NEWLINE PLATFORM_NEWLINE "[OpenRCT4]" PLATFORM_NEWLINE);
73     Memory::Free(ini);
74 }
75 
TEST_F(IniWriterTest,create_loose_bool_entry)76 TEST_F(IniWriterTest, create_loose_bool_entry)
77 {
78     OpenRCT2::MemoryStream ms(1000);
79     auto iw = CreateIniWriter(&ms);
80     ASSERT_NE(iw, nullptr);
81     iw->WriteBoolean("boolval", true);
82     uint8_t null_terminator = 0;
83     ms.Write(&null_terminator, 1);
84     ASSERT_GE(ms.GetPosition(), 16);
85     ASSERT_LE(ms.GetPosition(), 17); // Accommodate for varying-sized newline (Windows)
86     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
87     ms.SetPosition(0);
88     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
89     ASSERT_STREQ(ini, "boolval = true" PLATFORM_NEWLINE);
90     Memory::Free(ini);
91 }
92 
TEST_F(IniWriterTest,create_loose_enum_entry)93 TEST_F(IniWriterTest, create_loose_enum_entry)
94 {
95     OpenRCT2::MemoryStream ms(1000);
96     auto iw = CreateIniWriter(&ms);
97     ASSERT_NE(iw, nullptr);
98     iw->WriteEnum("by_string", "stringval");
99     iw->WriteEnum<int32_t>("int32_t", 0, Enum_Currency);
100     uint8_t null_terminator = 0;
101     ms.Write(&null_terminator, 1);
102     ASSERT_GE(ms.GetPosition(), 34);
103     ASSERT_LE(ms.GetPosition(), 37); // Accommodate for varying-sized newline (Windows)
104     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
105     ms.SetPosition(0);
106     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
107     ASSERT_STREQ(ini, "by_string = stringval" PLATFORM_NEWLINE "int32_t = 0" PLATFORM_NEWLINE);
108     Memory::Free(ini);
109 }
110 
TEST_F(IniWriterTest,create_loose_float_entry)111 TEST_F(IniWriterTest, create_loose_float_entry)
112 {
113     OpenRCT2::MemoryStream ms(1000);
114     auto iw = CreateIniWriter(&ms);
115     ASSERT_NE(iw, nullptr);
116     iw->WriteFloat("one", 1.);
117     uint8_t null_terminator = 0;
118     ms.Write(&null_terminator, 1);
119     ASSERT_GE(ms.GetPosition(), 16);
120     ASSERT_LE(ms.GetPosition(), 17); // Accommodate for varying-sized newline (Windows)
121     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
122     ms.SetPosition(0);
123     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
124     // This will be non-fatal due to float.
125     EXPECT_STREQ(ini, "one = 1.000000" PLATFORM_NEWLINE);
126     Memory::Free(ini);
127 }
128 
TEST_F(IniWriterTest,create_loose_int32_t_entry)129 TEST_F(IniWriterTest, create_loose_int32_t_entry)
130 {
131     OpenRCT2::MemoryStream ms(1000);
132     auto iw = CreateIniWriter(&ms);
133     ASSERT_NE(iw, nullptr);
134     iw->WriteInt32("one", 1);
135     iw->WriteInt32("zero", 0);
136     iw->WriteInt32("minusone", -1);
137     iw->WriteInt32("intmin", (std::numeric_limits<int32_t>::min)());
138     iw->WriteInt32("intmax", (std::numeric_limits<int32_t>::max)());
139     uint8_t null_terminator = 0;
140     ms.Write(&null_terminator, 1);
141     ASSERT_GE(ms.GetPosition(), 73);
142     ASSERT_LE(ms.GetPosition(), 78); // Accommodate for varying-sized newline (Windows)
143     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
144     ms.SetPosition(0);
145     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
146     ASSERT_STREQ(
147         ini,
148         "one = 1" PLATFORM_NEWLINE "zero = 0" PLATFORM_NEWLINE "minusone = -1" PLATFORM_NEWLINE
149         "intmin = -2147483648" PLATFORM_NEWLINE "intmax = 2147483647" PLATFORM_NEWLINE);
150     Memory::Free(ini);
151 }
152 
TEST_F(IniWriterTest,create_loose_string_entry)153 TEST_F(IniWriterTest, create_loose_string_entry)
154 {
155     OpenRCT2::MemoryStream ms(1000);
156     auto iw = CreateIniWriter(&ms);
157     ASSERT_NE(iw, nullptr);
158     iw->WriteString("path", u8"C:'\\some/dir\\here/神鷹暢遊");
159     uint8_t null_terminator = 0;
160     ms.Write(&null_terminator, 1);
161     ASSERT_GE(ms.GetPosition(), 43);
162     ASSERT_LE(ms.GetPosition(), 44); // Accommodate for varying-sized newline (Windows)
163     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
164     ms.SetPosition(0);
165     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
166     ASSERT_STREQ(ini, "path = \"C:'\\\\some/dir\\\\here/\xE7\xA5\x9E\xE9\xB7\xB9\xE6\x9A\xA2\xE9\x81\x8A\"" PLATFORM_NEWLINE);
167     Memory::Free(ini);
168 }
169 
TEST_F(IniWriterTest,create_multiple_section_with_values)170 TEST_F(IniWriterTest, create_multiple_section_with_values)
171 {
172     OpenRCT2::MemoryStream ms(1000);
173     auto iw = CreateIniWriter(&ms);
174     ASSERT_NE(iw, nullptr);
175     iw->WriteSection("bool");
176     iw->WriteBoolean("boolval", true);
177     iw->WriteSection("int");
178     iw->WriteInt32("one", 1);
179     iw->WriteInt32("zero", 0);
180     iw->WriteSection("string");
181     iw->WriteString("path", u8"C:'\\some/dir\\here/神鷹暢遊");
182     uint8_t null_terminator = 0;
183     ms.Write(&null_terminator, 1);
184     ASSERT_GE(ms.GetPosition(), 99);
185     ASSERT_LE(ms.GetPosition(), 108); // Accommodate for varying-sized newline (Windows)
186     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
187     ms.SetPosition(0);
188     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
189     ASSERT_STREQ(
190         ini,
191         "[bool]" PLATFORM_NEWLINE "boolval = true" PLATFORM_NEWLINE PLATFORM_NEWLINE "[int]" PLATFORM_NEWLINE
192         "one = 1" PLATFORM_NEWLINE "zero = 0" PLATFORM_NEWLINE PLATFORM_NEWLINE "[string]" PLATFORM_NEWLINE "path = "
193         "\"C:'\\\\some/dir\\\\here/\xE7\xA5\x9E\xE9\xB7\xB9\xE6\x9A\xA2\xE9\x81\x8A\"" PLATFORM_NEWLINE);
194     Memory::Free(ini);
195 }
196 
TEST_F(IniWriterTest,create_duplicate_sections)197 TEST_F(IniWriterTest, create_duplicate_sections)
198 {
199     OpenRCT2::MemoryStream ms(1000);
200     auto iw = CreateIniWriter(&ms);
201     ASSERT_NE(iw, nullptr);
202     iw->WriteSection("section");
203     iw->WriteSection("section");
204     iw->WriteSection("section");
205     uint8_t null_terminator = 0;
206     ms.Write(&null_terminator, 1);
207     ASSERT_GE(ms.GetPosition(), 33);
208     ASSERT_LE(ms.GetPosition(), 43); // Accommodate for varying-sized newline (Windows)
209     ASSERT_EQ(ms.GetLength(), ms.GetPosition());
210     ms.SetPosition(0);
211     const char* ini = reinterpret_cast<const char*>(ms.ReadString());
212     ASSERT_STREQ(
213         ini,
214         "[section]" PLATFORM_NEWLINE PLATFORM_NEWLINE "[section]" PLATFORM_NEWLINE PLATFORM_NEWLINE
215         "[section]" PLATFORM_NEWLINE);
216     Memory::Free(ini);
217 }
218