Files
salmanoff/stimBuffApis/lcameraBuff/tests/lcameraBuff_configure_hil_tests.cpp
T
hayodea e261787cfe Add env-gated lcameraBuff configure HIL tests on baked USB profile.
HIL attaches Y/U/V channels, verifies shared producer state, and detaches
using an alternate selector string for the U channel quale.

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

328 lines
9.0 KiB
C++

#include <boostAsioLinkageFix.h>
#include <gtest/gtest.h>
#include <hilSmoCallbacksStub.h>
#include <lcameraBuffInternal.h>
#include <lcameraBuffParams.h>
#include <support/bakedDeviceCatalog.h>
#include <support/probeComponentThread.h>
#include <user/deviceAttachmentSpec.h>
#include <user/senseApiDesc.h>
#include <yuvCaptureLayout.h>
#include <pixelAndColorFormatDecisions.h>
#include <yuvChannelStimulusBuffer.h>
#include <yuvStimProducer.h>
#include <spinscale/co/nonViralTaskNursery.h>
#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
namespace lcamera_buff_tests {
namespace {
constexpr const char *hilEnvVar = "LCAMERADEV_HIL";
constexpr const char *machineEnvVar = "LCAMERADEV_MACHINE";
constexpr const char *defaultMachineTag = "dell-laptop";
bool hilTestsEnabled()
{
const char *value = std::getenv(hilEnvVar);
return value != nullptr && std::string(value) == "1";
}
std::string machineTagFromEnvironment()
{
const char *value = std::getenv(machineEnvVar);
if (value != nullptr && std::string(value).size() > 0) {
return value;
}
return defaultMachineTag;
}
std::shared_ptr<smo::device::DeviceAttachmentSpec> makeAttachSpec(
const test_fixtures::BakedCameraProfile *profile,
const char *qualeIfaceApi)
{
auto spec = std::make_shared<smo::device::DeviceAttachmentSpec>();
spec->deviceIdentifier = "cam0";
spec->sensorType = 'e';
spec->qualeIfaceApi = qualeIfaceApi;
spec->stimBuffApi = "lcameraBuff";
spec->provider = "lcameraDev";
spec->deviceSelector = profile->exampleSelector;
spec->stimBuffApiParams = {
{"v-res", "480p"},
{"colour-space", "yuv"},
{"opt-planar", ""},
};
return spec;
}
std::shared_ptr<smo::device::DeviceAttachmentSpec> makeDetachSpec(
const test_fixtures::BakedCameraProfile *profile,
const char *qualeIfaceApi,
const char *deviceSelectorOverride)
{
auto spec = makeAttachSpec(profile, qualeIfaceApi);
spec->deviceSelector = deviceSelectorOverride;
return spec;
}
sscl::co::NonViralNonPostingInvoker initializeBuffCInd(
std::exception_ptr& exceptionStorage,
std::function<void()> callerLambda)
{
(void)exceptionStorage;
(void)callerLambda;
co_await smo::stim_buff::lcamera_buff::lcameraBuff_initializeCInd();
co_return;
}
sscl::co::NonViralNonPostingInvoker finalizeBuffCInd(
std::exception_ptr& exceptionStorage,
std::function<void()> callerLambda)
{
(void)exceptionStorage;
(void)callerLambda;
co_await smo::stim_buff::lcamera_buff::lcameraBuff_finalizeCInd();
co_return;
}
sscl::co::NonViralNonPostingInvoker attachDeviceCInd(
std::exception_ptr& exceptionStorage,
std::function<void()> callerLambda,
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& spec,
const std::shared_ptr<sscl::ComponentThread>& componentThread,
smo::stim_buff::StimBuffDeviceOpResult& result)
{
(void)exceptionStorage;
(void)callerLambda;
result = co_await smo::stim_buff::lcamera_buff::lcameraBuff_attachDeviceCReq(
sscl::co::ExplicitPostTarget(componentThread->getIoContext()),
spec,
componentThread);
co_return;
}
sscl::co::NonViralNonPostingInvoker detachDeviceCInd(
std::exception_ptr& exceptionStorage,
std::function<void()> callerLambda,
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& spec,
smo::stim_buff::StimBuffDeviceOpResult& result)
{
(void)exceptionStorage;
(void)callerLambda;
result = co_await smo::stim_buff::lcamera_buff::lcameraBuff_detachDeviceCReq(
sscl::co::ExplicitPostTarget(
smo::stim_buff::lcamera_buff::lcameraBuffThreadingModelDesc
.componentThread->getIoContext()),
spec);
co_return;
}
void runOnProbeThread(
const std::function<void(
const std::shared_ptr<sscl::ComponentThread>&)>& work)
{
sscl::tests::ProbeComponentThreadHarness harness("lcameraBuff-configure-hil");
harness.runSync(work);
}
class LcameraBuffConfigureHilTest : public ::testing::Test
{
protected:
void SetUp() override
{
if (!hilTestsEnabled()) {
GTEST_SKIP() << "Set " << hilEnvVar << "=1 to run hardware tests";
}
machineTag = machineTagFromEnvironment();
requiredProfiles =
sscl::tests::requiredProfilesForMachine(machineTag.c_str());
if (requiredProfiles.empty()) {
GTEST_SKIP() << "No baked profiles for machine tag "
<< machineTag;
}
const test_fixtures::BakedCameraProfile *profile =
findProfile("usb_hdmi_camera");
if (!profile) {
GTEST_SKIP() << "usb_hdmi_camera profile not available";
}
usbProfile = profile;
}
const test_fixtures::BakedCameraProfile *findProfile(
const char *profileTag) const
{
for (const test_fixtures::BakedCameraProfile *profile :
requiredProfiles)
{
if (std::string(profile->profileTag) == profileTag) {
return profile;
}
}
return nullptr;
}
std::string machineTag;
std::vector<const test_fixtures::BakedCameraProfile *> requiredProfiles;
const test_fixtures::BakedCameraProfile *usbProfile = nullptr;
};
TEST_F(LcameraBuffConfigureHilTest, AttachThreeYuvChannelsWithOptPlanar480p)
{
const char *libPathEnv = std::getenv("LCAMERADEV_LIB_PATH");
if (libPathEnv == nullptr || std::string(libPathEnv).empty())
{
GTEST_SKIP() << "Set LCAMERADEV_LIB_PATH to liblcameraDev.so for HIL attach";
}
runOnProbeThread(
[this](const std::shared_ptr<sscl::ComponentThread>& componentThread)
{
smo::stim_buff::lcamera_buff::lcameraBuffSmoHooksPtr =
&hilSmoCallbacksStub();
smo::stim_buff::lcamera_buff::lcameraBuffThreadingModelDesc
.componentThread = componentThread;
sscl::tests::runNonViralNurseryOnComponentThread(
componentThread,
[](sscl::co::NonViralTaskNursery::Slot::Lease& lease)
{
return initializeBuffCInd(
lease.getExceptionStorage(),
lease.getCallerLambda());
});
std::shared_ptr<smo::stim_buff::lcamera_buff::YuvStimProducer>
producer;
size_t expectedBufferCount = 0;
for (const char *qualeIfaceApi :
{"colour-yuv-y", "colour-yuv-u", "colour-yuv-v"})
{
const std::shared_ptr<smo::device::DeviceAttachmentSpec> spec =
makeAttachSpec(usbProfile, qualeIfaceApi);
smo::stim_buff::StimBuffDeviceOpResult attachResult;
sscl::tests::runNonViralNurseryOnComponentThread(
componentThread,
[&spec, &componentThread, &attachResult](
sscl::co::NonViralTaskNursery::Slot::Lease& lease)
{
return attachDeviceCInd(
lease.getExceptionStorage(),
lease.getCallerLambda(),
spec,
componentThread,
attachResult);
});
ASSERT_TRUE(attachResult.success);
++expectedBufferCount;
ASSERT_EQ(
smo::stim_buff::lcamera_buff::attachedStimulusProducers.size(),
1u);
producer = smo::stim_buff::lcamera_buff
::attachedStimulusProducers.front();
ASSERT_TRUE(producer != nullptr);
EXPECT_EQ(
producer->attachedStimulusBuffers.size(),
expectedBufferCount);
}
ASSERT_TRUE(producer != nullptr);
EXPECT_EQ(
producer->configuredMode.pixelFormatName,
"YUYV");
EXPECT_EQ(
producer->layoutPath,
smo::stim_buff::lcamera_buff::YuvCaptureLayoutPath
::PackedDeinterleave);
EXPECT_EQ(
producer->chromaSubsampling,
smo::stim_buff::lcamera_buff::YuvChromaSubsampling::Yuv422);
const size_t expectedUBytes =
smo::stim_buff::lcamera_buff
::computeDeinterleavedChannelByteSize(
smo::stim_buff::lcamera_buff::YuvChannelKind::U,
producer->configuredMode.width,
producer->configuredMode.height,
smo::stim_buff::lcamera_buff
::YuvChromaSubsampling::Yuv422);
EXPECT_EQ(expectedUBytes, 320u * 480u);
for (const std::shared_ptr<smo::stim_buff::StimulusBuffer>& bufferBase :
producer->attachedStimulusBuffers)
{
auto channelBuffer =
std::dynamic_pointer_cast<
smo::stim_buff::lcamera_buff::YuvChannelStimulusBuffer>(
bufferBase);
ASSERT_TRUE(channelBuffer != nullptr);
if (channelBuffer->channelKind
== smo::stim_buff::lcamera_buff::YuvChannelKind::U
|| channelBuffer->channelKind
== smo::stim_buff::lcamera_buff::YuvChannelKind::V)
{
EXPECT_EQ(channelBuffer->channelByteSize, expectedUBytes);
}
}
for (const char *qualeIfaceApi :
{"colour-yuv-v", "colour-yuv-u", "colour-yuv-y"})
{
const std::shared_ptr<smo::device::DeviceAttachmentSpec> spec =
(std::string(qualeIfaceApi) == "colour-yuv-u")
? makeDetachSpec(
usbProfile,
qualeIfaceApi,
"model-substr:HDMI USB Camera;location:external")
: makeAttachSpec(usbProfile, qualeIfaceApi);
smo::stim_buff::StimBuffDeviceOpResult detachResult;
sscl::tests::runNonViralNurseryOnComponentThread(
componentThread,
[&spec, &detachResult](
sscl::co::NonViralTaskNursery::Slot::Lease& lease)
{
return detachDeviceCInd(
lease.getExceptionStorage(),
lease.getCallerLambda(),
spec,
detachResult);
});
ASSERT_TRUE(detachResult.success);
}
sscl::tests::runNonViralNurseryOnComponentThread(
componentThread,
[](sscl::co::NonViralTaskNursery::Slot::Lease& lease)
{
return finalizeBuffCInd(
lease.getExceptionStorage(),
lease.getCallerLambda());
});
});
}
} // namespace
} // namespace lcamera_buff_tests