Files
Jungfraujoch/tests/JFJochImageBufferReaderTest.cpp
T
leonarski_fandClaude Opus 5.5 f215300a07 ImageBuffer reader: give up on an image MAX_IMAGE_LAG (100) behind the newest, and a unit test
Images reach the buffer in order give or take a few, so one still missing when the buffer holds an
image 100 past it is lost, not late: the read returns false instead of waiting for the end of the
collection. The newest image number comes from ImageBuffer::GetStatus, which scans every slot under
the buffer's lock, so it is asked only every 100 polls (~10 Hz).

The test puts CBOR images into an ImageBuffer the way the receiver does and checks read-back of the
pixels, waiting for an image not yet there, the lag limit, the end of the collection and the bounds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:27:10 +02:00

98 lines
3.8 KiB
C++

// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <catch2/catch_all.hpp>
#include <cstring>
#include <future>
#include <thread>
#include "../reader/JFJochImageBufferReader.h"
#include "../common/ZeroCopyReturnValue.h"
#include "../frame_serialize/CBORStream2Serializer.h"
// The receiver's way of putting an image in the buffer: serialize the message into a slot, hand it out.
static void PutImage(ImageBuffer &buffer, const DiffractionExperiment &experiment, int64_t number,
const std::vector<int32_t> &pixels) {
DataMessage msg{};
msg.number = number;
msg.image = CompressedImage(pixels, experiment.GetXPixelsNum(), experiment.GetYPixelsNum());
auto loc = buffer.GetImageSlot();
REQUIRE(loc != nullptr);
CBORStream2Serializer serializer((uint8_t *) loc->GetImage(), experiment.GetImageBufferLocationSize());
serializer.SerializeImage(msg);
loc->SetImageNumber(number);
loc->SetImageSize(serializer.GetBufferSize());
loc->SetIndexed(false);
loc->ReadyToSend();
loc->release();
}
TEST_CASE("JFJochImageBufferReader", "[JFJochReader]") {
DiffractionExperiment experiment(DetJF(1));
experiment.ImagesPerTrigger(200);
ImageBuffer buffer(4 * experiment.GetImageBufferLocationSize());
buffer.StartMeasurement(experiment);
StartMessage start{};
experiment.FillMessage(start);
REQUIRE(start.number_of_images == 200);
JFJochImageBufferReader reader(buffer);
reader.Open(start);
REQUIRE(reader.GetNumberOfImages() == 200);
REQUIRE(reader.GetDataset());
const size_t npixel = experiment.GetXPixelsNum() * experiment.GetYPixelsNum();
std::vector<int32_t> pixels(npixel, 7);
pixels[123] = 4567;
PutImage(buffer, experiment, 0, pixels);
JFJochReaderRawImage raw;
REQUIRE(reader.ReadRawImage(0, raw));
REQUIRE(raw.image.GetWidth() == experiment.GetXPixelsNum());
REQUIRE(raw.image.GetHeight() == experiment.GetYPixelsNum());
REQUIRE(raw.image_buffer.size() == npixel * sizeof(int32_t));
int32_t value;
memcpy(&value, raw.image.GetCompressed() + 123 * sizeof(int32_t), sizeof(int32_t));
REQUIRE(value == 4567);
auto image = reader.LoadImage(0);
REQUIRE(image);
REQUIRE(image->ImageData().number == 0);
REQUIRE_THROWS(reader.ReadRawImage(200, raw));
// An image not there yet is waited for: the read returns once the receiver puts it in.
auto pending = std::async(std::launch::async, [&] {
JFJochReaderRawImage r;
return reader.ReadRawImage(1, r);
});
REQUIRE(pending.wait_for(std::chrono::milliseconds(50)) == std::future_status::timeout);
PutImage(buffer, experiment, 1, pixels);
REQUIRE(pending.wait_for(std::chrono::seconds(5)) == std::future_status::ready);
REQUIRE(pending.get());
// Images arrive in order, give or take a few: one still missing when an image MAX_IMAGE_LAG past
// it is there is lost, while one nearer may still come.
PutImage(buffer, experiment, 150, pixels);
auto lost = std::async(std::launch::async, [&] {
JFJochReaderRawImage r;
return reader.ReadRawImage(150 - JFJochImageBufferReader::MAX_IMAGE_LAG, r);
});
REQUIRE(lost.wait_for(std::chrono::seconds(5)) == std::future_status::ready);
REQUIRE(!lost.get());
// After the end, an image that never came is not waited for.
auto missing = std::async(std::launch::async, [&] {
JFJochReaderRawImage r;
return reader.ReadRawImage(150 - JFJochImageBufferReader::MAX_IMAGE_LAG + 1, r);
});
REQUIRE(missing.wait_for(std::chrono::milliseconds(300)) == std::future_status::timeout);
reader.Ended();
REQUIRE(missing.wait_for(std::chrono::seconds(5)) == std::future_status::ready);
REQUIRE(!missing.get());
REQUIRE(buffer.Finalize(std::chrono::microseconds(1)));
}