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/IniReader.hpp"
11 
12 #include "openrct2/config/ConfigEnum.hpp"
13 #include "openrct2/core/MemoryStream.h"
14 
15 #include <gtest/gtest.h>
16 #include <limits>
17 #include <string>
18 
19 class IniReaderTest : public testing::Test
20 {
21 protected:
22     static const std::string predefined;
23     static const std::string duplicate;
24     static const std::string untrimmed;
25     static const std::string caseInsensitive;
26 };
27 
28 static auto Enum_Currency = ConfigEnum<int32_t>({});
29 
TEST_F(IniReaderTest,create_empty)30 TEST_F(IniReaderTest, create_empty)
31 {
32     OpenRCT2::MemoryStream ms(0);
33     ASSERT_EQ(ms.CanRead(), true);
34     ASSERT_EQ(ms.CanWrite(), true);
35     auto ir = CreateIniReader(&ms);
36     ASSERT_NE(ir, nullptr);
37     ASSERT_EQ(ir->GetBoolean("nobody", true), true);
38     ASSERT_EQ(ir->GetCString("expects", nullptr), nullptr);
39     ASSERT_EQ(ir->GetEnum<int32_t>("spanish", 12345, Enum_Currency), 12345);
40     ASSERT_EQ(ir->GetFloat("inquisition", 1.234f), 1.234f);
41     ASSERT_EQ(ir->GetInt32("universal_answer", 42), 42);
42     ASSERT_EQ(
43         ir->GetInt64("heat_death_of_the_universe", std::numeric_limits<int64_t>::max()), std::numeric_limits<int64_t>::max());
44 }
45 
TEST_F(IniReaderTest,read_prepared)46 TEST_F(IniReaderTest, read_prepared)
47 {
48     OpenRCT2::MemoryStream ms(predefined.c_str(), predefined.size());
49     ASSERT_EQ(ms.CanRead(), true);
50     ASSERT_EQ(ms.CanWrite(), false);
51     auto ir = CreateIniReader(&ms);
52     ASSERT_NE(ir, nullptr);
53     ASSERT_EQ(ir->ReadSection("doesnt_exist"), false);
54     ASSERT_EQ(ir->ReadSection("bool"), true);
55     // name of section
56     ASSERT_EQ(ir->GetInt32("bool", 42), 42);
57     // value from different section
58     ASSERT_EQ(ir->GetInt32("one", 42), 42);
59     // existing value as different type
60     ASSERT_EQ(ir->GetInt32("boolval", 42), 42);
61     ASSERT_EQ(ir->GetBoolean("boolval", false), true);
62     // skip one section
63     ASSERT_EQ(ir->ReadSection("string"), true);
64     // values from different sections
65     ASSERT_EQ(ir->GetInt32("one", 42), 42);
66     ASSERT_EQ(ir->GetBoolean("boolval", false), true);
67     const utf8* str = ir->GetCString("path", nullptr);
68     ASSERT_STREQ(str, u8"C:'\\some/dir\\here/神鷹暢遊");
69     Memory::Free(str);
70     // go back a section
71     ASSERT_EQ(ir->ReadSection("int"), true);
72     ASSERT_EQ(ir->GetInt32("one", 42), 1);
73 }
74 
TEST_F(IniReaderTest,read_duplicate)75 TEST_F(IniReaderTest, read_duplicate)
76 {
77     OpenRCT2::MemoryStream ms(duplicate.c_str(), duplicate.size());
78     ASSERT_EQ(ms.CanRead(), true);
79     ASSERT_EQ(ms.CanWrite(), false);
80     auto ir = CreateIniReader(&ms);
81     ASSERT_NE(ir, nullptr);
82     // there should only be data from the last section
83     ASSERT_EQ(ir->ReadSection("section"), true);
84     ASSERT_EQ(ir->GetBoolean("one", false), false);
85     ASSERT_EQ(ir->GetBoolean("two", false), false);
86     ASSERT_EQ(ir->GetBoolean("three", false), true);
87     ASSERT_EQ(ir->ReadSection("section"), true);
88     // try switching to another section
89     ASSERT_EQ(ir->ReadSection("doesnt_exist"), false);
90     // make sure we are still in the same section
91     ASSERT_EQ(ir->GetBoolean("one", false), false);
92     ASSERT_EQ(ir->GetBoolean("two", false), false);
93     ASSERT_EQ(ir->GetBoolean("three", false), true);
94     ASSERT_EQ(ir->GetInt32("fortytwo", 100), 41);
95     ASSERT_EQ(ir->ReadSection("section"), true);
96     // test 4 times, there are only 3 sections
97     ASSERT_EQ(ir->ReadSection("section"), true);
98 }
99 
TEST_F(IniReaderTest,read_untrimmed)100 TEST_F(IniReaderTest, read_untrimmed)
101 {
102     OpenRCT2::MemoryStream ms(untrimmed.c_str(), untrimmed.size());
103     ASSERT_EQ(ms.CanRead(), true);
104     ASSERT_EQ(ms.CanWrite(), false);
105     auto ir = CreateIniReader(&ms);
106     ASSERT_NE(ir, nullptr);
107     // there should only be data from the last section
108     ASSERT_EQ(ir->ReadSection("section"), true);
109     ASSERT_EQ(ir->GetBoolean("one", false), true);
110     const utf8* str = ir->GetCString("str", nullptr);
111     ASSERT_STREQ(str, "  xxx ");
112     Memory::Free(str);
113     ASSERT_EQ(ir->GetString("str", "yyy"), "  xxx ");
114     ASSERT_EQ(ir->GetString("nosuchthing", "  yyy "), "  yyy ");
115 }
116 
TEST_F(IniReaderTest,read_case_insensitive)117 TEST_F(IniReaderTest, read_case_insensitive)
118 {
119     OpenRCT2::MemoryStream ms(caseInsensitive.c_str(), caseInsensitive.size());
120     ASSERT_EQ(ms.CanRead(), true);
121     ASSERT_EQ(ms.CanWrite(), false);
122     auto ir = CreateIniReader(&ms);
123     ASSERT_NE(ir, nullptr);
124     ASSERT_EQ(ir->ReadSection("section"), true);
125     ASSERT_EQ(ir->GetString("foo", "yyy"), "bar");
126     ASSERT_EQ(ir->ReadSection("SeCtIoN"), true);
127 }
128 
129 const std::string IniReaderTest::predefined = "[bool]\n"
130                                               "boolval = true\n\n"
131                                               "[int]\n"
132                                               "one = 1\n"
133                                               "zero = 0\n\n"
134                                               "[string]\n"
135                                               "path = "
136                                               "\"C:'\\\\some/dir\\\\here/\xE7\xA5\x9E\xE9\xB7\xB9\xE6\x9A\xA2\xE9\x81\x8A\"\n";
137 
138 const std::string IniReaderTest::duplicate = "[section]\n"
139                                              "one = true\n"
140                                              "fortytwo = 13\n"
141                                              "[section]\n"
142                                              "two = true\n"
143                                              "[section]\n"
144                                              "three = true\n"
145                                              "fortytwo = 42\n"
146                                              "fortytwo = 41\n";
147 
148 const std::string IniReaderTest::untrimmed = "[section]\n"
149                                              "one =     true      \n"
150                                              "    str =    \"  xxx \"";
151 
152 const std::string IniReaderTest::caseInsensitive = "[sEcTiOn]\n"
153                                                    "foo = \"bar\"\n";
154