|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: #include <vector>
8: #include <tuple>
9: #include <functional>
10: #include "mystring.h"
11:
12: // デバッグ文字列を返す
13: // プリミティブ型
14: template <class T>
15: std::string
16: ToDebugString(T x) {
17: return std::to_string(x);
18: }
19:
20: // デバッグ文字列を返す
21: // bool 型
22: static std::string
23: ToDebugString(bool x)
24: {
25: return x ? "true" : "false";
26: }
27:
28: // デバッグ文字列を返す
29: // string 型
30: static std::string
31: ToDebugString(const std::string& x)
32: {
33: std::string rv { '"' };
34: // TODO: escape
35: rv += x;
36: rv += '"';
37: return rv;
38: }
39:
1.1.1.2 root 40: static std::string __unused
1.1 root 41: ToDebugString(const char *x)
42: {
43: return ToDebugString(std::string { x });
44: }
45:
1.1.1.2 root 46: // デバッグ文字列を返す
47: // std::vector<std::string>
48: static std::string
49: ToDebugString(const std::vector<std::string>& x)
50: {
51: std::string rv;
52:
53: for (const auto& item : x) {
54: if (rv.empty()) {
55: rv += "{";
56: } else {
57: rv += ",";
58: }
59: rv += ToDebugString(item);
60: }
61: rv += "}";
62: return rv;
63: }
64:
1.1 root 65: // Tuple with ToDebugString
66:
67: // 前方宣言
68: template <class... Args>
69: class TestTuple;
70:
71: // デバッグ文字列を返す
72: // TestTuple 型
73: template <class... Args>
74: std::string
75: ToDebugString(const TestTuple<Args...>& x)
76: {
77: return x.ToDebugString();
78: }
79:
80: // オレオレタプル
81: template <class... Args>
82: class TestTuple : public std::tuple<Args...>
83: {
84: // 継承コンストラクタ宣言
85: using std::tuple<Args...>::tuple;
86:
87: public:
88: // デバッグ文字列を返す
89: std::string ToDebugString() const {
90: return ToD(std::index_sequence_for<Args...>{});
91: }
92:
93: // std::get のメンバ関数化
94: // 型が決まらないといけないので [] には出来ない。
95: template <size_t i>
96: auto get() const {
97: return std::get<i>(*this);
98: }
99:
100: private:
101: // このへん参照
102: // https://theolizer.com/cpp-school2/cpp-school2-21/
103:
104: template <size_t... indices>
105: std::string ToD(std::index_sequence<indices...>) const {
106: // get<> にはテンプレート引数を渡さないといけないので
107: // 配列要素数がコンパイル時展開になるようにする。
108: // これでタプルの各要素が型ごとに異なる ToDebugString()
109: // の実体関数を呼び出して、結果の文字列が配列に入っている
110: // 状態になる、ようだ。
111: std::string a[] = {
112: (::ToDebugString(get<indices>()))...
113: };
114:
115: // a[] をよしなに連結すれば良い
116: std::string rv { '{' };
117: bool first = true;
118: for (const auto& s : a) {
119: if (first) {
120: first = false;
121: } else {
122: rv += ',';
123: }
124: rv += s;
125: }
126: rv += '}';
127: return rv;
128: }
129: };
130:
131: // テストデータ用テーブル
132: template <class... Args>
133: class TestTable : public std::vector<TestTuple<Args...> >
134: {
135: // 継承コンストラクタ宣言
136: using std::vector<TestTuple<Args... > >::vector;
137: };
138:
139:
140: // Test
141:
142: class TestClass
143: {
144: public:
145: virtual ~TestClass() {}
146:
147: template <class T, class R>
148: void Fail(T expectobj, R real) {
149: printf("Failed at: %s but %s\n",
150: ToDebugString(expectobj).c_str(),
151: ToDebugString(real).c_str());
152: exit(1);
153: }
154:
155: void Test() {
156: Init();
157:
158: for (auto&& t : testlist) {
159: printf("Testing: %s ", t.name.c_str());
160: t.func();
161: printf("Ok\n");
162: }
163: }
164:
165: protected:
1.1.1.2 root 166: // 継承して、TESTDEF マクロを使って testlist を作ること。
1.1 root 167: virtual void Init() = 0;
168:
169: // テスト関数のリスト
170: class Entry {
171: public:
172: std::string name;
173: std::function<void(void)> func;
174: };
175: std::vector<Entry> testlist;
176:
177: #define TESTDEF(x) testlist.push_back({ #x, [&]() { x(); } })
178:
179:
180: template <class T, class TWhere>
181: void xp_eq(T expect, T actual, const TWhere& where)
182: {
183: // eq なので == で比較。演算子オーバーロード対策。
184: if (expect == actual) {
185: return;
186: }
187:
188: Fail(where, actual);
189: }
190: };
191:
192: class Test_MyString : public TestClass
193: {
194: protected:
195: void Init() override {
1.1.1.2 root 196: // TESTDEF マクロに、テスト実行関数の識別子を渡すこと。
1.1 root 197: TESTDEF(string_ltrim_1);
1.1.1.2 root 198: TESTDEF(string_rtrim_1);
199: TESTDEF(rtrim_1);
200: TESTDEF(string_trim_1);
201: TESTDEF(startwith_ignorecase_1);
202: TESTDEF(string_split_1);
203: TESTDEF(string_split_2);
1.1.1.3 root 204: TESTDEF(format_number_1);
1.1.1.4 ! root 205: TESTDEF(SecToStr_1);
! 206: TESTDEF(TimeToStr_1);
1.1.1.2 root 207: }
208:
209: void string_ltrim_1() {
210: TestTable<std::string, std::string> table = {
211: { "", "" },
212: { "a", "a" },
213: { "abc", "abc" },
214: { "a ", "a " },
215: { " a", "a" },
216: { "a \t", "a \t" },
217: { "\t a", "a" },
218: { " a ", "a " },
219: { " abc ", "abc " },
220: { "\r\na\r\n", "a\r\n" }, // 行頭の改行も空白文字になる
221: { " ", "" },
222: };
223:
224: for (const auto& t : table) {
225: const auto& src = t.get<0>();
226: const auto& exp = t.get<1>();
227:
228: xp_eq(exp, string_ltrim(src), t);
229: }
230: }
231:
232: void string_rtrim_1() {
233: TestTable<std::string, std::string> table = {
234: { "", "" },
235: { "a", "a" },
236: { "abc", "abc" },
237: { "a ", "a" },
238: { " a", " a" },
239: { "a \t", "a" },
240: { "\t a", "\t a" },
241: { " a ", " a" },
242: { " abc ", " abc" },
243: { "\r\na\r\n", "\r\na" },
244: { " ", "" },
245: };
246:
247: for (const auto& t : table) {
248: auto src = t.get<0>();
249: const auto& exp = t.get<1>();
250:
251: string_rtrim(src);
252: xp_eq(exp, src, t);
253: }
254: }
255:
256: void rtrim_1() {
257: TestTable<std::string, std::string> table = {
258: { "", "" },
259: { "a", "a" },
260: { "abc", "abc" },
261: { "a ", "a" },
262: { " a", " a" },
263: { "a \t", "a" },
264: { "\t a", "\t a" },
265: { " a ", " a" },
266: { " abc ", " abc" },
267: { "\r\na\r\n", "\r\na" },
268: { " ", "" },
269: };
270:
271: for (const auto& t : table) {
272: const auto& src = t.get<0>();
273: const auto& exp = t.get<1>();
274:
275: std::vector<char> buf(src.size() + 1);
276: memcpy(buf.data(), src.c_str(), buf.size());
277: rtrim(buf.data());
278: xp_eq(exp, std::string(buf.data()), t);
279: }
280: }
281:
282: void string_trim_1() {
283: TestTable<std::string, std::string> table = {
284: { "", "" },
285: { "a", "a" },
286: { "abc", "abc" },
287: { "a ", "a" },
288: { " a", "a" },
289: { "a \t", "a" },
290: { "\t a", "a" },
291: { " a ", "a" },
292: { " abc ", "abc" },
293: { "\r\na\r\n", "a" },
294: { " ", "" },
295: };
296:
297: for (const auto& t : table) {
298: const auto& src = t.get<0>();
299: const auto& exp = t.get<1>();
300:
301: xp_eq(exp, string_trim(src), t);
302: }
1.1 root 303: }
304:
305: void startwith_ignorecase_1() {
306: TestTable<std::string, std::string, bool> table = {
1.1.1.2 root 307: { "ABCDEF", "A", true },
308: { "ABCDEF", "B", false },
309: { "ABCDEF", "AB", true },
310: { "ABCDEF", "BC", false },
311: { "ABCDEF", "a", true },
312: { "ABCDEF", "b", false },
313: { "ABCDEF", "ab", true },
314: { "ABCDEF", "bc", false },
315: { "aBCDEF", "a", true },
316: { "aBCDEF", "b", false },
317: { "aBCDEF", "ab", true },
318: { "aBCDEF", "ABcd", true },
319: { "", "A", false },
320: { "", "", true }, // ??? これはまだわかる
321: { "A", "", true }, // ??? 概念的には true だが果たして?
1.1 root 322: };
323:
324: for (auto t : table) {
325: auto arg0 = t.get<0>();
326: auto arg1 = t.get<1>();
327: auto expect = t.get<2>();
328:
329: xp_eq(expect, starts_with_ignorecase(arg0, arg1), t);
330: }
331: }
332:
1.1.1.2 root 333: void string_split_1() {
334: TestTable<std::string, std::vector<std::string>> table = {
335: { "", { } },
336: { "ab", { "ab" } },
337: { "ab,c", { "ab", "c" } },
338: { "ab,", { "ab", "" } }, // separator が末尾
339: { ",a,,", { "", "a", "", "" } }, // separator は連続しても独立
340: { ",", { "", "" } }, // separator のみ
341: };
342:
343: for (const auto& t : table) {
344: const auto& src = t.get<0>();
345: const auto& exp = t.get<1>();
346:
347: xp_eq(exp, string_split(src, ','), t);
348: }
349: }
350:
351: void string_split_2() {
352: TestTable<std::string, int, std::vector<std::string>> table = {
353: // 念のため等価のはず
354: { "", 0, { } },
355: { "ab", 0, { "ab" } },
356: { "ab,c", 0, { "ab", "c" } },
357: { "ab,", 0, { "ab", "" } },
358: { ",a,,", 0, { "", "a", "", "" } },
359: { ",", 0, { "", "" } },
360:
361: { "", 1, { } },
362: { "ab", 1, { "ab" } },
363: { "ab,c", 1, { "ab,c" } },
364: { "ab,", 1, { "ab," } },
365: { ",a,,", 1, { ",a,," } },
366: { ",", 1, { "," } },
367:
368: { "", 2, { } },
369: { "ab", 2, { "ab" } },
370: { "ab,c", 2, { "ab", "c" } },
371: { "ab,", 2, { "ab", "" } },
372: { "a,b,c", 2, { "a", "b,c" } },
373: { ",a,,", 2, { "", "a,," } },
374: { ",", 2, { "", "" } },
375: };
376:
377: for (const auto& t : table) {
378: const auto& src = t.get<0>();
379: auto num = t.get<1>();
380: const auto& exp = t.get<2>();
381:
382: xp_eq(exp, string_split(src, ',', num), t);
383: }
1.1 root 384: }
1.1.1.3 root 385:
386: void format_number_1() {
387: TestTable<uint64, std::string> table = {
388: { 0, "0" },
389: { 1, "1" },
390: { 999, "999" },
391: { 1000, "1,000" },
392: { 12345, "12,345" },
393: { 123456, "123,456" },
394: { 9876543, "9,876,543" },
395: { 987654321, "987,654,321" },
396: { 1000000000, "1,000,000,000" },
397: };
398:
399: for (const auto& t : table) {
400: auto val = t.get<0>();
401: const auto& exp = t.get<1>();
402:
403: xp_eq(exp, format_number(val), t);
404: }
405: }
1.1.1.4 ! root 406:
! 407: void SecToStr_1() {
! 408: TestTable<uint64, std::string> table = {
! 409: { 0, "0.000'000'000" },
! 410: { 1, "0.000'000'001" },
! 411: { 999999999, "0.999'999'999" },
! 412: { 4300020001, "4.300'020'001" },
! 413: { 9999999999, "9.999'999'999" },
! 414: { 10000000001, "10.000'000'001" },
! 415: { 100000000002, "100.000'000'002" },
! 416: { 1000000000003, "1000.000'000'003" },
! 417: { 10000000000004, "10000.000'000'004" },
! 418: { 100000000000005, "100000.000'000'005" },
! 419: { 1000000000000006, "1000000.000'000'006" },
! 420: { 10000000000000007, "10000000.000'000'007" },
! 421: { 100000000000000008, "100000000.000'000'008" },
! 422: };
! 423:
! 424: for (const auto& t : table) {
! 425: auto val = t.get<0>();
! 426: const auto& exp = t.get<1>();
! 427:
! 428: xp_eq(exp, SecToStr(val), t);
! 429: }
! 430: }
! 431:
! 432: void TimeToStr_1() {
! 433: TestTable<uint64, std::string> table = {
! 434: { 0, "0.000'000'000" },
! 435: { 1, "0.000'000'001" },
! 436: { 999999999, "0.999'999'999" },
! 437: { 4300020001, "4.300'020'001" },
! 438: { 9999999999, "9.999'999'999" },
! 439: { 10000000001, "10.000'000'001" },
! 440: { 100000000002, " 1:40.000'000'002" },
! 441: { 1000000000003, "16:40.000'000'003" },
! 442: { 10000000000004, " 2:46:40.000'000'004" },
! 443: { 100000000000005, " 1d 03:46:40.000'000'005" },
! 444: { 1000000000000006, " 11d 13:46:40.000'000'006" },
! 445: { 10000000000000007, "115d 17:46:40.000'000'007" },
! 446: { 100000000000000008, "1157d 09:46:40.000'000'008" },
! 447: };
! 448:
! 449: for (const auto& t : table) {
! 450: auto val = t.get<0>();
! 451: const auto& exp = t.get<1>();
! 452:
! 453: xp_eq(exp, TimeToStr(val), t);
! 454: }
! 455: }
1.1 root 456: };
457:
458: int
459: main(int ac, char *av[])
460: {
461: printf("compiled by ");
462: #if defined(__clang_version__)
463: printf("clang %s", __clang_version__);
464: #elif defined(__GNUC__) && defined(__VERSION__)
465: printf("gcc %s", __VERSION__);
466: #else
467: printf("unknown compiler");
468: #endif
469: printf("\n");
470:
471: Test_MyString{}.Test();
472: return 0;
473: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.