#include #include #include namespace VFS::Path { namespace { using namespace testing; template struct TypePair { using Type0 = T0; using Type1 = T1; }; struct VFSPathIsNormalizedTest : TestWithParam> { }; TEST_P(VFSPathIsNormalizedTest, shouldReturnExpectedResult) { EXPECT_EQ(isNormalized(GetParam().first), GetParam().second); } const std::pair isNormalizedTestParams[] = { { std::string_view(), true }, { "foo", true }, { "foo/bar", true }, { "foo/bar/baz", true }, { "/foo", false }, { "foo//", false }, { "foo\\", false }, { "Foo", false }, }; INSTANTIATE_TEST_SUITE_P(IsNormalizedTestParams, VFSPathIsNormalizedTest, ValuesIn(isNormalizedTestParams)); TEST(VFSPathNormalizeFilenameInPlaceTest, shouldRemoveLeadingSeparators) { std::string value("//foo"); normalizeFilenameInPlace(value); EXPECT_EQ(value, "foo"); } TEST(VFSPathNormalizeFilenameInPlaceTest, shouldRemoveDuplicatedSeparators) { std::string value("foo//bar///baz"); normalizeFilenameInPlace(value); EXPECT_EQ(value, "foo/bar/baz"); } TEST(VFSPathNormalizeFilenameInPlaceTest, shouldRemoveDuplicatedLeadingSeparator) { std::string value("//foo"); normalizeFilenameInPlace(value); EXPECT_EQ(value, "foo"); } TEST(VFSPathExtensionViewTest, shouldSupportDefaultConstructor) { constexpr ExtensionView extension; EXPECT_TRUE(extension.empty()); EXPECT_EQ(extension.value(), ""); } TEST(VFSPathExtensionViewTest, shouldSupportConstexprConstructorFromConstCharPtr) { constexpr ExtensionView extension("png"); EXPECT_FALSE(extension.empty()); EXPECT_EQ(extension.value(), "png"); } TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionOnNotNormalizedValue) { EXPECT_THROW([] { ExtensionView("PNG"); }(), std::invalid_argument); } TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionIfValueContainsExtensionSeparator) { EXPECT_THROW([] { ExtensionView(".png"); }(), std::invalid_argument); } TEST(VFSPathExtensionViewTest, constructorShouldThrowExceptionIfValueContainsSeparator) { EXPECT_THROW([] { ExtensionView("/png"); }(), std::invalid_argument); } template struct VFSPathExtensionViewOperatorsTest : Test { }; TYPED_TEST_SUITE_P(VFSPathExtensionViewOperatorsTest); TYPED_TEST_P(VFSPathExtensionViewOperatorsTest, supportsEqual) { using Type0 = typename TypeParam::Type0; using Type1 = typename TypeParam::Type1; const Type0 extension{ "png" }; const Type1 otherEqual{ "png" }; const Type1 otherNotEqual{ "jpg" }; EXPECT_EQ(extension, otherEqual); EXPECT_EQ(otherEqual, extension); EXPECT_NE(extension, otherNotEqual); EXPECT_NE(otherNotEqual, extension); } TYPED_TEST_P(VFSPathExtensionViewOperatorsTest, supportsLess) { using Type0 = typename TypeParam::Type0; using Type1 = typename TypeParam::Type1; const Type0 extension{ "png" }; const Type1 otherEqual{ "png" }; const Type1 otherLess{ "jpg" }; const Type1 otherGreater{ "tga" }; EXPECT_FALSE(extension < otherEqual); EXPECT_FALSE(otherEqual < extension); EXPECT_LT(otherLess, extension); EXPECT_FALSE(extension < otherLess); EXPECT_LT(extension, otherGreater); EXPECT_FALSE(otherGreater < extension); } REGISTER_TYPED_TEST_SUITE_P(VFSPathExtensionViewOperatorsTest, supportsEqual, supportsLess); using VFSPathExtensionViewOperatorsTypePairs = Types, TypePair, TypePair, TypePair>; INSTANTIATE_TYPED_TEST_SUITE_P( Typed, VFSPathExtensionViewOperatorsTest, VFSPathExtensionViewOperatorsTypePairs); TEST(VFSPathNormalizedTest, shouldSupportDefaultConstructor) { const Normalized value; EXPECT_EQ(value.value(), ""); } TEST(VFSPathNormalizedTest, shouldSupportConstructorFromString) { const std::string string("Foo\\Bar/baz"); const Normalized value(string); EXPECT_EQ(value.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, shouldSupportConstructorFromConstCharPtr) { const char* const ptr = "Foo\\Bar/baz"; const Normalized value(ptr); EXPECT_EQ(value.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, shouldSupportConstructorFromStringView) { const std::string_view view = "Foo\\Bar/baz"; const Normalized value(view); EXPECT_EQ(value.view(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, shouldSupportConstructorFromNormalizedView) { const NormalizedView view("foo/bar/baz"); const Normalized value(view); EXPECT_EQ(value.view(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, supportMovingValueOut) { Normalized value("Foo\\Bar/baz"); EXPECT_EQ(std::move(value).value(), "foo/bar/baz"); EXPECT_EQ(value.value(), ""); } TEST(VFSPathNormalizedTest, isNotEqualToNotNormalized) { const Normalized value("Foo\\Bar/baz"); EXPECT_NE(value.value(), "Foo\\Bar/baz"); } TEST(VFSPathNormalizedTest, shouldSupportOperatorLeftShiftToOStream) { const Normalized value("Foo\\Bar/baz"); std::stringstream stream; stream << value; EXPECT_EQ(stream.str(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, shouldSupportOperatorDivEqual) { Normalized value("foo/bar"); value /= NormalizedView("baz"); EXPECT_EQ(value.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, shouldSupportOperatorDivEqualWithStringView) { Normalized value("foo/bar"); value /= std::string_view("BAZ"); EXPECT_EQ(value.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedTest, operatorDivShouldNormalizeSuffix) { Normalized value("foo/bar"); value /= std::string_view("\\A\\\\B"); EXPECT_EQ(value.value(), "foo/bar/a/b"); } TEST(VFSPathNormalizedTest, operatorDivForNormalizedViewShouldNotPrependSeparatorToEmpty) { Normalized value; value /= NormalizedView("a"); EXPECT_EQ(value.value(), "a"); } TEST(VFSPathNormalizedTest, operatorDivForStringViewShouldNotPrependSeparatorToEmpty) { Normalized value; value /= std::string_view("a"); EXPECT_EQ(value.value(), "a"); } TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceAfterLastDot) { Normalized value("foo/ba.r.a"); constexpr ExtensionView extension("so"); ASSERT_TRUE(value.changeExtension(extension)); EXPECT_EQ(value.value(), "foo/ba.r.so"); } TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithoutADot) { Normalized value("foo/bar"); constexpr ExtensionView extension("so"); ASSERT_FALSE(value.changeExtension(extension)); EXPECT_EQ(value.value(), "foo/bar"); } TEST(VFSPathNormalizedTest, changeExtensionShouldIgnorePathWithDotBeforeSeparator) { Normalized value("foo.bar/baz"); constexpr ExtensionView extension("so"); ASSERT_FALSE(value.changeExtension(extension)); EXPECT_EQ(value.value(), "foo.bar/baz"); } TEST(VFSPathNormalizedTest, changeExtensionShouldReplaceWithShorterExtension) { Normalized value("foo/bar.nif"); constexpr ExtensionView extension("kf"); ASSERT_TRUE(value.changeExtension(extension)); EXPECT_EQ(value.value(), "foo/bar.kf"); } TEST(VFSPathNormalizedTest, filenameShouldReturnLastComponentOfThePath) { const Normalized value("foo/bar"); EXPECT_EQ(value.filename(), "bar"); } TEST(VFSPathNormalizedTest, filenameShouldReturnSameValueForPathWithSingleComponent) { const Normalized value("foo"); EXPECT_EQ(value.filename(), "foo"); } TEST(VFSPathNormalizedTest, appendShouldAddExtensionWithSeparator) { Normalized value("foo"); value.append(ExtensionView("ext")); EXPECT_EQ(value.value(), "foo.ext"); } TEST(VFSPathNormalizedTest, appendForEmptyShouldAddExtensionWithSeparator) { Normalized value; value.append(ExtensionView("ext")); EXPECT_EQ(value.value(), ".ext"); } template struct VFSPathNormalizedOperatorsTest : Test { }; TYPED_TEST_SUITE_P(VFSPathNormalizedOperatorsTest); TYPED_TEST_P(VFSPathNormalizedOperatorsTest, supportsEqual) { using Type0 = typename TypeParam::Type0; using Type1 = typename TypeParam::Type1; const Type0 normalized{ "a/foo/bar/baz" }; const Type1 otherEqual{ "a/foo/bar/baz" }; const Type1 otherNotEqual{ "b/foo/bar/baz" }; EXPECT_EQ(normalized, otherEqual); EXPECT_EQ(otherEqual, normalized); EXPECT_NE(normalized, otherNotEqual); EXPECT_NE(otherNotEqual, normalized); } TYPED_TEST_P(VFSPathNormalizedOperatorsTest, supportsLess) { using Type0 = typename TypeParam::Type0; using Type1 = typename TypeParam::Type1; const Type0 normalized{ "b/foo/bar/baz" }; const Type1 otherEqual{ "b/foo/bar/baz" }; const Type1 otherLess{ "a/foo/bar/baz" }; const Type1 otherGreater{ "c/foo/bar/baz" }; EXPECT_FALSE(normalized < otherEqual); EXPECT_FALSE(otherEqual < normalized); EXPECT_LT(otherLess, normalized); EXPECT_FALSE(normalized < otherLess); EXPECT_LT(normalized, otherGreater); EXPECT_FALSE(otherGreater < normalized); } REGISTER_TYPED_TEST_SUITE_P(VFSPathNormalizedOperatorsTest, supportsEqual, supportsLess); using VFSPathNormalizedOperatorsTypePairs = Types, TypePair, TypePair, TypePair, TypePair, TypePair, TypePair, TypePair, TypePair, TypePair>; INSTANTIATE_TYPED_TEST_SUITE_P(Typed, VFSPathNormalizedOperatorsTest, VFSPathNormalizedOperatorsTypePairs); TEST(VFSPathNormalizedViewTest, shouldSupportConstructorFromNormalized) { const Normalized value("Foo\\Bar/baz"); const NormalizedView view(value); EXPECT_EQ(view.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedViewTest, shouldSupportConstexprConstructorFromNormalizedStringLiteral) { constexpr NormalizedView view("foo/bar/baz"); EXPECT_EQ(view.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedViewTest, constructorShouldThrowExceptionOnNotNormalized) { EXPECT_THROW([] { NormalizedView("Foo\\Bar/baz"); }(), std::invalid_argument); } TEST(VFSPathNormalizedViewTest, shouldSupportOperatorDiv) { const NormalizedView a("foo/bar"); const NormalizedView b("baz"); const Normalized result = a / b; EXPECT_EQ(result.value(), "foo/bar/baz"); } TEST(VFSPathNormalizedViewTest, filenameShouldReturnLastComponentOfThePath) { const NormalizedView value("foo/bar"); EXPECT_EQ(value.filename(), "bar"); } TEST(VFSPathNormalizedViewTest, filenameShouldReturnSameValueForPathWithSingleComponent) { const NormalizedView value("foo"); EXPECT_EQ(value.filename(), "foo"); } TEST(VFSPathNormalizedViewTest, stemShouldOmitPathAndExtension) { const NormalizedView value("foo/bar.a"); EXPECT_EQ(value.stem(), "bar"); } TEST(VFSPathNormalizedViewTest, stemShouldReturnSameValueForPathWithSingleComponent) { const NormalizedView value("foo"); EXPECT_EQ(value.stem(), "foo"); } TEST(VFSPathNormalizedViewTest, stemShouldIncludeDotsBeforeTheExtension) { const NormalizedView value("last voyage of the u.s.s. constitution.swf"); EXPECT_EQ(value.stem(), "last voyage of the u.s.s. constitution"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleValue) { EXPECT_EQ(join(Normalized("foo")), "foo"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleView) { EXPECT_EQ(join(NormalizedView("foo")), "foo"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleString) { EXPECT_EQ(join(std::string("FOO")), "foo"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleStringView) { EXPECT_EQ(join(std::string_view("FOO")), "foo"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleConstCharArray) { EXPECT_EQ(join("FOO"), "foo"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleConstCharPtr) { const char* const ptr = "FOO"; EXPECT_EQ(join(ptr), "foo"); } TEST(VFSPathNormalizedJoinTest, shouldHandleSingleExtension) { constexpr ExtensionView extension("foo"); EXPECT_EQ(join(extension), ".foo"); } TEST(VFSPathNormalizedJoinTest, shouldJoinTwoPathComponents) { EXPECT_EQ(join(NormalizedView("foo"), std::string_view("BAR")), "foo/bar"); } TEST(VFSPathNormalizedJoinTest, shouldJoinComponentAndExtension) { EXPECT_EQ(join(NormalizedView("foo"), ExtensionView("bar")), "foo.bar"); } TEST(VFSPathNormalizedJoinTest, shouldJoinTwoExtensions) { EXPECT_EQ(join(ExtensionView("foo"), ExtensionView("bar")), ".foo.bar"); } TEST(VFSPathNormalizedJoinTest, shouldJoinTreePathComponents) { EXPECT_EQ(join(NormalizedView("a"), std::string_view("B"), Normalized("c")), "a/b/c"); } TEST(VFSPathNormalizedJoinedCapacityTest, forNormalizedShouldReturnSizeOnly) { EXPECT_EQ(joinedCapacity(Normalized("a")), 1); } TEST(VFSPathNormalizedJoinedCapacityTest, forConstCharPtrShouldReturnSizeOnly) { EXPECT_EQ(joinedCapacity("a"), 1); } TEST(VFSPathNormalizedJoinedCapacityTest, forExtensionViewShouldReturnSizePlusSeparator) { EXPECT_EQ(joinedCapacity(ExtensionView("a")), 2); } TEST(VFSPathNormalizedJoinedCapacityTest, shouldReturnSumOfSizesPlusNumberOfSeparators) { constexpr NormalizedView b("b"); const Normalized c("c"); constexpr std::string_view d("d"); const std::string e("e"); constexpr ExtensionView f("f"); EXPECT_EQ(joinedCapacity("a", b, c, d, e, f), 11); } } }