Files
salmanoff/stimBuffApis/lcameraBuff/tests/yuvStimProducer_state_tests.cpp
T
hayodea 63532a6ee2 Resolve device selector on detach and add YuvStimProducer state tests.
Detach finds the shared producer via lcameraDev_resolveDeviceSelectorCReq
then removes buffers by attach identity; unit tests cover quale guards.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 11:04:22 -04:00

245 lines
7.3 KiB
C++

#include <boost/asio/io_context.hpp>
#include <cameraIdentity.h>
#include <gtest/gtest.h>
#include <hilSmoCallbacksStub.h>
#include <lcameraBuffInternal.h>
#include <support/exceptionAssertions.h>
#include <yuvChannelStimulusBuffer.h>
#include <yuvStimProducer.h>
#include <memory>
#include <string>
namespace smo {
namespace stim_buff {
namespace lcamera_buff {
namespace {
constexpr const char *usbExternalSelector =
"model-substr:USB;location:external";
constexpr const char *usbAliasSelector =
"model-substr:USB Video Device;location:external";
std::shared_ptr<device::DeviceAttachmentSpec> makeLcameraAttachSpec(
const std::string& deviceSelector,
const std::string& qualeIfaceApi,
const std::string& deviceIdentifier = "cam0")
{
auto spec = std::make_shared<device::DeviceAttachmentSpec>();
spec->deviceIdentifier = deviceIdentifier;
spec->sensorType = 'e';
spec->qualeIfaceApi = qualeIfaceApi;
spec->stimBuffApi = "lcameraBuff";
spec->provider = "lcameraDev";
spec->deviceSelector = deviceSelector;
spec->stimBuffApiParams = {
{"v-res", "480p"},
{"colour-space", "yuv"},
{"opt-planar", ""},
};
return spec;
}
LcameraBuffParsedParams makeDefaultParsedParams()
{
LcameraBuffParsedParams parsedParams;
parsedParams.width = 640;
parsedParams.height = 480;
parsedParams.colourSpace = lcamera_dev::LcameraDevColourSpace::Yuv;
parsedParams.fullPlanarIsOptional = true;
return parsedParams;
}
lcamera_dev::LcameraDevConfiguredCameraMode makeYuyv480ConfiguredMode()
{
lcamera_dev::LcameraDevConfiguredCameraMode configuredMode;
configuredMode.width = 640;
configuredMode.height = 480;
configuredMode.colourSpace = lcamera_dev::LcameraDevColourSpace::Yuv;
configuredMode.pixelFormatName = "YUYV";
configuredMode.isFullyPlanar = false;
configuredMode.planeCount = 1;
return configuredMode;
}
lcamera_dev::CameraIdentityRecord makeTestCameraIdentity()
{
lcamera_dev::CameraIdentityRecord identity;
identity.id = "unit-test-camera-1";
identity.model = "UnitTestCam";
return identity;
}
class YuvStimProducerQualeIfaceTest
: public ::testing::Test
{
protected:
void SetUp() override
{
lcameraBuffSmoHooksPtr = &lcamera_buff_tests::hilSmoCallbacksStub();
}
void TearDown() override
{
lcameraBuffSmoHooksPtr = nullptr;
}
boost::asio::io_context ioContext;
const LcameraBuffParsedParams parsedParams = makeDefaultParsedParams();
const lcamera_dev::LcameraDevConfiguredCameraMode configuredMode =
makeYuyv480ConfiguredMode();
const lcamera_dev::CameraIdentityRecord cameraIdentity =
makeTestCameraIdentity();
YuvStimProducer makeProducer(
const std::shared_ptr<device::DeviceAttachmentSpec>& producerSpec)
{
return YuvStimProducer(
producerSpec,
ioContext,
nullptr,
cameraIdentity,
parsedParams,
configuredMode);
}
};
TEST_F(YuvStimProducerQualeIfaceTest, GetOrCreateSameSpecReturnsExistingBuffer)
{
auto producerSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y");
YuvStimProducer producer = makeProducer(producerSpec);
const auto firstBuffer =
producer.getOrCreateAttachedStimulusBuffer(producerSpec);
ASSERT_NE(firstBuffer, nullptr);
const auto secondBuffer =
producer.getOrCreateAttachedStimulusBuffer(producerSpec);
EXPECT_EQ(firstBuffer, secondBuffer);
EXPECT_EQ(producer.attachedStimulusBuffers.size(), 1u);
}
TEST_F(YuvStimProducerQualeIfaceTest,
RejectDuplicateQualeWhenAlternateSelectorResolvesToSameSession)
{
/* One YuvStimProducer == one resolved libcamera camera id / session.
* A second DAP line may spell a different deviceSelector yet resolve to
* that same session via attachToExistingProducer; it must not attach the
* same qualeIface API twice.
*/
auto producerSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y");
YuvStimProducer producer = makeProducer(producerSpec);
const auto firstSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y", "cam-y-line0");
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(firstSpec), nullptr);
const auto duplicateQualeAlternateSelectorSpec = makeLcameraAttachSpec(
usbAliasSelector, "colour-yuv-y", "cam-y-line1");
try {
producer.getOrCreateAttachedStimulusBuffer(
duplicateQualeAlternateSelectorSpec);
FAIL() << "Expected std::runtime_error";
}
catch (const std::runtime_error& exception)
{
sscl::tests::expectExceptionMessageContains(
exception,
"already attached for camera session");
sscl::tests::expectExceptionMessageContains(
exception,
"colour-yuv-y");
}
EXPECT_EQ(producer.attachedStimulusBuffers.size(), 1u);
EXPECT_NE(
firstSpec->deviceSelector,
duplicateQualeAlternateSelectorSpec->deviceSelector);
}
TEST_F(YuvStimProducerQualeIfaceTest,
AllowDistinctQualeIfacesForAlternateSelectorsOnSameSession)
{
auto producerSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y");
YuvStimProducer producer = makeProducer(producerSpec);
const auto ySpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y", "cam-y");
const auto uSpec = makeLcameraAttachSpec(
usbAliasSelector, "colour-yuv-u", "cam-u");
const auto vSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-v", "cam-v");
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(ySpec), nullptr);
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(uSpec), nullptr);
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(vSpec), nullptr);
EXPECT_EQ(producer.attachedStimulusBuffers.size(), 3u);
EXPECT_TRUE(producer.hasBufferWithQualeIfaceApi("colour-yuv-y"));
EXPECT_TRUE(producer.hasBufferWithQualeIfaceApi("colour-yuv-u"));
EXPECT_TRUE(producer.hasBufferWithQualeIfaceApi("colour-yuv-v"));
}
TEST_F(YuvStimProducerQualeIfaceTest,
RejectDuplicateQualeAfterYViaSelectorAAndUViaSelectorB)
{
auto producerSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y");
YuvStimProducer producer = makeProducer(producerSpec);
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(
makeLcameraAttachSpec(usbExternalSelector, "colour-yuv-y", "cam-y")),
nullptr);
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(
makeLcameraAttachSpec(usbAliasSelector, "colour-yuv-u", "cam-u")),
nullptr);
const auto duplicateYViaAlternateSelector = makeLcameraAttachSpec(
usbAliasSelector, "colour-yuv-y", "cam-y-second-dap-line");
EXPECT_THROW(
producer.getOrCreateAttachedStimulusBuffer(
duplicateYViaAlternateSelector),
std::runtime_error);
EXPECT_EQ(producer.attachedStimulusBuffers.size(), 2u);
}
TEST_F(YuvStimProducerQualeIfaceTest,
FindAttachedBufferByAttachIdentityWithAlternateSelector)
{
auto producerSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-y");
YuvStimProducer producer = makeProducer(producerSpec);
const auto uSpec = makeLcameraAttachSpec(
usbExternalSelector, "colour-yuv-u", "cam-u");
ASSERT_NE(producer.getOrCreateAttachedStimulusBuffer(uSpec), nullptr);
const auto foundBuffer =
producer.getAttachedStimulusBufferByAttachIdentity(
"cam-u", "colour-yuv-u");
ASSERT_NE(foundBuffer, nullptr);
EXPECT_EQ(
foundBuffer->deviceAttachmentSpec->deviceSelector,
usbExternalSelector);
EXPECT_EQ(
producer.getAttachedStimulusBuffer(
makeLcameraAttachSpec(
usbAliasSelector, "colour-yuv-u", "cam-u")),
nullptr);
EXPECT_NE(
producer.getAttachedStimulusBufferByAttachIdentity(
"cam-u", "colour-yuv-u"),
nullptr);
}
} // namespace
} // namespace lcamera_buff
} // namespace stim_buff
} // namespace smo