testing.hpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*=============================================================================
  2. Copyright (c) 2001-2015 Joel de Guzman
  3. Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. =============================================================================*/
  6. #if !defined(BOOST_SPIRIT_X3_TEST_UTILITIES)
  7. #define BOOST_SPIRIT_X3_TEST_UTILITIES
  8. #include <boost/regex.hpp>
  9. #include <boost/filesystem.hpp>
  10. #include <boost/filesystem/fstream.hpp>
  11. namespace boost { namespace spirit { namespace x3 { namespace testing
  12. {
  13. namespace fs = boost::filesystem;
  14. ////////////////////////////////////////////////////////////////////////////
  15. // compare
  16. //
  17. // Compares the contents of in with the template tem. The template
  18. // may include embedded regular expressions marked up within re_prefix
  19. // and re_suffix tags. For example, given the default RE markup, this
  20. // template <%[0-9]+%> will match any integer in in. The function
  21. // will return the first non-matching position. The flag full_match
  22. // indicates a full match. It is possible for returned pos to be
  23. // at the end of in (in.end()) while still returning full_match ==
  24. // false. In that case, we have a partial match.
  25. ////////////////////////////////////////////////////////////////////////////
  26. template <typename Iterator>
  27. struct compare_result
  28. {
  29. compare_result(
  30. Iterator pos
  31. , bool full_match
  32. ) : pos(pos), full_match(full_match) {}
  33. Iterator pos;
  34. bool full_match;
  35. };
  36. template <typename Range>
  37. compare_result<typename Range::const_iterator>
  38. compare(
  39. Range const& in
  40. , Range const& tem
  41. , char const* re_prefix = "<%"
  42. , char const* re_suffix = "%>"
  43. );
  44. ////////////////////////////////////////////////////////////////////////////
  45. // compare
  46. //
  47. // 1) Call f, given the contents of input_path loaded in a string.
  48. // The result of calling f is the output string.
  49. // 2) Compare the result of calling f with expected template
  50. // file (expect_path) using the low-level compare utility
  51. // abive
  52. ////////////////////////////////////////////////////////////////////////////
  53. template <typename F>
  54. bool compare(
  55. fs::path input_path, fs::path expect_path
  56. , F f
  57. , char const* re_prefix = "<%"
  58. , char const* re_suffix = "%>"
  59. );
  60. ////////////////////////////////////////////////////////////////////////////
  61. // for_each_file
  62. //
  63. // For each *.input and *.expect file in a given directory,
  64. // call the function f, passing in the *.input and *.expect paths.
  65. ////////////////////////////////////////////////////////////////////////////
  66. template <typename F>
  67. int for_each_file(fs::path p, F f);
  68. ////////////////////////////////////////////////////////////////////////////
  69. // load_file
  70. //
  71. // Load file into a string.
  72. ////////////////////////////////////////////////////////////////////////////
  73. std::string load(fs::path p);
  74. ////////////////////////////////////////////////////////////////////////////
  75. // Implementation
  76. ////////////////////////////////////////////////////////////////////////////
  77. template <typename Iterator>
  78. inline bool is_regex(
  79. Iterator& first
  80. , Iterator last
  81. , std::string& re
  82. , char const* re_prefix
  83. , char const* re_suffix
  84. )
  85. {
  86. boost::regex e(re_prefix + std::string("(.*?)") + re_suffix);
  87. boost::match_results<Iterator> what;
  88. if (boost::regex_search(
  89. first, last, what, e
  90. , boost::match_default | boost::match_continuous))
  91. {
  92. re = what[1].str();
  93. first = what[0].second;
  94. return true;
  95. }
  96. return false;
  97. }
  98. template <typename Range>
  99. inline compare_result<typename Range::const_iterator>
  100. compare(
  101. Range const& in
  102. , Range const& tem
  103. , char const* re_prefix
  104. , char const* re_suffix
  105. )
  106. {
  107. typedef typename Range::const_iterator iter_t;
  108. typedef compare_result<iter_t> compare_result_t;
  109. iter_t in_first = in.begin();
  110. iter_t in_last = in.end();
  111. iter_t tem_first = tem.begin();
  112. iter_t tem_last = tem.end();
  113. std::string re;
  114. while (in_first != in_last && tem_first != tem_last)
  115. {
  116. if (is_regex(tem_first, tem_last, re, re_prefix, re_suffix))
  117. {
  118. boost::match_results<iter_t> what;
  119. boost::regex e(re);
  120. if (!boost::regex_search(
  121. in_first, in_last, what, e
  122. , boost::match_default | boost::match_continuous))
  123. {
  124. // RE mismatch: exit now.
  125. return compare_result_t(in_first, false);
  126. }
  127. else
  128. {
  129. // RE match: gobble the matching string.
  130. in_first = what[0].second;
  131. }
  132. }
  133. else
  134. {
  135. // Char by char comparison. Exit if we have a mismatch.
  136. if (*in_first++ != *tem_first++)
  137. return compare_result_t(in_first, false);
  138. }
  139. }
  140. // Ignore trailing spaces in template
  141. bool has_trailing_nonspaces = false;
  142. while (tem_first != tem_last)
  143. {
  144. if (!std::isspace(*tem_first++))
  145. {
  146. has_trailing_nonspaces = true;
  147. break;
  148. }
  149. }
  150. while (in_first != in_last)
  151. {
  152. if (!std::isspace(*in_first++))
  153. {
  154. has_trailing_nonspaces = true;
  155. break;
  156. }
  157. }
  158. // return a full match only if the template is fully matched and if there
  159. // are no more characters to match in the source
  160. return compare_result_t(in_first, !has_trailing_nonspaces);
  161. }
  162. template <typename F>
  163. inline int for_each_file(fs::path p, F f)
  164. {
  165. try
  166. {
  167. if (fs::exists(p) && fs::is_directory(p))
  168. {
  169. for (auto i = fs::directory_iterator(p); i != fs::directory_iterator(); ++i)
  170. {
  171. auto ext = fs::extension(i->path());
  172. if (ext == ".input")
  173. {
  174. auto input_path = i->path();
  175. auto expect_path = input_path;
  176. expect_path.replace_extension(".expect");
  177. f(input_path, expect_path);
  178. }
  179. }
  180. }
  181. else
  182. {
  183. std::cerr << "Directory: " << fs::absolute(p) << " does not exist." << std::endl;
  184. return 1;
  185. }
  186. }
  187. catch (const fs::filesystem_error& ex)
  188. {
  189. std::cerr << ex.what() << '\n';
  190. return 1;
  191. }
  192. return 0;
  193. }
  194. inline std::string load(fs::path p)
  195. {
  196. boost::filesystem::ifstream file(p);
  197. if (!file)
  198. return "";
  199. std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
  200. return contents;
  201. }
  202. template <typename F>
  203. inline bool compare(
  204. fs::path input_path, fs::path expect_path
  205. , F f
  206. , char const* re_prefix
  207. , char const* re_suffix
  208. )
  209. {
  210. std::string output = f(load(input_path), input_path);
  211. std::string expected = load(expect_path);
  212. auto result = compare(output, expected, re_prefix, re_suffix);
  213. if (!result.full_match)
  214. {
  215. std::cout << "=============================================" << std::endl;
  216. std::cout << "==== Mismatch Found:" << std::endl;
  217. int line = 1;
  218. int col = 1;
  219. for (auto i = output.begin(); i != result.pos; ++i)
  220. {
  221. if (*i == '\n')
  222. {
  223. line++;
  224. col = 0;
  225. }
  226. ++col;
  227. }
  228. std::cerr
  229. << "==== File: " << expect_path
  230. << ", Line: " << line
  231. << ", Column: " << col
  232. << std::endl;
  233. std::cerr << "=============================================" << std::endl;
  234. // Print output
  235. std::cerr << output;
  236. std::cerr << "=============================================" << std::endl;
  237. std::cerr << "==== End" << std::endl;
  238. std::cerr << "=============================================" << std::endl;
  239. return false;
  240. }
  241. return true;
  242. }
  243. }}}}
  244. #endif