Line data Source code
1 : // Copyright (c) 2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdatabase
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 2 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 along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : // self
21 : //
22 : #include "main.h"
23 :
24 :
25 : // snaplogger lib
26 : //
27 : #include <snapdatabase/exception.h>
28 : #include <snapdatabase/data/xml.h>
29 :
30 :
31 : // C++ lib
32 : //
33 : #include <fstream>
34 :
35 :
36 : // C lib
37 : //
38 : #include <sys/stat.h>
39 : #include <sys/types.h>
40 :
41 :
42 :
43 : namespace
44 : {
45 :
46 :
47 8 : std::string get_folder_name()
48 : {
49 16 : std::string const xml_path(SNAP_CATCH2_NAMESPACE::g_tmp_dir + "/xml");
50 :
51 8 : if(mkdir(xml_path.c_str(), 0700) != 0)
52 : {
53 7 : if(errno != EEXIST)
54 : {
55 0 : perror(("could not create directory \"" + xml_path + "\"").c_str());
56 0 : return std::string();
57 : }
58 : }
59 :
60 8 : return xml_path;
61 : }
62 :
63 :
64 : }
65 :
66 :
67 :
68 9 : CATCH_TEST_CASE("XML Basics", "[xml]")
69 : {
70 14 : CATCH_START_SECTION("empty")
71 : {
72 2 : std::string const xml;
73 :
74 2 : std::string const xml_path(get_folder_name());
75 2 : std::string const filename(xml_path + "/empty.xml");
76 :
77 : // create empty file
78 : {
79 2 : std::ofstream f;
80 1 : f.open(filename);
81 1 : CATCH_REQUIRE(f.is_open());
82 : }
83 :
84 1 : CATCH_REQUIRE_THROWS_MATCHES(
85 : snapdatabase::xml(filename)
86 : , snapdatabase::unexpected_token
87 : , Catch::Matchers::ExceptionMessage(
88 : "snapdatabase_error: File \""
89 : + filename
90 : + "\" cannot be empty or include anything other than a processor tag and comments before the root tag.", true));
91 : }
92 : CATCH_END_SECTION()
93 :
94 14 : CATCH_START_SECTION("empty root tag")
95 : {
96 2 : std::string const xml;
97 :
98 2 : std::string const xml_path(get_folder_name());
99 2 : std::string const filename(xml_path + "/empty-tag.xml");
100 :
101 : // create file with one empty tag
102 : {
103 2 : std::ofstream f;
104 1 : f.open(filename);
105 1 : CATCH_REQUIRE(f.is_open());
106 1 : f << "<empty/>";
107 : }
108 :
109 1 : CATCH_REQUIRE_THROWS_MATCHES(
110 : snapdatabase::xml(filename)
111 : , snapdatabase::unexpected_token
112 : , Catch::Matchers::ExceptionMessage(
113 : "snapdatabase_error: File \""
114 : + filename
115 : + "\" root tag cannot be an empty tag.", true));
116 : }
117 : CATCH_END_SECTION()
118 :
119 14 : CATCH_START_SECTION("empty root")
120 : {
121 2 : std::string const xml;
122 :
123 2 : std::string const xml_path(get_folder_name());
124 2 : std::string const filename(xml_path + "/empty-tag.xml");
125 :
126 : // create file with one empty tag
127 : {
128 2 : std::ofstream f;
129 1 : f.open(filename);
130 1 : CATCH_REQUIRE(f.is_open());
131 1 : f << "<empty></empty>";
132 : }
133 :
134 2 : snapdatabase::xml x(filename);
135 2 : snapdatabase::xml_node::pointer_t root(x.root());
136 1 : CATCH_REQUIRE(root != nullptr);
137 1 : CATCH_REQUIRE(root->tag_name() == "empty");
138 1 : CATCH_REQUIRE(root->text().empty());
139 1 : CATCH_REQUIRE(root->all_attributes().empty());
140 1 : CATCH_REQUIRE(root->parent() == nullptr);
141 1 : CATCH_REQUIRE(root->first_child() == nullptr);
142 1 : CATCH_REQUIRE(root->last_child() == nullptr);
143 1 : CATCH_REQUIRE(root->next() == nullptr);
144 1 : CATCH_REQUIRE(root->previous() == nullptr);
145 : }
146 : CATCH_END_SECTION()
147 :
148 14 : CATCH_START_SECTION("empty root with preprocessor")
149 : {
150 2 : std::string const xml;
151 :
152 2 : std::string const xml_path(get_folder_name());
153 2 : std::string const filename(xml_path + "/empty-tag.xml");
154 :
155 : // create file with one empty tag
156 : {
157 2 : std::ofstream f;
158 1 : f.open(filename);
159 1 : CATCH_REQUIRE(f.is_open());
160 1 : f << "<?xml version=\"1.0\"?><still-empty></still-empty>";
161 : }
162 :
163 2 : snapdatabase::xml x(filename);
164 2 : snapdatabase::xml_node::pointer_t root(x.root());
165 1 : CATCH_REQUIRE(root != nullptr);
166 1 : CATCH_REQUIRE(root->tag_name() == "still-empty");
167 1 : CATCH_REQUIRE(root->text().empty());
168 1 : CATCH_REQUIRE(root->all_attributes().empty());
169 1 : CATCH_REQUIRE(root->parent() == nullptr);
170 1 : CATCH_REQUIRE(root->first_child() == nullptr);
171 1 : CATCH_REQUIRE(root->last_child() == nullptr);
172 1 : CATCH_REQUIRE(root->next() == nullptr);
173 1 : CATCH_REQUIRE(root->previous() == nullptr);
174 : }
175 : CATCH_END_SECTION()
176 :
177 14 : CATCH_START_SECTION("empty root with comment & preprocessor")
178 : {
179 2 : std::string const xml;
180 :
181 2 : std::string const xml_path(get_folder_name());
182 2 : std::string const filename(xml_path + "/quite-empty.xml");
183 :
184 : // create file with one empty tag
185 : {
186 2 : std::ofstream f;
187 1 : f.open(filename);
188 1 : CATCH_REQUIRE(f.is_open());
189 1 : f << "<!-- name='rotor' --><?xml version=\"1.0\"?><quite-empty></quite-empty>";
190 : }
191 :
192 2 : snapdatabase::xml x(filename);
193 2 : snapdatabase::xml_node::pointer_t root(x.root());
194 1 : CATCH_REQUIRE(root != nullptr);
195 1 : CATCH_REQUIRE(root->tag_name() == "quite-empty");
196 1 : CATCH_REQUIRE(root->text().empty());
197 1 : CATCH_REQUIRE(root->all_attributes().empty());
198 1 : CATCH_REQUIRE(root->parent() == nullptr);
199 1 : CATCH_REQUIRE(root->first_child() == nullptr);
200 1 : CATCH_REQUIRE(root->last_child() == nullptr);
201 1 : CATCH_REQUIRE(root->next() == nullptr);
202 1 : CATCH_REQUIRE(root->previous() == nullptr);
203 : }
204 : CATCH_END_SECTION()
205 :
206 14 : CATCH_START_SECTION("empty root with comment & preprocessor & attributes")
207 : {
208 2 : std::string const xml;
209 :
210 2 : std::string const xml_path(get_folder_name());
211 2 : std::string const filename(xml_path + "/root-attributes.xml");
212 :
213 : // create file with one empty tag
214 : {
215 2 : std::ofstream f;
216 1 : f.open(filename);
217 1 : CATCH_REQUIRE(f.is_open());
218 1 : f << "<!--\n"
219 : "name='next level'\n"
220 : "-->\n"
221 : "\n"
222 : "<?xml version=\"1.0\"?>\n"
223 : "<root-canal quite=\"quite\" size='123' very=\"true\">"
224 : " \t \t \t "
225 : "</root-canal>";
226 : }
227 :
228 2 : snapdatabase::xml x(filename);
229 2 : snapdatabase::xml_node::pointer_t root(x.root());
230 1 : CATCH_REQUIRE(root != nullptr);
231 1 : CATCH_REQUIRE(root->tag_name() == "root-canal");
232 1 : CATCH_REQUIRE(root->text() == " \t \t \t ");
233 1 : CATCH_REQUIRE(root->all_attributes().size() == 3);
234 1 : CATCH_REQUIRE(root->attribute("quite") == "quite");
235 1 : CATCH_REQUIRE(root->attribute("size") == "123");
236 1 : CATCH_REQUIRE(root->attribute("very") == "true");
237 1 : CATCH_REQUIRE(root->parent() == nullptr);
238 1 : CATCH_REQUIRE(root->first_child() == nullptr);
239 1 : CATCH_REQUIRE(root->last_child() == nullptr);
240 1 : CATCH_REQUIRE(root->next() == nullptr);
241 1 : CATCH_REQUIRE(root->previous() == nullptr);
242 : }
243 : CATCH_END_SECTION()
244 :
245 14 : CATCH_START_SECTION("entities test")
246 : {
247 2 : std::string const xml;
248 :
249 2 : std::string const xml_path(get_folder_name());
250 2 : std::string const filename(xml_path + "/entities.xml");
251 :
252 : // create file with one empty tag
253 : {
254 2 : std::ofstream f;
255 1 : f.open(filename);
256 1 : CATCH_REQUIRE(f.is_open());
257 1 : f << "<!--\n"
258 : "name='entities'\n"
259 : "-->\n"
260 : "\n"
261 : "<?xml version=\"1.0\"?>\n"
262 : "<entity-a-gogo quite=\"quite\" size='123'"
263 : " very=\""true"\" special-entry=\""<it's special & weird>"\">"
264 : "</entity-a-gogo>";
265 : }
266 :
267 2 : snapdatabase::xml x(filename);
268 2 : snapdatabase::xml_node::pointer_t root(x.root());
269 1 : CATCH_REQUIRE(root != nullptr);
270 1 : CATCH_REQUIRE(root->tag_name() == "entity-a-gogo");
271 1 : CATCH_REQUIRE(root->all_attributes().size() == 4);
272 1 : CATCH_REQUIRE(root->attribute("quite") == "quite");
273 1 : CATCH_REQUIRE(root->attribute("size") == "123");
274 1 : CATCH_REQUIRE(root->attribute("very") == "\"true\"");
275 1 : CATCH_REQUIRE(root->attribute("special-entry") == "\"<it's special & weird>\"");
276 1 : CATCH_REQUIRE(root->parent() == nullptr);
277 1 : CATCH_REQUIRE(root->first_child() == nullptr);
278 1 : CATCH_REQUIRE(root->last_child() == nullptr);
279 1 : CATCH_REQUIRE(root->next() == nullptr);
280 1 : CATCH_REQUIRE(root->previous() == nullptr);
281 : }
282 : CATCH_END_SECTION()
283 7 : }
284 :
285 :
286 3 : CATCH_TEST_CASE("XML Tree", "[xml]")
287 : {
288 2 : CATCH_START_SECTION("tree")
289 : {
290 2 : std::string const xml;
291 :
292 2 : std::string const xml_path(get_folder_name());
293 2 : std::string const filename(xml_path + "/tree.xml");
294 :
295 : // create empty file
296 : {
297 2 : std::ofstream f;
298 1 : f.open(filename);
299 1 : CATCH_REQUIRE(f.is_open());
300 : f << "<root>"
301 : << "<parent>"
302 : << "<child>DATA 1</child>"
303 : << "<child>DATA 2</child>"
304 : << "<child>DATA 3</child>"
305 : << "</parent>"
306 1 : << "</root>";
307 : }
308 :
309 2 : snapdatabase::xml x(filename);
310 2 : snapdatabase::xml_node::pointer_t root(x.root());
311 1 : CATCH_REQUIRE(root != nullptr);
312 1 : CATCH_REQUIRE(root->parent() == nullptr);
313 2 : snapdatabase::xml_node::pointer_t parent_node(root->first_child());
314 1 : CATCH_REQUIRE(parent_node != nullptr);
315 1 : CATCH_REQUIRE(root->last_child() == parent_node);
316 1 : CATCH_REQUIRE(root->next() == nullptr);
317 1 : CATCH_REQUIRE(root->previous() == nullptr);
318 :
319 1 : CATCH_REQUIRE(parent_node->parent() == root);
320 2 : snapdatabase::xml_node::pointer_t child1_node(parent_node->first_child());
321 2 : snapdatabase::xml_node::pointer_t child2_node(child1_node->next());
322 2 : snapdatabase::xml_node::pointer_t child3_node(child2_node->next());
323 1 : CATCH_REQUIRE(child1_node != nullptr);
324 1 : CATCH_REQUIRE(child2_node != nullptr);
325 1 : CATCH_REQUIRE(child3_node != nullptr);
326 1 : CATCH_REQUIRE(parent_node->last_child() == child3_node);
327 1 : CATCH_REQUIRE(parent_node->next() == nullptr);
328 1 : CATCH_REQUIRE(parent_node->previous() == nullptr);
329 :
330 1 : CATCH_REQUIRE(child1_node->parent() == parent_node);
331 1 : CATCH_REQUIRE(child2_node->parent() == parent_node);
332 1 : CATCH_REQUIRE(child3_node->parent() == parent_node);
333 :
334 1 : CATCH_REQUIRE(child1_node->first_child() == nullptr);
335 1 : CATCH_REQUIRE(child2_node->first_child() == nullptr);
336 1 : CATCH_REQUIRE(child3_node->first_child() == nullptr);
337 :
338 1 : CATCH_REQUIRE(child1_node->last_child() == nullptr);
339 1 : CATCH_REQUIRE(child2_node->last_child() == nullptr);
340 1 : CATCH_REQUIRE(child3_node->last_child() == nullptr);
341 :
342 1 : CATCH_REQUIRE(child1_node->text() == "DATA 1");
343 1 : CATCH_REQUIRE(child2_node->text() == "DATA 2");
344 1 : CATCH_REQUIRE(child3_node->text() == "DATA 3");
345 :
346 1 : CATCH_REQUIRE(child1_node->next() == child2_node);
347 1 : CATCH_REQUIRE(child2_node->previous() == child1_node);
348 :
349 1 : CATCH_REQUIRE(child2_node->next() == child3_node);
350 1 : CATCH_REQUIRE(child3_node->previous() == child2_node);
351 :
352 1 : CATCH_REQUIRE(child3_node->next() == nullptr);
353 1 : CATCH_REQUIRE(child1_node->previous() == nullptr);
354 : }
355 : CATCH_END_SECTION()
356 7 : }
357 :
358 :
359 :
360 : // vim: ts=4 sw=4 et
|