| 1 | // Copyright (c) 2014 Tomoki Imai |
| 2 | // Copyright (c) 2023 Nikita Knizev |
| 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 | #include <boost/spirit/include/support_line_pos_iterator.hpp> |
| 8 | |
| 9 | #include <boost/core/lightweight_test.hpp> |
| 10 | #include <boost/type_traits/is_same.hpp> |
| 11 | #include <boost/assign.hpp> |
| 12 | #include <boost/static_assert.hpp> |
| 13 | #include <iterator> |
| 14 | #include <string> |
| 15 | #include <vector> |
| 16 | |
| 17 | struct validation { |
| 18 | validation(size_t line, size_t column) |
| 19 | : line(line), column(column), inside_newline(true) { |
| 20 | } |
| 21 | |
| 22 | validation(size_t line, size_t column, std::string current) |
| 23 | : line(line), column(column), current(current), inside_newline(false) { |
| 24 | } |
| 25 | |
| 26 | size_t line; |
| 27 | size_t column; |
| 28 | std::string current; |
| 29 | bool inside_newline; |
| 30 | }; |
| 31 | |
| 32 | typedef std::vector<validation> validations; |
| 33 | |
| 34 | struct string_forward_iterator : public boost::iterator_adaptor< |
| 35 | string_forward_iterator // Derived |
| 36 | , std::string::const_iterator // Base |
| 37 | , boost::use_default // Value |
| 38 | , boost::forward_traversal_tag // CategoryOrTraversal |
| 39 | > |
| 40 | { |
| 41 | explicit string_forward_iterator(std::string::const_iterator it) |
| 42 | : string_forward_iterator::iterator_adaptor_(it) {} |
| 43 | }; |
| 44 | BOOST_STATIC_ASSERT(boost::is_same<std::iterator_traits<string_forward_iterator>::iterator_category, std::forward_iterator_tag>::value); |
| 45 | |
| 46 | template <typename Iterator> |
| 47 | void test(Iterator first, Iterator last, validations const& validations, bool singlechar_newline) { |
| 48 | typedef boost::spirit::line_pos_iterator<Iterator> pos_iterator_t; |
| 49 | |
| 50 | pos_iterator_t const input_begin(first); |
| 51 | pos_iterator_t const input_end(last); |
| 52 | pos_iterator_t position(input_begin); |
| 53 | validations::const_iterator expected = validations.begin(); |
| 54 | |
| 55 | for (; position != input_end && expected != validations.end(); ++position, ++expected) { |
| 56 | if (expected->inside_newline && singlechar_newline) |
| 57 | if (++expected == validations.end()) |
| 58 | break; |
| 59 | boost::iterator_range<pos_iterator_t> const range = get_current_line(input_begin, position, input_end); |
| 60 | std::string const current(range.begin(), range.end()); |
| 61 | |
| 62 | BOOST_TEST_EQ(expected->line, get_line(position)); |
| 63 | BOOST_TEST_EQ(expected->column, get_column(input_begin, position)); |
| 64 | BOOST_TEST_EQ(expected->current, current); |
| 65 | } |
| 66 | if (expected != validations.end() && expected->inside_newline && singlechar_newline) |
| 67 | ++expected; |
| 68 | |
| 69 | BOOST_TEST(position == input_end); |
| 70 | BOOST_TEST(expected == validations.end()); |
| 71 | } |
| 72 | |
| 73 | void test(std::string const& input, validations const& validations, bool singlechar_newline) { |
| 74 | test(first: input.begin(), last: input.end(), validations, singlechar_newline); |
| 75 | test(first: string_forward_iterator(input.begin()), last: string_forward_iterator(input.end()), |
| 76 | validations, singlechar_newline); |
| 77 | } |
| 78 | |
| 79 | void test_only(std::string const& line_break) { |
| 80 | std::string const input = line_break + line_break + line_break; |
| 81 | validations const validations = boost::assign::list_of<validation> |
| 82 | (u: 1,us: 1,us: "" )(2,1) |
| 83 | (2,1,"" )(3,1) |
| 84 | (3,1,"" )(4,1); |
| 85 | test(input, validations, singlechar_newline: line_break.size() == 1); |
| 86 | } |
| 87 | |
| 88 | void test_in_text(std::string const& line_break) { |
| 89 | std::string const input = "foo" + line_break + "bar" + line_break + "git" ; |
| 90 | validations const validations = boost::assign::list_of<validation> |
| 91 | (u: 1,us: 1,us: "foo" )(1,2,"foo" )(1,3,"foo" )(1,4,"foo" )(2,1) |
| 92 | (2,1,"bar" )(2,2,"bar" )(2,3,"bar" )(2,4,"bar" )(3,1) |
| 93 | (3,1,"git" )(3,2,"git" )(3,3,"git" ); |
| 94 | test(input, validations, singlechar_newline: line_break.size() == 1); |
| 95 | } |
| 96 | |
| 97 | void test_at_begin(std::string const& line_break) { |
| 98 | std::string const input = line_break + "bar" + line_break + "git" ; |
| 99 | validations const validations = boost::assign::list_of<validation> |
| 100 | (u: 1,us: 1,us: "" )(2,1) |
| 101 | (2,1,"bar" )(2,2,"bar" )(2,3,"bar" )(2,4,"bar" )(3,1) |
| 102 | (3,1,"git" )(3,2,"git" )(3,3,"git" ); |
| 103 | test(input, validations, singlechar_newline: line_break.size() == 1); |
| 104 | } |
| 105 | |
| 106 | void test_at_end(std::string const& line_break) { |
| 107 | std::string const input = "foo" + line_break + "bar" + line_break; |
| 108 | validations const validations = boost::assign::list_of<validation> |
| 109 | (u: 1,us: 1,us: "foo" )(1,2,"foo" )(1,3,"foo" )(1,4,"foo" )(2,1) |
| 110 | (2,1,"bar" )(2,2,"bar" )(2,3,"bar" )(2,4,"bar" )(3,1); |
| 111 | test(input, validations, singlechar_newline: line_break.size() == 1); |
| 112 | } |
| 113 | |
| 114 | void test_mixed() { |
| 115 | std::string const input = "\n\n\r\r\n\r" ; |
| 116 | validations const validations = boost::assign::list_of<validation> |
| 117 | (u: 1,us: 1,us: "" ) // \n |
| 118 | (2,1,"" )(3,1) // \n\r |
| 119 | (3,1,"" )(4,1) // \r\n |
| 120 | (4,1,"" ); // \r |
| 121 | test(input, validations, singlechar_newline: false); |
| 122 | } |
| 123 | |
| 124 | void test(std::string const& line_break) |
| 125 | { |
| 126 | test_only(line_break); |
| 127 | test_in_text(line_break); |
| 128 | test_at_begin(line_break); |
| 129 | test_at_end(line_break); |
| 130 | } |
| 131 | |
| 132 | int main() |
| 133 | { |
| 134 | test(line_break: "\n" ); |
| 135 | test(line_break: "\r\n" ); |
| 136 | test(line_break: "\r" ); |
| 137 | test(line_break: "\n\r" ); |
| 138 | test_mixed(); |
| 139 | |
| 140 | return boost::report_errors(); |
| 141 | } |
| 142 | |