File:  [Isaki's NoNo m68k/m88k emulator] / nono / lib / test_mystring.cpp
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:56 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2021 nono project
// Licensed under nono-license.txt
//

#include <vector>
#include <tuple>
#include <functional>
#include "mystring.h"

// デバッグ文字列を返す
// プリミティブ型
template <class T>
std::string
ToDebugString(T x) {
	return std::to_string(x);
}

// デバッグ文字列を返す
// bool 型
static std::string
ToDebugString(bool x)
{
	return x ? "true" : "false";
}

// デバッグ文字列を返す
// string 型
static std::string
ToDebugString(const std::string& x)
{
	std::string rv { '"' };
	// TODO: escape
	rv += x;
	rv += '"';
	return rv;
}

static std::string __unused
ToDebugString(const char *x)
{
	return ToDebugString(std::string { x });
}

// デバッグ文字列を返す
// std::vector<std::string>
static std::string
ToDebugString(const std::vector<std::string>& x)
{
	std::string rv;

	for (const auto& item : x) {
		if (rv.empty()) {
			rv += "{";
		} else {
			rv += ",";
		}
		rv += ToDebugString(item);
	}
	rv += "}";
	return rv;
}

// Tuple with ToDebugString

// 前方宣言
template <class... Args>
class TestTuple;

// デバッグ文字列を返す
// TestTuple 型
template <class... Args>
std::string
ToDebugString(const TestTuple<Args...>& x)
{
	return x.ToDebugString();
}

// オレオレタプル
template <class... Args>
class TestTuple : public std::tuple<Args...>
{
	// 継承コンストラクタ宣言
	using std::tuple<Args...>::tuple;

 public:
	// デバッグ文字列を返す
	std::string ToDebugString() const {
		return ToD(std::index_sequence_for<Args...>{});
	}

	// std::get のメンバ関数化
	// 型が決まらないといけないので [] には出来ない。
	template <size_t i>
	auto get() const {
		return std::get<i>(*this);
	}

 private:
	// このへん参照
	// https://theolizer.com/cpp-school2/cpp-school2-21/

	template <size_t... indices>
	std::string ToD(std::index_sequence<indices...>) const {
		// get<> にはテンプレート引数を渡さないといけないので
		// 配列要素数がコンパイル時展開になるようにする。
		// これでタプルの各要素が型ごとに異なる ToDebugString()
		// の実体関数を呼び出して、結果の文字列が配列に入っている
		// 状態になる、ようだ。
		std::string a[] = {
			(::ToDebugString(get<indices>()))...
		};

		// a[] をよしなに連結すれば良い
		std::string rv { '{' };
		bool first = true;
		for (const auto& s : a) {
			if (first) {
				first = false;
			} else {
				rv += ',';
			}
			rv += s;
		}
		rv += '}';
		return rv;
	}
};

// テストデータ用テーブル
template <class... Args>
class TestTable : public std::vector<TestTuple<Args...> >
{
	// 継承コンストラクタ宣言
	using std::vector<TestTuple<Args... > >::vector;
};


// Test

class TestClass
{
 public:
	virtual ~TestClass() {}

	template <class T, class R>
	void Fail(T expectobj, R real) {
		printf("Failed at: %s but %s\n",
			ToDebugString(expectobj).c_str(),
			ToDebugString(real).c_str());
		exit(1);
	}

	void Test() {
		Init();

		for (auto&& t : testlist) {
			printf("Testing: %s ", t.name.c_str());
			t.func();
			printf("Ok\n");
		}
	}

 protected:
	// 継承して、TESTDEF マクロを使って testlist を作ること。
	virtual void Init() = 0;

	// テスト関数のリスト
	class Entry {
	 public:
		std::string name;
		std::function<void(void)> func;
	};
	std::vector<Entry> testlist;

#define TESTDEF(x) testlist.push_back({ #x, [&]() { x(); } })


	template <class T, class TWhere>
	void xp_eq(T expect, T actual, const TWhere& where)
	{
		// eq なので == で比較。演算子オーバーロード対策。
		if (expect == actual) {
			return;
		}

		Fail(where, actual);
	}
};

class Test_MyString : public TestClass
{
 protected:
	void Init() override {
		// TESTDEF マクロに、テスト実行関数の識別子を渡すこと。
		TESTDEF(string_ltrim_1);
		TESTDEF(string_rtrim_1);
		TESTDEF(rtrim_1);
		TESTDEF(string_trim_1);
		TESTDEF(startwith_ignorecase_1);
		TESTDEF(string_split_1);
		TESTDEF(string_split_2);
		TESTDEF(format_number_1);
		TESTDEF(SecToStr_1);
		TESTDEF(TimeToStr_1);
	}

	void string_ltrim_1() {
		TestTable<std::string, std::string> table = {
			{ "",		"" },
			{ "a",		"a" },
			{ "abc",	"abc" },
			{ "a ",		"a " },
			{ " a",		"a" },
			{ "a \t",	"a \t" },
			{ "\t a",	"a" },
			{ "  a  ",	"a  " },
			{ " abc ",	"abc " },
			{ "\r\na\r\n",	"a\r\n" },	// 行頭の改行も空白文字になる
			{ "  ",		"" },
		};

		for (const auto& t : table) {
			const auto& src = t.get<0>();
			const auto& exp = t.get<1>();

			xp_eq(exp, string_ltrim(src), t);
		}
	}

	void string_rtrim_1() {
		TestTable<std::string, std::string> table = {
			{ "",		"" },
			{ "a",		"a" },
			{ "abc",	"abc" },
			{ "a ",		"a" },
			{ " a",		" a" },
			{ "a \t",	"a" },
			{ "\t a",	"\t a" },
			{ "  a  ",	"  a" },
			{ " abc ",	" abc" },
			{ "\r\na\r\n",	"\r\na" },
			{ "  ",		"" },
		};

		for (const auto& t : table) {
			auto src = t.get<0>();
			const auto& exp = t.get<1>();

			string_rtrim(src);
			xp_eq(exp, src, t);
		}
	}

	void rtrim_1() {
		TestTable<std::string, std::string> table = {
			{ "",		"" },
			{ "a",		"a" },
			{ "abc",	"abc" },
			{ "a ",		"a" },
			{ " a",		" a" },
			{ "a \t",	"a" },
			{ "\t a",	"\t a" },
			{ "  a  ",	"  a" },
			{ " abc ",	" abc" },
			{ "\r\na\r\n",	"\r\na" },
			{ "  ",		"" },
		};

		for (const auto& t : table) {
			const auto& src = t.get<0>();
			const auto& exp = t.get<1>();

			std::vector<char> buf(src.size() + 1);
			memcpy(buf.data(), src.c_str(), buf.size());
			rtrim(buf.data());
			xp_eq(exp, std::string(buf.data()), t);
		}
	}

	void string_trim_1() {
		TestTable<std::string, std::string> table = {
			{ "",		"" },
			{ "a",		"a" },
			{ "abc",	"abc" },
			{ "a ",		"a" },
			{ " a",		"a" },
			{ "a \t",	"a" },
			{ "\t a",	"a" },
			{ "  a  ",	"a" },
			{ " abc ",	"abc" },
			{ "\r\na\r\n",	"a" },
			{ "  ",		"" },
		};

		for (const auto& t : table) {
			const auto& src = t.get<0>();
			const auto& exp = t.get<1>();

			xp_eq(exp, string_trim(src), t);
		}
	}

	void startwith_ignorecase_1() {
		TestTable<std::string, std::string, bool> table = {
			{ "ABCDEF",	"A",	true },
			{ "ABCDEF",	"B",	false },
			{ "ABCDEF",	"AB",	true },
			{ "ABCDEF",	"BC",	false },
			{ "ABCDEF",	"a",	true },
			{ "ABCDEF",	"b",	false },
			{ "ABCDEF",	"ab",	true },
			{ "ABCDEF",	"bc",	false },
			{ "aBCDEF",	"a",	true },
			{ "aBCDEF",	"b",	false },
			{ "aBCDEF",	"ab",	true },
			{ "aBCDEF",	"ABcd",	true },
			{ "",		"A",	false },
			{ "",		"",		true },	// ??? これはまだわかる
			{ "A",		"",		true },	// ??? 概念的には true だが果たして?
		};

		for (auto t : table) {
			auto arg0 = t.get<0>();
			auto arg1 = t.get<1>();
			auto expect = t.get<2>();

			xp_eq(expect, starts_with_ignorecase(arg0, arg1), t);
		}
	}

	void string_split_1() {
		TestTable<std::string, std::vector<std::string>> table = {
			{ "",		{ } },
			{ "ab",		{ "ab" } },
			{ "ab,c",	{ "ab", "c" } },
			{ "ab,",	{ "ab", "" } },			// separator が末尾
			{ ",a,,",	{ "", "a", "", "" } },	// separator は連続しても独立
			{ ",",		{ "", "" } },			// separator のみ
		};

		for (const auto& t : table) {
			const auto& src = t.get<0>();
			const auto& exp = t.get<1>();

			xp_eq(exp, string_split(src, ','), t);
		}
	}

	void string_split_2() {
		TestTable<std::string, int, std::vector<std::string>> table = {
			// 念のため等価のはず
			{ "",		0, { } },
			{ "ab",		0, { "ab" } },
			{ "ab,c",	0, { "ab", "c" } },
			{ "ab,",	0, { "ab", "" } },
			{ ",a,,",	0, { "", "a", "", "" } },
			{ ",",		0, { "", "" } },

			{ "",		1, { } },
			{ "ab",		1, { "ab" } },
			{ "ab,c",	1, { "ab,c" } },
			{ "ab,",	1, { "ab," } },
			{ ",a,,",	1, { ",a,," } },
			{ ",",		1, { "," } },

			{ "",		2, { } },
			{ "ab",		2, { "ab" } },
			{ "ab,c",	2, { "ab", "c" } },
			{ "ab,",	2, { "ab", "" } },
			{ "a,b,c",	2, { "a", "b,c" } },
			{ ",a,,",	2, { "", "a,," } },
			{ ",",		2, { "", "" } },
		};

		for (const auto& t : table) {
			const auto& src = t.get<0>();
			auto        num = t.get<1>();
			const auto& exp = t.get<2>();

			xp_eq(exp, string_split(src, ',', num), t);
		}
	}

	void format_number_1() {
		TestTable<uint64, std::string> table = {
			{ 0,			"0" },
			{ 1,			"1" },
			{ 999,			"999" },
			{ 1000,			"1,000" },
			{ 12345,		"12,345" },
			{ 123456,		"123,456" },
			{ 9876543,		"9,876,543" },
			{ 987654321,	"987,654,321" },
			{ 1000000000,	"1,000,000,000" },
		};

		for (const auto& t : table) {
			auto        val = t.get<0>();
			const auto& exp = t.get<1>();

			xp_eq(exp, format_number(val), t);
		}
	}

	void SecToStr_1() {
		TestTable<uint64, std::string> table = {
			{ 0,					            "0.000'000'000" },
			{ 1,					            "0.000'000'001" },
			{ 999999999,			            "0.999'999'999" },
			{ 4300020001,			            "4.300'020'001" },
			{ 9999999999,			            "9.999'999'999" },
			{ 10000000001,			           "10.000'000'001" },
			{ 100000000002,			          "100.000'000'002" },
			{ 1000000000003,		         "1000.000'000'003" },
			{ 10000000000004,		        "10000.000'000'004" },
			{ 100000000000005,		       "100000.000'000'005" },
			{ 1000000000000006,		      "1000000.000'000'006" },
			{ 10000000000000007,	     "10000000.000'000'007" },
			{ 100000000000000008,	    "100000000.000'000'008" },
		};

		for (const auto& t : table) {
			auto        val = t.get<0>();
			const auto& exp = t.get<1>();

			xp_eq(exp, SecToStr(val), t);
		}
	}

	void TimeToStr_1() {
		TestTable<uint64, std::string> table = {
			{ 0,					            "0.000'000'000" },
			{ 1,					            "0.000'000'001" },
			{ 999999999,			            "0.999'999'999" },
			{ 4300020001,			            "4.300'020'001" },
			{ 9999999999,			            "9.999'999'999" },
			{ 10000000001,			           "10.000'000'001" },
			{ 100000000002,			        " 1:40.000'000'002" },
			{ 1000000000003,		        "16:40.000'000'003" },
			{ 10000000000004,		     " 2:46:40.000'000'004" },
			{ 100000000000005,		"  1d 03:46:40.000'000'005" },
			{ 1000000000000006,		" 11d 13:46:40.000'000'006" },
			{ 10000000000000007,	"115d 17:46:40.000'000'007" },
			{ 100000000000000008,	"1157d 09:46:40.000'000'008" },
		};

		for (const auto& t : table) {
			auto        val = t.get<0>();
			const auto& exp = t.get<1>();

			xp_eq(exp, TimeToStr(val), t);
		}
	}
};

int
main(int ac, char *av[])
{
	printf("compiled by ");
#if defined(__clang_version__)
	printf("clang %s", __clang_version__);
#elif defined(__GNUC__) && defined(__VERSION__)
	printf("gcc %s", __VERSION__);
#else
	printf("unknown compiler");
#endif
	printf("\n");

	Test_MyString{}.Test();
	return 0;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.