blob: c02587eb4d2ad5aa6cabfd9473074f40fe6ee3a9 [file]
/*
* Copyright (C) 2025 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 "SwiftCxxInteropTestbed.h"
#include <optional>
#include <wtf/Assertions.h>
namespace SwiftCxxInteropTestbed {
// Handed to a stored handler that resetStoredIntCompletionHandler() has to call itself.
static constexpr int teardownValue = -2;
// Left behind in a probe that has been moved from, so a test sees an obviously wrong value rather than a
// plausible one if the bridge hands Swift the source of a move instead of the destination.
static constexpr int movedFromValue = -1;
static int& liveMoveOnlyProbes()
{
static int count = 0;
return count;
}
MoveOnlyProbe::MoveOnlyProbe(int value)
: m_value(value)
{
++liveMoveOnlyProbes();
}
MoveOnlyProbe::MoveOnlyProbe(MoveOnlyProbe&& other)
: m_value(other.m_value)
{
other.m_value = movedFromValue;
++liveMoveOnlyProbes();
}
MoveOnlyProbe::~MoveOnlyProbe()
{
--liveMoveOnlyProbes();
}
int liveMoveOnlyProbeCount()
{
return liveMoveOnlyProbes();
}
static int& liveCopyCountingProbes()
{
static int count = 0;
return count;
}
static int& copyCountingProbeCopies()
{
static int count = 0;
return count;
}
CopyCountingProbe::CopyCountingProbe(int value)
: m_value(value)
{
++liveCopyCountingProbes();
}
CopyCountingProbe::CopyCountingProbe(const CopyCountingProbe& other)
: m_value(other.m_value)
{
++copyCountingProbeCopies();
++liveCopyCountingProbes();
}
CopyCountingProbe::CopyCountingProbe(CopyCountingProbe&& other)
: m_value(other.m_value)
{
other.m_value = movedFromValue;
++liveCopyCountingProbes();
}
CopyCountingProbe::~CopyCountingProbe()
{
--liveCopyCountingProbes();
}
int liveCopyCountingProbeCount()
{
return liveCopyCountingProbes();
}
int copyCountingProbeCopyCount()
{
return copyCountingProbeCopies();
}
void resetCopyCountingProbeCounts()
{
copyCountingProbeCopies() = 0;
}
static int& liveCountingProbeErrors()
{
static int count = 0;
return count;
}
CountingProbeError::CountingProbeError(ProbeError value)
: m_value(value)
{
++liveCountingProbeErrors();
}
CountingProbeError::CountingProbeError(const CountingProbeError& other)
: m_value(other.m_value)
{
++liveCountingProbeErrors();
}
CountingProbeError::~CountingProbeError()
{
--liveCountingProbeErrors();
}
int liveCountingProbeErrorCount()
{
return liveCountingProbeErrors();
}
int callIntBoolFunction(bool argument, IntBoolFunction&& function)
{
return function(argument);
}
void callIntCompletionHandler(int argument, IntCompletionHandler&& completionHandler)
{
completionHandler(argument);
}
void callVoidCompletionHandler(VoidCompletionHandler&& completionHandler)
{
completionHandler();
}
static std::optional<IntCompletionHandler>& storedIntCompletionHandler()
{
static std::optional<IntCompletionHandler> handler;
return handler;
}
void storeIntCompletionHandler(IntCompletionHandler&& completionHandler)
{
// Overwriting the slot would destroy an uncalled handler: that trips
// ~CompletionHandler's assertion and strands whatever the handler owned, so a test
// awaiting a continuation it held would hang instead of failing.
RELEASE_ASSERT(!storedIntCompletionHandler());
storedIntCompletionHandler().emplace(WTF::move(completionHandler));
}
void invokeStoredIntCompletionHandler(int argument)
{
auto handler = WTF::move(storedIntCompletionHandler());
storedIntCompletionHandler().reset();
if (handler)
(*handler)(argument);
}
void resetStoredIntCompletionHandler()
{
auto handler = WTF::move(storedIntCompletionHandler());
storedIntCompletionHandler().reset();
// Call rather than just drop it, both because ~CompletionHandler requires it and so
// that a test which returned early fails on the sentinel instead of hanging. Teardown
// must never assert itself, so check the handler has not already run.
if (handler && *handler)
(*handler)(teardownValue);
}
void callMoveOnlyProbeCompletionHandler(int argument, MoveOnlyProbeCompletionHandler&& completionHandler)
{
completionHandler(MoveOnlyProbe { argument });
}
void callCopyCountingProbeCompletionHandler(int argument, CopyCountingProbeCompletionHandler&& completionHandler)
{
completionHandler(CopyCountingProbe { argument });
}
static std::optional<MoveOnlyProbeCompletionHandler>& storedMoveOnlyProbeCompletionHandler()
{
static std::optional<MoveOnlyProbeCompletionHandler> handler;
return handler;
}
void storeMoveOnlyProbeCompletionHandler(MoveOnlyProbeCompletionHandler&& completionHandler)
{
// See storeIntCompletionHandler().
RELEASE_ASSERT(!storedMoveOnlyProbeCompletionHandler());
storedMoveOnlyProbeCompletionHandler().emplace(WTF::move(completionHandler));
}
void invokeStoredMoveOnlyProbeCompletionHandler(int argument)
{
auto handler = WTF::move(storedMoveOnlyProbeCompletionHandler());
storedMoveOnlyProbeCompletionHandler().reset();
if (handler)
(*handler)(MoveOnlyProbe { argument });
}
void resetStoredMoveOnlyProbeCompletionHandler()
{
auto handler = WTF::move(storedMoveOnlyProbeCompletionHandler());
storedMoveOnlyProbeCompletionHandler().reset();
// See resetStoredIntCompletionHandler().
if (handler && *handler)
(*handler)(MoveOnlyProbe { teardownValue });
}
static int& sharedProbeRefs()
{
static int count = 0;
return count;
}
static int& sharedProbeDerefs()
{
static int count = 0;
return count;
}
void SharedProbe::ref()
{
++sharedProbeRefs();
++m_refCount;
}
void SharedProbe::deref()
{
++sharedProbeDerefs();
--m_refCount;
// Deliberately never freed: see the class comment. A test that drove the count to zero has to be
// able to report that rather than crash on the next access.
}
static SharedProbe& sharedProbe()
{
static SharedProbe probe;
return probe;
}
int callSharedProbeHolderCompletionHandler(SharedProbeHolderCompletionHandler&& completionHandler)
{
completionHandler(SharedProbeHolder { &sharedProbe() });
return sharedProbe().refCount();
}
int sharedProbeRefCalls()
{
return sharedProbeRefs();
}
int sharedProbeDerefCalls()
{
return sharedProbeDerefs();
}
int sharedProbeRefCount()
{
return sharedProbe().refCount();
}
void resetSharedProbe()
{
sharedProbeRefs() = 0;
sharedProbeDerefs() = 0;
sharedProbe().resetRefCount();
}
SelfReferentialProbe::SelfReferentialProbe(int value)
: m_value(value)
, m_self(&m_value)
{
}
SelfReferentialProbe::SelfReferentialProbe(SelfReferentialProbe&& other)
: m_value(other.m_value)
, m_self(&m_value)
{
other.m_value = movedFromValue;
}
SelfReferentialProbe::~SelfReferentialProbe()
{
// Non-trivial on purpose. See the class comment: this is what makes Swift relocate the value with
// the move constructor above instead of copying its bytes.
m_self = nullptr;
}
void callSelfReferentialProbeCompletionHandler(int argument, SelfReferentialProbeCompletionHandler&& completionHandler)
{
completionHandler(SelfReferentialProbe { argument });
}
IntExpected makeIntExpected(int value)
{
return IntExpected { value };
}
IntExpected makeIntUnexpected(ProbeError error)
{
return IntExpected { makeUnexpected(error) };
}
CopyCountingProbeExpected makeCopyCountingProbeExpected(int value)
{
// In place, so that the probe the `Expected` holds is the only one this constructed: a test counting
// copies is measuring what Swift did, not what building the `Expected` did.
return CopyCountingProbeExpected { std::in_place, value };
}
CopyCountingProbeExpected makeCopyCountingProbeUnexpected(ProbeError error)
{
return CopyCountingProbeExpected { makeUnexpected(error) };
}
SharedProbeHolderExpected makeSharedProbeHolderExpected()
{
return SharedProbeHolderExpected { SharedProbeHolder { &sharedProbe() } };
}
SharedProbeHolderExpected makeSharedProbeHolderUnexpected(ProbeError error)
{
return SharedProbeHolderExpected { makeUnexpected(error) };
}
MoveOnlyProbeExpected makeMoveOnlyProbeExpected(int value)
{
return MoveOnlyProbeExpected { std::in_place, value };
}
MoveOnlyProbeExpected makeMoveOnlyProbeUnexpected(ProbeError error)
{
return MoveOnlyProbeExpected { makeUnexpected(error) };
}
SelfReferentialProbeExpected makeSelfReferentialProbeExpected(int value)
{
return SelfReferentialProbeExpected { std::in_place, value };
}
SelfReferentialProbeExpected makeSelfReferentialProbeUnexpected(ProbeError error)
{
return SelfReferentialProbeExpected { makeUnexpected(error) };
}
CountedErrorExpected makeCountedErrorExpected(int value)
{
return CountedErrorExpected { std::in_place, value };
}
CountedErrorExpected makeCountedErrorUnexpected(ProbeError error)
{
return CountedErrorExpected { makeUnexpected(CountingProbeError { error }) };
}
};