Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ADApp/pluginSrc/NDPluginAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ NDPluginAttribute::NDPluginAttribute(const char *portName, int queueSize, int bl
NDArrayPort, NDArrayAddr, std::max<int>(maxAttributes,2), maxBuffers, maxMemory,
asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynGenericPointerMask,
asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynGenericPointerMask,
ASYN_MULTIDEVICE, 1, priority, stackSize, 1)
ASYN_MULTIDEVICE, 1, priority, stackSize, 1, true)
{
int i;
// static const char *functionName = "NDPluginAttribute::NDPluginAttribute";
Expand Down
22 changes: 22 additions & 0 deletions ADApp/pluginTests/AttributePluginWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* AttributePluginWrapper.cpp
*
* Created on: 27 Apr 2026
* Author: Jakub Wlodek
*/

#include "AttributePluginWrapper.h"

AttributePluginWrapper::AttributePluginWrapper(const std::string& port,
const std::string& detectorPort,
int maxAttributes)
: NDPluginAttribute(port.c_str(), 50, 0, detectorPort.c_str(), 0,
maxAttributes, 0, 0, 0, 0),
AsynPortClientContainer(port)
{
}

AttributePluginWrapper::~AttributePluginWrapper()
{
cleanup();
}
22 changes: 22 additions & 0 deletions ADApp/pluginTests/AttributePluginWrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* AttributePluginWrapper.h
*
* Created on: 27 Apr 2026
* Author: Jakub Wlodek
*/

#ifndef ADAPP_PLUGINTESTS_ATTRIBUTEPLUGINWRAPPER_H_
#define ADAPP_PLUGINTESTS_ATTRIBUTEPLUGINWRAPPER_H_

#include <NDPluginAttribute.h>
#include "AsynPortClientContainer.h"

class AttributePluginWrapper : public NDPluginAttribute, public AsynPortClientContainer
{
public:
AttributePluginWrapper(const std::string& port, const std::string& detectorPort,
int maxAttributes);
virtual ~AttributePluginWrapper();
};

#endif /* ADAPP_PLUGINTESTS_ATTRIBUTEPLUGINWRAPPER_H_ */
2 changes: 2 additions & 0 deletions ADApp/pluginTests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ifeq ($(WITH_BOOST),YES)
ADTestUtility_SRCS += ROIPluginWrapper.cpp
ADTestUtility_SRCS += OverlayPluginWrapper.cpp
ADTestUtility_SRCS += CodecPluginWrapper.cpp
ADTestUtility_SRCS += AttributePluginWrapper.cpp

PROD_IOC_Linux += plugin-test
PROD_IOC_Darwin += plugin-test
Expand All @@ -55,6 +56,7 @@ ifeq ($(WITH_BOOST),YES)
plugin-test_SRCS += test_NDPluginTimeSeries.cpp
plugin-test_SRCS += test_NDPluginFFT.cpp
plugin-test_SRCS += test_NDPluginCodec.cpp
plugin-test_SRCS += test_NDPluginAttribute.cpp
plugin-test_SRCS += test_NDPluginAttrPlot.cpp
plugin-test_SRCS += test_NDPluginROI.cpp
plugin-test_SRCS += test_NDPluginOverlay.cpp
Expand Down
147 changes: 147 additions & 0 deletions ADApp/pluginTests/test_NDPluginAttribute.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* test_NDPluginAttribute.cpp
*
* Tests NDPluginAttribute, including with compressed arrays.
*
* Created on: 27 Apr 2026
* Author: Jakub Wlodek
*/

#include <string.h>
#include <stdlib.h>

#include "boost/test/unit_test.hpp"

#include <NDPluginDriver.h>
#include <NDArray.h>
#include <asynDriver.h>
#include <Codec.h>
#include <NDPluginAttribute.h>
#include <NDPluginCodec.h>

#include "testingutilities.h"
#include "AttributePluginWrapper.h"

#define TEST_XSIZE 8
#define TEST_YSIZE 8
#define TEST_NELEMENTS (TEST_XSIZE * TEST_YSIZE)

struct AttributeTestFixture
{
NDArrayPool *arrayPool;
asynNDArrayDriver *dummy_driver;
AttributePluginWrapper *attr;

AttributeTestFixture()
{
std::string dummy_port("simAttr"), testport("testAttr");
uniqueAsynPortName(dummy_port);
uniqueAsynPortName(testport);

dummy_driver = new asynNDArrayDriver(
dummy_port.c_str(), 1, 0, 0,
asynGenericPointerMask, asynGenericPointerMask, 0, 0, 0, 0);
arrayPool = dummy_driver->pNDArrayPool;

attr = new AttributePluginWrapper(testport.c_str(), dummy_port.c_str(), 4);
attr->start();

attr->write(NDPluginDriverEnableCallbacksString, 1);
attr->write(NDPluginDriverBlockingCallbacksString, 1);
}

~AttributeTestFixture()
{
delete attr;
delete dummy_driver;
}

NDArray *createTestArray(bool compress = false)
{
size_t dims[2] = {TEST_XSIZE, TEST_YSIZE};
NDArray *arr = arrayPool->alloc(2, dims, NDUInt16, 0, NULL);
epicsUInt16 *pData = (epicsUInt16 *)arr->pData;
for (int i = 0; i < TEST_NELEMENTS; i++) {
pData[i] = (epicsUInt16)(i % 256);
}
#ifdef HAVE_ZLIB
if (compress) {
NDCodecStatus_t status;
char errorMessage[256] = "";
NDArray *compressed = compressZlib(arr, 6, &status, errorMessage);
arr->release();
return compressed;
}
#else
(void)compress;
#endif
return arr;
}

void processArray(NDArray *pArray)
{
attr->lock();
attr->processCallbacks(pArray);
attr->unlock();
}
};

BOOST_FIXTURE_TEST_SUITE(AttributePluginTests, AttributeTestFixture)

/* Test that NDPluginAttribute reads attributes from an uncompressed array */
BOOST_AUTO_TEST_CASE(test_attribute_uncompressed)
{
NDArray *input = createTestArray();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you are using in the other tests, missing BOOST_REQUIRE(input != NULL)

double testVal = 42.5;
input->pAttributeList->add("TestAttr", "", NDAttrFloat64, &testVal);

attr->write(NDPluginAttributeAttrNameString, std::string("TestAttr"), 0);

processArray(input);

double readVal = attr->readDouble(NDPluginAttributeValString, 0);
BOOST_CHECK_CLOSE(readVal, 42.5, 0.001);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BOOST_CHECK_CLOSE(readVal, 42.5, 0.001);
BOOST_CHECK_CLOSE(readVal, testVal, 0.001);


input->release();
}

#ifdef HAVE_ZLIB
/* Test that NDPluginAttribute accepts a zlib-compressed array and still reads attributes */
BOOST_AUTO_TEST_CASE(test_attribute_compressed_array)
{
NDArray *input = createTestArray(true);
BOOST_REQUIRE(input != NULL);
BOOST_CHECK_EQUAL(input->codec.name, codecName[NDCODEC_ZLIB]);

double testVal = 99.0;
input->pAttributeList->add("CompAttr", "", NDAttrFloat64, &testVal);

attr->write(NDPluginAttributeAttrNameString, std::string("CompAttr"), 0);

processArray(input);

double readVal = attr->readDouble(NDPluginAttributeValString, 0);
BOOST_CHECK_CLOSE(readVal, 99.0, 0.001);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
BOOST_CHECK_CLOSE(readVal, 99.0, 0.001);
BOOST_CHECK_CLOSE(readVal, testVal, 0.001);


input->release();
}

/* Test that uniqueId is readable from a compressed array */
BOOST_AUTO_TEST_CASE(test_attribute_compressed_unique_id)
{
NDArray *input = createTestArray(true);
BOOST_REQUIRE(input != NULL);
input->uniqueId = 12345;

attr->write(NDPluginAttributeAttrNameString, std::string("NDArrayUniqueId"), 0);

processArray(input);

double readVal = attr->readDouble(NDPluginAttributeValString, 0);
BOOST_CHECK_CLOSE(readVal, 12345.0, 0.001);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not BOOST_CHECK_EQUAL?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think CHECK_EQUAL should be OK. I usually use CHECK_CLOSE or the framework equivalent for floats due to precision issues, but since we're not doing any calculations or the like here it should match exactly.


input->release();
}
#endif /* HAVE_ZLIB */

BOOST_AUTO_TEST_SUITE_END()
Loading