1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2002-2005 Marcin Kalicinski
3 //
4 // Distributed under the Boost Software License, Version 1.0.
5 // (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // For more information, see www.boost.org
9 // ----------------------------------------------------------------------------
10 #ifndef BOOST_PROPERTY_TREE_TEST_UTILS_INCLUDED
11 #define BOOST_PROPERTY_TREE_TEST_UTILS_INCLUDED
12 
13 #define BOOST_PROPERTY_TREE_DEBUG               // Enable ptree debugging
14 #include <boost/property_tree/ptree.hpp>
15 
16 // Do not deprecate insecure CRT calls on VC8
17 #if (defined(BOOST_MSVC) && (BOOST_MSVC >= 1400) && !defined(_CRT_SECURE_NO_DEPRECATE))
18 #   define _CRT_SECURE_NO_DEPRECATE
19 #endif
20 
21 #include <boost/test/minimal.hpp>
22 #include <boost/property_tree/detail/ptree_utils.hpp>
23 #include <fstream>
24 #include <cstring>
25 #include <sstream>
26 
27 template<class Ptree>
total_size(const Ptree & pt)28 typename Ptree::size_type total_size(const Ptree &pt)
29 {
30     typename Ptree::size_type size = 1;
31     for (typename Ptree::const_iterator it = pt.begin(); it != pt.end(); ++it)
32         size += total_size(it->second);
33     return size;
34 }
35 
36 template<class Ptree>
total_keys_size(const Ptree & pt)37 typename Ptree::size_type total_keys_size(const Ptree &pt)
38 {
39     typename Ptree::size_type size = 0;
40     for (typename Ptree::const_iterator it = pt.begin(); it != pt.end(); ++it)
41     {
42         size += it->first.size();
43         size += total_keys_size(it->second);
44     }
45     return size;
46 }
47 
48 template<class Ptree>
total_data_size(const Ptree & pt)49 typename Ptree::size_type total_data_size(const Ptree &pt)
50 {
51     typename Ptree::size_type size = pt.data().size();
52     for (typename Ptree::const_iterator it = pt.begin(); it != pt.end(); ++it)
53         size += total_data_size(it->second);
54     return size;
55 }
56 
57 class test_file
58 {
59 public:
test_file(const char * test_data,const char * filename)60     test_file(const char *test_data, const char *filename)
61     {
62         if (test_data && filename)
63         {
64             name = filename;
65             std::ofstream stream(name.c_str());
66             using namespace std;
67             stream.write(test_data, strlen(test_data));
68             BOOST_CHECK(stream.good());
69         }
70     }
~test_file()71     ~test_file()
72     {
73         if (!name.empty())
74             remove(name.c_str());
75     }
76 private:
77     std::string name;
78 };
79 
80 template<class Ptree>
get_test_ptree()81 Ptree get_test_ptree()
82 {
83     using namespace boost::property_tree;
84     typedef typename Ptree::key_type Str;
85     Ptree pt;
86     pt.put_value(detail::widen<Str>("data0"));
87     pt.put(detail::widen<Str>("key1"), detail::widen<Str>("data1"));
88     pt.put(detail::widen<Str>("key1.key"), detail::widen<Str>("data2"));
89     pt.put(detail::widen<Str>("key2"), detail::widen<Str>("data3"));
90     pt.put(detail::widen<Str>("key2.key"), detail::widen<Str>("data4"));
91     return pt;
92 }
93 
94 // Generic test for file parser
95 template<class Ptree, class ReadFunc, class WriteFunc>
generic_parser_test(Ptree & pt,ReadFunc rf,WriteFunc wf,const char * test_data_1,const char * test_data_2,const char * filename_1,const char * filename_2,const char * filename_out)96 void generic_parser_test(Ptree &pt,
97                          ReadFunc rf,
98                          WriteFunc wf,
99                          const char *test_data_1,
100                          const char *test_data_2,
101                          const char *filename_1,
102                          const char *filename_2,
103                          const char *filename_out)
104 {
105 
106     using namespace boost::property_tree;
107 
108     // Create test files
109     test_file file_1(test_data_1, filename_1);
110     test_file file_2(test_data_2, filename_2);
111     test_file file_out("", filename_out);
112 
113     rf(filename_1, pt);        // Read file
114     wf(filename_out, pt);      // Write file
115     Ptree pt2;
116     rf(filename_out, pt2);     // Read file again
117 
118     // Compare original with read
119     BOOST_CHECK(pt == pt2);
120 
121 }
122 
123 // Generic test for file parser with expected success
124 template<class Ptree, class ReadFunc, class WriteFunc>
generic_parser_test_ok(ReadFunc rf,WriteFunc wf,const char * test_data_1,const char * test_data_2,const char * filename_1,const char * filename_2,const char * filename_out,unsigned int total_size,unsigned int total_data_size,unsigned int total_keys_size)125 void generic_parser_test_ok(ReadFunc rf,
126                             WriteFunc wf,
127                             const char *test_data_1,
128                             const char *test_data_2,
129                             const char *filename_1,
130                             const char *filename_2,
131                             const char *filename_out,
132                             unsigned int total_size,
133                             unsigned int total_data_size,
134                             unsigned int total_keys_size)
135 {
136 
137     using namespace boost::property_tree;
138 
139     std::cerr << "(progress) Starting generic parser test with test file \"" << filename_1 << "\"\n";
140 
141     // Make sure no instances exist
142     //BOOST_CHECK(Ptree::debug_get_instances_count() == 0);
143 
144     try
145     {
146 
147         // Read file
148         Ptree pt;
149         generic_parser_test<Ptree, ReadFunc, WriteFunc>(pt, rf, wf,
150                                                         test_data_1, test_data_2,
151                                                         filename_1, filename_2, filename_out);
152 
153         // Determine total sizes
154         typename Ptree::size_type actual_total_size = ::total_size(pt);
155         typename Ptree::size_type actual_data_size = ::total_data_size(pt);
156         typename Ptree::size_type actual_keys_size = ::total_keys_size(pt);
157         if (actual_total_size != total_size ||
158             actual_data_size != total_data_size ||
159             actual_keys_size != total_keys_size)
160             std::cerr << "Sizes: " << (unsigned)::total_size(pt) << ", " << (unsigned)::total_data_size(pt) << ", " << (unsigned)::total_keys_size(pt) << "\n";
161 
162         // Check total sizes
163         BOOST_CHECK(actual_total_size == total_size);
164         BOOST_CHECK(actual_data_size == total_data_size);
165         BOOST_CHECK(actual_keys_size == total_keys_size);
166 
167     }
168     catch (std::runtime_error &e)
169     {
170         BOOST_ERROR(e.what());
171     }
172 
173     // Test for leaks
174     //BOOST_CHECK(Ptree::debug_get_instances_count() == 0);
175 
176 }
177 
178 // Generic test for file parser with expected error
179 template<class Ptree, class ReadFunc, class WriteFunc, class Error>
generic_parser_test_error(ReadFunc rf,WriteFunc wf,const char * test_data_1,const char * test_data_2,const char * filename_1,const char * filename_2,const char * filename_out,unsigned long expected_error_line)180 void generic_parser_test_error(ReadFunc rf,
181                                WriteFunc wf,
182                                const char *test_data_1,
183                                const char *test_data_2,
184                                const char *filename_1,
185                                const char *filename_2,
186                                const char *filename_out,
187                                unsigned long expected_error_line)
188 {
189 
190     std::cerr << "(progress) Starting generic parser test with test file \"" << filename_1 << "\"\n";
191 
192     // Make sure no instances exist
193     //BOOST_CHECK(Ptree::debug_get_instances_count() == 0);
194 
195     {
196 
197         // Create ptree as a copy of test ptree (to test if read failure does not damage ptree)
198         Ptree pt(get_test_ptree<Ptree>());
199         try
200         {
201             generic_parser_test<Ptree, ReadFunc, WriteFunc>(pt, rf, wf,
202                                                             test_data_1, test_data_2,
203                                                             filename_1, filename_2, filename_out);
204             BOOST_ERROR("No required exception thrown");
205         }
206         catch (Error &e)
207         {
208             BOOST_CHECK(e.line() == expected_error_line);           // Test line number
209             BOOST_CHECK(pt == get_test_ptree<Ptree>());             // Test if error damaged contents
210         }
211         catch (...)
212         {
213             BOOST_ERROR("Invalid exception type thrown");
214             throw;
215         }
216 
217     }
218 
219     // Test for leaks
220     //BOOST_CHECK(Ptree::debug_get_instances_count() == 0);
221 
222 }
223 
224 template <typename Ch> std::basic_ostream<Ch>& errstream();
225 template <> inline
errstream()226 std::basic_ostream<char>& errstream() { return std::cerr; }
227 #ifndef BOOST_NO_CWCHAR
228 template <> inline
errstream()229 std::basic_ostream<wchar_t>& errstream() { return std::wcerr; }
230 #endif
231 
232 template <class Ptree, class ReadFunc, class WriteFunc>
check_exact_roundtrip(ReadFunc rf,WriteFunc wf,const char * test_data)233 void check_exact_roundtrip(ReadFunc rf, WriteFunc wf, const char *test_data) {
234     std::cerr << "(progress) Starting exact roundtrip test with test data:\n"
235               << test_data << "\n-----\n";
236     using namespace boost::property_tree;
237     typedef typename Ptree::key_type::value_type Ch;
238     typedef typename Ptree::key_type Str;
239     Str native_test_data = detail::widen<Str>(test_data);
240 
241     std::basic_istringstream<Ch> in_stream(native_test_data);
242     std::basic_ostringstream<Ch> out_stream;
243     Ptree tree;
244     rf(in_stream, tree);
245     wf(out_stream, tree);
246     std::cerr << "(progress) Roundtripped data:\n";
247     errstream<Ch>() << out_stream.str();
248     std::cerr << "\n-----\n";
249     BOOST_CHECK(native_test_data == out_stream.str());
250 }
251 
252 #endif
253