1/*
2 * Copyright 2022 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Niels Sascha Reedijk, niels.reedijk@gmail.com
7 */
8
9#include "ExclusiveBorrowTest.h"
10
11#include <atomic>
12#include <tuple>
13
14#include <cppunit/TestCaller.h>
15#include <cppunit/TestSuite.h>
16
17#include <ExclusiveBorrow.h>
18
19using BPrivate::Network::BBorrow;
20using BPrivate::Network::BBorrowError;
21using BPrivate::Network::BExclusiveBorrow;
22using BPrivate::Network::make_exclusive_borrow;
23
24
25class DeleteTestHelper
26{
27public:
28	DeleteTestHelper(std::atomic<bool>& deleted)
29		:
30		fDeleted(deleted)
31	{
32	}
33
34	~DeleteTestHelper() { fDeleted.store(true); }
35
36private:
37	std::atomic<bool>& fDeleted;
38};
39
40
41class Base
42{
43public:
44	Base() {}
45
46
47	virtual ~Base() {}
48
49
50	virtual bool IsDerived() { return false; }
51};
52
53
54class Derived : public Base
55{
56public:
57	Derived() {}
58
59
60	virtual ~Derived() {}
61
62
63	virtual bool IsDerived() override { return true; }
64};
65
66
67ExclusiveBorrowTest::ExclusiveBorrowTest()
68{
69}
70
71
72void
73ExclusiveBorrowTest::ObjectDeleteTest()
74{
75	// Case 1: object never gets borrowed and goes out of scope
76	std::atomic<bool> deleted = false;
77	{
78		auto object = make_exclusive_borrow<DeleteTestHelper>(deleted);
79	}
80	CPPUNIT_ASSERT_EQUAL_MESSAGE("(1) Expected object to be deleted", true, deleted.load());
81
82	// Case 2: object gets borrowed, returned and then goes out of scope
83	deleted.store(false);
84	{
85		auto object = make_exclusive_borrow<DeleteTestHelper>(deleted);
86		{
87			auto borrow = BBorrow<DeleteTestHelper>(object);
88		}
89		CPPUNIT_ASSERT_EQUAL_MESSAGE("(2) Object should not be deleted", false, deleted.load());
90	}
91	CPPUNIT_ASSERT_EQUAL_MESSAGE("(2) Expected object to be deleted", true, deleted.load());
92
93	// Case 3: object gets borrowed, forfeited and then borrow goes out of scope
94	deleted.store(false);
95	{
96		auto borrow = BBorrow<DeleteTestHelper>(nullptr);
97		{
98			auto object = make_exclusive_borrow<DeleteTestHelper>(deleted);
99			borrow = BBorrow<DeleteTestHelper>(object);
100		}
101		CPPUNIT_ASSERT_EQUAL_MESSAGE("(3) Object should not be deleted", false, deleted.load());
102	}
103	CPPUNIT_ASSERT_EQUAL_MESSAGE("(3) Expected object to be deleted", true, deleted.load());
104}
105
106
107void
108ExclusiveBorrowTest::OwnershipTest()
109{
110	auto ownedObject = make_exclusive_borrow<int>(1);
111	CPPUNIT_ASSERT(*ownedObject == 1);
112
113	auto borrow = BBorrow<int>(ownedObject);
114	try {
115		std::ignore = *ownedObject == 1;
116		CPPUNIT_FAIL("Unexpected access to the owned object while borrowed");
117	} catch (const BBorrowError& e) {
118		// expected
119	}
120
121	try {
122		std::ignore = *borrow == 1;
123		// should succeed
124	} catch (const BBorrowError& e) {
125		CPPUNIT_FAIL("Unexpected error accessing the borrowed object");
126	}
127
128	try {
129		auto borrowAgain = BBorrow<int>(ownedObject);
130		CPPUNIT_FAIL("Unexpectedly able to borrow the owned object again");
131	} catch (const BBorrowError& e) {
132		// expected
133	}
134
135	try {
136		borrow = BBorrow<int>(nullptr);
137		std::ignore = *borrow == 1;
138		CPPUNIT_FAIL("Unexpected access to an empty borrowed object");
139	} catch (const BBorrowError& e) {
140		// expected
141	}
142
143	try {
144		std::ignore = *ownedObject == 1;
145	} catch (const BBorrowError& e) {
146		CPPUNIT_FAIL("Unexpected error accessing the owned object");
147	}
148}
149
150
151void
152ExclusiveBorrowTest::PolymorphismTest()
153{
154	auto owned = make_exclusive_borrow<Derived>();
155	{
156		auto borrowDerived = BBorrow<Derived>(owned);
157		CPPUNIT_ASSERT_EQUAL(true, borrowDerived->IsDerived());
158	}
159	{
160		auto borrowBase = BBorrow<Base>(owned);
161		CPPUNIT_ASSERT_EQUAL(true, borrowBase->IsDerived());
162	}
163}
164
165
166void
167ExclusiveBorrowTest::ReleaseTest()
168{
169	auto ownedObject = make_exclusive_borrow<int>(1);
170	auto ownedPointer = std::addressof(*ownedObject);
171	try {
172		auto borrow = BBorrow<int>(ownedObject);
173		auto invalidClaimedPointer = ownedObject.Release();
174		CPPUNIT_FAIL("Unexpectedly able to release a borrowed pointer");
175	} catch (const BBorrowError&) {
176		// expected to fail
177	}
178
179	auto validClaimedPointer = ownedObject.Release();
180	CPPUNIT_ASSERT_EQUAL_MESSAGE("Expected released pointer to point to the same object",
181		validClaimedPointer.get(), ownedPointer);
182}
183
184
185/* static */ void
186ExclusiveBorrowTest::AddTests(BTestSuite& parent)
187{
188	CppUnit::TestSuite& suite = *new CppUnit::TestSuite("ExclusiveBorrowTest");
189
190	suite.addTest(new CppUnit::TestCaller<ExclusiveBorrowTest>(
191		"ExclusiveBorrowTest::ObjectDeleteTest", &ExclusiveBorrowTest::ObjectDeleteTest));
192	suite.addTest(new CppUnit::TestCaller<ExclusiveBorrowTest>(
193		"ExclusiveBorrowTest::OwnershipTest", &ExclusiveBorrowTest::OwnershipTest));
194	suite.addTest(new CppUnit::TestCaller<ExclusiveBorrowTest>(
195		"ExclusiveBorrowTest::PolymorphismTest", &ExclusiveBorrowTest::PolymorphismTest));
196	suite.addTest(new CppUnit::TestCaller<ExclusiveBorrowTest>(
197		"ExclusiveBorrowTest::ReleaseTest", &ExclusiveBorrowTest::ReleaseTest));
198
199	parent.addTest("ExclusiveBorrowTest", &suite);
200}
201