/*- * Copyright 2021 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef RSPAMD_RSPAMD_CXX_LOCAL_PTR_HXX #define RSPAMD_RSPAMD_CXX_LOCAL_PTR_HXX #define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL #include "doctest/doctest.h" #include "libutil/cxx/local_shared_ptr.hxx" namespace test_internal { struct deleter_test { bool *pv; deleter_test(bool &v) { v = false; pv = &v; } ~deleter_test() { *pv = true; } }; } namespace std { template<> struct hash { inline auto operator()(const test_internal::deleter_test &) const noexcept -> auto { return 42; } }; } TEST_SUITE("local_ptr") { using namespace test_internal; TEST_CASE("shared_ptr from nullptr") { rspamd::local_shared_ptr pi(static_cast(nullptr)); CHECK((!!pi ? false : true)); CHECK(!pi); CHECK(pi.get() == nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); } TEST_CASE("shared_ptr from ptr") { int *p = new int(7); rspamd::local_shared_ptr pi(p); CHECK((pi? true: false)); CHECK(!!pi); CHECK(pi.get() == p); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(*pi == 7); } TEST_CASE("shared_ptr copy") { rspamd::local_shared_ptr pi; rspamd::local_shared_ptr pi2(pi); CHECK(pi2 == pi); CHECK((pi2? false: true)); CHECK(!pi2); CHECK(pi2.get() == nullptr); CHECK(pi2.use_count() == pi.use_count()); rspamd::local_shared_ptr pi3(pi); CHECK(pi3 == pi); CHECK((pi3? false: true)); CHECK(!pi3); CHECK(pi3.get() == nullptr); CHECK(pi3.use_count() == pi.use_count()); rspamd::local_shared_ptr pi4(pi3); CHECK(pi4 == pi3); CHECK((pi4? false: true)); CHECK(!pi4); CHECK(pi4.get() == nullptr); CHECK(pi4.use_count() == pi3.use_count()); int * p = new int(7); rspamd::local_shared_ptr pi5(p); rspamd::local_shared_ptr pi6(pi5); CHECK(pi5 == pi6); CHECK((pi6? true: false)); CHECK(!!pi6); CHECK(pi6.get() == p); CHECK(pi6.use_count() == 2); CHECK(!pi6.unique()); CHECK(*pi6 == 7); CHECK(pi6.use_count() == pi6.use_count()); CHECK(!(pi5 < pi6 || pi5 < pi6)); // shared ownership test auto pi7 = pi6; CHECK(pi5 == pi7); CHECK((pi7? true: false)); CHECK(!!pi7); CHECK(pi7.get() == p); CHECK(pi7.use_count() == 3); CHECK(!pi7.unique()); CHECK(*pi7 == 7); CHECK(pi7.use_count() == pi7.use_count()); CHECK(!(pi5 < pi7 || pi5 < pi7)); // shared ownership test } TEST_CASE("shared_ptr move") { rspamd::local_shared_ptr pi(new int); rspamd::local_shared_ptr pi2(std::move(pi)); CHECK(!(pi2 == pi)); CHECK((!pi2? false: true)); CHECK(!pi); CHECK(pi.get() == nullptr); CHECK(pi2.get() != nullptr); CHECK(pi.use_count() != pi2.use_count()); std::swap(pi, pi2); CHECK(!(pi2 == pi)); CHECK((!pi? false: true)); CHECK(!pi2); CHECK(pi.get() != nullptr); CHECK(pi2.get() == nullptr); CHECK(pi.use_count() != pi2.use_count()); } TEST_CASE("shared_ptr dtor") { bool t; { rspamd::local_shared_ptr pi(new deleter_test{t}); CHECK((!pi ? false : true)); CHECK(!!pi); CHECK(pi.get() != nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); } CHECK(t == true); { rspamd::local_shared_ptr pi(new deleter_test{t}); CHECK((!pi ? false : true)); CHECK(!!pi); CHECK(pi.get() != nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); rspamd::local_shared_ptr pi2(pi); CHECK(pi2 == pi); CHECK(pi.use_count() == 2); pi.reset(); CHECK(!(pi2 == pi)); CHECK(pi2.use_count() == 1); CHECK(t == false); pi = pi2; CHECK(pi2 == pi); CHECK(pi.use_count() == 2); CHECK(t == false); } CHECK(t == true); } TEST_CASE("make_shared dtor") { bool t; { auto pi = rspamd::local_make_shared(t); CHECK((!pi ? false : true)); CHECK(!!pi); CHECK(pi.get() != nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); rspamd::local_shared_ptr pi2(pi); CHECK(pi2 == pi); CHECK(pi.use_count() == 2); pi.reset(); CHECK(!(pi2 == pi)); CHECK(pi2.use_count() == 1); CHECK(t == false); pi = pi2; CHECK(pi2 == pi); CHECK(pi.use_count() == 2); CHECK(t == false); } CHECK(t == true); } TEST_CASE("shared_ptr dtor") { bool t; { rspamd::local_shared_ptr pi(new deleter_test{t}); CHECK((!pi ? false : true)); CHECK(!!pi); CHECK(pi.get() != nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); } CHECK(t == true); { rspamd::local_shared_ptr pi(new deleter_test{t}); CHECK((!pi ? false : true)); CHECK(!!pi); CHECK(pi.get() != nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); rspamd::local_shared_ptr pi2(pi); CHECK(pi2 == pi); CHECK(pi.use_count() == 2); pi.reset(); CHECK(!(pi2 == pi)); CHECK(pi2.use_count() == 1); CHECK(t == false); pi = pi2; CHECK(pi2 == pi); CHECK(pi.use_count() == 2); CHECK(t == false); } CHECK(t == true); } TEST_CASE("weak_ptr") { bool t; { rspamd::local_shared_ptr pi(new deleter_test{t}); CHECK((!pi ? false : true)); CHECK(!!pi); CHECK(pi.get() != nullptr); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); rspamd::local_weak_ptr wp(pi); CHECK(wp.lock().get() != nullptr); CHECK(pi.use_count() == 1); CHECK(wp.use_count() == 1); pi.reset(); CHECK(pi.use_count() == 0); CHECK(wp.use_count() == 0); } CHECK(t == true); rspamd::local_weak_ptr wp; { rspamd::local_shared_ptr pi(new deleter_test{t}); wp = pi; CHECK(!wp.expired()); CHECK(wp.lock().get() != nullptr); } CHECK(t == true); CHECK(wp.expired()); } TEST_CASE("std::swap") { bool t; { rspamd::local_shared_ptr pi(new deleter_test{t}); CHECK(pi.use_count() == 1); CHECK(pi.unique()); CHECK(t == false); rspamd::local_shared_ptr pi1; CHECK(pi1.get() == nullptr); CHECK(pi1.use_count() == 0); std::swap(pi1, pi); CHECK(pi.use_count() == 0); CHECK(pi.get() == nullptr); CHECK(pi1.get() != nullptr); std::swap(pi, pi1); CHECK(pi.use_count() != 0); CHECK(pi.get() != nullptr); CHECK(pi1.get() == nullptr); } CHECK(t == true); } TEST_CASE("std::hash") { bool v; deleter_test dt(v); CHECK(std::hash()(dt) == 42); auto pi = rspamd::local_make_shared(v); rspamd::local_shared_ptr pi1; CHECK(std::hash()(pi) == 42); // No hash for nullptr, different from std::smart_pointers! CHECK_THROWS(std::hash()(pi1)); } } #endif //RSPAMD_RSPAMD_CXX_LOCAL_PTR_HXX