diff options
Diffstat (limited to 'test/base')
-rw-r--r-- | test/base/CMakeLists.txt | 40 | ||||
-rw-r--r-- | test/base/Event2Test.cpp | 54 | ||||
-rw-r--r-- | test/base/HandlerRegistryTest.cpp | 37 | ||||
-rw-r--r-- | test/base/PropertyTreeTest.cpp | 26 | ||||
-rw-r--r-- | test/base/SelfResolvableTest.cpp | 110 | ||||
-rw-r--r-- | test/base/StringTest.cpp | 112 | ||||
-rw-r--r-- | test/base/StringToNumberConverterTest.cpp | 166 | ||||
-rw-r--r-- | test/base/StringUtilTest.cpp | 118 | ||||
-rw-r--r-- | test/base/SubProcessHelper/CruEcho.cpp | 9 | ||||
-rw-r--r-- | test/base/SubProcessHelper/CruTee.cpp | 10 | ||||
-rw-r--r-- | test/base/SubProcessTest.cpp | 30 | ||||
-rw-r--r-- | test/base/platform/unix/UnixFileStreamTest.cpp | 30 | ||||
-rw-r--r-- | test/base/platform/unix/UnixFileTest.cpp | 38 | ||||
-rw-r--r-- | test/base/platform/win/StreamConvertTest.cpp | 39 | ||||
-rw-r--r-- | test/base/platform/win/Win32FileStreamTest.cpp | 37 |
15 files changed, 856 insertions, 0 deletions
diff --git a/test/base/CMakeLists.txt b/test/base/CMakeLists.txt new file mode 100644 index 00000000..683c8295 --- /dev/null +++ b/test/base/CMakeLists.txt @@ -0,0 +1,40 @@ +add_executable(CruBaseTest + Event2Test.cpp + HandlerRegistryTest.cpp + PropertyTreeTest.cpp + SelfResolvableTest.cpp + StringTest.cpp + StringToNumberConverterTest.cpp + StringUtilTest.cpp + SubProcessTest.cpp +) +target_link_libraries(CruBaseTest PRIVATE CruBase CruTestBase) + +add_executable(CruTestHelperEcho + SubProcessHelper/CruEcho.cpp +) + +add_executable(CruTestHelperTee + SubProcessHelper/CruTee.cpp +) + +target_compile_definitions(CruBaseTest PRIVATE + CRU_TEST_HELPER_ECHO_LOCATION="$<TARGET_FILE:CruTestHelperEcho>" + CRU_TEST_HELPER_TEE_LOCATION="$<TARGET_FILE:CruTestHelperTee>" +) + +if (UNIX AND NOT EMSCRIPTEN) + target_sources(CruBaseTest PRIVATE + platform/unix/UnixFileTest.cpp + platform/unix/UnixFileStreamTest.cpp + ) +endif() + +if (WIN32) + target_sources(CruBaseTest PRIVATE + platform/win/StreamConvertTest.cpp + platform/win/Win32FileStreamTest.cpp + ) +endif() + +cru_catch_discover_tests(CruBaseTest) diff --git a/test/base/Event2Test.cpp b/test/base/Event2Test.cpp new file mode 100644 index 00000000..0c67f28e --- /dev/null +++ b/test/base/Event2Test.cpp @@ -0,0 +1,54 @@ +#include "cru/base/Event2.h" + +#include <catch2/catch_test_macros.hpp> + +using cru::Event2; + +TEST_CASE("Event2", "[event2]") { + Event2 event; + + int counter = 0; + + auto handler = [&counter] { counter++; }; + auto token = event.AddHandler(handler); + + auto handler2 = [&counter](decltype(event)::Context* context) { counter++; }; + + SECTION("two handlers should work.") { + event.Raise(); + REQUIRE(counter == 1); + event.Raise(); + REQUIRE(counter == 2); + + event.AddHandler(handler2); + event.Raise(); + REQUIRE(counter == 4); + } + + SECTION("handler revoker should work.") { + token.RevokeHandler(); + event.Raise(); + REQUIRE(counter == 0); + + token = event.AddHandler(handler); + event.AddHandler(handler2); + event.Raise(); + REQUIRE(counter == 2); + + token.RevokeHandler(); + event.Raise(); + REQUIRE(counter == 3); + } + + SECTION("stop handling should work.") { + auto short_circuit_handler = [&counter](decltype(event)::Context* context) { + context->SetStopHandling(); + }; + + event.AddHandler(short_circuit_handler); + event.AddHandler(handler2); + + event.Raise(); + REQUIRE(counter == 1); + } +} diff --git a/test/base/HandlerRegistryTest.cpp b/test/base/HandlerRegistryTest.cpp new file mode 100644 index 00000000..aacef70f --- /dev/null +++ b/test/base/HandlerRegistryTest.cpp @@ -0,0 +1,37 @@ +#include "cru/base/HandlerRegistry.h" + +#include <catch2/catch_test_macros.hpp> + +#include <algorithm> + +TEST_CASE("HandlerRegistry", "[handler_registry]") { + using namespace cru; + HandlerRegistry<void()> registry; + + int counter = 1; + + auto tag1 = registry.AddHandler([&counter] { counter++; }); + auto tag2 = registry.AddHandler([&counter] { counter++; }); + + for (const auto& handler : registry) { + handler(); + } + + REQUIRE(counter == 3); + + registry.RemoveHandler(tag1); + + for (const auto& handler : registry) { + handler(); + } + + REQUIRE(counter == 4); + + registry.RemoveHandler(tag2); + + for (const auto& handler : registry) { + handler(); + } + + REQUIRE(counter == 4); +} diff --git a/test/base/PropertyTreeTest.cpp b/test/base/PropertyTreeTest.cpp new file mode 100644 index 00000000..24d7ca9e --- /dev/null +++ b/test/base/PropertyTreeTest.cpp @@ -0,0 +1,26 @@ +#include "cru/base/PropertyTree.h" + +#include <catch2/catch_test_macros.hpp> + +TEST_CASE("PropertyTree", "[property_tree]") { + using cru::PropertySubTreeRef; + using cru::PropertyTree; + + PropertyTree tree({ + {u"k1", u"v1"}, + {u"k2", u"v2"}, + {u"k3.sub", u"v3"}, + }); + + REQUIRE(tree.GetValue(u"k1") == u"v1"); + REQUIRE(tree.GetValue(u"k2") == u"v2"); + REQUIRE(tree.GetValue(u"k3.sub") == u"v3"); + + PropertySubTreeRef sub_tree = tree.GetSubTreeRef(u"k3"); + REQUIRE(sub_tree.GetValue(u"sub") == u"v3"); + + PropertySubTreeRef root_tree = sub_tree.GetParent(); + REQUIRE(root_tree.GetValue(u"k1") == u"v1"); + REQUIRE(root_tree.GetValue(u"k2") == u"v2"); + REQUIRE(root_tree.GetValue(u"k3.sub") == u"v3"); +} diff --git a/test/base/SelfResolvableTest.cpp b/test/base/SelfResolvableTest.cpp new file mode 100644 index 00000000..c214bd77 --- /dev/null +++ b/test/base/SelfResolvableTest.cpp @@ -0,0 +1,110 @@ +#include "cru/base/Base.h" +#include "cru/base/SelfResolvable.h" + +#include <catch2/catch_test_macros.hpp> + +#include <memory> + +namespace { +class SelfResolvableTestClassBase { + public: + SelfResolvableTestClassBase() = default; + CRU_DELETE_COPY(SelfResolvableTestClassBase) + CRU_DEFAULT_MOVE(SelfResolvableTestClassBase) + virtual ~SelfResolvableTestClassBase() = default; +}; + +class SelfResolvableTestClass + : public SelfResolvableTestClassBase, + public cru::SelfResolvable<SelfResolvableTestClass> { + public: + SelfResolvableTestClass() : ptr_(new int(123)) {} + CRU_DELETE_COPY(SelfResolvableTestClass) + CRU_DEFAULT_MOVE(SelfResolvableTestClass) + ~SelfResolvableTestClass() override = default; + + private: + std::shared_ptr<int> ptr_; +}; +} // namespace + +TEST_CASE("SelfResolvable resolver should work.", "[self-resolvable]") { + SelfResolvableTestClass test_object; + + auto resolver = test_object.CreateResolver(); + REQUIRE(resolver.Resolve() == &test_object); + + auto resolver_copy = resolver; + REQUIRE(resolver.Resolve() == &test_object); + REQUIRE(resolver.Resolve() == &test_object); + + auto resolver_move = std::move(resolver_copy); + REQUIRE(resolver.Resolve() == &test_object); + REQUIRE(resolver_copy.IsValid() == false); + REQUIRE(resolver_move.Resolve() == &test_object); +} + +TEST_CASE("SelfResolvable object destructed should work.", + "[self-resolvable]") { + SelfResolvableTestClass* test_object = new SelfResolvableTestClass(); + + auto resolver = test_object->CreateResolver(); + auto resolver_copy = resolver; + + delete test_object; + + REQUIRE(resolver.Resolve() == nullptr); + REQUIRE(resolver_copy.Resolve() == nullptr); + + auto resolver_copy2 = resolver_copy; + REQUIRE(resolver_copy2.Resolve() == nullptr); + + auto resolver_move = std::move(resolver_copy); + REQUIRE(resolver_copy.IsValid() == false); + REQUIRE(resolver_move.Resolve() == nullptr); +} + +TEST_CASE("SelfResolvable object moved should work.", "[self-resolvable]") { + SelfResolvableTestClass test_object; + + auto resolver = test_object.CreateResolver(); + auto resolver_copy = resolver; + + SelfResolvableTestClass moved_object = std::move(test_object); + + REQUIRE(resolver.Resolve() == &moved_object); + REQUIRE(resolver_copy.Resolve() == &moved_object); + + auto resolver_copy2 = resolver_copy; + REQUIRE(resolver_copy2.Resolve() == &moved_object); + + auto resolver_move = std::move(resolver_copy); + REQUIRE(resolver_copy.IsValid() == false); + REQUIRE(resolver_move.Resolve() == &moved_object); +} + +TEST_CASE("SelfResolvable should work for casted type.", "[self-resolvable]") { + auto test_object = new SelfResolvableTestClass(); + + cru::ObjectResolver<SelfResolvableTestClassBase> base_resolver = + test_object->CreateResolver(); + + REQUIRE(base_resolver.Resolve() == test_object); + + auto base_resolver2 = base_resolver; + REQUIRE(base_resolver2.Resolve() == test_object); + + auto base_resolver3 = std::move(base_resolver2); + REQUIRE(base_resolver3.Resolve() == test_object); + + auto moved_object = new SelfResolvableTestClass(std::move(*test_object)); + delete test_object; + + REQUIRE(base_resolver.Resolve() == moved_object); + REQUIRE(base_resolver3.Resolve() == moved_object); + + delete moved_object; + + REQUIRE(base_resolver.Resolve() == nullptr); + REQUIRE(base_resolver3.Resolve() == nullptr); +} diff --git a/test/base/StringTest.cpp b/test/base/StringTest.cpp new file mode 100644 index 00000000..9ed351e5 --- /dev/null +++ b/test/base/StringTest.cpp @@ -0,0 +1,112 @@ +#include "cru/base/Format.h" +#include "cru/base/String.h" + +#include <catch2/catch_test_macros.hpp> + +TEST_CASE("String Append", "[string]") { + using cru::String; + + String s; + s.append(u"ha"); + s.append(s); + REQUIRE(s == String(u"haha")); +} + +TEST_CASE("String IndexConvert", "[string]") { + using cru::String; + + String s(u"123"); + REQUIRE(s.IndexFromCodePointToCodeUnit(1) == 1); + REQUIRE(s.IndexFromCodeUnitToCodePoint(1) == 1); + REQUIRE(s.IndexFromCodeUnitToCodePoint(3) == 3); + REQUIRE(s.IndexFromCodeUnitToCodePoint(3) == 3); +} + +TEST_CASE("String Format", "[string]") { + using cru::Format; + using cru::String; + + REQUIRE(Format(u"{} + {} = {}", 123, 321, 444) == String(u"123 + 321 = 444")); +} + +TEST_CASE("String Trim", "[string]") { + using cru::String; + REQUIRE(String(u" abc ").Trim() == u"abc"); +} + +TEST_CASE("String SplitToLines", "[string]") { + using cru::String; + + String s(u"abc\ndef\nghi"); + auto lines = s.SplitToLines(); + REQUIRE(lines.size() == 3); + REQUIRE(lines[0] == String(u"abc")); + REQUIRE(lines[1] == String(u"def")); + REQUIRE(lines[2] == String(u"ghi")); +} + +TEST_CASE("String SplitToLinesWithEmptyLine", "[string]") { + using cru::String; + + String s(u"abc\n \ndef\n\nghi\n"); + auto lines = s.SplitToLines(); + REQUIRE(lines.size() == 6); + REQUIRE(lines[0] == String(u"abc")); + REQUIRE(lines[1] == String(u" ")); + REQUIRE(lines[2] == String(u"def")); + REQUIRE(lines[3] == String(u"")); + REQUIRE(lines[4] == String(u"ghi")); + REQUIRE(lines[5] == String(u"")); +} + +TEST_CASE("String SplitToLinesRemoveSpaceLine", "[string]") { + using cru::String; + + String s(u"abc\n \ndef\n\nghi\n"); + auto lines = s.SplitToLines(true); + REQUIRE(lines.size() == 3); + REQUIRE(lines[0] == String(u"abc")); + REQUIRE(lines[1] == String(u"def")); + REQUIRE(lines[2] == String(u"ghi")); +} + +TEST_CASE("StringView ToUtf8", "[string]") { + using cru::StringView; + StringView utf16_text = u"aπ你🤣!"; + std::string_view utf8_text = "aπ你🤣!"; + + REQUIRE(utf16_text.ToUtf8() == utf8_text); +} + +TEST_CASE("String FromUtf8", "[string]") { + std::u16string_view utf16_text = u"aπ你🤣!"; + std::string_view utf8_text = "aπ你🤣!"; + + REQUIRE(cru::String::FromUtf8(utf8_text) == utf16_text); +} + +TEST_CASE("StringView ParseToDouble", "[string]") { + using cru::StringToNumberFlags; + using cru::StringView; + REQUIRE(StringView(u"3.14159").ParseToDouble() == 3.14159); + REQUIRE( + StringView(u" 3.14159") + .ParseToDouble(nullptr, StringToNumberFlags::kAllowLeadingSpaces) == + 3.14159); + REQUIRE(StringView(u" 3.14159 ") + .ParseToDouble(nullptr, + StringToNumberFlags::kAllowLeadingSpaces | + StringToNumberFlags::kAllowTrailingSpaces) == + 3.14159); +} + +TEST_CASE("String ParseToDoubleList", "[string]") { + using cru::StringView; + + auto list = StringView(u" 1.23 2.34 3.45 ").ParseToDoubleList(); + + REQUIRE(list.size() == 3); + REQUIRE(list[0] == 1.23); + REQUIRE(list[1] == 2.34); + REQUIRE(list[2] == 3.45); +} diff --git a/test/base/StringToNumberConverterTest.cpp b/test/base/StringToNumberConverterTest.cpp new file mode 100644 index 00000000..82062bdb --- /dev/null +++ b/test/base/StringToNumberConverterTest.cpp @@ -0,0 +1,166 @@ +#include "cru/base/Exception.h" +#include "cru/base/StringToNumberConverter.h" + +#include <catch2/catch_test_macros.hpp> + +TEST_CASE("StringToIntegerConverterImpl Base0", "[string]") { + using namespace cru; + StringToIntegerConverter converter({}, 0); + Index processed_characters_count; + + REQUIRE(converter.Parse("12345678", &processed_characters_count) == + StringToIntegerResult(false, 12345678)); + REQUIRE(processed_characters_count == 8); + + REQUIRE(converter.Parse("0", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 1); + + REQUIRE(converter.Parse("012", &processed_characters_count) == + StringToIntegerResult(false, 012)); + REQUIRE(processed_characters_count == 3); + + REQUIRE(converter.Parse("0x12", &processed_characters_count) == + StringToIntegerResult(false, 0x12)); + REQUIRE(processed_characters_count == 4); + + REQUIRE(converter.Parse("0X12", &processed_characters_count) == + StringToIntegerResult(false, 0x12)); + REQUIRE(processed_characters_count == 4); + + REQUIRE(converter.Parse("0b101", &processed_characters_count) == + StringToIntegerResult(false, 0b101)); + REQUIRE(processed_characters_count == 5); + + REQUIRE(converter.Parse("0B101", &processed_characters_count) == + StringToIntegerResult(false, 0b101)); + REQUIRE(processed_characters_count == 5); + + REQUIRE(converter.Parse("-123", &processed_characters_count) == + StringToIntegerResult(true, 123)); + REQUIRE(processed_characters_count == 4); + + REQUIRE(converter.Parse("-0x10", &processed_characters_count) == + StringToIntegerResult(true, 0x10)); + REQUIRE(processed_characters_count == 5); +} + +TEST_CASE("StringToIntegerConverterImpl Base0ForError", "[string]") { + using namespace cru; + StringToIntegerConverter converter({}, 0); + Index processed_characters_count; + + REQUIRE(converter.Parse("a", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 0); + + REQUIRE(converter.Parse("0a", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 0); + + REQUIRE(converter.Parse("0x", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 0); + + REQUIRE(converter.Parse("0xx", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 0); + + REQUIRE(converter.Parse(" 0", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 0); + + REQUIRE(converter.Parse("0 ", &processed_characters_count) == + StringToIntegerResult(false, 0)); + REQUIRE(processed_characters_count == 0); +} + +TEST_CASE("StringToIntegerConverterImpl ThrowOnErrorFlag", "[string]") { + using namespace cru; + StringToIntegerConverter converter(StringToNumberFlags::kThrowOnError, 0); + Index processed_characters_count; + REQUIRE_THROWS_AS(converter.Parse("?", &processed_characters_count), + Exception); +} + +TEST_CASE("StringToIntegerConverterImpl AllowLeadingZeroFlag", "[string]") { + using namespace cru; + StringToIntegerConverter converter( + StringToNumberFlags::kAllowLeadingSpaces, 0); + Index processed_characters_count; + REQUIRE(converter.Parse(" 123", &processed_characters_count) == + StringToIntegerResult(false, 123)); + REQUIRE(processed_characters_count == 6); +} + +TEST_CASE("StringToIntegerConverterImpl AllowTrailingZeroFlag", "[string]") { + using namespace cru; + StringToIntegerConverter converter( + StringToNumberFlags::kAllowTrailingSpaces, 0); + Index processed_characters_count; + REQUIRE(converter.Parse("123 ", &processed_characters_count) == + StringToIntegerResult(false, 123)); + REQUIRE(processed_characters_count == 6); +} + +TEST_CASE("StringToIntegerConverterImpl AllowTrailingJunk", "[string]") { + using namespace cru; + StringToIntegerConverter converter( + StringToNumberFlags::kAllowTrailingJunk, 0); + Index processed_characters_count; + REQUIRE(converter.Parse("123 12", &processed_characters_count) == + StringToIntegerResult(false, 123)); + REQUIRE(processed_characters_count == 3); +} + +TEST_CASE("StringToIntegerConverterImpl AllowLeadingZeroForInteger", + "[string]") { + using namespace cru; + StringToIntegerConverter converter( + StringToNumberFlags::kAllowLeadingZeroForInteger, 0); + Index processed_characters_count; + REQUIRE(converter.Parse("0x0012", &processed_characters_count) == + StringToIntegerResult(false, 0x12)); + REQUIRE(processed_characters_count == 6); + + REQUIRE(converter.Parse("000011", &processed_characters_count) == + StringToIntegerResult(false, 000011)); + REQUIRE(processed_characters_count == 6); + + REQUIRE(converter.Parse("0b0011", &processed_characters_count) == + StringToIntegerResult(false, 0b0011)); + REQUIRE(processed_characters_count == 6); +} + +TEST_CASE("StringToIntegerConverterImpl CompositeFlags", "[string]") { + using namespace cru; + StringToIntegerConverter converter( + StringToNumberFlags::kAllowLeadingSpaces | + StringToNumberFlags::kAllowTrailingJunk | + StringToNumberFlags::kAllowLeadingZeroForInteger, + 0); + Index processed_characters_count; + + REQUIRE(converter.Parse(" 0x00123!!!", &processed_characters_count) == + StringToIntegerResult(false, 0x00123)); + REQUIRE(processed_characters_count == 10); +} + +TEST_CASE("StringToIntegerConverterImpl OtherBase", "[string]") { + using namespace cru; + StringToIntegerConverter converter({}, 7); + Index processed_characters_count; + + REQUIRE(converter.Parse("12", &processed_characters_count) == + StringToIntegerResult(false, 9)); + REQUIRE(processed_characters_count == 2); + + REQUIRE(converter.Parse("-12", &processed_characters_count) == + StringToIntegerResult(true, 9)); + REQUIRE(processed_characters_count == 3); + + converter.base = 11; + REQUIRE(converter.Parse("1a", &processed_characters_count) == + StringToIntegerResult(false, 21)); + REQUIRE(processed_characters_count == 2); +} diff --git a/test/base/StringUtilTest.cpp b/test/base/StringUtilTest.cpp new file mode 100644 index 00000000..1da6e963 --- /dev/null +++ b/test/base/StringUtilTest.cpp @@ -0,0 +1,118 @@ +#include "cru/base/String.h" +#include "cru/base/StringUtil.h" + +#include <catch2/catch_test_macros.hpp> + +using cru::Index; +using cru::k_invalid_code_point; + +TEST_CASE("StringUtil Utf8NextCodePoint", "[string]") { + using cru::Utf8NextCodePoint; + std::string_view text = "aπ你🤣!"; + Index current = 0; + REQUIRE(Utf8NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x0061); + REQUIRE(Utf8NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x03C0); + REQUIRE(Utf8NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x4F60); + REQUIRE(Utf8NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x1F923); + REQUIRE(Utf8NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x0021); + REQUIRE(Utf8NextCodePoint(text.data(), text.size(), current, ¤t) == + k_invalid_code_point); + REQUIRE(current == static_cast<Index>(text.size())); +} + +TEST_CASE("StringUtil Utf16NextCodePoint", "[string]") { + using cru::Utf16NextCodePoint; + std::u16string_view text = u"aπ你🤣!"; + Index current = 0; + REQUIRE(Utf16NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x0061); + REQUIRE(Utf16NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x03C0); + REQUIRE(Utf16NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x4F60); + REQUIRE(Utf16NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x1F923); + REQUIRE(Utf16NextCodePoint(text.data(), text.size(), current, ¤t) == + 0x0021); + REQUIRE(Utf16NextCodePoint(text.data(), text.size(), current, ¤t) == + k_invalid_code_point); + REQUIRE(current == static_cast<Index>(text.size())); +} + +TEST_CASE("StringUtil Utf16PreviousCodePoint", "[string]") { + using cru::Utf16PreviousCodePoint; + std::u16string_view text = u"aπ你🤣!"; + Index current = text.size(); + REQUIRE(Utf16PreviousCodePoint(text.data(), text.size(), current, ¤t) == + 0x0021); + REQUIRE(Utf16PreviousCodePoint(text.data(), text.size(), current, ¤t) == + 0x1F923); + REQUIRE(Utf16PreviousCodePoint(text.data(), text.size(), current, ¤t) == + 0x4F60); + REQUIRE(Utf16PreviousCodePoint(text.data(), text.size(), current, ¤t) == + 0x03C0); + REQUIRE(Utf16PreviousCodePoint(text.data(), text.size(), current, ¤t) == + 0x0061); + REQUIRE(Utf16PreviousCodePoint(text.data(), text.size(), current, ¤t) == + k_invalid_code_point); + REQUIRE(current == 0); +} + +TEST_CASE("StringUtil Utf8CodePointIterator", "[string]") { + using cru::Utf8CodePointIterator; + std::string_view text = "aπ你🤣!"; + std::vector<cru::CodePoint> code_points; + + for (auto cp : Utf8CodePointIterator(text.data(), text.size())) { + code_points.push_back(cp); + } + + std::vector<cru::CodePoint> expected_code_points{0x0061, 0x03C0, 0x4F60, + 0x1F923, 0x0021}; + + REQUIRE(code_points == expected_code_points); +} + +TEST_CASE("StringUtil Utf16CodePointIterator", "[string]") { + using cru::Utf16CodePointIterator; + std::u16string_view text = u"aπ你🤣!"; + std::vector<cru::CodePoint> code_points; + + for (auto cp : Utf16CodePointIterator(text.data(), text.size())) { + code_points.push_back(cp); + } + + std::vector<cru::CodePoint> expected_code_points{0x0061, 0x03C0, 0x4F60, + 0x1F923, 0x0021}; + + REQUIRE(code_points == expected_code_points); +} + +// TEST(WinString, IndexUtf8ToUtf16) { +// using cru::platform::win::IndexUtf8ToUtf16; +// std::string_view utf8_string = "aπ你🤣!"; +// std::wstring_view utf16_string = L"aπ你🤣!"; +// REQUIRE(IndexUtf8ToUtf16(utf8_string, 0, utf16_string), 0); +// REQUIRE(IndexUtf8ToUtf16(utf8_string, 1, utf16_string), 1); +// REQUIRE(IndexUtf8ToUtf16(utf8_string, 3, utf16_string), 2); +// REQUIRE(IndexUtf8ToUtf16(utf8_string, 6, utf16_string), 3); +// REQUIRE(IndexUtf8ToUtf16(utf8_string, 10, utf16_string), 5); +// REQUIRE(IndexUtf8ToUtf16(utf8_string, 11, utf16_string), 6); +// } + +// TEST(WinString, IndexUtf16ToUtf8) { +// using cru::platform::win::IndexUtf16ToUtf8; +// std::string_view utf8_string = "aπ你🤣!"; +// std::wstring_view utf16_string = L"aπ你🤣!"; +// REQUIRE(IndexUtf16ToUtf8(utf16_string, 0, utf8_string), 0); +// REQUIRE(IndexUtf16ToUtf8(utf16_string, 1, utf8_string), 1); +// REQUIRE(IndexUtf16ToUtf8(utf16_string, 2, utf8_string), 3); +// REQUIRE(IndexUtf16ToUtf8(utf16_string, 3, utf8_string), 6); +// REQUIRE(IndexUtf16ToUtf8(utf16_string, 5, utf8_string), 10); +// REQUIRE(IndexUtf16ToUtf8(utf16_string, 6, utf8_string), 11); +// } diff --git a/test/base/SubProcessHelper/CruEcho.cpp b/test/base/SubProcessHelper/CruEcho.cpp new file mode 100644 index 00000000..5f23c027 --- /dev/null +++ b/test/base/SubProcessHelper/CruEcho.cpp @@ -0,0 +1,9 @@ +#include <iostream> + +int main(int argc, char* argv[]) { + for (int i = 1; i < argc - 1; ++i) { + std::cout << argv[i] << " "; + } + std::cout << argv[argc - 1]; + return 0; +} diff --git a/test/base/SubProcessHelper/CruTee.cpp b/test/base/SubProcessHelper/CruTee.cpp new file mode 100644 index 00000000..4470d2a8 --- /dev/null +++ b/test/base/SubProcessHelper/CruTee.cpp @@ -0,0 +1,10 @@ +#include <iostream> +#include <string> + +int main() { + std::string s; + while (std::cin >> s) { + std::cout << s; + } + return 0; +} diff --git a/test/base/SubProcessTest.cpp b/test/base/SubProcessTest.cpp new file mode 100644 index 00000000..d353dec0 --- /dev/null +++ b/test/base/SubProcessTest.cpp @@ -0,0 +1,30 @@ +#include "cru/base/String.h" +#include "cru/base/SubProcess.h" + +#include <catch2/catch_test_macros.hpp> + +using cru::String; +using cru::SubProcess; + +TEST_CASE("SubProcess", "[subprocess]") { + SECTION("echo should work.") { + SubProcess process = SubProcess::Create( + String::FromUtf8(CRU_TEST_HELPER_ECHO_LOCATION), {u"abc"}); + process.Wait(); + REQUIRE(process.GetExitResult().IsSuccess()); + auto output = process.GetStdoutStream()->ReadToEndAsUtf8String(); + REQUIRE(output == u"abc"); + } + + SECTION("tee should work.") { + constexpr auto str = "abc"; + SubProcess process = + SubProcess::Create(String::FromUtf8(CRU_TEST_HELPER_TEE_LOCATION)); + process.GetStdinStream()->Write(str, 3); + process.GetStdinStream()->Close(); + process.Wait(); + REQUIRE(process.GetExitResult().IsSuccess()); + auto output = process.GetStdoutStream()->ReadToEndAsUtf8String(); + REQUIRE(output == u"abc"); + } +} diff --git a/test/base/platform/unix/UnixFileStreamTest.cpp b/test/base/platform/unix/UnixFileStreamTest.cpp new file mode 100644 index 00000000..417fccc2 --- /dev/null +++ b/test/base/platform/unix/UnixFileStreamTest.cpp @@ -0,0 +1,30 @@ +#include "cru/base/platform/unix/UnixFileStream.h" + +#include <catch2/catch_test_macros.hpp> + +#include <fcntl.h> +#include <filesystem> + +TEST_CASE("UnixFileStream Work", "[stream]") { + using namespace cru; + using namespace cru::io; + using namespace cru::platform::unix; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .generic_string(); + mkstemp(temp_file_path.data()); + + UnixFileStream file(temp_file_path.c_str(), O_WRONLY | O_CREAT); + file.Write("abc", 3); + file.Close(); + + UnixFileStream file2(temp_file_path.c_str(), O_RDONLY); + auto buffer = std::make_unique<std::byte[]>(3); + file2.Read(buffer.get(), 3); + REQUIRE(std::string_view(reinterpret_cast<const char*>(buffer.get()), 3) == + "abc"); + file2.Close(); + + std::filesystem::remove(temp_file_path); +} diff --git a/test/base/platform/unix/UnixFileTest.cpp b/test/base/platform/unix/UnixFileTest.cpp new file mode 100644 index 00000000..d5bba0db --- /dev/null +++ b/test/base/platform/unix/UnixFileTest.cpp @@ -0,0 +1,38 @@ + +#include "cru/base/platform/unix/UnixFileStream.h" + +#include <catch2/catch_test_macros.hpp> + +#include <fcntl.h> +#include <filesystem> + +TEST_CASE("UnixFile Work", "[unix]") { + using namespace cru; + using namespace cru::platform::unix; + + auto calledTimes = 0; + auto mockClose = [&calledTimes](int _) { + calledTimes += 1; + return 0; + }; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .generic_string(); + mkstemp(temp_file_path.data()); + + auto fdNumber = ::open(temp_file_path.c_str(), O_WRONLY | O_CREAT); + + { + UnixFileDescriptor fd(fdNumber, true, std::move(mockClose)); + REQUIRE(calledTimes == 0); + UnixFileDescriptor fd2(std::move(fd)); + REQUIRE(calledTimes == 0); + UnixFileDescriptor fd3; + fd3 = std::move(fd2); + REQUIRE(calledTimes == 0); + } + REQUIRE(calledTimes == 1); + + std::filesystem::remove(temp_file_path); +} diff --git a/test/base/platform/win/StreamConvertTest.cpp b/test/base/platform/win/StreamConvertTest.cpp new file mode 100644 index 00000000..90ebb568 --- /dev/null +++ b/test/base/platform/win/StreamConvertTest.cpp @@ -0,0 +1,39 @@ +#include "cru/base/io/OpenFileFlag.h" +#include "cru/base/platform/win/Exception.h" +#include "cru/base/platform/win/StreamConvert.h" +#include "cru/base/platform/win/Win32FileStream.h" + +#include <catch2/catch_test_macros.hpp> + +#include <cstdio> +#include <filesystem> + +TEST_CASE("StreamConvert FileStreamWork", "[stream]") { + using namespace cru; + using namespace cru::io; + using namespace cru::platform::win; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .native(); + _wmktemp(temp_file_path.data()); + + String path = temp_file_path; + + Win32FileStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); + file.Write("abc", 3); + file.Close(); + + Win32FileStream file2(path, OpenFileFlags::Read); + IStream* com_stream = ConvertStreamToComStream(&file2); + LARGE_INTEGER position; + position.QuadPart = 0; + ThrowIfFailed(com_stream->Seek(position, SEEK_SET, nullptr)); + auto buffer = std::make_unique<char[]>(3); + ThrowIfFailed(com_stream->Read(buffer.get(), 3, nullptr)); + REQUIRE(std::string_view(buffer.get(), 3) == "abc"); + com_stream->Release(); + file2.Close(); + + std::filesystem::remove(temp_file_path); +} diff --git a/test/base/platform/win/Win32FileStreamTest.cpp b/test/base/platform/win/Win32FileStreamTest.cpp new file mode 100644 index 00000000..dc2f49d4 --- /dev/null +++ b/test/base/platform/win/Win32FileStreamTest.cpp @@ -0,0 +1,37 @@ +#include "cru/base/io/OpenFileFlag.h" +#include "cru/base/platform/win/Win32FileStream.h" + +#include <catch2/catch_test_macros.hpp> + +#include <cstdio> +#include <filesystem> + +TEST_CASE("Win32FileStream Work", "[stream]") { + using namespace cru; + using namespace cru::io; + using namespace cru::platform::win; + + auto temp_file_path = + (std::filesystem::temp_directory_path() / "cru_test_temp.XXXXXX") + .native(); + _wmktemp(temp_file_path.data()); + + String path = temp_file_path; + + Win32FileStream file(path, OpenFileFlags::Write | OpenFileFlags::Create); + auto write_count = file.Write("abc", 3); + REQUIRE(write_count == 3); + file.Close(); + + REQUIRE(std::filesystem::file_size(path.ToUtf8()) == 3); + + Win32FileStream file2(path, OpenFileFlags::Read); + auto buffer = std::make_unique<std::byte[]>(3); + auto read_count = file2.Read(buffer.get(), 3); + REQUIRE(read_count == 3); + REQUIRE(std::string_view(reinterpret_cast<const char*>(buffer.get()), 3) == + "abc"); + file2.Close(); + + std::filesystem::remove(temp_file_path); +} |