123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /*=============================================================================
- Copyright (c) 2001-2015 Joel de Guzman
- Distributed under the Boost Software License, Version 1.0. (See accompanying
- file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- =============================================================================*/
- #if !defined(BOOST_SPIRIT_X3_TEST_UTILITIES)
- #define BOOST_SPIRIT_X3_TEST_UTILITIES
- #include <boost/regex.hpp>
- #include <boost/filesystem.hpp>
- #include <boost/filesystem/fstream.hpp>
- namespace boost { namespace spirit { namespace x3 { namespace testing
- {
- namespace fs = boost::filesystem;
- ////////////////////////////////////////////////////////////////////////////
- // compare
- //
- // Compares the contents of in with the template tem. The template
- // may include embedded regular expressions marked up within re_prefix
- // and re_suffix tags. For example, given the default RE markup, this
- // template <%[0-9]+%> will match any integer in in. The function
- // will return the first non-matching position. The flag full_match
- // indicates a full match. It is possible for returned pos to be
- // at the end of in (in.end()) while still returning full_match ==
- // false. In that case, we have a partial match.
- ////////////////////////////////////////////////////////////////////////////
- template <typename Iterator>
- struct compare_result
- {
- compare_result(
- Iterator pos
- , bool full_match
- ) : pos(pos), full_match(full_match) {}
- Iterator pos;
- bool full_match;
- };
- template <typename Range>
- compare_result<typename Range::const_iterator>
- compare(
- Range const& in
- , Range const& tem
- , char const* re_prefix = "<%"
- , char const* re_suffix = "%>"
- );
- ////////////////////////////////////////////////////////////////////////////
- // compare
- //
- // 1) Call f, given the contents of input_path loaded in a string.
- // The result of calling f is the output string.
- // 2) Compare the result of calling f with expected template
- // file (expect_path) using the low-level compare utility
- // abive
- ////////////////////////////////////////////////////////////////////////////
- template <typename F>
- bool compare(
- fs::path input_path, fs::path expect_path
- , F f
- , char const* re_prefix = "<%"
- , char const* re_suffix = "%>"
- );
- ////////////////////////////////////////////////////////////////////////////
- // for_each_file
- //
- // For each *.input and *.expect file in a given directory,
- // call the function f, passing in the *.input and *.expect paths.
- ////////////////////////////////////////////////////////////////////////////
- template <typename F>
- int for_each_file(fs::path p, F f);
- ////////////////////////////////////////////////////////////////////////////
- // load_file
- //
- // Load file into a string.
- ////////////////////////////////////////////////////////////////////////////
- std::string load(fs::path p);
- ////////////////////////////////////////////////////////////////////////////
- // Implementation
- ////////////////////////////////////////////////////////////////////////////
- template <typename Iterator>
- inline bool is_regex(
- Iterator& first
- , Iterator last
- , std::string& re
- , char const* re_prefix
- , char const* re_suffix
- )
- {
- boost::regex e(re_prefix + std::string("(.*?)") + re_suffix);
- boost::match_results<Iterator> what;
- if (boost::regex_search(
- first, last, what, e
- , boost::match_default | boost::match_continuous))
- {
- re = what[1].str();
- first = what[0].second;
- return true;
- }
- return false;
- }
- template <typename Range>
- inline compare_result<typename Range::const_iterator>
- compare(
- Range const& in
- , Range const& tem
- , char const* re_prefix
- , char const* re_suffix
- )
- {
- typedef typename Range::const_iterator iter_t;
- typedef compare_result<iter_t> compare_result_t;
- iter_t in_first = in.begin();
- iter_t in_last = in.end();
- iter_t tem_first = tem.begin();
- iter_t tem_last = tem.end();
- std::string re;
- while (in_first != in_last && tem_first != tem_last)
- {
- if (is_regex(tem_first, tem_last, re, re_prefix, re_suffix))
- {
- boost::match_results<iter_t> what;
- boost::regex e(re);
- if (!boost::regex_search(
- in_first, in_last, what, e
- , boost::match_default | boost::match_continuous))
- {
- // RE mismatch: exit now.
- return compare_result_t(in_first, false);
- }
- else
- {
- // RE match: gobble the matching string.
- in_first = what[0].second;
- }
- }
- else
- {
- // Char by char comparison. Exit if we have a mismatch.
- if (*in_first++ != *tem_first++)
- return compare_result_t(in_first, false);
- }
- }
- // Ignore trailing spaces in template
- bool has_trailing_nonspaces = false;
- while (tem_first != tem_last)
- {
- if (!std::isspace(*tem_first++))
- {
- has_trailing_nonspaces = true;
- break;
- }
- }
- while (in_first != in_last)
- {
- if (!std::isspace(*in_first++))
- {
- has_trailing_nonspaces = true;
- break;
- }
- }
- // return a full match only if the template is fully matched and if there
- // are no more characters to match in the source
- return compare_result_t(in_first, !has_trailing_nonspaces);
- }
- template <typename F>
- inline int for_each_file(fs::path p, F f)
- {
- try
- {
- if (fs::exists(p) && fs::is_directory(p))
- {
- for (auto i = fs::directory_iterator(p); i != fs::directory_iterator(); ++i)
- {
- auto ext = fs::extension(i->path());
- if (ext == ".input")
- {
- auto input_path = i->path();
- auto expect_path = input_path;
- expect_path.replace_extension(".expect");
- f(input_path, expect_path);
- }
- }
- }
- else
- {
- std::cerr << "Directory: " << fs::absolute(p) << " does not exist." << std::endl;
- return 1;
- }
- }
- catch (const fs::filesystem_error& ex)
- {
- std::cerr << ex.what() << '\n';
- return 1;
- }
- return 0;
- }
- inline std::string load(fs::path p)
- {
- boost::filesystem::ifstream file(p);
- if (!file)
- return "";
- std::string contents((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
- return contents;
- }
- template <typename F>
- inline bool compare(
- fs::path input_path, fs::path expect_path
- , F f
- , char const* re_prefix
- , char const* re_suffix
- )
- {
- std::string output = f(load(input_path), input_path);
- std::string expected = load(expect_path);
- auto result = compare(output, expected, re_prefix, re_suffix);
- if (!result.full_match)
- {
- std::cout << "=============================================" << std::endl;
- std::cout << "==== Mismatch Found:" << std::endl;
- int line = 1;
- int col = 1;
- for (auto i = output.begin(); i != result.pos; ++i)
- {
- if (*i == '\n')
- {
- line++;
- col = 0;
- }
- ++col;
- }
- std::cerr
- << "==== File: " << expect_path
- << ", Line: " << line
- << ", Column: " << col
- << std::endl;
- std::cerr << "=============================================" << std::endl;
- // Print output
- std::cerr << output;
- std::cerr << "=============================================" << std::endl;
- std::cerr << "==== End" << std::endl;
- std::cerr << "=============================================" << std::endl;
- return false;
- }
- return true;
- }
- }}}}
- #endif
|