From 25d7b9c0139280a5c4a7a25ed7b6372a702f676a Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Sat, 13 Jun 2026 18:50:31 -0400 Subject: [PATCH] LcamDev: Add baked in camera profiles; use new test supports --- commonLibs/lcameraDev/CMakeLists.txt | 10 + commonLibs/lcameraDev/tests/CMakeLists.txt | 30 +++ commonLibs/lcameraDev/tests/catalogHelpers.h | 45 ++++ .../lcameraDev/tests/lcameraDev_hil_tests.cpp | 193 ++++++++++++++++++ .../tests/selectorResolve_tests.cpp | 79 +++++-- commonLibs/lcameraDev/tools/probeRunner.cpp | 81 +------- scripts/extractBakedCameraProfiles.sh | 79 +++++++ tests/fixtures/bakedCameraProfiles.h | 61 ++++++ 8 files changed, 488 insertions(+), 90 deletions(-) create mode 100644 commonLibs/lcameraDev/tests/catalogHelpers.h create mode 100644 commonLibs/lcameraDev/tests/lcameraDev_hil_tests.cpp create mode 100755 scripts/extractBakedCameraProfiles.sh create mode 100644 tests/fixtures/bakedCameraProfiles.h diff --git a/commonLibs/lcameraDev/CMakeLists.txt b/commonLibs/lcameraDev/CMakeLists.txt index 861232c..ec92f0b 100644 --- a/commonLibs/lcameraDev/CMakeLists.txt +++ b/commonLibs/lcameraDev/CMakeLists.txt @@ -56,6 +56,12 @@ if(ENABLE_LIB_lcameraDev) option(ENABLE_LCAMERADEV_TOOLS "Build lcameraDev probe/list tools" ON) if(ENABLE_LCAMERADEV_TOOLS) + if(NOT TARGET spinscale_test_support) + message(FATAL_ERROR + "lcameraDev probe tools require spinscale_test_support. " + "Configure with -DENABLE_TESTS=ON.") + endif() + add_executable(lcameraDev_list_cameras tools/lcameraDevListCameras.cpp tools/probeRunner.cpp @@ -65,10 +71,12 @@ if(ENABLE_LIB_lcameraDev) ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include + ${CMAKE_SOURCE_DIR}/libspinscale/tests ) target_link_libraries(lcameraDev_list_cameras PRIVATE lcameraDev spinscale + spinscale_test_support Boost::system Boost::log ) @@ -82,10 +90,12 @@ if(ENABLE_LIB_lcameraDev) ${CMAKE_CURRENT_SOURCE_DIR}/tools ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include + ${CMAKE_SOURCE_DIR}/libspinscale/tests ) target_link_libraries(lcameraDev_probe PRIVATE lcameraDev spinscale + spinscale_test_support Boost::system Boost::log ) diff --git a/commonLibs/lcameraDev/tests/CMakeLists.txt b/commonLibs/lcameraDev/tests/CMakeLists.txt index acdd49a..9c84aa1 100644 --- a/commonLibs/lcameraDev/tests/CMakeLists.txt +++ b/commonLibs/lcameraDev/tests/CMakeLists.txt @@ -5,7 +5,10 @@ add_executable(lcameraDev_unit_tests ) target_include_directories(lcameraDev_unit_tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/commonLibs/lcameraDev + ${CMAKE_SOURCE_DIR}/commonLibs/lcameraDev/tests + ${CMAKE_SOURCE_DIR}/tests/fixtures ${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ) @@ -20,3 +23,30 @@ target_link_libraries(lcameraDev_unit_tests add_dependencies(lcameraDev_unit_tests gtest_main) add_test(NAME lcameraDev_unit_tests COMMAND lcameraDev_unit_tests) + +add_executable(lcameraDev_hil_tests + lcameraDev_hil_tests.cpp +) + +target_include_directories(lcameraDev_hil_tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/commonLibs/lcameraDev + ${CMAKE_SOURCE_DIR}/commonLibs/lcameraDev/tests + ${CMAKE_SOURCE_DIR}/tests/fixtures + ${CMAKE_SOURCE_DIR}/libspinscale/tests + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_BINARY_DIR}/include +) + +target_link_libraries(lcameraDev_hil_tests + gtest_main + lcameraDev + spinscale + spinscale_test_support + ${Boost_LIBRARIES} +) + +add_dependencies(lcameraDev_hil_tests gtest_main spinscale_test_support) + +add_test(NAME lcameraDev_hil_tests COMMAND lcameraDev_hil_tests) +set_tests_properties(lcameraDev_hil_tests PROPERTIES LABELS "HIL") diff --git a/commonLibs/lcameraDev/tests/catalogHelpers.h b/commonLibs/lcameraDev/tests/catalogHelpers.h new file mode 100644 index 0000000..a6e9572 --- /dev/null +++ b/commonLibs/lcameraDev/tests/catalogHelpers.h @@ -0,0 +1,45 @@ +#ifndef LCAMERA_DEV_TESTS_CATALOG_HELPERS_H +#define LCAMERA_DEV_TESTS_CATALOG_HELPERS_H + +#include +#include +#include +#include + +namespace lcamera_dev { +namespace tests { + +inline CameraIdentityRecord profileToIdentityRecord( + const test_fixtures::BakedCameraProfile& profile) +{ + CameraIdentityRecord record; + record.id = profile.libcameraId; + record.model = profile.model; + record.locationLabel = profile.location; + return record; +} + +inline std::vector bakedProfilesAsRecords( + const char *machineTag) +{ + std::vector records; + + for (std::size_t i = 0; i < test_fixtures::bakedCameraProfileCount; ++i) + { + const test_fixtures::BakedCameraProfile& profile = + test_fixtures::bakedCameraProfiles[i]; + + if (std::string(profile.machineTag) != machineTag) { + continue; + } + + records.push_back(profileToIdentityRecord(profile)); + } + + return records; +} + +} // namespace tests +} // namespace lcamera_dev + +#endif // LCAMERA_DEV_TESTS_CATALOG_HELPERS_H diff --git a/commonLibs/lcameraDev/tests/lcameraDev_hil_tests.cpp b/commonLibs/lcameraDev/tests/lcameraDev_hil_tests.cpp new file mode 100644 index 0000000..5674c3f --- /dev/null +++ b/commonLibs/lcameraDev/tests/lcameraDev_hil_tests.cpp @@ -0,0 +1,193 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace lcamera_dev { +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; +} + +sscl::co::NonViralNonPostingInvoker enumerateCamerasCInd( + std::exception_ptr& exceptionStorage, + std::function callerLambda, + std::vector& enumeratedCameras) +{ + (void)exceptionStorage; + (void)callerLambda; + + enumeratedCameras = co_await lcameraDev_enumerateCamerasCReq(); + co_return; +} + +sscl::co::NonViralNonPostingInvoker getOrCreateCInd( + std::exception_ptr& exceptionStorage, + std::function callerLambda, + const char *deviceSelector, + lcamera_dev::LcameraDevGetOrCreateResult& createResult) +{ + (void)exceptionStorage; + (void)callerLambda; + + createResult = co_await lcameraDev_getOrCreateDeviceCReq(deviceSelector); + co_return; +} + +sscl::co::NonViralNonPostingInvoker releaseCInd( + std::exception_ptr& exceptionStorage, + std::function callerLambda, + const std::shared_ptr& deviceSession) +{ + (void)exceptionStorage; + (void)callerLambda; + + co_await lcameraDev_releaseDeviceCReq(deviceSession); + co_return; +} + +void runLcameraDevMainAndNurseryTask( + const std::function&)>& work) +{ + sscl::tests::ProbeComponentThreadHarness harness("lcameraDev-hil"); + harness.runSync( + [&work](const std::shared_ptr& componentThread) + { + lcameraDev_main(componentThread); + work(componentThread); + lcameraDev_exit(); + }); +} + +class LcameraDevHilTest : 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; + } + } + + std::string machineTag; + std::vector requiredProfiles; +}; + +TEST_F(LcameraDevHilTest, EnumerateMatchesBakedCatalog) +{ + std::vector enumeratedCameras; + + runLcameraDevMainAndNurseryTask( + [&enumeratedCameras]( + const std::shared_ptr& componentThread) + { + sscl::tests::runNonViralNurseryOnComponentThread( + componentThread, + [&enumeratedCameras]( + sscl::co::NonViralTaskNursery::Slot::Lease& lease) + { + return enumerateCamerasCInd( + lease.getExceptionStorage(), + lease.getCallerLambda(), + enumeratedCameras); + }); + }); + + EXPECT_GE(enumeratedCameras.size(), requiredProfiles.size()); + + for (const test_fixtures::BakedCameraProfile *profile : requiredProfiles) + { + bool found = false; + for (const lcamera_dev::LcameraDevCameraInfo& camera : enumeratedCameras) + { + if (camera.id == profile->libcameraId) { + found = true; + break; + } + } + + EXPECT_TRUE(found) + << "Missing baked profile camera id=" << profile->libcameraId; + } +} + +TEST_F(LcameraDevHilTest, GetOrCreateByBakedSelector) +{ + runLcameraDevMainAndNurseryTask( + [this](const std::shared_ptr& componentThread) + { + for (const test_fixtures::BakedCameraProfile *profile : + requiredProfiles) + { + lcamera_dev::LcameraDevGetOrCreateResult createResult; + + sscl::tests::runNonViralNurseryOnComponentThread( + componentThread, + [profile, &createResult]( + sscl::co::NonViralTaskNursery::Slot::Lease& lease) + { + return getOrCreateCInd( + lease.getExceptionStorage(), + lease.getCallerLambda(), + profile->exampleSelector, + createResult); + }); + + EXPECT_TRUE(createResult.deviceSession != nullptr) + << profile->profileTag; + EXPECT_EQ( + createResult.resolvedIdentity.id, + profile->libcameraId) + << profile->profileTag; + + sscl::tests::runNonViralNurseryOnComponentThread( + componentThread, + [&createResult]( + sscl::co::NonViralTaskNursery::Slot::Lease& lease) + { + return releaseCInd( + lease.getExceptionStorage(), + lease.getCallerLambda(), + createResult.deviceSession); + }); + } + }); +} + +} // namespace +} // namespace lcamera_dev diff --git a/commonLibs/lcameraDev/tests/selectorResolve_tests.cpp b/commonLibs/lcameraDev/tests/selectorResolve_tests.cpp index 44bb6a4..b584fbd 100644 --- a/commonLibs/lcameraDev/tests/selectorResolve_tests.cpp +++ b/commonLibs/lcameraDev/tests/selectorResolve_tests.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -8,6 +9,8 @@ namespace lcamera_dev { namespace { +constexpr const char *dellLaptopMachineTag = "dell-laptop"; + static CameraIdentityRecord makeRecord( const std::string& id, const std::string& model = "", @@ -20,7 +23,7 @@ static CameraIdentityRecord makeRecord( return record; } -static std::vector sampleRecords() +static std::vector syntheticAmbiguityRecords() { return { makeRecord("/base/cam0", "imx219", "back"), @@ -31,18 +34,68 @@ static std::vector sampleRecords() TEST(FormatCameraListForDiagnosticsTest, ListsIndexedCameras) { - const std::vector records = sampleRecords(); + const std::vector records = + tests::bakedProfilesAsRecords(dellLaptopMachineTag); const std::string text = formatCameraListForDiagnostics(records); EXPECT_NE(text.find("Known cameras:"), std::string::npos); - EXPECT_NE(text.find("[0] id=/base/cam0"), std::string::npos); - EXPECT_NE(text.find("model=imx219"), std::string::npos); + EXPECT_NE(text.find("HDMI USB Camera"), std::string::npos); EXPECT_NE(text.find("location=external"), std::string::npos); + EXPECT_NE(text.find("Integrated_Webcam_HD"), std::string::npos); + EXPECT_NE(text.find("location=front"), std::string::npos); +} + +TEST(ResolveSelectorAgainstRecordsTest, BakedUsbHdmiCameraMatchesExampleSelector) +{ + const std::vector records = + tests::bakedProfilesAsRecords(dellLaptopMachineTag); + const std::vector criteria = + parseDeviceSelector("model-substr:HDMI;location:EXTERNAL"); + + const CameraIdentityRecord resolved = + resolveSelectorAgainstRecords(criteria, records); + + EXPECT_EQ( + resolved.id, + "\\_SB_.PCI0.XHC_.RHUB.HS01-1:1.0-32e4:9415"); + EXPECT_EQ(resolved.locationLabel, "external"); +} + +TEST(ResolveSelectorAgainstRecordsTest, BakedIntegratedWebcamMatchesExampleSelector) +{ + const std::vector records = + tests::bakedProfilesAsRecords(dellLaptopMachineTag); + const std::vector criteria = + parseDeviceSelector("model-substr:Integrated;location:front"); + + const CameraIdentityRecord resolved = + resolveSelectorAgainstRecords(criteria, records); + + EXPECT_EQ( + resolved.id, + "\\_SB_.PCI0.XHC_.RHUB.HS04-4:1.0-1bcf:2b8a"); + EXPECT_EQ(resolved.locationLabel, "front"); +} + +TEST(ResolveSelectorAgainstRecordsTest, BakedUsbCameraMatchesLibcameraId) +{ + const std::vector records = + tests::bakedProfilesAsRecords(dellLaptopMachineTag); + const std::vector criteria = + parseDeviceSelector( + "lcamera-id:\\_SB_.PCI0.XHC_.RHUB.HS01-1:1.0-32e4:9415"); + + const CameraIdentityRecord resolved = + resolveSelectorAgainstRecords(criteria, records); + + EXPECT_EQ( + resolved.id, + "\\_SB_.PCI0.XHC_.RHUB.HS01-1:1.0-32e4:9415"); } TEST(ResolveSelectorAgainstRecordsTest, MatchesLibcameraId) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("lcamera-id:/base/cam1"); @@ -54,7 +107,7 @@ TEST(ResolveSelectorAgainstRecordsTest, MatchesLibcameraId) TEST(ResolveSelectorAgainstRecordsTest, MatchesModelSubstrAndLocation) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("model-substr:Logitech;location:EXTERNAL"); @@ -66,7 +119,7 @@ TEST(ResolveSelectorAgainstRecordsTest, MatchesModelSubstrAndLocation) TEST(ResolveSelectorAgainstRecordsTest, IndexSelectsNthCamera) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("index:2"); @@ -78,7 +131,7 @@ TEST(ResolveSelectorAgainstRecordsTest, IndexSelectsNthCamera) TEST(ResolveSelectorAgainstRecordsTest, IndexCombinedWithModel) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("index:0;model:imx219"); @@ -90,7 +143,7 @@ TEST(ResolveSelectorAgainstRecordsTest, IndexCombinedWithModel) TEST(ResolveSelectorAgainstRecordsTest, ZeroMatchesThrowsWithDiagnostics) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("model:does-not-exist"); @@ -111,7 +164,7 @@ TEST(ResolveSelectorAgainstRecordsTest, ZeroMatchesThrowsWithDiagnostics) TEST(ResolveSelectorAgainstRecordsTest, AmbiguousSelectorThrows) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("model:imx219"); @@ -122,7 +175,7 @@ TEST(ResolveSelectorAgainstRecordsTest, AmbiguousSelectorThrows) TEST(ResolveSelectorAgainstRecordsTest, IndexOutOfRangeThrows) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("index:9"); @@ -133,7 +186,7 @@ TEST(ResolveSelectorAgainstRecordsTest, IndexOutOfRangeThrows) TEST(ResolveSelectorAgainstRecordsTest, IndexConflictsWithOtherClauses) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("index:0;location:front"); @@ -144,7 +197,7 @@ TEST(ResolveSelectorAgainstRecordsTest, IndexConflictsWithOtherClauses) TEST(ResolveSelectorAgainstRecordsTest, InvalidIndexValueThrows) { - const std::vector records = sampleRecords(); + const std::vector records = syntheticAmbiguityRecords(); const std::vector criteria = parseDeviceSelector("index:not-a-number"); diff --git a/commonLibs/lcameraDev/tools/probeRunner.cpp b/commonLibs/lcameraDev/tools/probeRunner.cpp index b3349e3..e1ebee5 100644 --- a/commonLibs/lcameraDev/tools/probeRunner.cpp +++ b/commonLibs/lcameraDev/tools/probeRunner.cpp @@ -1,88 +1,15 @@ -#include - #include -#include -#include -#include + +#include namespace lcamera_dev_probe { -namespace { - -constexpr sscl::ThreadId PROBE_PUPPETEER_THREAD_ID = 1; - -class DummyPuppeteerComponent -: public sscl::pptr::PuppeteerComponent -{ -public: - explicit DummyPuppeteerComponent( - const std::shared_ptr& componentThread) - : sscl::pptr::PuppeteerComponent(componentThread) - {} - - void handleLoopExceptionHook() override - { - std::cerr << "lcameraDev probe: puppeteer loop exception\n"; - } -}; - -void probePuppeteerMain( - const sscl::PuppeteerThread::EntryFnArguments& args, - const std::function&)>& work, - std::promise& donePromise) -{ - sscl::PuppeteerThread& thr = args.usableBeforeJolt; - thr.initializeTls(); - sscl::ComponentThread::setPuppeteerThreadId(PROBE_PUPPETEER_THREAD_ID); - - std::shared_ptr thrPtr = - std::static_pointer_cast(thr.shared_from_this()); - sscl::ComponentThread::setPuppeteerThread(thrPtr); - - try { - work(thrPtr); - donePromise.set_value(nullptr); - } - catch (...) { - donePromise.set_value(std::current_exception()); - } - - thr.getIoContext().stop(); -} - -} // namespace - void runOnComponentThread( const std::function&)>& work) { - std::promise donePromise; - std::future doneFuture = donePromise.get_future(); - - DummyPuppeteerComponent dummyComponent{ - std::shared_ptr()}; - - std::shared_ptr probeThread = - std::make_shared( - PROBE_PUPPETEER_THREAD_ID, - "lcameraDev-probe", - [&work, &donePromise]( - const sscl::PuppeteerThread::EntryFnArguments& args) - { - probePuppeteerMain(args, work, donePromise); - }, - dummyComponent, - nullptr); - - dummyComponent.thread = probeThread; - - probeThread->thread.join(); - - std::exception_ptr probeException = doneFuture.get(); - if (probeException) { - std::rethrow_exception(probeException); - } + sscl::tests::ProbeComponentThreadHarness harness("lcameraDev-probe"); + harness.runSync(work); } } // namespace lcamera_dev_probe diff --git a/scripts/extractBakedCameraProfiles.sh b/scripts/extractBakedCameraProfiles.sh new file mode 100755 index 0000000..93a9d2b --- /dev/null +++ b/scripts/extractBakedCameraProfiles.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# Extract libcamera enumeration output as C++ BakedCameraProfile initializer rows. +# +# Usage: +# scripts/extractBakedCameraProfiles.sh +# +# Example: +# scripts/extractBakedCameraProfiles.sh build-agent dell-laptop + +set -euo pipefail + +if [[ $# -lt 2 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +BUILD_DIR="$1" +MACHINE_TAG="$2" +LIST_CAMERAS="${BUILD_DIR}/commonLibs/lcameraDev/lcameraDev_list_cameras" + +if [[ ! -x "${LIST_CAMERAS}" ]]; then + echo "Missing executable: ${LIST_CAMERAS}" >&2 + echo "Build with -DENABLE_LIB_lcameraDev=ON -DENABLE_TESTS=ON" >&2 + exit 1 +fi + +echo "// MACHINE: ${MACHINE_TAG}" +echo "// Paste rows into tests/fixtures/bakedCameraProfiles.h" +echo + +"${LIST_CAMERAS}" 2>/dev/null | awk -v machine="${MACHINE_TAG}" ' +/^lcameraDev: found / { next } +/^\s+\[[0-9]+\] id=/ { + line = $0 + sub(/^\s+\[[0-9]+\] /, "", line) + + id = "" + model = "" + location = "" + + n = split(line, parts, " ") + for (i = 1; i <= n; i++) { + if (parts[i] ~ /^id=/) { + id = substr(parts[i], 4) + } else if (parts[i] ~ /^model=/) { + model = substr(parts[i], 7) + } else if (parts[i] ~ /^location=/) { + location = substr(parts[i], 10) + } + } + + profile_tag = "camera_index_" index + if (location == "external") { + profile_tag = "usb_camera" + } else if (location == "front" || location == "back") { + profile_tag = "integrated_webcam" + } + + selector = "index:" index + if (model != "") { + selector = "model-substr:" substr(model, 1, 12) + if (location != "") { + selector = selector ";location:" location + } + } + + printf "{\n" + printf "\t\"%s\",\n", machine + printf "\t\"%s\",\n", profile_tag + printf "\t\"%s\",\n", id + printf "\t\"%s\",\n", model + printf "\t\"%s\",\n", location + printf "\t\"\",\n" + printf "\t\"\",\n" + printf "\t\"%s\",\n", selector + printf "\ttrue,\n" + printf "},\n" +} +' index=0 diff --git a/tests/fixtures/bakedCameraProfiles.h b/tests/fixtures/bakedCameraProfiles.h new file mode 100644 index 0000000..588d47f --- /dev/null +++ b/tests/fixtures/bakedCameraProfiles.h @@ -0,0 +1,61 @@ +#ifndef TESTS_FIXTURES_BAKED_CAMERA_PROFILES_H +#define TESTS_FIXTURES_BAKED_CAMERA_PROFILES_H + +/** Baked camera profiles captured from real hardware for offline unit tests. + * + * To add profiles for a new machine or camera: + * 1. Build lcameraDev_list_cameras with -DENABLE_LIB_lcameraDev=ON -DENABLE_TESTS=ON + * 2. Run scripts/extractBakedCameraProfiles.sh + * 3. Merge the printed rows into the matching MACHINE section below. + */ + +#include + +namespace test_fixtures { + +struct BakedCameraProfile +{ + const char *machineTag; + const char *profileTag; + const char *libcameraId; + const char *model; + const char *location; + const char *usbVidPid; + const char *usbSerial; + const char *exampleSelector; + bool requiredOnMachine; +}; + +// MACHINE: dell-laptop +// Captured via lcameraDev_list_cameras on 2026-06-10. +inline constexpr BakedCameraProfile bakedCameraProfiles[] = { + { + "dell-laptop", + "usb_hdmi_camera", + "\\_SB_.PCI0.XHC_.RHUB.HS01-1:1.0-32e4:9415", + "HDMI USB Camera: HDMI USB Camer", + "external", + "32e4:9415", + "1020181e58586223", + "model-substr:HDMI;location:EXTERNAL", + true, + }, + { + "dell-laptop", + "integrated_webcam", + "\\_SB_.PCI0.XHC_.RHUB.HS04-4:1.0-1bcf:2b8a", + "Integrated_Webcam_HD: Integrate", + "front", + "1bcf:2b8a", + "", + "model-substr:Integrated;location:front", + true, + }, +}; + +inline constexpr std::size_t bakedCameraProfileCount = + sizeof(bakedCameraProfiles) / sizeof(bakedCameraProfiles[0]); + +} // namespace test_fixtures + +#endif // TESTS_FIXTURES_BAKED_CAMERA_PROFILES_H