@@ -8,8 +8,10 @@
#include "capture.h"
+#include <semaphore>
#include <sstream>
#include <string>
+#include <thread>
#include <tuple>
#include <vector>
@@ -142,4 +144,263 @@ INSTANTIATE_TEST_SUITE_P(MultiStream,
testing::ValuesIn(NUMREQUESTS)),
SimpleCapture::nameParameters);
+class TestWithCamera : public testing::Test, public CameraHolder
+{
+protected:
+ void SetUp() override { acquireCamera(); }
+ void TearDown() override { releaseCamera(); }
+};
+
+class BufferPool : public TestWithCamera
+{
+protected:
+ void SetUp() override
+ {
+ TestWithCamera::SetUp();
+
+ config_ = camera_->generateConfiguration({ StreamRole::Viewfinder });
+ ASSERT_TRUE(config_);
+ ASSERT_FALSE(config_->empty());
+
+ ASSERT_EQ(camera_->configure(config_.get()), 0);
+ }
+
+ void TearDown() override
+ {
+ config_.reset();
+ TestWithCamera::TearDown();
+ }
+
+ std::vector<std::unique_ptr<Request>>
+ createRequests(std::size_t count)
+ {
+ std::vector<std::unique_ptr<Request>> requests;
+
+ for (std::size_t i = 0; i < count; i++) {
+ auto request = camera_->createRequest(i);
+ [&] { ASSERT_TRUE(request); }();
+
+ for (const auto &cfg : *config_)
+ request->enableStream(cfg.stream(), true);
+
+ requests.push_back(std::move(request));
+ }
+
+ return requests;
+ }
+
+ std::unique_ptr<CameraConfiguration> config_;
+};
+
+template<typename T, typename... Args>
+struct ScopedSignalReceiver {
+ Signal<Args...> &signal;
+ T *object = nullptr;
+
+ template<typename Func>
+ ScopedSignalReceiver(Signal<Args...> &s, T *o, Func f)
+ : signal(s), object(o)
+ {
+ signal.connect(o, std::move(f));
+ }
+
+ ~ScopedSignalReceiver()
+ {
+ signal.disconnect(object);
+ }
+};
+
+template<typename T, typename... Args>
+ScopedSignalReceiver(Signal<Args...> &, T *) -> ScopedSignalReceiver<T, Args...>;
+
+struct ScopedCameraStart {
+ Camera &camera;
+
+ ScopedCameraStart(Camera &c)
+ : camera(c)
+ {
+ [&]() { ASSERT_EQ(camera.start(), 0); }();
+ }
+
+ ~ScopedCameraStart()
+ {
+ EXPECT_EQ(camera.stop(), 0);
+ }
+};
+
+TEST_F(BufferPool, BufferBeforeRequest)
+{
+ FrameBufferAllocator fba(camera_);
+
+ std::size_t buffers = -1;
+ for (const auto &cfg : *config_) {
+ ASSERT_GT(fba.allocate(cfg.stream()), 0);
+ buffers = std::min(buffers, fba.buffers(cfg.stream()).size());
+ }
+
+ auto requests = createRequests(buffers);
+
+ std::counting_semaphore<> requestCompletedSemaphore(0);
+ bool requestCompletedOk = true;
+ unsigned int requestCompleted = 0;
+ ScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *request) {
+ if (requestCompleted < requests.size())
+ requestCompletedOk &= request == requests[requestCompleted].get();
+
+ requestCompletedSemaphore.release(1);
+ requestCompleted += 1;
+ });
+
+ {
+ ScopedCameraStart scs(*camera_);
+
+ for (const auto &cfg : *config_) {
+ Stream *stream = cfg.stream();
+ for (const auto &buffer : fba.buffers(stream))
+ EXPECT_EQ(camera_->addBuffer(stream, buffer.get()), 0);
+ }
+
+ for (const auto &request : requests)
+ ASSERT_EQ(camera_->queueRequest(request.get()), 0);
+
+ auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(buffers);
+
+ for (size_t i = 0; i < buffers; i++) {
+ if (!requestCompletedSemaphore.try_acquire_until(deadline))
+ break;
+ }
+ }
+
+ EXPECT_TRUE(requestCompletedOk);
+ ASSERT_FALSE(requestCompletedSemaphore.try_acquire());
+ EXPECT_EQ(requestCompleted, buffers);
+}
+
+TEST_F(BufferPool, RequestBeforeBuffer)
+{
+ FrameBufferAllocator fba(camera_);
+
+ std::size_t buffers = -1;
+ for (const auto &cfg : *config_) {
+ ASSERT_GT(fba.allocate(cfg.stream()), 0);
+ buffers = std::min(buffers, fba.buffers(cfg.stream()).size());
+ }
+
+ auto requests = createRequests(buffers);
+
+ std::counting_semaphore<> requestCompletedSemaphore(0);
+ bool requestCompletedOk = true;
+ unsigned int requestCompleted = 0;
+ ScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *request) {
+ if (requestCompleted < requests.size())
+ requestCompletedOk &= request == requests[requestCompleted].get();
+
+ requestCompletedSemaphore.release(1);
+ requestCompleted += 1;
+ });
+
+ {
+ ScopedCameraStart scs(*camera_);
+
+ for (const auto &request : requests)
+ ASSERT_EQ(camera_->queueRequest(request.get()), 0);
+
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ ASSERT_FALSE(requestCompletedSemaphore.try_acquire());
+ ASSERT_EQ(requestCompleted, 0);
+
+ auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(buffers);
+
+ for (const auto &cfg : *config_) {
+ Stream *stream = cfg.stream();
+ for (const auto &buffer : fba.buffers(stream))
+ EXPECT_EQ(camera_->addBuffer(stream, buffer.get()), 0);
+ }
+
+ for (size_t i = 0; i < buffers; i++) {
+ if (!requestCompletedSemaphore.try_acquire_until(deadline))
+ break;
+ }
+ }
+
+ EXPECT_TRUE(requestCompletedOk);
+ ASSERT_FALSE(requestCompletedSemaphore.try_acquire());
+ EXPECT_EQ(requestCompleted, buffers);
+}
+
+TEST_F(BufferPool, BufferWithoutRequest)
+{
+ FrameBufferAllocator fba(camera_);
+ std::set<std::pair<const Stream *, FrameBuffer *>> buffers;
+
+ for (const auto &cfg : *config_) {
+ Stream *stream = cfg.stream();
+ ASSERT_GT(fba.allocate(stream), 0);
+
+ for (const auto &buffer : fba.buffers(stream))
+ buffers.insert({ stream, buffer.get() });
+ }
+
+ unsigned int requestCompleted = 0;
+ ScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *) {
+ requestCompleted += 1;
+ });
+
+ bool bufferCompletedOk = true;
+ ScopedSignalReceiver bcr(camera_->bufferCompleted, this, [&](Request *request, const Stream *stream, FrameBuffer *buffer) {
+ bufferCompletedOk &= !request;
+ bufferCompletedOk &= buffer->metadata().status == libcamera::FrameMetadata::FrameCancelled;
+ bufferCompletedOk &= buffers.erase({ stream, buffer });
+ });
+
+ {
+ ScopedCameraStart scs(*camera_);
+
+ for (const auto &cfg : *config_) {
+ Stream *stream = cfg.stream();
+ for (const auto &buffer : fba.buffers(stream))
+ EXPECT_EQ(camera_->addBuffer(stream, buffer.get()), 0);
+ }
+ }
+
+ EXPECT_EQ(requestCompleted, 0);
+ EXPECT_EQ(buffers.size(), 0);
+ EXPECT_TRUE(bufferCompletedOk);
+}
+
+TEST_F(BufferPool, RequestWithoutBuffers)
+{
+ constexpr std::size_t kRequestCount = 128;
+ auto requests = createRequests(kRequestCount);
+
+ unsigned int requestCompleted = 0;
+ bool requestCompletedOk = true;
+ ScopedSignalReceiver rcr(camera_->requestCompleted, this, [&](Request *request) {
+ if (requestCompleted < requests.size()) {
+ requestCompletedOk &= request == requests[requestCompleted].get();
+ requestCompletedOk &= request->status() == Request::Status::RequestCancelled;
+ }
+
+ requestCompleted += 1;
+ });
+
+ unsigned int bufferCompleted = 0;
+ ScopedSignalReceiver bcr(camera_->bufferCompleted, this, [&](Request *, const Stream *, FrameBuffer *) {
+ bufferCompleted += 1;
+ });
+
+ {
+ ScopedCameraStart scs(*camera_);
+
+ for (const auto &request : requests)
+ ASSERT_EQ(camera_->queueRequest(request.get()), 0);
+
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ }
+
+ EXPECT_EQ(requestCompleted, requests.size());
+ EXPECT_TRUE(requestCompletedOk);
+ EXPECT_EQ(bufferCompleted, 0);
+}
+
} /* namespace */
Add a couple tests that test the functioning of the buffer pool of a camera. Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> --- src/apps/lc-compliance/tests/capture_test.cpp | 261 ++++++++++++++++++ 1 file changed, 261 insertions(+)