• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  (C) Copyright John Maddock 2006-7.
2 //  Use, modification and distribution are subject to the
3 //  Boost Software License, Version 1.0. (See accompanying file
4 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef BOOST_MATH_HANDLE_TEST_RESULT
7 #define BOOST_MATH_HANDLE_TEST_RESULT
8 
9 #include <boost/math/tools/stats.hpp>
10 #include <boost/math/tools/precision.hpp>
11 #include <boost/lexical_cast.hpp>
12 #include <boost/regex.hpp>
13 #include <boost/test/test_tools.hpp>
14 #include <boost/filesystem.hpp>
15 #include <boost/filesystem/fstream.hpp>
16 #include <boost/interprocess/sync/named_mutex.hpp>
17 #include <boost/interprocess/sync/scoped_lock.hpp>
18 #include <boost/math/special_functions/fpclassify.hpp>
19 #include <iostream>
20 #include <iomanip>
21 #include <vector>
22 #include <set>
23 
24 #include <boost/math/tools/test.hpp>
25 
sanitize_string(const std::string & s)26 inline std::string sanitize_string(const std::string& s)
27 {
28    static const boost::regex e("[^a-zA-Z0-9]+");
29    return boost::regex_replace(s, e, "_");
30 }
31 
32 static std::string content;
33 boost::filesystem::path path_to_content;
34 
35 struct content_loader
36 {
37    boost::interprocess::named_mutex mu;
38    boost::interprocess::scoped_lock<boost::interprocess::named_mutex> lock;
content_loadercontent_loader39    content_loader() : mu(boost::interprocess::open_or_create, "handle_test_result"), lock(mu)
40    {
41       boost::filesystem::path p(__FILE__);
42       p = p.parent_path();
43       p /= "doc";
44       p /= "accuracy_tables.qbk";
45       path_to_content = p;
46       if(boost::filesystem::exists(p))
47       {
48          boost::filesystem::ifstream is(p);
49          if(is.good())
50          {
51             do
52             {
53                char c = static_cast<char>(is.get());
54                if(c != EOF)
55                   content.append(1, c);
56             } while(is.good());
57          }
58       }
59    }
~content_loadercontent_loader60    ~content_loader()
61    {
62       boost::filesystem::ofstream os(path_to_content);
63       os << content;
64    }
instantiatecontent_loader65    void instantiate()const
66    {
67    }
68 };
69 
70 static const content_loader loader;
71 
load_table(std::vector<std::vector<std::string>> & table,std::string::const_iterator begin,std::string::const_iterator end)72 void load_table(std::vector<std::vector<std::string> >& table, std::string::const_iterator begin, std::string::const_iterator end)
73 {
74    static const boost::regex item_e(
75       "\\["
76       "([^\\[\\]]*(?0)?)*"
77       "\\]"
78       );
79 
80    boost::regex_token_iterator<std::string::const_iterator> i(begin, end, item_e), j;
81 
82    while(i != j)
83    {
84       // Add a row:
85       table.push_back(std::vector<std::string>());
86       boost::regex_token_iterator<std::string::const_iterator> k(i->first + 1, i->second - 1, item_e);
87       while(k != j)
88       {
89          // Add a cell:
90          table.back().push_back(std::string(k->first + 1, k->second - 1));
91          ++k;
92       }
93       ++i;
94    }
95 }
96 
save_table(std::vector<std::vector<std::string>> & table)97 std::string save_table(std::vector<std::vector<std::string> >& table)
98 {
99    std::string result;
100 
101    for(std::vector<std::vector<std::string> >::const_iterator i = table.begin(), j = table.end(); i != j; ++i)
102    {
103       result += "[";
104       for(std::vector<std::string>::const_iterator k = i->begin(), l = i->end(); k != l; ++k)
105       {
106          result += "[";
107          result += *k;
108          result += "]";
109       }
110       result += "]\n";
111    }
112    return result;
113 }
114 
add_to_all_sections(const std::string & id,std::string list_name="all_sections")115 void add_to_all_sections(const std::string& id, std::string list_name = "all_sections")
116 {
117    std::string::size_type pos = content.find("[template " + list_name + "[]"), end_pos;
118    if(pos == std::string::npos)
119    {
120       //
121       // Just append to the end:
122       //
123       content.append("\n[template ").append(list_name).append("[]\n[").append(id).append("]\n]\n");
124    }
125    else
126    {
127       //
128       // Read in the all list of sections, add our new one (in alphabetical order),
129       // and then rewrite the whole thing:
130       //
131       static const boost::regex item_e(
132          "\\["
133          "([^\\[\\]]*(?0)?)*"
134          "\\]|\\]"
135          );
136       boost::regex_token_iterator<std::string::const_iterator> i(content.begin() + pos + 12 + list_name.size(), content.end(), item_e), j;
137       std::set<std::string> sections;
138       while(i != j)
139       {
140          if(i->length() == 1)
141          {
142             end_pos = i->first - content.begin();
143             break;
144          }
145          sections.insert(std::string(i->first + 1, i->second - 1));
146          ++i;
147       }
148       sections.insert(id);
149       std::string new_list = "\n";
150       for(std::set<std::string>::const_iterator sec = sections.begin(); sec != sections.end(); ++sec)
151       {
152          new_list += "[" + *sec + "]\n";
153       }
154       content.replace(pos + 12 + list_name.size(), end_pos - pos - 12 - list_name.size(), new_list);
155    }
156 }
157 
add_cell(const std::string & cell_name,const std::string & table_name,const std::string & row_name,const std::string & type_name)158 void add_cell(const std::string& cell_name, const std::string& table_name, const std::string& row_name, const std::string& type_name)
159 {
160    //
161    // Load the table, add our data, and re-write:
162    //
163    std::string table_id = "table_" + sanitize_string(table_name);
164    std::string column_heading = BOOST_COMPILER;
165    column_heading += "[br]";
166    column_heading += BOOST_PLATFORM;
167    column_heading += "[br]";
168    column_heading += type_name;
169    boost::regex table_e("\\[table:" + table_id
170       + "\\s[^\\[]+"
171          "((\\["
172             "([^\\[\\]]*(?2)?)*"
173          "\\]\\s*)*\\s*)"
174        "\\]"
175        );
176 
177    boost::smatch table_location;
178    if(regex_search(content, table_location, table_e))
179    {
180       std::vector<std::vector<std::string> > table_data;
181       load_table(table_data, table_location[1].first, table_location[1].second);
182       //
183       // Figure out which column we're on:
184       //
185       unsigned column_id = 1001u;
186       for(unsigned i = 0; i < table_data[0].size(); ++i)
187       {
188          if(table_data[0][i] == column_heading)
189          {
190             column_id = i;
191             break;
192          }
193       }
194       if(column_id > 1000)
195       {
196          //
197          // Need a new column, must be adding a new compiler to the table!
198          //
199          table_data[0].push_back(column_heading);
200          for(unsigned i = 1; i < table_data.size(); ++i)
201             table_data[i].push_back(std::string());
202          column_id = table_data[0].size() - 1;
203       }
204       //
205       // Figure out the row:
206       //
207       unsigned row_id = 1001;
208       for(unsigned i = 1; i < table_data.size(); ++i)
209       {
210          if(table_data[i][0] == row_name)
211          {
212             row_id = i;
213             break;
214          }
215       }
216       if(row_id > 1000)
217       {
218          //
219          // Need a new row, add it now:
220          //
221          table_data.push_back(std::vector<std::string>());
222          table_data.back().push_back(row_name);
223          for(unsigned i = 1; i < table_data[0].size(); ++i)
224             table_data.back().push_back(std::string());
225          row_id = table_data.size() - 1;
226       }
227       //
228       // Update the entry:
229       //
230       std::string& s = table_data[row_id][column_id];
231       if(s.empty())
232       {
233          std::cout << "Adding " << cell_name << " to empty cell.";
234          s = "[" + cell_name + "]";
235       }
236       else
237       {
238          if(cell_name.find("_boost_") != std::string::npos)
239          {
240             std::cout << "Adding " << cell_name << " to start of cell.";
241             s.insert(0, "[" + cell_name + "][br][br]");
242          }
243          else
244          {
245             std::cout << "Adding " << cell_name << " to end of cell.";
246             if((s.find("_boost_") != std::string::npos) && (s.find("[br]") == std::string::npos))
247                s += "[br]"; // extra break if we're adding directly after the boost results.
248             s += "[br][" + cell_name + "]";
249          }
250       }
251       //
252       // Convert back to a string and insert into content:
253       std::string c = save_table(table_data);
254       content.replace(table_location.position(1), table_location.length(1), c);
255    }
256    else
257    {
258       //
259       // Create a new table and try again:
260       //
261       std::string new_table = "\n[template " + table_id;
262       new_table += "[]\n[table:" + table_id;
263       new_table += " Error rates for ";
264       new_table += table_name;
265       new_table += "\n[[][";
266       new_table += column_heading;
267       new_table += "]]\n";
268       new_table += "[[";
269       new_table += row_name;
270       new_table += "][[";
271       new_table += cell_name;
272       new_table += "]]]\n]\n]\n";
273 
274       std::string::size_type pos = content.find("[/tables:]");
275       if(pos != std::string::npos)
276          content.insert(pos + 10, new_table);
277       else
278          content += "\n\n[/tables:]\n" + new_table;
279       //
280       // Add a section for this table as well:
281       //
282       std::string section_id = "section_" + sanitize_string(table_name);
283       if(content.find(section_id + "[]") == std::string::npos)
284       {
285          std::string new_section = "\n[template " + section_id + "[]\n[section:" + section_id + " " + table_name + "]\n[" + table_id + "]\n[endsect]\n]\n";
286          pos = content.find("[/sections:]");
287          if(pos != std::string::npos)
288             content.insert(pos + 12, new_section);
289          else
290             content += "\n\n[/sections:]\n" + new_section;
291          add_to_all_sections(section_id);
292       }
293       //
294       // Add to list of all tables (not in sections):
295       //
296       add_to_all_sections(table_id, "all_tables");
297    }
298 }
299 
set_result(const std::string & cell_name,const std::string & cell_content,const std::string & table_name,const std::string & row_name,const std::string & type_name)300 void set_result(const std::string& cell_name, const std::string& cell_content, const std::string& table_name, const std::string& row_name, const std::string& type_name)
301 {
302    loader.instantiate();
303    const boost::regex e("\\[template\\s+" + cell_name +
304       "\\[\\]([^\\n]*)\\]$");
305 
306    boost::smatch what;
307    if(regex_search(content, what, e))
308    {
309       content.replace(what.position(1), what.length(1), cell_content);
310    }
311    else
312    {
313       // Need to add new content:
314       std::string::size_type pos = content.find("[/Cell Content:]");
315       std::string t = "\n[template " + cell_name + "[] " + cell_content + "]";
316       if(pos != std::string::npos)
317          content.insert(pos + 16, t);
318       else
319       {
320          content.insert(0, t);
321          content.insert(0, "[/Cell Content:]");
322       }
323    }
324    //
325    // Check to verify that our content is actually used somewhere,
326    // if not we need to create a place for it:
327    //
328    if(content.find("[" + cell_name + "]") == std::string::npos)
329       add_cell(cell_name, table_name, row_name, type_name);
330 }
331 
set_error_content(const std::string & id,const std::string & error_s)332 void set_error_content(const std::string& id, const std::string& error_s)
333 {
334    boost::regex content_e("\\[template\\s+" + id +
335       "\\[\\]\\s+"
336       "("
337          "[^\\]\\[]*"
338          "(?:"
339             "\\["
340             "([^\\[\\]]*(?2)?)*"
341             "\\]"
342             "[^\\]\\[]*"
343          ")*"
344 
345        ")"
346        "\\]");
347    boost::smatch what;
348    if(regex_search(content, what, content_e))
349    {
350       // replace existing content:
351       content.replace(what.position(1), what.length(1), error_s);
352    }
353    else
354    {
355       // add new content:
356       std::string::size_type pos = content.find("[/error_content:]");
357       if(pos != std::string::npos)
358       {
359          content.insert(pos + 17, "\n[template " + id + "[]\n" + error_s + "\n]\n");
360       }
361       else
362          content.append("\n[/error_content:]\n[template " + id + "[]\n" + error_s + "\n]\n");
363    }
364    //
365    // Add to all_errors if not already there:
366    //
367    if(content.find("[" + id + "]") == std::string::npos)
368    {
369       // Find all_errors template:
370       std::string::size_type pos = content.find("[template all_errors[]\n");
371       if(pos != std::string::npos)
372       {
373          content.insert(pos + 23, "[" + id + "]\n");
374       }
375       else
376       {
377          content.append("\n[template all_errors[]\n[").append(id).append("]\n]\n");
378       }
379    }
380 }
381 
remove_error_content(const std::string & error_id)382 void remove_error_content(const std::string& error_id)
383 {
384    // remove use template first:
385    std::string::size_type pos = content.find("[" + error_id + "]");
386    if(pos != std::string::npos)
387    {
388       content.erase(pos, 2 + error_id.size());
389    }
390    // then the template define itself:
391    boost::regex content_e("\\[template\\s+" + error_id +
392       "\\[\\]\\s+"
393       "("
394          "[^\\]\\[]*"
395          "(?:"
396             "\\["
397             "([^\\[\\]]*(?2)?)*"
398             "\\]"
399             "[^\\]\\[]*"
400          ")*"
401       ")"
402       "\\]");
403    boost::smatch what;
404    if(regex_search(content, what, content_e))
405    {
406       content.erase(what.position(), what.length());
407    }
408 }
409 
410 template <class T, class Seq>
handle_test_result(const boost::math::tools::test_result<T> & result,const Seq & worst,int row,const char * type_name,const char * test_name,const char * group_name)411 void handle_test_result(const boost::math::tools::test_result<T>& result,
412                        const Seq& worst, int row,
413                        const char* type_name,
414                        const char* test_name,
415                        const char* group_name)
416 {
417    T eps = boost::math::tools::epsilon<T>();
418    T max_error_found = (result.max)() / eps;
419    T mean_error_found = result.rms() / eps;
420 
421    std::string cell_name = sanitize_string(BOOST_COMPILER) + "_" + sanitize_string(BOOST_PLATFORM) + "_" + sanitize_string(type_name)
422       + "_" + sanitize_string(test_name) + "_" + sanitize_string(TEST_LIBRARY_NAME) + "_" + sanitize_string(group_name);
423 
424    std::stringstream ss;
425    ss << std::setprecision(3);
426    if(std::string(TEST_LIBRARY_NAME) != "boost")
427       ss << "(['" << TEST_LIBRARY_NAME << ":] ";
428    else
429       ss << "[role blue ";
430 
431    if((result.max)() > std::sqrt(eps))
432       ss << "[role red ";
433 
434 
435    ss << "Max = ";
436    if((boost::math::isfinite)(max_error_found))
437       ss << max_error_found;
438    else
439       ss << "+INF";
440    ss << "[epsilon] (Mean = ";
441    if((boost::math::isfinite)(mean_error_found))
442       ss << mean_error_found;
443    else
444       ss << "+INF";
445    ss << "[epsilon])";
446 
447    //
448    // Now check for error output from gross errors or unexpected exceptions:
449    //
450    std::stringbuf* pbuf = dynamic_cast<std::stringbuf*>(std::cerr.rdbuf());
451    bool have_errors = false;
452    std::string error_id = "errors_" + cell_name;
453    if(pbuf)
454    {
455       std::string err_s = pbuf->str();
456       if(err_s.size())
457       {
458          if(err_s.size() > 4096)
459          {
460             std::string::size_type pos = err_s.find("\n", 4096);
461             if(pos != std::string::npos)
462             {
463                err_s.erase(pos);
464                err_s += "\n*** FURTHER CONTENT HAS BEEN TRUNCATED FOR BREVITY ***\n";
465             }
466          }
467          std::string::size_type pos = err_s.find("\n");
468          while(pos != std::string::npos)
469          {
470             err_s.replace(pos, 1, "[br]");
471             pos = err_s.find("\n");
472          }
473          err_s = "[h4 Error Output For " + std::string(test_name) + std::string(" with compiler ") + std::string(BOOST_COMPILER)
474             + std::string(" and library ") + std::string(TEST_LIBRARY_NAME) + " and test data "
475             + std::string(group_name) + "]\n\n[#" + error_id + "]\n" + err_s + std::string("\n\n\n");
476          ss << "  [link " << error_id << " And other failures.]";
477          pbuf->str("");
478          set_error_content(error_id, err_s);
479          have_errors = true;
480       }
481    }
482    if(!have_errors)
483       remove_error_content(error_id);
484 
485 
486    if(std::string(TEST_LIBRARY_NAME) != "boost")
487       ss << ")";
488    else
489       ss << "]";
490 
491    if((result.max)() > std::sqrt(eps))
492       ss << "]";
493 
494    std::string cell_content = ss.str();
495 
496    set_result(cell_name, cell_content, test_name, group_name, type_name);
497 }
498 
499 struct error_stream_replacer
500 {
501    std::streambuf* old_buf;
502    std::stringstream ss;
error_stream_replacererror_stream_replacer503    error_stream_replacer()
504    {
505       old_buf = std::cerr.rdbuf();
506       std::cerr.rdbuf(ss.rdbuf());
507    }
~error_stream_replacererror_stream_replacer508    ~error_stream_replacer()
509    {
510       std::cerr.rdbuf(old_buf);
511    }
512 };
513 
514 #endif // BOOST_MATH_HANDLE_TEST_RESULT
515 
516