blob: 53345157cf3a88edc53af5eba461d0be10114b7c [file]
/*
* Copyright (C) 2026 Apple Inc. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h"
#include <wtf/Borrow.h>
#include "Helpers/Test.h"
#include <wtf/CanBorrow.h>
namespace TestWebKitAPI {
// Implements the CanBorrow protocol by hand rather than inheriting from CanBorrow,
// so that these tests can observe the borrowed flag instead of only crashing on it.
class ObservableBorrowable {
public:
bool isBorrowed() const { return m_isBorrowed; }
unsigned crashIfBorrowedCount() const { return m_crashIfBorrowedCount; }
int value() const { return m_value; }
void crashIfBorrowed() const { ++m_crashIfBorrowedCount; }
bool setIsBorrowed(bool isBorrowed) const { return std::exchange(m_isBorrowed, isBorrowed); }
private:
int m_value { 42 };
mutable bool m_isBorrowed { false };
mutable unsigned m_crashIfBorrowedCount { 0 };
};
class Borrowable : public CanBorrow {
public:
int value() const { return m_value; }
private:
int m_value { 7 };
};
TEST(WTF_Borrow, BorrowMarksAndUnmarksTheObject)
{
ObservableBorrowable object;
EXPECT_FALSE(object.isBorrowed());
{
Borrow borrow(object);
EXPECT_TRUE(object.isBorrowed());
}
EXPECT_FALSE(object.isBorrowed());
}
TEST(WTF_Borrow, BorrowExposesTheObject)
{
ObservableBorrowable object;
Borrow borrow(object);
EXPECT_EQ(&borrow.get(), &object);
EXPECT_EQ(borrow->value(), 42);
EXPECT_EQ(&static_cast<ObservableBorrowable&>(borrow), &object);
}
TEST(WTF_Borrow, BorrowHelperFunction)
{
ObservableBorrowable object;
EXPECT_EQ(borrow(object)->value(), 42);
EXPECT_FALSE(object.isBorrowed());
}
// The inner borrow restores the outer borrow's state rather than clearing it.
TEST(WTF_Borrow, NestedBorrowsRestorePreviousState)
{
ObservableBorrowable object;
{
Borrow outer(object);
{
Borrow inner(object);
EXPECT_TRUE(object.isBorrowed());
}
EXPECT_TRUE(object.isBorrowed());
}
EXPECT_FALSE(object.isBorrowed());
}
TEST(WTF_Borrow, CrashIfBorrowedIsReachedThroughTheProtocol)
{
ObservableBorrowable object;
object.crashIfBorrowed();
EXPECT_EQ(object.crashIfBorrowedCount(), 1u);
}
TEST(WTF_Borrow, DestroyAfterBorrowEndsIsFine)
{
auto object = makeUniqueWithoutFastMallocCheck<Borrowable>();
{
Borrow borrow(*object);
EXPECT_EQ(borrow->value(), 7);
}
object = nullptr;
EXPECT_FALSE(object);
}
// The CanBorrow assertions are debug-only for now; see the FIXMEs in CanBorrow.h.
TEST(WTF_BorrowDeathTest, MAYBE_ASSERT_ENABLED_DEATH_TEST(DestroyWhileBorrowedCrashes))
{
auto shouldCrash = [] {
auto* object = new Borrowable;
Borrow borrow(*object);
delete object;
};
ASSERT_DEATH_IF_SUPPORTED(shouldCrash(), "");
}
TEST(WTF_BorrowDeathTest, MAYBE_ASSERT_ENABLED_DEATH_TEST(CrashIfBorrowedWhileBorrowedCrashes))
{
auto shouldCrash = [] {
Borrowable object;
Borrow borrow(object);
object.crashIfBorrowed();
};
ASSERT_DEATH_IF_SUPPORTED(shouldCrash(), "");
}
// WTF_FORBID_HEAP_ALLOCATION rules out `new Borrow(...)`, but not embedding a Borrow
// in a heap-allocated object, which would let the borrow outlive the borrowed object.
#if OS(DARWIN)
TEST(WTF_BorrowDeathTest, MAYBE_ASSERT_ENABLED_DEATH_TEST(HeapAllocatedBorrowCrashes))
{
auto shouldCrash = [] {
struct Holder {
explicit Holder(Borrowable& object)
: borrow(object)
{
}
Borrow<Borrowable> borrow;
};
// Both deliberately leaked: the Holder constructor is expected to crash, and
// nothing else here may be destroyed while the borrow is outstanding, or the
// test would pass on the wrong assertion.
SUPPRESS_UNCOUNTED_LOCAL auto* object = new Borrowable;
SUPPRESS_UNCOUNTED_LOCAL auto* holder = new Holder(*object);
EXPECT_EQ(holder->borrow->value(), 7);
};
ASSERT_DEATH_IF_SUPPORTED(shouldCrash(), "");
}
#endif
} // namespace TestWebKitAPI