e261787cfe
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>
328 lines
9.0 KiB
C++
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
|