/* * Copyright (c) 2021, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "test_platform.h" #include <string.h> #include <openthread/config.h> #include "test_util.hpp" #include "common/code_utils.hpp" #include "common/owned_ptr.hpp" #include "common/retain_ptr.hpp" namespace ot { class TestObject : public RetainCountable { public: TestObject(void) : mWasFreed(false) { } void Free(void) { mWasFreed = true; } void ResetTestFlags(void) { mWasFreed = false; } uint16_t GetRetainCount(void) const { return RetainCountable::GetRetainCount(); } bool WasFreed(void) const { return mWasFreed && (GetRetainCount() == 0); } private: bool mWasFreed; }; static constexpr uint16_t kSkipRetainCountCheck = 0xffff; template <typename PointerType> void VerifyPointer(const PointerType &aPointer, const TestObject *aObject, uint16_t aRetainCount = kSkipRetainCountCheck) { if (aObject == nullptr) { VerifyOrQuit(aPointer.IsNull()); VerifyOrQuit(aPointer.Get() == nullptr); VerifyOrQuit(aPointer == nullptr); } else { VerifyOrQuit(!aPointer.IsNull()); VerifyOrQuit(aPointer.Get() == aObject); VerifyOrQuit(aPointer == aObject); VerifyOrQuit(aPointer != nullptr); VerifyOrQuit(!aPointer->WasFreed()); VerifyOrQuit(!(*aPointer).WasFreed()); if (aRetainCount != kSkipRetainCountCheck) { VerifyOrQuit(aObject->GetRetainCount() == aRetainCount); } } VerifyOrQuit(aPointer == aPointer); } void TestOwnedPtr(void) { TestObject obj1; TestObject obj2; TestObject obj3; printf("\n====================================================================================\n"); printf("Testing `OwnedPtr`\n"); printf("\n - Default constructor (null pointer)"); { OwnedPtr<TestObject> ptr; VerifyPointer(ptr, nullptr); } printf("\n - Constructor taking ownership of an object"); obj1.ResetTestFlags(); { OwnedPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1); } VerifyOrQuit(obj1.WasFreed()); printf("\n - Move constructor taking over from another"); obj1.ResetTestFlags(); { OwnedPtr<TestObject> ptr1(&obj1); OwnedPtr<TestObject> ptr2(ptr1.PassOwnership()); VerifyPointer(ptr1, nullptr); VerifyPointer(ptr2, &obj1); } VerifyOrQuit(obj1.WasFreed()); printf("\n - `Free()` method"); obj1.ResetTestFlags(); { OwnedPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1); ptr.Free(); VerifyOrQuit(obj1.WasFreed()); VerifyPointer(ptr, nullptr); ptr.Free(); VerifyOrQuit(obj1.WasFreed()); VerifyPointer(ptr, nullptr); } printf("\n - `Reset()` method"); obj1.ResetTestFlags(); obj2.ResetTestFlags(); obj3.ResetTestFlags(); { OwnedPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1); ptr.Reset(&obj2); VerifyOrQuit(obj1.WasFreed()); VerifyOrQuit(!obj2.WasFreed()); VerifyPointer(ptr, &obj2); ptr.Reset(); VerifyOrQuit(obj2.WasFreed()); VerifyPointer(ptr, nullptr); ptr.Reset(&obj3); VerifyPointer(ptr, &obj3); } VerifyOrQuit(obj1.WasFreed()); VerifyOrQuit(obj2.WasFreed()); VerifyOrQuit(obj3.WasFreed()); printf("\n - Self `Reset()`"); obj1.ResetTestFlags(); { OwnedPtr<TestObject> ptr1(&obj1); OwnedPtr<TestObject> ptr2; VerifyPointer(ptr1, &obj1); ptr1.Reset(&obj1); VerifyPointer(ptr1, &obj1); ptr2.Reset(nullptr); VerifyPointer(ptr2, nullptr); } VerifyOrQuit(obj1.WasFreed()); printf("\n - Move assignment (operator `=`)"); obj1.ResetTestFlags(); obj2.ResetTestFlags(); obj3.ResetTestFlags(); { OwnedPtr<TestObject> ptr1(&obj1); OwnedPtr<TestObject> ptr2(&obj2); OwnedPtr<TestObject> ptr3(&obj3); VerifyPointer(ptr1, &obj1); VerifyPointer(ptr2, &obj2); VerifyPointer(ptr3, &obj3); // Move from non-null (ptr1) to non-null (ptr2) ptr2 = ptr1.PassOwnership(); VerifyPointer(ptr1, nullptr); VerifyPointer(ptr2, &obj1); VerifyOrQuit(!obj1.WasFreed()); VerifyOrQuit(obj2.WasFreed()); // Move from null (ptr1) to non-null (ptr3) ptr3 = ptr1.PassOwnership(); VerifyPointer(ptr1, nullptr); VerifyPointer(ptr3, nullptr); VerifyOrQuit(obj3.WasFreed()); // Move from non-null (ptr2) to null (ptr1) ptr1 = ptr2.PassOwnership(); VerifyPointer(ptr1, &obj1); VerifyPointer(ptr2, nullptr); VerifyOrQuit(!obj1.WasFreed()); // Move from null (ptr2) to null (ptr3) ptr3 = ptr2.PassOwnership(); VerifyPointer(ptr2, nullptr); VerifyPointer(ptr3, nullptr); VerifyOrQuit(!obj1.WasFreed()); } VerifyOrQuit(obj1.WasFreed()); printf("\n - Self move assignment (operator `=`)"); obj1.ResetTestFlags(); { OwnedPtr<TestObject> ptr1(&obj1); OwnedPtr<TestObject> ptr2; VerifyPointer(ptr1, &obj1); VerifyPointer(ptr2, nullptr); // Move from non-null (ptr1) to itself ptr1 = ptr1.PassOwnership(); VerifyPointer(ptr1, &obj1); // Move from null (ptr2) to itself ptr2 = ptr2.PassOwnership(); VerifyPointer(ptr2, nullptr); } VerifyOrQuit(obj1.WasFreed()); printf("\n - `Release()` method"); obj1.ResetTestFlags(); { OwnedPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1); VerifyOrQuit(ptr.Release() == &obj1); VerifyOrQuit(!obj1.WasFreed()); VerifyPointer(ptr, nullptr); VerifyOrQuit(ptr.Release() == nullptr); VerifyOrQuit(!obj1.WasFreed()); VerifyPointer(ptr, nullptr); } printf("\n\n-- PASS\n"); } void TestRetainPtr(void) { TestObject obj1; TestObject obj2; TestObject obj3; printf("\n====================================================================================\n"); printf("Testing `RetainPtr`\n"); VerifyOrQuit(obj1.GetRetainCount() == 0); VerifyOrQuit(obj2.GetRetainCount() == 0); VerifyOrQuit(obj3.GetRetainCount() == 0); printf("\n - Default constructor (null pointer)"); { RetainPtr<TestObject> ptr; VerifyPointer(ptr, nullptr); } printf("\n - Constructor taking over management of an object"); obj1.ResetTestFlags(); { RetainPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1, 1); } VerifyOrQuit(obj1.WasFreed()); printf("\n - Two constructed `RetainPtr`s of the same object"); obj1.ResetTestFlags(); { RetainPtr<TestObject> ptr1(&obj1); RetainPtr<TestObject> ptr2(&obj1); VerifyPointer(ptr1, &obj1, 2); VerifyPointer(ptr2, &obj1, 2); } VerifyOrQuit(obj1.WasFreed()); printf("\n - Copy constructor"); obj1.ResetTestFlags(); { RetainPtr<TestObject> ptr1(&obj1); RetainPtr<TestObject> ptr2(ptr1); VerifyPointer(ptr1, &obj1, 2); VerifyPointer(ptr2, &obj1, 2); } VerifyOrQuit(obj1.WasFreed()); printf("\n - `Reset()` method"); obj1.ResetTestFlags(); obj2.ResetTestFlags(); obj3.ResetTestFlags(); { RetainPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1, 1); ptr.Reset(&obj2); VerifyOrQuit(obj1.WasFreed()); VerifyOrQuit(!obj2.WasFreed()); VerifyPointer(ptr, &obj2, 1); ptr.Reset(); VerifyOrQuit(obj2.WasFreed()); VerifyPointer(ptr, nullptr); ptr.Reset(&obj3); VerifyPointer(ptr, &obj3, 1); } VerifyOrQuit(obj1.WasFreed()); VerifyOrQuit(obj2.WasFreed()); VerifyOrQuit(obj3.WasFreed()); printf("\n - Self `Reset()`"); obj1.ResetTestFlags(); { RetainPtr<TestObject> ptr1(&obj1); RetainPtr<TestObject> ptr2; VerifyPointer(ptr1, &obj1, 1); ptr1.Reset(&obj1); VerifyPointer(ptr1, &obj1, 1); ptr2.Reset(nullptr); VerifyPointer(ptr2, nullptr); } VerifyOrQuit(obj1.WasFreed()); printf("\n - Assignment `=`"); obj1.ResetTestFlags(); obj2.ResetTestFlags(); { RetainPtr<TestObject> ptr1(&obj1); RetainPtr<TestObject> ptr2(&obj2); RetainPtr<TestObject> ptr3; VerifyPointer(ptr1, &obj1, 1); VerifyPointer(ptr2, &obj2, 1); VerifyPointer(ptr3, nullptr); VerifyOrQuit(ptr1 != ptr2); VerifyOrQuit(ptr1 != ptr3); VerifyOrQuit(ptr2 != ptr3); // Set from non-null (ptr1) to non-null (ptr2) ptr2 = ptr1; VerifyPointer(ptr1, &obj1, 2); VerifyPointer(ptr2, &obj1, 2); VerifyOrQuit(obj2.WasFreed()); VerifyOrQuit(ptr1 == ptr2); // Set from null (ptr3) to non-null (ptr1) ptr1 = ptr3; VerifyPointer(ptr1, nullptr); VerifyPointer(ptr3, nullptr); VerifyPointer(ptr2, &obj1, 1); VerifyOrQuit(ptr1 == ptr3); // Move from null (ptr1) to null (ptr3) ptr3 = ptr1; VerifyPointer(ptr1, nullptr); VerifyPointer(ptr3, nullptr); VerifyOrQuit(ptr1 == ptr3); // Move from non-null (ptr2) to null (ptr3) ptr3 = ptr2; VerifyPointer(ptr2, &obj1, 2); VerifyPointer(ptr3, &obj1, 2); VerifyOrQuit(ptr2 == ptr3); } VerifyOrQuit(obj1.WasFreed()); VerifyOrQuit(obj2.WasFreed()); printf("\n - Self assignment `=`"); obj1.ResetTestFlags(); { RetainPtr<TestObject> ptr1(&obj1); RetainPtr<TestObject> ptr2; VerifyPointer(ptr1, &obj1, 1); VerifyPointer(ptr2, nullptr); // Set from non-null (ptr1) to itself. We use `*(&ptr1) to // silence `clang` error/warning not allowing explicit self // assignment to same variable. ptr1 = *(&ptr1); VerifyPointer(ptr1, &obj1, 1); // Set from null (ptr2) to itself ptr2 = *(&ptr2); VerifyPointer(ptr2, nullptr); } VerifyOrQuit(obj1.WasFreed()); printf("\n - `Release()` method"); obj1.ResetTestFlags(); { RetainPtr<TestObject> ptr(&obj1); VerifyPointer(ptr, &obj1, 1); VerifyOrQuit(ptr.Release() == &obj1); VerifyPointer(ptr, nullptr); VerifyOrQuit(ptr.Release() == nullptr); VerifyPointer(ptr, nullptr); } VerifyOrQuit(!obj1.WasFreed()); VerifyOrQuit(obj1.GetRetainCount() == 1); printf("\n\n-- PASS\n"); } } // namespace ot int main(void) { ot::TestOwnedPtr(); ot::TestRetainPtr(); printf("\nAll tests passed.\n"); return 0; }