2026-06-13 16:08:21 -04:00
|
|
|
#include <gtest/gtest.h>
|
2026-06-13 18:50:31 -04:00
|
|
|
#include <catalogHelpers.h>
|
2026-06-13 16:08:21 -04:00
|
|
|
#include <selectorParse.h>
|
|
|
|
|
#include <selectorResolve.h>
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace lcamera_dev {
|
|
|
|
|
namespace {
|
|
|
|
|
|
2026-06-13 18:50:31 -04:00
|
|
|
constexpr const char *dellLaptopMachineTag = "dell-laptop";
|
|
|
|
|
|
2026-06-13 16:08:21 -04:00
|
|
|
static CameraIdentityRecord makeRecord(
|
|
|
|
|
const std::string& id,
|
|
|
|
|
const std::string& model = "",
|
|
|
|
|
const std::string& locationLabel = "")
|
|
|
|
|
{
|
|
|
|
|
CameraIdentityRecord record;
|
|
|
|
|
record.id = id;
|
|
|
|
|
record.model = model;
|
|
|
|
|
record.locationLabel = locationLabel;
|
|
|
|
|
return record;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-13 18:50:31 -04:00
|
|
|
static std::vector<CameraIdentityRecord> syntheticAmbiguityRecords()
|
2026-06-13 16:08:21 -04:00
|
|
|
{
|
|
|
|
|
return {
|
|
|
|
|
makeRecord("/base/cam0", "imx219", "back"),
|
|
|
|
|
makeRecord("/base/cam1", "Logitech C920", "external"),
|
|
|
|
|
makeRecord("/base/cam2", "imx219", "front"),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(FormatCameraListForDiagnosticsTest, ListsIndexedCameras)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records =
|
|
|
|
|
tests::bakedProfilesAsRecords(dellLaptopMachineTag);
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::string text = formatCameraListForDiagnostics(records);
|
|
|
|
|
|
|
|
|
|
EXPECT_NE(text.find("Known cameras:"), std::string::npos);
|
2026-06-13 18:50:31 -04:00
|
|
|
EXPECT_NE(text.find("HDMI USB Camera"), std::string::npos);
|
2026-06-13 16:08:21 -04:00
|
|
|
EXPECT_NE(text.find("location=external"), std::string::npos);
|
2026-06-13 18:50:31 -04:00
|
|
|
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<CameraIdentityRecord> records =
|
|
|
|
|
tests::bakedProfilesAsRecords(dellLaptopMachineTag);
|
|
|
|
|
const std::vector<SelectorCriterion> 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<CameraIdentityRecord> records =
|
|
|
|
|
tests::bakedProfilesAsRecords(dellLaptopMachineTag);
|
|
|
|
|
const std::vector<SelectorCriterion> 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<CameraIdentityRecord> records =
|
|
|
|
|
tests::bakedProfilesAsRecords(dellLaptopMachineTag);
|
|
|
|
|
const std::vector<SelectorCriterion> 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");
|
2026-06-13 16:08:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, MatchesLibcameraId)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("lcamera-id:/base/cam1");
|
|
|
|
|
|
|
|
|
|
const CameraIdentityRecord resolved =
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(resolved.id, "/base/cam1");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, MatchesModelSubstrAndLocation)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("model-substr:Logitech;location:EXTERNAL");
|
|
|
|
|
|
|
|
|
|
const CameraIdentityRecord resolved =
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(resolved.id, "/base/cam1");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, IndexSelectsNthCamera)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("index:2");
|
|
|
|
|
|
|
|
|
|
const CameraIdentityRecord resolved =
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(resolved.id, "/base/cam2");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, IndexCombinedWithModel)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("index:0;model:imx219");
|
|
|
|
|
|
|
|
|
|
const CameraIdentityRecord resolved =
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records);
|
|
|
|
|
|
|
|
|
|
EXPECT_EQ(resolved.id, "/base/cam0");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, ZeroMatchesThrowsWithDiagnostics)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("model:does-not-exist");
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records);
|
|
|
|
|
FAIL() << "Expected std::runtime_error";
|
|
|
|
|
}
|
|
|
|
|
catch (const std::runtime_error& exc)
|
|
|
|
|
{
|
|
|
|
|
EXPECT_NE(
|
|
|
|
|
std::string(exc.what()).find("No camera matches deviceSelector"),
|
|
|
|
|
std::string::npos);
|
|
|
|
|
EXPECT_NE(
|
|
|
|
|
std::string(exc.what()).find("Known cameras:"),
|
|
|
|
|
std::string::npos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, AmbiguousSelectorThrows)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("model:imx219");
|
|
|
|
|
|
|
|
|
|
EXPECT_THROW(
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records),
|
|
|
|
|
std::runtime_error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, IndexOutOfRangeThrows)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("index:9");
|
|
|
|
|
|
|
|
|
|
EXPECT_THROW(
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records),
|
|
|
|
|
std::runtime_error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, IndexConflictsWithOtherClauses)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("index:0;location:front");
|
|
|
|
|
|
|
|
|
|
EXPECT_THROW(
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records),
|
|
|
|
|
std::runtime_error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST(ResolveSelectorAgainstRecordsTest, InvalidIndexValueThrows)
|
|
|
|
|
{
|
2026-06-13 18:50:31 -04:00
|
|
|
const std::vector<CameraIdentityRecord> records = syntheticAmbiguityRecords();
|
2026-06-13 16:08:21 -04:00
|
|
|
const std::vector<SelectorCriterion> criteria =
|
|
|
|
|
parseDeviceSelector("index:not-a-number");
|
|
|
|
|
|
|
|
|
|
EXPECT_THROW(
|
|
|
|
|
resolveSelectorAgainstRecords(criteria, records),
|
|
|
|
|
std::runtime_error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
} // namespace lcamera_dev
|