openMSX
StringOp_test.cc
Go to the documentation of this file.
1 #include "catch.hpp"
2 #include "StringOp.hh"
3 #include <type_traits>
4 
5 using namespace StringOp;
6 using std::string;
7 using std::string_view;
8 
9 static void checkTrimRight(const string& s, char c, const string& expected)
10 {
11  string test = s;
12  trimRight(test, c);
13  CHECK(test == expected);
14 
15  string_view ref = s;
16  trimRight(ref, c);
17  CHECK(ref == expected);
18 }
19 static void checkTrimRight(const string& s, const char* chars, const string& expected)
20 {
21  string test = s;
22  trimRight(test, chars);
23  CHECK(test == expected);
24 
25  string_view ref = s;
26  trimRight(ref, chars);
27  CHECK(ref == expected);
28 }
29 
30 static void checkTrimLeft(const string& s, char c, const string& expected)
31 {
32  string test = s;
33  trimLeft(test, c);
34  CHECK(test == expected);
35 
36  string_view ref = s;
37  trimLeft(ref, c);
38  CHECK(ref == expected);
39 }
40 static void checkTrimLeft(const string& s, const char* chars, const string& expected)
41 {
42  string test = s;
43  trimLeft(test, chars);
44  CHECK(test == expected);
45 
46  string_view ref = s;
47  trimLeft(ref, chars);
48  CHECK(ref == expected);
49 }
50 
51 static void checkSplitOnFirst(const string& s, const string& first, const string& last)
52 {
53  auto [f1, l1] = splitOnFirst(s, '-');
54  auto [f2, l2] = splitOnFirst(s, " -+");
55  static_assert(std::is_same_v<decltype(f1), std::string_view>);
56  static_assert(std::is_same_v<decltype(f2), std::string_view>);
57  static_assert(std::is_same_v<decltype(l1), std::string_view>);
58  static_assert(std::is_same_v<decltype(l2), std::string_view>);
59  CHECK(f1 == first);
60  CHECK(f2 == first);
61  CHECK(l1 == last);
62  CHECK(l2 == last);
63 }
64 
65 static void checkSplitOnLast(const string& s, const string& first, const string& last)
66 {
67  auto [f1, l1] = splitOnLast(s, '-');
68  auto [f2, l2] = splitOnLast(s, " -+");
69  static_assert(std::is_same_v<decltype(f1), std::string_view>);
70  static_assert(std::is_same_v<decltype(f2), std::string_view>);
71  static_assert(std::is_same_v<decltype(l1), std::string_view>);
72  static_assert(std::is_same_v<decltype(l2), std::string_view>);
73  CHECK(f1 == first);
74  CHECK(f2 == first);
75  CHECK(l1 == last);
76  CHECK(l2 == last);
77 }
78 
79 static void checkSplit(const string& s, const std::vector<string_view> expected)
80 {
81  //CHECK(split(s, '-') == expected);
82 
83  std::vector<string_view> result;
84  for (const auto& ss : StringOp::split_view(s, '-')) {
85  result.push_back(ss);
86  }
87  CHECK(result == expected);
88 }
89 
90 static void checkParseRange(const string& s, const std::vector<unsigned>& expected)
91 {
92  auto parsed = parseRange(s, 0, 63);
93  std::vector<unsigned> result;
94  parsed.foreachSetBit([&](unsigned i) { result.push_back(i); });
95  CHECK(result == expected);
96 }
97 
98 
99 TEST_CASE("StringOp")
100 {
101  SECTION("stringTo<int>") {
102  std::optional<int> NOK;
103  using OK = std::optional<int>;
104 
105  // empty string is invalid
106  CHECK(StringOp::stringTo<int>("") == NOK);
107 
108  // valid decimal values, positive ..
109  CHECK(StringOp::stringTo<int>("0") == OK(0));
110  CHECK(StringOp::stringTo<int>("03") == OK(3));
111  CHECK(StringOp::stringTo<int>("097") == OK(97));
112  CHECK(StringOp::stringTo<int>("12") == OK(12));
113  // .. and negative
114  CHECK(StringOp::stringTo<int>("-0") == OK(0));
115  CHECK(StringOp::stringTo<int>("-11") == OK(-11));
116 
117  // invalid
118  CHECK(StringOp::stringTo<int>("-") == NOK);
119  CHECK(StringOp::stringTo<int>("zz") == NOK);
120  CHECK(StringOp::stringTo<int>("+") == NOK);
121  CHECK(StringOp::stringTo<int>("+12") == NOK);
122 
123  // leading whitespace is invalid, trailing stuff is invalid
124  CHECK(StringOp::stringTo<int>(" 14") == NOK);
125  CHECK(StringOp::stringTo<int>("15 ") == NOK);
126  CHECK(StringOp::stringTo<int>("15bar") == NOK);
127 
128  // hexadecimal
129  CHECK(StringOp::stringTo<int>("0x1a") == OK(26));
130  CHECK(StringOp::stringTo<int>("0x1B") == OK(27));
131  CHECK(StringOp::stringTo<int>("0X1c") == OK(28));
132  CHECK(StringOp::stringTo<int>("0X1D") == OK(29));
133  CHECK(StringOp::stringTo<int>("-0x100") == OK(-256));
134  CHECK(StringOp::stringTo<int>("0x") == NOK);
135  CHECK(StringOp::stringTo<int>("0x12g") == NOK);
136  CHECK(StringOp::stringTo<int>("0x-123") == NOK);
137 
138  // binary
139  CHECK(StringOp::stringTo<int>("0b") == NOK);
140  CHECK(StringOp::stringTo<int>("0b2") == NOK);
141  CHECK(StringOp::stringTo<int>("0b100") == OK(4));
142  CHECK(StringOp::stringTo<int>("-0B1001") == OK(-9));
143  CHECK(StringOp::stringTo<int>("0b-11") == NOK);
144 
145  // overflow
146  CHECK(StringOp::stringTo<int>("-2147483649") == NOK);
147  CHECK(StringOp::stringTo<int>("2147483648") == NOK);
148  CHECK(StringOp::stringTo<int>("999999999999999") == NOK);
149  CHECK(StringOp::stringTo<int>("-999999999999999") == NOK);
150  // edge cases (no overflow)
151  CHECK(StringOp::stringTo<int>("-2147483648") == OK(-2147483648));
152  CHECK(StringOp::stringTo<int>("2147483647") == OK(2147483647));
153  CHECK(StringOp::stringTo<int>("-0x80000000") == OK(-2147483648));
154  CHECK(StringOp::stringTo<int>("0x7fffffff") == OK(2147483647));
155  }
156  SECTION("stringTo<unsigned>") {
157  std::optional<unsigned> NOK;
158  using OK = std::optional<unsigned>;
159 
160  // empty string is invalid
161  CHECK(StringOp::stringTo<unsigned>("") == NOK);
162 
163  // valid decimal values, only positive ..
164  CHECK(StringOp::stringTo<unsigned>("0") == OK(0));
165  CHECK(StringOp::stringTo<unsigned>("08") == OK(8));
166  CHECK(StringOp::stringTo<unsigned>("0123") == OK(123));
167  CHECK(StringOp::stringTo<unsigned>("13") == OK(13));
168  // negative is invalid
169  CHECK(StringOp::stringTo<unsigned>("-0") == NOK);
170  CHECK(StringOp::stringTo<unsigned>("-12") == NOK);
171 
172  // invalid
173  CHECK(StringOp::stringTo<unsigned>("-") == NOK);
174  CHECK(StringOp::stringTo<unsigned>("zz") == NOK);
175  CHECK(StringOp::stringTo<unsigned>("+") == NOK);
176  CHECK(StringOp::stringTo<unsigned>("+12") == NOK);
177 
178  // leading whitespace is invalid, trailing stuff is invalid
179  CHECK(StringOp::stringTo<unsigned>(" 16") == NOK);
180  CHECK(StringOp::stringTo<unsigned>("17 ") == NOK);
181  CHECK(StringOp::stringTo<unsigned>("17qux") == NOK);
182 
183  // hexadecimal
184  CHECK(StringOp::stringTo<unsigned>("0x2a") == OK(42));
185  CHECK(StringOp::stringTo<unsigned>("0x2B") == OK(43));
186  CHECK(StringOp::stringTo<unsigned>("0X2c") == OK(44));
187  CHECK(StringOp::stringTo<unsigned>("0X2D") == OK(45));
188  CHECK(StringOp::stringTo<unsigned>("0x") == NOK);
189  CHECK(StringOp::stringTo<unsigned>("-0x456") == NOK);
190  CHECK(StringOp::stringTo<unsigned>("0x-123") == NOK);
191 
192  // binary
193  CHECK(StringOp::stringTo<unsigned>("0b1100") == OK(12));
194  CHECK(StringOp::stringTo<unsigned>("0B1010") == OK(10));
195  CHECK(StringOp::stringTo<unsigned>("0b") == NOK);
196  CHECK(StringOp::stringTo<unsigned>("-0b101") == NOK);
197  CHECK(StringOp::stringTo<unsigned>("0b2") == NOK);
198  CHECK(StringOp::stringTo<unsigned>("0b-11") == NOK);
199 
200  // overflow
201  CHECK(StringOp::stringTo<unsigned>("4294967296") == NOK);
202  CHECK(StringOp::stringTo<unsigned>("999999999999999") == NOK);
203  // edge case (no overflow)
204  CHECK(StringOp::stringTo<unsigned>("4294967295") == OK(4294967295));
205  CHECK(StringOp::stringTo<unsigned>("0xffffffff") == OK(4294967295));
206  }
207 
208  SECTION("stringToBool") {
209  CHECK(stringToBool("0") == false);
210  CHECK(stringToBool("1") == true);
211  CHECK(stringToBool("Yes") == true);
212  CHECK(stringToBool("yes") == true);
213  CHECK(stringToBool("YES") == true);
214  CHECK(stringToBool("No") == false);
215  CHECK(stringToBool("no") == false);
216  CHECK(stringToBool("NO") == false);
217  CHECK(stringToBool("True") == true);
218  CHECK(stringToBool("true") == true);
219  CHECK(stringToBool("TRUE") == true);
220  CHECK(stringToBool("False") == false);
221  CHECK(stringToBool("false") == false);
222  CHECK(stringToBool("FALSE") == false);
223  // These two behave different as Tcl
224  CHECK(stringToBool("2") == false); // is true in Tcl
225  CHECK(stringToBool("foobar") == false); // is error in Tcl
226  }
227  /*SECTION("toLower") {
228  CHECK(toLower("") == "");
229  CHECK(toLower("foo") == "foo");
230  CHECK(toLower("FOO") == "foo");
231  CHECK(toLower("fOo") == "foo");
232  CHECK(toLower(string("FoO")) == "foo");
233  }*/
234  SECTION("trimRight") {
235  checkTrimRight("", ' ', "");
236  checkTrimRight(" ", ' ', "");
237  checkTrimRight("foo", ' ', "foo");
238  checkTrimRight(" foo", ' ', " foo");
239  checkTrimRight("foo ", ' ', "foo");
240 
241  checkTrimRight("", "o ", "");
242  checkTrimRight(" o ", "o ", "");
243  checkTrimRight("foobar", "o ", "foobar");
244  checkTrimRight(" foobar", "o ", " foobar");
245  checkTrimRight("foo ", "o ", "f");
246  }
247  SECTION("trimLeft") {
248  checkTrimLeft("", ' ', "");
249  checkTrimLeft(" ", ' ', "");
250  checkTrimLeft("foo", ' ', "foo");
251  checkTrimLeft("foo ", ' ', "foo ");
252  checkTrimLeft(" foo", ' ', "foo");
253 
254  checkTrimLeft("", "f ", "");
255  checkTrimLeft(" f ", "f ", "");
256  checkTrimLeft("foo", "f ", "oo");
257  checkTrimLeft("barfoo ", "f ", "barfoo ");
258  checkTrimLeft(" foo", "f ", "oo");
259  }
260  SECTION("splitOnFirst") {
261  checkSplitOnFirst("", "", "");
262  checkSplitOnFirst("-", "", "");
263  checkSplitOnFirst("foo-", "foo", "");
264  checkSplitOnFirst("-foo", "", "foo");
265  checkSplitOnFirst("foo-bar", "foo", "bar");
266  checkSplitOnFirst("foo-bar-qux", "foo", "bar-qux");
267  checkSplitOnFirst("-bar-qux", "", "bar-qux");
268  checkSplitOnFirst("foo-bar-", "foo", "bar-");
269  }
270  SECTION("splitOnLast") {
271  checkSplitOnLast("", "", "");
272  checkSplitOnLast("-", "", "");
273  checkSplitOnLast("foo-", "foo", "");
274  checkSplitOnLast("-foo", "", "foo");
275  checkSplitOnLast("foo-bar", "foo", "bar");
276  checkSplitOnLast("foo-bar-qux", "foo-bar", "qux");
277  checkSplitOnLast("-bar-qux", "-bar", "qux");
278  checkSplitOnLast("foo-bar-", "foo-bar", "");
279  }
280  SECTION("split") {
281  checkSplit("", {});
282  checkSplit("-", {""});
283  checkSplit("foo-", {"foo"});
284  checkSplit("-foo", {"", "foo"});
285  checkSplit("foo-bar", {"foo", "bar"});
286  checkSplit("foo-bar-qux", {"foo", "bar", "qux"});
287  checkSplit("-bar-qux", {"", "bar", "qux"});
288  checkSplit("foo-bar-", {"foo", "bar"});
289  }
290  SECTION("parseRange") {
291  checkParseRange("", {});
292  checkParseRange("5", {5});
293  checkParseRange("5,8", {5,8});
294  checkParseRange("5,5", {5});
295  checkParseRange("5-7", {5,6,7});
296  checkParseRange("7-5", {5,6,7});
297  checkParseRange("5-7,19", {5,6,7,19});
298  checkParseRange("15,5-7", {5,6,7,15});
299  checkParseRange("6,5-7", {5,6,7});
300  checkParseRange("5-8,10-12", {5,6,7,8,10,11,12});
301  checkParseRange("5-9,6-10", {5,6,7,8,9,10});
302 
303  CHECK_THROWS (parseRange( "4", 5, 10));
304  CHECK_NOTHROW(parseRange( "5", 5, 10));
305  CHECK_NOTHROW(parseRange("10", 5, 10));
306  CHECK_THROWS (parseRange("11", 5, 10));
307  }
308  SECTION("caseless") {
309  caseless op;
310  CHECK( op("abc", "xyz"));
311  CHECK(!op("xyz", "abc"));
312  CHECK(!op("abc", "abc"));
313  CHECK( op("ABC", "xyz"));
314  CHECK(!op("xyz", "ABC"));
315  CHECK(!op("ABC", "abc"));
316  CHECK( op("aBC", "Xyz"));
317  CHECK(!op("xYz", "AbC"));
318  CHECK(!op("ABc", "abC"));
319 
320  CHECK( op("abc", "ABCdef"));
321  CHECK(!op("AbcDef", "AbC"));
322  }
323  SECTION("casecmp") {
324  casecmp op;
325  CHECK( op("abc", "abc"));
326  CHECK( op("abc", "ABC"));
327  CHECK(!op("abc", "xyz"));
328  CHECK(!op("ab", "abc"));
329  CHECK(!op("ab", "ABC"));
330  CHECK(!op("abc", "ab"));
331  CHECK(!op("abc", "AB"));
332  }
333 }
void test(const IterableBitSet< N > &s, std::initializer_list< size_t > list)
TEST_CASE("StringOp")
CHECK(m3==m3)
IterableBitSet< 64 > parseRange(string_view str, unsigned min, unsigned max)
Definition: StringOp.cc:177
bool stringToBool(string_view str)
Definition: StringOp.cc:12
std::pair< string_view, string_view > splitOnLast(string_view str, string_view chars)
Definition: StringOp.cc:108
void trimRight(string &str, const char *chars)
Definition: StringOp.cc:29
std::pair< string_view, string_view > splitOnFirst(string_view str, string_view chars)
Definition: StringOp.cc:91
void trimLeft(string &str, const char *chars)
Definition: StringOp.cc:58
auto split_view(std::string_view str, char c)
Definition: StringOp.hh:78