Tests: add tests for lcameraDev, fix qutex tests
This commit is contained in:
@@ -27,6 +27,7 @@ target_link_libraries(attachmentSupport PUBLIC
|
|||||||
)
|
)
|
||||||
target_link_libraries(attachmentSupport PRIVATE
|
target_link_libraries(attachmentSupport PRIVATE
|
||||||
${ATTACHMENT_SUPPORT_URING_LIBRARIES}
|
${ATTACHMENT_SUPPORT_URING_LIBRARIES}
|
||||||
|
${OPENCL_LIBRARIES}
|
||||||
)
|
)
|
||||||
target_link_directories(attachmentSupport PRIVATE
|
target_link_directories(attachmentSupport PRIVATE
|
||||||
${ATTACHMENT_SUPPORT_URING_LIBRARY_DIRS}
|
${ATTACHMENT_SUPPORT_URING_LIBRARY_DIRS}
|
||||||
|
|||||||
@@ -90,4 +90,8 @@ if(ENABLE_LIB_lcameraDev)
|
|||||||
Boost::log
|
Boost::log
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(ENABLE_TESTS)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -6,12 +6,6 @@ namespace lcamera_dev {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool startsWith(const std::string& text, const std::string& prefix)
|
|
||||||
{
|
|
||||||
return text.size() >= prefix.size()
|
|
||||||
&& text.compare(0, prefix.size(), prefix) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectorCriterionKind parseCriterionKind(const std::string& prefixToken)
|
SelectorCriterionKind parseCriterionKind(const std::string& prefixToken)
|
||||||
{
|
{
|
||||||
if (prefixToken == "lcamera-id") {
|
if (prefixToken == "lcamera-id") {
|
||||||
|
|||||||
@@ -129,6 +129,18 @@ CameraIdentityRecord resolveSelectorAgainstRecords(
|
|||||||
const CameraIdentityRecord& indexedRecord =
|
const CameraIdentityRecord& indexedRecord =
|
||||||
records.at(static_cast<size_t>(*indexCriterion));
|
records.at(static_cast<size_t>(*indexCriterion));
|
||||||
|
|
||||||
|
bool hasNonIndexCriteria = false;
|
||||||
|
for (const SelectorCriterion& criterion : criteria)
|
||||||
|
{
|
||||||
|
if (criterion.kind != SelectorCriterionKind::Index)
|
||||||
|
{
|
||||||
|
hasNonIndexCriteria = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasNonIndexCriteria) { return indexedRecord; }
|
||||||
|
|
||||||
auto it = std::find_if(
|
auto it = std::find_if(
|
||||||
matches.begin(), matches.end(),
|
matches.begin(), matches.end(),
|
||||||
[&indexedRecord](const CameraIdentityRecord* candidate) {
|
[&indexedRecord](const CameraIdentityRecord* candidate) {
|
||||||
@@ -141,13 +153,6 @@ CameraIdentityRecord resolveSelectorAgainstRecords(
|
|||||||
"index: criterion conflicts with other selector clauses");
|
"index: criterion conflicts with other selector clauses");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches.size() > 1)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Ambiguous deviceSelector: multiple cameras match\n"
|
|
||||||
+ formatCameraListForDiagnostics(records));
|
|
||||||
}
|
|
||||||
|
|
||||||
return indexedRecord;
|
return indexedRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
add_executable(lcameraDev_unit_tests
|
||||||
|
selectorParse_tests.cpp
|
||||||
|
selectorResolve_tests.cpp
|
||||||
|
cameraIdentity_tests.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(lcameraDev_unit_tests PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/commonLibs/lcameraDev
|
||||||
|
${CMAKE_SOURCE_DIR}/include
|
||||||
|
${CMAKE_BINARY_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(lcameraDev_unit_tests
|
||||||
|
gtest_main
|
||||||
|
lcameraDev
|
||||||
|
spinscale
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_dependencies(lcameraDev_unit_tests gtest_main)
|
||||||
|
|
||||||
|
add_test(NAME lcameraDev_unit_tests COMMAND lcameraDev_unit_tests)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <cameraIdentity.h>
|
||||||
|
#include <libcamera/property_ids.h>
|
||||||
|
|
||||||
|
namespace lcamera_dev {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(LocationPropertyToLabelTest, MapsKnownLocations)
|
||||||
|
{
|
||||||
|
using namespace libcamera::properties;
|
||||||
|
|
||||||
|
EXPECT_EQ(locationPropertyToLabel(CameraLocationFront), "front");
|
||||||
|
EXPECT_EQ(locationPropertyToLabel(CameraLocationBack), "back");
|
||||||
|
EXPECT_EQ(locationPropertyToLabel(CameraLocationExternal), "external");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(LocationPropertyToLabelTest, UnknownLocationReturnsEmptyString)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(locationPropertyToLabel(-1), "");
|
||||||
|
EXPECT_EQ(locationPropertyToLabel(99), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace lcamera_dev
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <selectorParse.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace lcamera_dev {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(TrimWhitespaceTest, StripsLeadingAndTrailingWhitespace)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(trimWhitespace(" foo bar "), "foo bar");
|
||||||
|
EXPECT_EQ(trimWhitespace("\t\nvalue\r\n"), "value");
|
||||||
|
EXPECT_EQ(trimWhitespace("no-trim"), "no-trim");
|
||||||
|
EXPECT_EQ(trimWhitespace(""), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, BareOpaqueIdIsLibcameraId)
|
||||||
|
{
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("/base/soc/i2c@1/imx219@10");
|
||||||
|
|
||||||
|
ASSERT_EQ(criteria.size(), 1u);
|
||||||
|
EXPECT_EQ(criteria[0].kind, SelectorCriterionKind::LibcameraId);
|
||||||
|
EXPECT_EQ(criteria[0].value, "/base/soc/i2c@1/imx219@10");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, ExplicitLcameraIdPrefix)
|
||||||
|
{
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("lcamera-id:foo bar baz");
|
||||||
|
|
||||||
|
ASSERT_EQ(criteria.size(), 1u);
|
||||||
|
EXPECT_EQ(criteria[0].kind, SelectorCriterionKind::LibcameraId);
|
||||||
|
EXPECT_EQ(criteria[0].value, "foo bar baz");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, ParsesTypedPrefixes)
|
||||||
|
{
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector(
|
||||||
|
"index:0;model:imx219;model-substr:Logi;location:external");
|
||||||
|
|
||||||
|
ASSERT_EQ(criteria.size(), 4u);
|
||||||
|
EXPECT_EQ(criteria[0].kind, SelectorCriterionKind::Index);
|
||||||
|
EXPECT_EQ(criteria[0].value, "0");
|
||||||
|
EXPECT_EQ(criteria[1].kind, SelectorCriterionKind::Model);
|
||||||
|
EXPECT_EQ(criteria[1].value, "imx219");
|
||||||
|
EXPECT_EQ(criteria[2].kind, SelectorCriterionKind::ModelSubstr);
|
||||||
|
EXPECT_EQ(criteria[2].value, "Logi");
|
||||||
|
EXPECT_EQ(criteria[3].kind, SelectorCriterionKind::Location);
|
||||||
|
EXPECT_EQ(criteria[3].value, "external");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, EscapedSemicolonInValue)
|
||||||
|
{
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("lcamera-id:foo\\;bar;model:aaaa");
|
||||||
|
|
||||||
|
ASSERT_EQ(criteria.size(), 2u);
|
||||||
|
EXPECT_EQ(criteria[0].kind, SelectorCriterionKind::LibcameraId);
|
||||||
|
EXPECT_EQ(criteria[0].value, "foo;bar");
|
||||||
|
EXPECT_EQ(criteria[1].kind, SelectorCriterionKind::Model);
|
||||||
|
EXPECT_EQ(criteria[1].value, "aaaa");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, EmptySelectorThrows)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(parseDeviceSelector(""), std::runtime_error);
|
||||||
|
EXPECT_THROW(parseDeviceSelector(" "), std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, UnknownPrefixThrows)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(
|
||||||
|
parseDeviceSelector("serial:abc"),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, EmptyClauseThrows)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(
|
||||||
|
parseDeviceSelector("model:imx219;;location:front"),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ParseDeviceSelectorTest, EmptyValueThrows)
|
||||||
|
{
|
||||||
|
EXPECT_THROW(
|
||||||
|
parseDeviceSelector("model:"),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace lcamera_dev
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <selectorParse.h>
|
||||||
|
#include <selectorResolve.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace lcamera_dev {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<CameraIdentityRecord> sampleRecords()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
makeRecord("/base/cam0", "imx219", "back"),
|
||||||
|
makeRecord("/base/cam1", "Logitech C920", "external"),
|
||||||
|
makeRecord("/base/cam2", "imx219", "front"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FormatCameraListForDiagnosticsTest, ListsIndexedCameras)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
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("location=external"), std::string::npos);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ResolveSelectorAgainstRecordsTest, MatchesLibcameraId)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("index:2");
|
||||||
|
|
||||||
|
const CameraIdentityRecord resolved =
|
||||||
|
resolveSelectorAgainstRecords(criteria, records);
|
||||||
|
|
||||||
|
EXPECT_EQ(resolved.id, "/base/cam2");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ResolveSelectorAgainstRecordsTest, IndexCombinedWithModel)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("model:imx219");
|
||||||
|
|
||||||
|
EXPECT_THROW(
|
||||||
|
resolveSelectorAgainstRecords(criteria, records),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ResolveSelectorAgainstRecordsTest, IndexOutOfRangeThrows)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("index:9");
|
||||||
|
|
||||||
|
EXPECT_THROW(
|
||||||
|
resolveSelectorAgainstRecords(criteria, records),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ResolveSelectorAgainstRecordsTest, IndexConflictsWithOtherClauses)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("index:0;location:front");
|
||||||
|
|
||||||
|
EXPECT_THROW(
|
||||||
|
resolveSelectorAgainstRecords(criteria, records),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ResolveSelectorAgainstRecordsTest, InvalidIndexValueThrows)
|
||||||
|
{
|
||||||
|
const std::vector<CameraIdentityRecord> records = sampleRecords();
|
||||||
|
const std::vector<SelectorCriterion> criteria =
|
||||||
|
parseDeviceSelector("index:not-a-number");
|
||||||
|
|
||||||
|
EXPECT_THROW(
|
||||||
|
resolveSelectorAgainstRecords(criteria, records),
|
||||||
|
std::runtime_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace lcamera_dev
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <spinscale/cps/qutex.h>
|
#include <spinscale/cps/qutex.h>
|
||||||
#include <spinscale/cps/lockerAndInvokerBase.h>
|
#include <spinscale/cps/lockerAndInvokerBase.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -15,19 +16,48 @@ public:
|
|||||||
: sscl::cps::LockerAndInvokerBase(addr), awakened(false) {}
|
: sscl::cps::LockerAndInvokerBase(addr), awakened(false) {}
|
||||||
|
|
||||||
bool awakened;
|
bool awakened;
|
||||||
sscl::cps::Qutex* registeredQutex = nullptr;
|
mutable sscl::cps::Qutex* registeredQutex = nullptr;
|
||||||
sscl::cps::LockerAndInvokerBase::List::iterator queueIterator;
|
mutable sscl::cps::LockerAndInvokerBase::List::iterator queueIterator;
|
||||||
|
|
||||||
sscl::cps::LockerAndInvokerBase::List::iterator getLockvokerIteratorForQutex(sscl::cps::Qutex& qutex) override {
|
sscl::cps::LockerAndInvokerBase::List::iterator
|
||||||
|
getLockvokerIteratorForQutex(sscl::cps::Qutex& qutex) const override
|
||||||
|
{
|
||||||
registeredQutex = &qutex;
|
registeredQutex = &qutex;
|
||||||
queueIterator = qutex.registerInQueue(std::shared_ptr<sscl::cps::LockerAndInvokerBase>(this));
|
|
||||||
return queueIterator;
|
for (auto it = qutex.queue.begin(); it != qutex.queue.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((**it) == *this)
|
||||||
|
{
|
||||||
|
queueIterator = it;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void awaken(bool forceAwaken = false) override {
|
throw std::runtime_error(
|
||||||
|
"MockLockerAndInvoker: not registered in qutex queue");
|
||||||
|
}
|
||||||
|
|
||||||
|
void awaken(bool forceAwaken = false) override
|
||||||
|
{
|
||||||
(void)forceAwaken;
|
(void)forceAwaken;
|
||||||
awakened = true;
|
awakened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t getLockSetSize() const override
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sscl::cps::Qutex& getLockAt(size_t index) const override
|
||||||
|
{
|
||||||
|
if (index != 0 || registeredQutex == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"MockLockerAndInvoker: invalid lock index or no registered qutex");
|
||||||
|
}
|
||||||
|
|
||||||
|
return *registeredQutex;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class QutexTest : public ::testing::Test {
|
class QutexTest : public ::testing::Test {
|
||||||
@@ -45,7 +75,7 @@ protected:
|
|||||||
// Clean up
|
// Clean up
|
||||||
}
|
}
|
||||||
|
|
||||||
sscl::cps::Qutex qutex;
|
sscl::cps::Qutex qutex{"test-qutex"};
|
||||||
std::shared_ptr<MockLockerAndInvoker> mock1, mock2, mock3, mock4, mock5;
|
std::shared_ptr<MockLockerAndInvoker> mock1, mock2, mock3, mock4, mock5;
|
||||||
|
|
||||||
// Unique addresses for testing
|
// Unique addresses for testing
|
||||||
@@ -79,7 +109,7 @@ TEST_F(QutexTest, QueueRegistrationAndUnregistration) {
|
|||||||
// Test single lock acquisition when queue is empty
|
// Test single lock acquisition when queue is empty
|
||||||
TEST_F(QutexTest, SingleLockAcquisitionEmptyQueue) {
|
TEST_F(QutexTest, SingleLockAcquisitionEmptyQueue) {
|
||||||
// Register mock1
|
// Register mock1
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
|
|
||||||
// Try to acquire with nRequiredLocks = 1
|
// Try to acquire with nRequiredLocks = 1
|
||||||
bool acquired = qutex.tryAcquire(*mock1, 1);
|
bool acquired = qutex.tryAcquire(*mock1, 1);
|
||||||
@@ -90,9 +120,9 @@ TEST_F(QutexTest, SingleLockAcquisitionEmptyQueue) {
|
|||||||
// Test single lock acquisition when at front of queue
|
// Test single lock acquisition when at front of queue
|
||||||
TEST_F(QutexTest, SingleLockAcquisitionAtFront) {
|
TEST_F(QutexTest, SingleLockAcquisitionAtFront) {
|
||||||
// Register multiple lockvokers
|
// Register multiple lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
auto it3 = qutex.registerInQueue(mock3);
|
(void)qutex.registerInQueue(mock3);
|
||||||
|
|
||||||
// mock1 should be at front, mock3 at back
|
// mock1 should be at front, mock3 at back
|
||||||
EXPECT_EQ(qutex.queue.front().get(), mock1.get());
|
EXPECT_EQ(qutex.queue.front().get(), mock1.get());
|
||||||
@@ -112,8 +142,8 @@ TEST_F(QutexTest, SingleLockAcquisitionAtFront) {
|
|||||||
// Test single lock acquisition failure when not at front
|
// Test single lock acquisition failure when not at front
|
||||||
TEST_F(QutexTest, SingleLockAcquisitionNotAtFront) {
|
TEST_F(QutexTest, SingleLockAcquisitionNotAtFront) {
|
||||||
// Register multiple lockvokers
|
// Register multiple lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
|
|
||||||
// mock2 (not at front) should fail
|
// mock2 (not at front) should fail
|
||||||
bool acquired = qutex.tryAcquire(*mock2, 1);
|
bool acquired = qutex.tryAcquire(*mock2, 1);
|
||||||
@@ -124,10 +154,10 @@ TEST_F(QutexTest, SingleLockAcquisitionNotAtFront) {
|
|||||||
// Test multi-lock acquisition (nRequiredLocks > 1)
|
// Test multi-lock acquisition (nRequiredLocks > 1)
|
||||||
TEST_F(QutexTest, MultiLockAcquisition) {
|
TEST_F(QutexTest, MultiLockAcquisition) {
|
||||||
// Register 4 lockvokers
|
// Register 4 lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
auto it3 = qutex.registerInQueue(mock3);
|
(void)qutex.registerInQueue(mock3);
|
||||||
auto it4 = qutex.registerInQueue(mock4);
|
(void)qutex.registerInQueue(mock4);
|
||||||
|
|
||||||
// For nRequiredLocks = 2, need to be in top 50% (top 2 out of 4)
|
// For nRequiredLocks = 2, need to be in top 50% (top 2 out of 4)
|
||||||
// mock1 (position 1) should succeed
|
// mock1 (position 1) should succeed
|
||||||
@@ -159,16 +189,16 @@ TEST_F(QutexTest, MultiLockAcquisition) {
|
|||||||
// Test multi-lock acquisition with 3 required locks
|
// Test multi-lock acquisition with 3 required locks
|
||||||
TEST_F(QutexTest, MultiLockAcquisitionThreeLocks) {
|
TEST_F(QutexTest, MultiLockAcquisitionThreeLocks) {
|
||||||
// Register 6 lockvokers
|
// Register 6 lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
auto it3 = qutex.registerInQueue(mock3);
|
(void)qutex.registerInQueue(mock3);
|
||||||
auto it4 = qutex.registerInQueue(mock4);
|
(void)qutex.registerInQueue(mock4);
|
||||||
auto it5 = qutex.registerInQueue(mock5);
|
(void)qutex.registerInQueue(mock5);
|
||||||
|
|
||||||
// Create one more mock
|
// Create one more mock
|
||||||
int addr6 = 6;
|
int addr6 = 6;
|
||||||
auto mock6 = std::make_shared<MockLockerAndInvoker>(&addr6);
|
auto mock6 = std::make_shared<MockLockerAndInvoker>(&addr6);
|
||||||
auto it6 = qutex.registerInQueue(mock6);
|
(void)qutex.registerInQueue(mock6);
|
||||||
|
|
||||||
// For nRequiredLocks = 3, need to be in top 66% (top 4 out of 6)
|
// For nRequiredLocks = 3, need to be in top 66% (top 4 out of 6)
|
||||||
// Positions 1, 2, 3, 4 should succeed
|
// Positions 1, 2, 3, 4 should succeed
|
||||||
@@ -201,7 +231,7 @@ TEST_F(QutexTest, MultiLockAcquisitionThreeLocks) {
|
|||||||
// Test acquisition failure when already owned
|
// Test acquisition failure when already owned
|
||||||
TEST_F(QutexTest, AcquisitionFailureWhenOwned) {
|
TEST_F(QutexTest, AcquisitionFailureWhenOwned) {
|
||||||
// Register mock1
|
// Register mock1
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
|
|
||||||
// Manually set as owned
|
// Manually set as owned
|
||||||
qutex.isOwned = true;
|
qutex.isOwned = true;
|
||||||
@@ -215,17 +245,17 @@ TEST_F(QutexTest, AcquisitionFailureWhenOwned) {
|
|||||||
// Test backoff with single item (should not rotate)
|
// Test backoff with single item (should not rotate)
|
||||||
TEST_F(QutexTest, BackoffSingleItem) {
|
TEST_F(QutexTest, BackoffSingleItem) {
|
||||||
// Register only one lockvoker
|
// Register only one lockvoker
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
|
|
||||||
// Set as owned first
|
// Set as owned first
|
||||||
qutex.isOwned = true;
|
qutex.isOwned = true;
|
||||||
|
|
||||||
// Backoff should not rotate and should awaken the front item
|
// nRequiredLocks > 1 avoids the "front item with nRequiredLocks==1" guard
|
||||||
mock1->awakened = false;
|
mock1->awakened = false;
|
||||||
qutex.backoff(*mock1, 1);
|
qutex.backoff(*mock1, 2);
|
||||||
|
|
||||||
EXPECT_FALSE(qutex.isOwned);
|
EXPECT_FALSE(qutex.isOwned);
|
||||||
EXPECT_EQ(qutex.queue.size(), 1);
|
EXPECT_EQ(qutex.queue.size(), 1u);
|
||||||
// Should not awaken since there's only one item
|
// Should not awaken since there's only one item
|
||||||
EXPECT_FALSE(mock1->awakened);
|
EXPECT_FALSE(mock1->awakened);
|
||||||
}
|
}
|
||||||
@@ -233,9 +263,9 @@ TEST_F(QutexTest, BackoffSingleItem) {
|
|||||||
// Test backoff with multiple items and rotation
|
// Test backoff with multiple items and rotation
|
||||||
TEST_F(QutexTest, BackoffWithRotation) {
|
TEST_F(QutexTest, BackoffWithRotation) {
|
||||||
// Register multiple lockvokers
|
// Register multiple lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
auto it3 = qutex.registerInQueue(mock3);
|
(void)qutex.registerInQueue(mock3);
|
||||||
|
|
||||||
// Set as owned first
|
// Set as owned first
|
||||||
qutex.isOwned = true;
|
qutex.isOwned = true;
|
||||||
@@ -259,8 +289,8 @@ TEST_F(QutexTest, BackoffWithRotation) {
|
|||||||
// Test backoff with rotation to back when queue smaller than nRequiredLocks
|
// Test backoff with rotation to back when queue smaller than nRequiredLocks
|
||||||
TEST_F(QutexTest, BackoffRotationToBack) {
|
TEST_F(QutexTest, BackoffRotationToBack) {
|
||||||
// Register only 2 lockvokers
|
// Register only 2 lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
|
|
||||||
// Set as owned first
|
// Set as owned first
|
||||||
qutex.isOwned = true;
|
qutex.isOwned = true;
|
||||||
@@ -285,11 +315,10 @@ TEST_F(QutexTest, BackoffRotationToBack) {
|
|||||||
// Test release functionality
|
// Test release functionality
|
||||||
TEST_F(QutexTest, Release) {
|
TEST_F(QutexTest, Release) {
|
||||||
// Register multiple lockvokers
|
// Register multiple lockvokers
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
auto it2 = qutex.registerInQueue(mock2);
|
(void)qutex.registerInQueue(mock2);
|
||||||
|
|
||||||
// Set as owned
|
ASSERT_TRUE(qutex.tryAcquire(*mock1, 1));
|
||||||
qutex.isOwned = true;
|
|
||||||
|
|
||||||
// Release should set isOwned to false and awaken front item
|
// Release should set isOwned to false and awaken front item
|
||||||
mock1->awakened = false;
|
mock1->awakened = false;
|
||||||
@@ -299,15 +328,11 @@ TEST_F(QutexTest, Release) {
|
|||||||
EXPECT_TRUE(mock1->awakened);
|
EXPECT_TRUE(mock1->awakened);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test release with empty queue
|
// Test release without a prior acquire is rejected
|
||||||
TEST_F(QutexTest, ReleaseEmptyQueue) {
|
TEST_F(QutexTest, ReleaseWithoutAcquireThrows) {
|
||||||
// Set as owned
|
|
||||||
qutex.isOwned = true;
|
qutex.isOwned = true;
|
||||||
|
|
||||||
// Release with empty queue should just set isOwned to false
|
EXPECT_THROW(qutex.release(), std::runtime_error);
|
||||||
qutex.release();
|
|
||||||
|
|
||||||
EXPECT_FALSE(qutex.isOwned);
|
|
||||||
EXPECT_TRUE(qutex.queue.empty());
|
EXPECT_TRUE(qutex.queue.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,7 +351,7 @@ TEST_F(QutexTest, ExceptionOnEmptyQueueBackoff) {
|
|||||||
// Test edge case: single lockvoker with multiple required locks
|
// Test edge case: single lockvoker with multiple required locks
|
||||||
TEST_F(QutexTest, SingleLockvokerMultipleRequiredLocks) {
|
TEST_F(QutexTest, SingleLockvokerMultipleRequiredLocks) {
|
||||||
// Register only one lockvoker
|
// Register only one lockvoker
|
||||||
auto it1 = qutex.registerInQueue(mock1);
|
(void)qutex.registerInQueue(mock1);
|
||||||
|
|
||||||
// Should succeed regardless of nRequiredLocks when only one item
|
// Should succeed regardless of nRequiredLocks when only one item
|
||||||
bool acquired = qutex.tryAcquire(*mock1, 5);
|
bool acquired = qutex.tryAcquire(*mock1, 5);
|
||||||
|
|||||||
Reference in New Issue
Block a user