Use XMLNode::set_property API in xml tests
[ardour.git] / libs / pbd / test / xml_test.cc
1 #include "xml_test.h"
2
3 #include <glib.h>
4 #include "pbd/gstdio_compat.h"
5 #include "pbd/xml++.h"
6
7 #include <stdint.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10
11 #ifdef PLATFORM_WINDOWS
12 #include <fcntl.h>
13 #endif
14
15 #include <sstream>
16
17 #include <glibmm/miscutils.h>
18 #include <glibmm/fileutils.h>
19 #include <glibmm/convert.h>
20
21 #include <libxml/xpath.h>
22
23 #include "pbd/file_utils.h"
24 #include "pbd/timing.h"
25
26 #include "test_common.h"
27
28 using namespace std;
29 using namespace PBD;
30
31 CPPUNIT_TEST_SUITE_REGISTRATION (XMLTest);
32
33 namespace {
34
35 xmlChar* xml_version = xmlCharStrdup("1.0");
36
37 bool
38 write_xml(const string& filename)
39 {
40         xmlDocPtr doc;
41         int result;
42
43         xmlKeepBlanksDefault(0);
44         doc = xmlNewDoc(xml_version);
45
46         result = xmlSaveFormatFileEnc(filename.c_str(), doc, "UTF-8", 1);
47
48         xmlFreeDoc(doc);
49
50         if (result == -1) {
51                 return false;
52         }
53         return true;
54 }
55
56 }
57
58 void
59 XMLTest::testXMLFilenameEncoding ()
60 {
61         vector<string> i18n_files;
62
63         Searchpath i18n_path(test_search_path());
64         i18n_path.add_subdirectory_to_paths("i18n_test");
65
66         PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst");
67
68         CPPUNIT_ASSERT (i18n_files.size() == 8);
69
70         string output_dir = test_output_directory ("XMLFilenameEncodingUTF8");
71
72         // This is testing that libxml expects the filename encoding to be utf-8
73         // on Windows and that writing the xml files should be successful for all
74         // the filenames in the test data set but it should also work for other
75         // platforms as well
76         for (vector<string>::iterator i = i18n_files.begin (); i != i18n_files.end ();
77              ++i) {
78                 string input_path = *i;
79                 string output_filename = Glib::path_get_basename (input_path);
80                 string output_path = Glib::build_filename (output_dir, output_filename);
81
82                 CPPUNIT_ASSERT (write_xml (output_path));
83         }
84 }
85
86
87 static const char * const root_node_name = "Session";
88 static const char * const child_node_name = "Child";
89 static const char * const grandchild_node_name = "GrandChild";
90 static const char * const great_grandchild_node_name = "GreatGrandChild";
91
92 std::vector<std::pair<std::string, std::string> >
93 get_test_properties ()
94 {
95         std::vector<std::pair<std::string, std::string> > props;
96
97         props.push_back (std::make_pair ("id", "1234567890"));
98         props.push_back (std::make_pair ("name", "Awesome Name"));
99         props.push_back (std::make_pair ("type", "Human"));
100         props.push_back (std::make_pair ("flags", "MustExist,IsGodLike,HasFabulousHair"));
101         props.push_back (std::make_pair ("muted", "no"));
102         props.push_back (std::make_pair ("opaque", "yes"));
103         props.push_back (std::make_pair ("locked", "false"));
104         props.push_back (std::make_pair ("automatic", "true"));
105         props.push_back (std::make_pair ("whole-file", "yes"));
106         props.push_back (std::make_pair ("external", "false"));
107         props.push_back (std::make_pair ("hidden", "no"));
108         props.push_back (std::make_pair ("start", "123456789098"));
109         props.push_back (std::make_pair ("length", "123456789"));
110         props.push_back (std::make_pair ("stretch", "1"));
111         props.push_back (std::make_pair ("shift", "1"));
112         props.push_back (std::make_pair ("master-source-0", "12345"));
113         props.push_back (std::make_pair ("master-source-1", "54321"));
114         props.push_back (std::make_pair ("source-0", "123"));
115         props.push_back (std::make_pair ("source-1", "321"));
116         props.push_back (std::make_pair ("default-fade-in", "yes"));
117         props.push_back (std::make_pair ("default-fade-out", "no"));
118         props.push_back (std::make_pair ("fade-in-active", "no"));
119         props.push_back (std::make_pair ("fade-out-active", "yes"));
120         props.push_back (std::make_pair ("channels", "2"));
121         props.push_back (std::make_pair ("beat", "0"));
122         props.push_back (std::make_pair ("pulse", "1.3333333"));
123         props.push_back (std::make_pair ("sync-position", "4321"));
124         props.push_back (std::make_pair ("ancestral-start", "987654321"));
125         props.push_back (std::make_pair ("ancestral-length", "12345678"));
126
127         return props;
128 }
129
130 static const std::vector<std::pair<std::string, std::string> > properties = get_test_properties ();
131
132 static
133 std::string
134 get_event_content (uint32_t lines)
135 {
136         stringstream sstr;
137         sstr.precision (17);
138         for (uint32_t i = 0; i < lines; ++i) {
139                 sstr << 0.12345678901234567;
140                 sstr << ' ';
141                 sstr << -0.9876543210987654;
142                 sstr << '\n';
143         }
144         return sstr.str();
145 }
146
147 struct NodeOptions
148 {
149
150         NodeOptions (const std::string& p_node_name, uint32_t p_node_count,
151                      uint32_t p_node_property_count, const std::string& p_node_content = std::string())
152             : node_name (p_node_name)
153             , node_count (p_node_count)
154             , node_property_count (p_node_property_count)
155             , node_content (p_node_content)
156         {
157         }
158
159         std::string node_name;
160         uint32_t node_count;
161         uint32_t node_property_count;
162         std::string node_content;
163 };
164
165 bool
166 create_child_nodes (XMLNode& parent_node, std::vector<NodeOptions>::iterator begin,
167                     std::vector<NodeOptions>::iterator end)
168 {
169         if (begin == end) return true;
170
171         NodeOptions options = *begin;
172
173         std::vector<NodeOptions>::iterator child_node_iter = ++begin;
174
175         for (uint32_t node_count = 0; node_count < options.node_count; ++node_count) {
176
177                 XMLNode* new_node = new XMLNode (options.node_name);
178
179                 if (!new_node) {
180                         return false;
181                 }
182
183                 for (uint32_t prop_count = 0; prop_count < options.node_property_count; ++prop_count) {
184                         new_node->set_property (properties[prop_count].first.c_str (), properties[prop_count].second);
185                 }
186
187                 if (!options.node_content.empty()) {
188                         XMLNode* content_node = new XMLNode ("");
189                         if (!content_node) {
190                                 return false;
191                         }
192                         content_node->set_content (options.node_content);
193                         new_node->add_child_nocopy (*content_node);
194                 }
195
196                 create_child_nodes (*new_node, child_node_iter, end);
197
198                 // Terrible API "design"
199                 parent_node.add_child_nocopy (*new_node);
200         }
201         return true;
202 }
203
204
205 bool
206 create_xml_doc (XMLTree& xml_doc, std::vector<NodeOptions>& options)
207 {
208         XMLNode* root = new XMLNode (root_node_name);
209
210         if (!root) return false;
211
212         xml_doc.set_root (root);
213
214         return create_child_nodes (*xml_doc.root(), options.begin(), options.end());
215 }
216
217 static const uint32_t test_iterations = 10;
218
219 void
220 test_xml_document (const std::string& test_name,
221                    std::vector<NodeOptions>& node_options)
222 {
223         const string test_output_dir = test_output_directory (test_name);
224
225         const std::string output_file_basename = Glib::build_filename (test_output_dir, test_name);
226
227         TimingData create_timing_data, write_timing_data, read_timing_data;
228
229         for (uint32_t iter = 0; iter < test_iterations; ++iter) {
230
231                 char buf[16];
232                 snprintf (buf, sizeof(buf), "%d", iter);
233
234                 const std::string output_file_path = output_file_basename + buf + ".xml";
235
236                 create_timing_data.start_timing ();
237
238                 XMLTree test_xml;
239
240                 CPPUNIT_ASSERT (create_xml_doc (test_xml, node_options));
241
242                 create_timing_data.add_elapsed ();
243
244                 write_timing_data.start_timing ();
245
246                 test_xml.write (output_file_path);
247
248                 write_timing_data.add_elapsed ();
249
250                 read_timing_data.start_timing ();
251
252                 PBD::Timing read_timing;
253
254                 XMLTree read_doc (output_file_path);
255
256                 read_timing_data.add_elapsed ();
257
258                 // check that what we have read is identical to what was written
259                 CPPUNIT_ASSERT (*read_doc.root() == *test_xml.root());
260
261                 // These files are too big to keep around
262                 CPPUNIT_ASSERT (g_remove (output_file_path.c_str ()) == 0);
263         }
264
265         std::cerr << std::endl;
266         std::cerr << "   Create : " << create_timing_data.summary ();
267         std::cerr << "   Write : " << write_timing_data.summary ();
268         std::cerr << "   Read : " << read_timing_data.summary ();
269 }
270
271 void
272 XMLTest::testPerfSmallXMLDocument ()
273 {
274         std::vector<NodeOptions> node_options;
275
276         // A config like file with nodes with properties with name/value pairs
277         node_options.push_back (NodeOptions (child_node_name, 256, 2));
278
279         test_xml_document ("testPerfSmallXMLDocument", node_options);
280 }
281
282 void
283 XMLTest::testPerfMediumXMLDocument ()
284 {
285         std::vector<NodeOptions> node_options;
286
287         // A normal Session like size file
288         node_options.push_back (NodeOptions (child_node_name, 32, 2));
289         node_options.push_back (NodeOptions (grandchild_node_name, 32, 16, get_event_content (16)));
290         node_options.push_back (NodeOptions (great_grandchild_node_name, 8, 8));
291
292         test_xml_document ("testPerfMediumXMLDocument", node_options);
293 }
294
295 void
296 XMLTest::testPerfLargeXMLDocument ()
297 {
298         std::vector<NodeOptions> node_options;
299
300         // A large Session like size file
301         node_options.push_back (NodeOptions (child_node_name, 32, 2));
302         node_options.push_back (NodeOptions (grandchild_node_name, 128, 16, get_event_content (32)));
303         node_options.push_back (NodeOptions (great_grandchild_node_name, 16, 8));
304
305         test_xml_document ("testPerfLargeXMLDocument", node_options);
306 }