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