#include #include #include #include #include #include #include #include #include #include #include #include #define CL_TARGET_OPENCL_VERSION 120 #include #include #include #include #include namespace smo { namespace stim_buff { namespace { class TestStimulusProducer : public StimulusProducer { public: using StimulusProducer::StimulusProducer; std::shared_ptr getOrCreateAttachedStimulusBuffer( const std::shared_ptr& deviceAttachmentSpec) override { (void)deviceAttachmentSpec; throw std::logic_error("not used in these unit tests"); } bool exportsQualeIfaceApi(const std::string& qualeIfaceApi) const override { (void)qualeIfaceApi; return true; } protected: sscl::co::ViralNonPostingInvoker stimFrameProductionTimesliceCInd( sscl::SyncCancelerForAsyncWork& canceler) override { (void)canceler; co_return; } }; std::optional searchForLibInSmoSearchPathsStub( const std::string& libraryPath) { (void)libraryPath; return std::nullopt; } std::shared_ptr componentThreadGetSelfStub() { return sscl::ComponentThread::getSelf(); } OptionParser& optionParserGetOptionsStub() { return OptionParser::getOptions(); } void releaseUseHostPtrBufferStub( std::shared_ptr buffer) { (void)buffer; } std::shared_ptr computeManagerGetDeviceStub() { return nullptr; } void computeManagerReleaseDeviceStub( std::shared_ptr device) { (void)device; } std::shared_ptr comparatorManagerGetComparatorTypeStub(smo::cologex::ComparatorTypeId typeId) { (void)typeId; return nullptr; } std::unique_ptr comparatorGetNewInstanceStub( const std::shared_ptr& comparatorType) { (void)comparatorType; return nullptr; } std::shared_ptr createUseHostPtrBufferStub( void* hostPtr, size_t size, cl_mem_flags flags) { static const std::vector> emptyDevices; return std::make_shared( hostPtr, size, flags, emptyDevices); } const SmoCallbacks kTestSmoCallbacks = { .searchForLibInSmoSearchPaths = searchForLibInSmoSearchPathsStub, .ComponentThread_getSelf = componentThreadGetSelfStub, .OptionParser_getOptions = optionParserGetOptionsStub, .ComputeManager_createUseHostPtrBuffer = createUseHostPtrBufferStub, .ComputeManager_releaseUseHostPtrBuffer = releaseUseHostPtrBufferStub, .ComputeManager_getDevice = computeManagerGetDeviceStub, .ComputeManager_releaseDevice = computeManagerReleaseDeviceStub, .ComparatorManager_getComparatorType = comparatorManagerGetComparatorTypeStub, .Comparator_getNewInstance = comparatorGetNewInstanceStub, }; StagingBuffer::IOEngineConstraints testBufferConstraints() { const size_t pageSize = static_cast(sysconf(_SC_PAGE_SIZE)); const size_t channelByteSize = pageSize; return StagingBuffer::IOEngineConstraints( 1, channelByteSize, pageSize, pageSize); } std::shared_ptr makeAttachSpec( const std::string& deviceSelector, const std::string& qualeIfaceApi, const std::string& deviceIdentifier = "cam0") { auto spec = std::make_shared(); spec->deviceIdentifier = deviceIdentifier; spec->sensorType = 'e'; spec->qualeIfaceApi = qualeIfaceApi; spec->stimBuffApi = "testBuff"; spec->provider = "testProvider"; spec->deviceSelector = deviceSelector; return spec; } std::shared_ptr makeAttachedTestBuffer( TestStimulusProducer& producer, const std::shared_ptr& attachSpec) { const StagingBuffer::IOEngineConstraints constraints = testBufferConstraints(); return std::make_shared( producer, attachSpec, CONFIG_STIMBUFF_FRAME_PERIOD_MS, constraints, constraints, kTestSmoCallbacks, CL_MEM_READ_WRITE); } class StimulusProducerQualeIfaceTest : public ::testing::Test { protected: boost::asio::io_context ioContext; std::shared_ptr producerSpec = makeAttachSpec("model-substr:USB;location:external", "colour-yuv-y"); TestStimulusProducer producer{producerSpec, ioContext}; }; TEST_F(StimulusProducerQualeIfaceTest, HasBufferWithQualeIfaceApiDetectsChannel) { EXPECT_FALSE(producer.hasBufferWithQualeIfaceApi("colour-yuv-y")); const auto attachSpec = makeAttachSpec( "model-substr:USB;location:external", "colour-yuv-y", "cam-y"); const auto buffer = makeAttachedTestBuffer(producer, attachSpec); ASSERT_TRUE(producer.addAttachedStimulusBufferIfNotExists(buffer)); EXPECT_TRUE(producer.hasBufferWithQualeIfaceApi("colour-yuv-y")); EXPECT_FALSE(producer.hasBufferWithQualeIfaceApi("colour-yuv-u")); } TEST_F(StimulusProducerQualeIfaceTest, AddAttachedStimulusBufferIfNotExistsAllowsSameQualeDifferentSelector) { const auto firstSpec = makeAttachSpec( "model-substr:USB;location:external", "colour-yuv-y", "cam-y-0"); const auto firstBuffer = makeAttachedTestBuffer(producer, firstSpec); ASSERT_TRUE(producer.addAttachedStimulusBufferIfNotExists(firstBuffer)); const auto secondSpec = makeAttachSpec( "model-substr:USB Video Device;location:external", "colour-yuv-y", "cam-y-1"); const auto secondBuffer = makeAttachedTestBuffer(producer, secondSpec); EXPECT_TRUE(producer.addAttachedStimulusBufferIfNotExists(secondBuffer)); EXPECT_EQ(producer.attachedStimulusBuffers.size(), 2u); EXPECT_TRUE(producer.hasBufferWithQualeIfaceApi("colour-yuv-y")); EXPECT_NE(firstSpec->deviceSelector, secondSpec->deviceSelector); } TEST_F(StimulusProducerQualeIfaceTest, EnsureNoDuplicateQualeIfaceRejectsAlternateSelector) { const auto firstSpec = makeAttachSpec( "model-substr:USB;location:external", "colour-yuv-y", "cam-y-0"); const auto firstBuffer = makeAttachedTestBuffer(producer, firstSpec); ASSERT_TRUE(producer.addAttachedStimulusBufferIfNotExists(firstBuffer)); const auto duplicateQualeSpec = makeAttachSpec( "model-substr:USB Video Device;location:external", "colour-yuv-y", "cam-y-1"); try { producer.ensureNoDuplicateQualeIface( duplicateQualeSpec->qualeIfaceApi); FAIL() << "Expected std::runtime_error"; } catch (const std::runtime_error& exception) { sscl::tests::expectExceptionMessageContains( exception, "duplicate qualeIface 'colour-yuv-y'"); } } TEST_F(StimulusProducerQualeIfaceTest, EnsureNoDuplicateQualeIfaceAllowsDistinctChannels) { const auto ySpec = makeAttachSpec( "model-substr:USB;location:external", "colour-yuv-y", "cam-y"); ASSERT_TRUE(producer.addAttachedStimulusBufferIfNotExists( makeAttachedTestBuffer(producer, ySpec))); EXPECT_NO_THROW( producer.ensureNoDuplicateQualeIface("colour-yuv-u")); EXPECT_NO_THROW( producer.ensureNoDuplicateQualeIface("colour-yuv-v")); } TEST_F(StimulusProducerQualeIfaceTest, GetAttachedStimulusBufferByAttachIdentityIgnoresSelectorString) { const auto attachedSpec = makeAttachSpec( "model-substr:USB;location:external", "colour-yuv-u", "cam-u"); ASSERT_TRUE(producer.addAttachedStimulusBufferIfNotExists( makeAttachedTestBuffer(producer, attachedSpec))); const auto foundBuffer = producer.getAttachedStimulusBufferByAttachIdentity( "cam-u", "colour-yuv-u"); ASSERT_NE(foundBuffer, nullptr); EXPECT_EQ(foundBuffer, producer.attachedStimulusBuffers.front()); EXPECT_NE( foundBuffer->deviceAttachmentSpec->deviceSelector, "model-substr:USB Video Device;location:external"); EXPECT_EQ( producer.getAttachedStimulusBufferByAttachIdentity( "cam-u", "colour-yuv-y"), nullptr); EXPECT_EQ( producer.getAttachedStimulusBufferByAttachIdentity( "cam-y", "colour-yuv-u"), nullptr); } } // namespace } // namespace stim_buff } // namespace smo