Line data Source code
1 : // Copyright (c) 2019-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/basic-xml
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : // self
20 : //
21 : #include "catch_main.h"
22 :
23 :
24 : // basic-xml
25 : //
26 : #include <basic-xml/exception.h>
27 : #include <basic-xml/xml.h>
28 :
29 :
30 : // C++
31 : //
32 : #include <fstream>
33 :
34 :
35 :
36 7 : CATCH_TEST_CASE("xml", "[xml][valid]")
37 : {
38 10 : CATCH_START_SECTION("empty root")
39 : {
40 2 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::get_folder_name());
41 2 : std::string const filename(xml_path + "/empty-tag.xml");
42 :
43 : // create file with one empty tag
44 : {
45 2 : std::ofstream f;
46 1 : f.open(filename);
47 1 : CATCH_REQUIRE(f.is_open());
48 1 : f << "<empty></empty>";
49 : }
50 :
51 2 : basic_xml::xml x(filename);
52 2 : basic_xml::node::pointer_t root(x.root());
53 1 : CATCH_REQUIRE(root != nullptr);
54 1 : CATCH_REQUIRE(root->tag_name() == "empty");
55 1 : CATCH_REQUIRE(root->text().empty());
56 1 : CATCH_REQUIRE(root->all_attributes().empty());
57 1 : CATCH_REQUIRE(root->parent() == nullptr);
58 1 : CATCH_REQUIRE(root->first_child() == nullptr);
59 1 : CATCH_REQUIRE(root->last_child() == nullptr);
60 1 : CATCH_REQUIRE(root->next() == nullptr);
61 1 : CATCH_REQUIRE(root->previous() == nullptr);
62 : }
63 : CATCH_END_SECTION()
64 :
65 10 : CATCH_START_SECTION("empty root with preprocessor")
66 : {
67 2 : std::string const filename("empty-tag.xml");
68 :
69 : // create file with one empty tag
70 2 : std::stringstream ss;
71 1 : ss << "<?xml version=\"1.0\"?><still-empty></still-empty>";
72 :
73 2 : basic_xml::xml x(filename, ss);
74 2 : basic_xml::node::pointer_t root(x.root());
75 1 : CATCH_REQUIRE(root != nullptr);
76 1 : CATCH_REQUIRE(root->tag_name() == "still-empty");
77 1 : CATCH_REQUIRE(root->text().empty());
78 1 : CATCH_REQUIRE(root->all_attributes().empty());
79 1 : CATCH_REQUIRE(root->parent() == nullptr);
80 1 : CATCH_REQUIRE(root->first_child() == nullptr);
81 1 : CATCH_REQUIRE(root->last_child() == nullptr);
82 1 : CATCH_REQUIRE(root->next() == nullptr);
83 1 : CATCH_REQUIRE(root->previous() == nullptr);
84 : }
85 : CATCH_END_SECTION()
86 :
87 10 : CATCH_START_SECTION("empty root with comment & preprocessor")
88 : {
89 2 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::get_folder_name());
90 2 : std::string const filename(xml_path + "/quite-empty.xml");
91 :
92 : // create file with one empty tag
93 : {
94 2 : std::ofstream f;
95 1 : f.open(filename);
96 1 : CATCH_REQUIRE(f.is_open());
97 1 : f << "<!-- name='rotor' --><?xml version=\"1.0\"?><quite-empty></quite-empty>";
98 : }
99 :
100 2 : basic_xml::xml x(filename);
101 2 : basic_xml::node::pointer_t root(x.root());
102 1 : CATCH_REQUIRE(root != nullptr);
103 1 : CATCH_REQUIRE(root->tag_name() == "quite-empty");
104 1 : CATCH_REQUIRE(root->text().empty());
105 1 : CATCH_REQUIRE(root->all_attributes().empty());
106 1 : CATCH_REQUIRE(root->parent() == nullptr);
107 1 : CATCH_REQUIRE(root->first_child() == nullptr);
108 1 : CATCH_REQUIRE(root->last_child() == nullptr);
109 1 : CATCH_REQUIRE(root->next() == nullptr);
110 1 : CATCH_REQUIRE(root->previous() == nullptr);
111 : }
112 : CATCH_END_SECTION()
113 :
114 10 : CATCH_START_SECTION("empty root with comment & preprocessor & attributes")
115 : {
116 2 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::get_folder_name());
117 2 : std::string const filename(xml_path + "/root-attributes.xml");
118 :
119 : // create file with one empty tag
120 : {
121 2 : std::ofstream f;
122 1 : f.open(filename);
123 1 : CATCH_REQUIRE(f.is_open());
124 1 : f << "<!--\n"
125 : "name='next level'\n"
126 : "-->\n"
127 : "\n"
128 : "<?xml version=\"1.0\"?>\n"
129 : "<root-canal quite=\"quite\" size='123' very=\"true\">"
130 : " \t \t \t "
131 : "</root-canal>";
132 : }
133 :
134 2 : basic_xml::xml x(filename);
135 2 : basic_xml::node::pointer_t root(x.root());
136 1 : CATCH_REQUIRE(root != nullptr);
137 1 : CATCH_REQUIRE(root->tag_name() == "root-canal");
138 1 : CATCH_REQUIRE(root->text() == "");
139 1 : CATCH_REQUIRE(root->text(true) == "");
140 1 : CATCH_REQUIRE(root->text(false) == " \t \t \t ");
141 1 : CATCH_REQUIRE(root->all_attributes().size() == 3);
142 1 : CATCH_REQUIRE(root->attribute("quite") == "quite");
143 1 : CATCH_REQUIRE(root->attribute("size") == "123");
144 1 : CATCH_REQUIRE(root->attribute("very") == "true");
145 1 : CATCH_REQUIRE(root->parent() == nullptr);
146 1 : CATCH_REQUIRE(root->first_child() == nullptr);
147 1 : CATCH_REQUIRE(root->last_child() == nullptr);
148 1 : CATCH_REQUIRE(root->next() == nullptr);
149 1 : CATCH_REQUIRE(root->previous() == nullptr);
150 : }
151 : CATCH_END_SECTION()
152 :
153 10 : CATCH_START_SECTION("entities test")
154 : {
155 2 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::get_folder_name());
156 2 : std::string const filename(xml_path + "/entities.xml");
157 :
158 : // create file with one empty tag
159 : {
160 2 : std::ofstream f;
161 1 : f.open(filename);
162 1 : CATCH_REQUIRE(f.is_open());
163 1 : f << "<!--\n"
164 : "name='entities'\n"
165 : "-->\n"
166 : "\n"
167 : "<?xml version=\"1.0\"?>\n"
168 : "<entity-a-gogo quite=\"quite\" size='123'"
169 : " very=\""true"\" special-entry=\""<it's special & weird>"\">"
170 : "</entity-a-gogo>";
171 : }
172 :
173 2 : basic_xml::xml x(filename);
174 2 : basic_xml::node::pointer_t root(x.root());
175 1 : CATCH_REQUIRE(root != nullptr);
176 1 : CATCH_REQUIRE(root->tag_name() == "entity-a-gogo");
177 1 : CATCH_REQUIRE(root->all_attributes().size() == 4);
178 1 : CATCH_REQUIRE(root->attribute("quite") == "quite");
179 1 : CATCH_REQUIRE(root->attribute("size") == "123");
180 1 : CATCH_REQUIRE(root->attribute("very") == "\"true\"");
181 1 : CATCH_REQUIRE(root->attribute("special-entry") == "\"<it's special & weird>\"");
182 1 : CATCH_REQUIRE(root->parent() == nullptr);
183 1 : CATCH_REQUIRE(root->first_child() == nullptr);
184 1 : CATCH_REQUIRE(root->last_child() == nullptr);
185 1 : CATCH_REQUIRE(root->next() == nullptr);
186 1 : CATCH_REQUIRE(root->previous() == nullptr);
187 : }
188 : CATCH_END_SECTION()
189 5 : }
190 :
191 :
192 6 : CATCH_TEST_CASE("xml", "[xml][invalid]")
193 : {
194 8 : CATCH_START_SECTION("file missing")
195 : {
196 2 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::get_folder_name());
197 2 : std::string const filename(xml_path + "/file-does-not-exist.xml");
198 :
199 1 : CATCH_REQUIRE_THROWS_MATCHES(
200 : basic_xml::xml(filename)
201 : , basic_xml::file_not_found
202 : , Catch::Matchers::ExceptionMessage(
203 : "xml_error: could not open XML file \""
204 : + filename
205 : + "\": "
206 : + strerror(ENOENT)
207 : + ".", true));
208 : }
209 : CATCH_END_SECTION()
210 :
211 8 : CATCH_START_SECTION("file no permission")
212 : {
213 2 : std::string const filename("/root/.bashrc");
214 :
215 1 : CATCH_REQUIRE_THROWS_MATCHES(
216 : basic_xml::xml(filename)
217 : , basic_xml::file_not_found
218 : , Catch::Matchers::ExceptionMessage(
219 : "xml_error: could not open XML file \""
220 : + filename
221 : + "\": "
222 : + strerror(EACCES)
223 : + ".", true));
224 : }
225 : CATCH_END_SECTION()
226 :
227 8 : CATCH_START_SECTION("parse empty xml file")
228 : {
229 2 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::get_folder_name());
230 2 : std::string const filename(xml_path + "/empty.xml");
231 :
232 : // create empty file
233 : {
234 2 : std::ofstream f;
235 1 : f.open(filename);
236 1 : CATCH_REQUIRE(f.is_open());
237 : }
238 :
239 1 : CATCH_REQUIRE_THROWS_MATCHES(
240 : basic_xml::xml(filename)
241 : , basic_xml::unexpected_token
242 : , Catch::Matchers::ExceptionMessage(
243 : "xml_error: "
244 : + filename
245 : + ":1: cannot be empty or include anything other than a processor tag and comments before the root tag.", true));
246 : }
247 : CATCH_END_SECTION()
248 :
249 8 : CATCH_START_SECTION("empty root tag")
250 : {
251 2 : std::string const filename("empty-tag.xml");
252 :
253 : // create file with one empty tag
254 2 : std::stringstream ss;
255 1 : ss << "<empty/>";
256 :
257 1 : CATCH_REQUIRE_THROWS_MATCHES(
258 : basic_xml::xml(filename, ss)
259 : , basic_xml::unexpected_token
260 : , Catch::Matchers::ExceptionMessage(
261 : "xml_error: "
262 : + filename
263 : + ":1: root tag cannot be an empty tag.", true));
264 : }
265 : CATCH_END_SECTION()
266 10 : }
267 :
268 :
269 :
270 : // vim: ts=4 sw=4 et
|