Revision 6f166ddc542990214ed885ffdb622418b78f873c authored by Christoph Lehmann on 12 April 2023, 06:30:25 UTC, committed by Christoph Lehmann on 12 April 2023, 06:30:25 UTC
Draft: Remove mat vec provider

See merge request ogs/ogs!4549
2 parent s c7b9252 + d833389
Raw File
TestStrongType.cpp
/**
 * \file
 * \copyright
 * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org)
 *            Distributed under a Modified BSD License.
 *              See accompanying file LICENSE.txt or
 *              http://www.opengeosys.org/project/license
 *
 */

#include <gmock/gmock-matchers.h>
#include <gtest/gtest.h>

#include <array>

#include "BaseLib/StrongType.h"

TEST(BaseLibStrongType, CopyOrMoveValueIn_Class)
{
    using StrongVector =
        BaseLib::StrongType<std::vector<int>, struct StrongVectorTag>;

    std::vector<int> const v{1, 2};

    // copy value in
    {
        StrongVector const sv{v};

        EXPECT_THAT(sv(), ::testing::ContainerEq(v));
    }

    // copy value in, mutable vector
    {
        std::vector<int> v_copy = v;

        StrongVector sv{v_copy};

        EXPECT_THAT(sv(), ::testing::ContainerEq(v));

        sv()[0] = 8;
        sv().back() = 6;

        EXPECT_THAT(v_copy, ::testing::ContainerEq(v))
            << "the input vector was modified";
        EXPECT_EQ(8, sv().front());
        EXPECT_EQ(6, sv()[1]);
    }

    // move value in
    {
        using StrongIntPtr =
            BaseLib::StrongType<std::shared_ptr<int>, struct StrongIntPtrTag>;

        auto ip = std::make_shared<int>(23);
        StrongIntPtr const sip{std::move(ip)};

        EXPECT_FALSE(ip) << "the input pointer was not moved from";
        EXPECT_EQ(23, **sip);
    }
}

TEST(BaseLibStrongType, CopyValueIn_PrimitiveType)
{
    using StrongDouble = BaseLib::StrongType<double, struct StrongDoubleTag>;

    double const d = 3;

    // copy value in
    {
        StrongDouble sd{d};

        EXPECT_EQ(3, sd());
    }

    // copy value in, mutable value
    {
        double d2 = d;

        StrongDouble sd{d2};

        EXPECT_EQ(3, sd());

        sd() = 5;

        EXPECT_EQ(3, d2) << "the original data was modified";
        EXPECT_EQ(5, sd());
    }
}

TEST(BaseLibStrongType, Construction)
{
    using Vector = std::vector<int>;
    using StrongVector = BaseLib::StrongType<Vector, struct StrongVectorTag>;

    // default construct
    {
        StrongVector const sv;

        EXPECT_TRUE(sv().empty());
    }

    StrongVector const sv{{1, 2}};

    // copy construct
    {
        StrongVector sv2{sv};
        StrongVector sv3{sv2};

        EXPECT_THAT(sv2(), ::testing::ContainerEq(sv()));
        EXPECT_THAT(sv3(), ::testing::ContainerEq(sv()));

        sv3()[0] = 8;
        sv3().back() = 6;

        EXPECT_THAT(sv2(), ::testing::ContainerEq(sv()))
            << "only sv3 should have changed, but sv2 has, too";
    }

    // move construct
    {
        using StrongIntPtr =
            BaseLib::StrongType<std::shared_ptr<int>, struct StrongIntPtrTag>;

        StrongIntPtr sip{std::make_shared<int>(42)};
        StrongIntPtr const sip2{std::move(sip)};

        EXPECT_FALSE(*sip);
        EXPECT_EQ(42, **sip2);
    }
}

TEST(BaseLibStrongType, Assignment)
{
    using StrongVector =
        BaseLib::StrongType<std::vector<int>, struct StrongVectorTag>;

    StrongVector const sv{{7, 9}};

    // copy assign
    {
        StrongVector sv2{sv};
        StrongVector sv3;
        sv3 = sv2;

        EXPECT_THAT(sv3(), ::testing::ContainerEq(sv()));

        sv3()[0] = 8;
        sv3().back() = 6;

        EXPECT_THAT(sv2(), ::testing::ContainerEq(sv()))
            << "only sv3 should have changed, but sv2 has, too";
    }

    // move assign
    {
        using StrongIntPtr =
            BaseLib::StrongType<std::shared_ptr<int>, struct StrongIntPtrTag>;

        StrongIntPtr sip{std::make_shared<int>(42)};
        StrongIntPtr sip2;
        sip2 = std::move(sip);

        EXPECT_FALSE(*sip);
        EXPECT_EQ(42, **sip2);
    }
}

TEST(BaseLibStrongType, AccessOperators)
{
    using T = std::vector<int>;
    using ST = BaseLib::StrongType<T, struct Tag>;

    // const access
    {
        ST const st{{1, 2, 3, 4}};

        EXPECT_EQ(4, st().size());
        EXPECT_EQ(4, (*st).size());
        EXPECT_EQ(4, st->size());

        EXPECT_THAT(st(), ::testing::ContainerEq(*st));
        EXPECT_EQ(st(), *st);  // uses the std::vector comparison operator
    }

    // non-const access
    {
        T const t{1, 2, 3, 4};
        ST st{t};

        EXPECT_EQ(4, st->size());

        st().push_back(7);
        st().push_back(0);

        EXPECT_EQ(6, (*st).size());

        (*st).pop_back();

        EXPECT_EQ(5, st->size());

        st->pop_back();

        EXPECT_EQ(4, st().size());
        EXPECT_THAT(*st, ::testing::ContainerEq(t));
    }
}

TEST(BaseLibStrongType, Constexpr)
{
    using ST = BaseLib::StrongType<int, struct Tag>;
    constexpr ST s{5};

    static_assert(s() == 5);

    std::array<double, s()> a = {};
    static_assert(a.size() == 5);
}

TEST(BaseLibStrongType, CompileTimePure)
{
    auto check = []<typename T>(std::type_identity<T>)
    {
        using ST = BaseLib::StrongType<T, struct Tag1>;

        // construction and assignment
        {
            static_assert(std::is_default_constructible_v<ST> ==
                          std::is_default_constructible_v<T>);
            static_assert(std::is_copy_constructible_v<ST> ==
                          std::is_copy_constructible_v<T>);
            static_assert(std::is_move_constructible_v<ST> ==
                          std::is_move_constructible_v<T>);

            static_assert(std::is_copy_assignable_v<ST> ==
                          std::is_copy_assignable_v<T>);
            static_assert(std::is_move_assignable_v<ST> ==
                          std::is_move_assignable_v<T>);
        }

        // construction from value type
        {
            static_assert(std::is_constructible_v<ST, T>);
            static_assert(std::is_constructible_v<ST, T&>);
            static_assert(std::is_constructible_v<ST, T const&>);
            static_assert(std::is_constructible_v<ST, T&&>);
        }

        // no implicit conversions to value type
        {
            static_assert(!std::is_convertible_v<ST, T>);
            static_assert(!std::is_convertible_v<ST, T&>);
            static_assert(!std::is_convertible_v<ST, T const&>);
            static_assert(!std::is_convertible_v<ST, T&&>);
        }

        // self test of the unit test: normal function calls work
        {
            static_assert(std::is_invocable_v<void (*)(ST), ST> ==
                          std::is_move_constructible_v<ST>);
            // or, equivalently (passing r-value ref instead of value):
            static_assert(std::is_invocable_v<void (*)(ST), ST&&> ==
                          std::is_move_constructible_v<ST>);

            static_assert(std::is_invocable_v<void (*)(ST), ST const&> ==
                          std::is_copy_constructible_v<ST>);

            static_assert(std::is_invocable_v<void (*)(ST&), ST&>);
            static_assert(std::is_invocable_v<void (*)(ST const&), ST>);
            static_assert(std::is_invocable_v<void (*)(ST &&), ST>);
        }
    };

    check(std::type_identity<std::vector<int>>{});
    check(std::type_identity<std::unique_ptr<char>>{});

    struct NoMoveCtor
    {
        NoMoveCtor() = default;
        NoMoveCtor(NoMoveCtor&&) = delete;
    };

    check(std::type_identity<NoMoveCtor>{});
}

TEST(BaseLibStrongType, CompileTimeMixed)
{
    using V1 = std::vector<int>;
    using S1 = BaseLib::StrongType<V1, struct Tag1>;
    using S2 = BaseLib::StrongType<V1, struct Tag2>;

    // no mixed construction possible
    {
        static_assert(!std::is_constructible_v<S1, S2>);
        static_assert(!std::is_constructible_v<S1, S2&>);
        static_assert(!std::is_constructible_v<S1, S2 const&>);
        static_assert(!std::is_constructible_v<S1, S2&&>);
    }

    // no mixed implicit conversions
    {
        static_assert(!std::is_convertible_v<S1, S2>);
        static_assert(!std::is_convertible_v<S1, S2&>);
        static_assert(!std::is_convertible_v<S1, S2 const&>);
        static_assert(!std::is_convertible_v<S1, S2&&>);
    }

    // no mixed assignment
    {
        static_assert(!std::is_assignable_v<S1&, S2>);
        static_assert(!std::is_assignable_v<S1&, S2&>);
        static_assert(!std::is_assignable_v<S1&, S2 const&>);
        static_assert(!std::is_assignable_v<S1&, S2&&>);
    }

    // no mixed function calls (this is a central property of strong types)
    {
        static_assert(!std::is_invocable_v<void (*)(S2), S1>);
        static_assert(!std::is_invocable_v<void (*)(S2&), S1>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), S1>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), S1>);

        static_assert(!std::is_invocable_v<void (*)(S2), S1&>);
        static_assert(!std::is_invocable_v<void (*)(S2&), S1&>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), S1&>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), S1&>);

        static_assert(!std::is_invocable_v<void (*)(S2), S1 const&>);
        static_assert(!std::is_invocable_v<void (*)(S2&), S1 const&>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), S1 const&>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), S1 const&>);

        static_assert(!std::is_invocable_v<void (*)(S2), S1&&>);
        static_assert(!std::is_invocable_v<void (*)(S2&), S1&&>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), S1&&>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), S1&&>);
    }

    // no function calls with the base type (this is a central property of
    // strong types)
    {
        static_assert(!std::is_invocable_v<void (*)(S2), V1>);
        static_assert(!std::is_invocable_v<void (*)(S2&), V1>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), V1>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), V1>);

        static_assert(!std::is_invocable_v<void (*)(S2), V1&>);
        static_assert(!std::is_invocable_v<void (*)(S2&), V1&>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), V1&>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), V1&>);

        static_assert(!std::is_invocable_v<void (*)(S2), V1 const&>);
        static_assert(!std::is_invocable_v<void (*)(S2&), V1 const&>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), V1 const&>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), V1 const&>);

        static_assert(!std::is_invocable_v<void (*)(S2), V1&&>);
        static_assert(!std::is_invocable_v<void (*)(S2&), V1&&>);
        static_assert(!std::is_invocable_v<void (*)(S2 const&), V1&&>);
        static_assert(!std::is_invocable_v<void (*)(S2 &&), V1&&>);
    }
}

TEST(BaseLibStrongType, DefaultInitialize)
{
    // int is a primitive type, but it should be initialized properly, anyway
    BaseLib::StrongType<int, struct Tag> si;

    ASSERT_EQ(0, si());
}
back to top