4#define BOOST_TEST_DYN_LINK
6#define BOOST_TEST_MODULE testDeviceToJmap
15#include <nlohmann/json.hpp>
18#include <boost/test/unit_test.hpp>
28using namespace boost::unit_test_framework;
32#ifndef CHIMERATK_DEVICE_TO_JMAP
33# define CHIMERATK_DEVICE_TO_JMAP "chimeratk-device-to-jmap"
36BOOST_AUTO_TEST_SUITE(testDeviceToJmap)
41static const std::string dummySourceCdd =
"(dummy?map=simpleJsonFile.jmap)";
42static const std::string lmapDmap =
"logicalnamemap.dmap";
55 const std::string& extraArgs,
const std::string& stdoutFile,
const std::string& stderrFile,
int& exitCode) {
58 int status = std::system(command.c_str());
59 if(!WIFEXITED(status)) {
62 exitCode = WEXITSTATUS(status);
69static std::string readFile(
const std::string& path) {
70 std::ifstream stream(path, std::ios::binary);
71 std::ostringstream buffer;
72 buffer << stream.rdbuf();
105 if(sourceDescriptor.
fundamentalType() == DataDescriptor::FundamentalType::numeric) {
123 size_t mismatches = 0;
124 for(
const auto& registerInfo : source) {
125 if(registerInfo.getNumberOfDimensions() > 1) {
128 const std::string path = registerInfo.getRegisterName();
138 for(
const auto& registerInfo : destination) {
139 if(registerInfo.getNumberOfDimensions() > 1) {
142 const std::string path = registerInfo.getRegisterName();
153static size_t roundTrip(
const std::string& sourceCdd,
const std::string& toolArguments,
const std::string& outputFile,
154 const std::string& stdoutFile,
const std::string& stderrFile) {
156 source.
open(sourceCdd);
160 BOOST_REQUIRE(runTool(toolArguments, stdoutFile, stderrFile, exitCode));
161 BOOST_REQUIRE(exitCode == 0);
164 destination.
open(
"(dummy?map=" + outputFile +
")");
167 return compareCatalogues(sourceCatalogue, destinationCatalogue);
173static std::string generateFromDummy(
const std::string& outputFile) {
175 BOOST_REQUIRE(runTool(std::format(R
"(--device "{}" --output {})", dummySourceCdd, outputFile), outputFile + ".stdout",
176 outputFile +
".stderr", exitCode));
177 BOOST_REQUIRE(exitCode == 0);
184static std::string generateFromLMap() {
185 std::string outputFile =
"devToJmap_lmap.jmap";
187 BOOST_REQUIRE(runTool(std::format(R
"(--device LMAP0 --dmap {} --output {})", lmapDmap, outputFile),
188 outputFile + ".stdout", outputFile +
".stderr", exitCode));
189 BOOST_REQUIRE(exitCode == 0);
205static void cleanup(
const std::vector<std::string>& files) {
206 for(
const auto& file : files) {
207 std::remove(file.c_str());
214static std::set<std::string> collectWritableInterrupts(
const RegisterCatalogue& catalogue) {
215 std::set<std::string> result;
216 for(
const auto& registerInfo : catalogue) {
217 if(registerInfo.isWriteable() &&
219 registerInfo.getDataDescriptor().fundamentalType() != DataDescriptor::FundamentalType::nodata) {
220 result.insert(registerInfo.getRegisterName());
229static std::set<std::string> collectTwoDimensional(
const RegisterCatalogue& catalogue) {
230 std::set<std::string> result;
231 for(
const auto& registerInfo : catalogue) {
232 if(registerInfo.getNumberOfDimensions() > 1) {
233 result.insert(registerInfo.getRegisterName());
249 roundTrip(dummySourceCdd, std::format(R
"(--device "{}" --output {})", dummySourceCdd, "devToJmap_dummy.jmap"),
250 "devToJmap_dummy.jmap",
"devToJmap_dummy.jmap.stdout",
"devToJmap_dummy.jmap.stderr");
251 BOOST_TEST(mismatches == 0U);
252 cleanup({
"devToJmap_dummy.jmap",
"devToJmap_dummy.jmap.stdout",
"devToJmap_dummy.jmap.stderr"});
262 roundTrip(
"LMAP0", std::format(R
"(--device LMAP0 --dmap {} --output {})", lmapDmap, "devToJmap_lmaprt.jmap"),
263 "devToJmap_lmaprt.jmap",
"devToJmap_lmaprt.jmap.stdout",
"devToJmap_lmaprt.jmap.stderr");
264 BOOST_TEST(mismatches == 0U);
265 cleanup({
"devToJmap_lmaprt.jmap",
"devToJmap_lmaprt.jmap.stdout",
"devToJmap_lmaprt.jmap.stderr"});
274 auto outputFile = generateFromLMap();
275 auto catalogue = openGenerated(outputFile);
296 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
304 auto outputFile = generateFromLMap();
305 auto catalogue = openGenerated(outputFile);
313 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
322 auto lmapFile = generateFromLMap();
323 auto lmapCatalogue = openGenerated(lmapFile);
324 BOOST_TEST(lmapCatalogue.hasRegister(
"/var_string"));
325 BOOST_TEST(lmapCatalogue.getRegister(
"/var_string").getDataDescriptor().fundamentalType() ==
326 DataDescriptor::FundamentalType::string);
327 cleanup({lmapFile, lmapFile +
".stdout", lmapFile +
".stderr"});
329 auto dummyFile = generateFromDummy(
"devToJmap_dummy_string.jmap");
330 auto dummyCatalogue = openGenerated(dummyFile);
331 BOOST_TEST(dummyCatalogue.hasRegister(
"/BSP/SOME_INFO"));
332 BOOST_TEST(dummyCatalogue.getRegister(
"/BSP/SOME_INFO").getDataDescriptor().fundamentalType() ==
333 DataDescriptor::FundamentalType::string);
334 cleanup({dummyFile, dummyFile +
".stdout", dummyFile +
".stderr"});
344 std::string customFile =
"devToJmap_strlen16.jmap";
346 BOOST_REQUIRE(runTool(std::format(R
"(--device LMAP0 --dmap {} --string-length 16 --output {})", lmapDmap, customFile),
347 customFile + ".stdout", customFile +
".stderr", exitCode));
348 BOOST_REQUIRE(exitCode == 0);
349 nlohmann::json customJmap = nlohmann::json::parse(readFile(customFile));
350 BOOST_TEST(customJmap[
"addressSpace"][
"var_string"][
"bytesPerElement"] == 16U);
351 cleanup({customFile, customFile +
".stdout", customFile +
".stderr"});
353 auto defaultFile = generateFromLMap();
354 nlohmann::json defaultJmap = nlohmann::json::parse(readFile(defaultFile));
355 BOOST_TEST(defaultJmap[
"addressSpace"][
"var_string"][
"bytesPerElement"] == 80U);
356 cleanup({defaultFile, defaultFile +
".stdout", defaultFile +
".stderr"});
363 auto dummyFile = generateFromDummy(
"devToJmap_dummy_void.jmap");
364 auto catalogue = openGenerated(dummyFile);
365 BOOST_TEST(catalogue.
hasRegister(
"/BSP/VOID_INTERRUPT_0"));
367 DataDescriptor::FundamentalType::nodata);
368 BOOST_TEST(catalogue.
hasRegister(
"/BSP/VOID_INTERRUPT_3_0_1"));
370 DataDescriptor::FundamentalType::nodata);
371 cleanup({dummyFile, dummyFile +
".stdout", dummyFile +
".stderr"});
379 auto dummyFile = generateFromDummy(
"devToJmap_dummy_bool.jmap");
380 auto catalogue = openGenerated(dummyFile);
381 for(
const std::string& path : {std::string(
"/DAQ/DOUBLE_BUF/ENA"), std::string(
"/DAQ/DOUBLE_BUF/INACTIVE_BUF_ID")}) {
384 const bool isNumericOrBool = fundamentalType == DataDescriptor::FundamentalType::numeric ||
385 fundamentalType == DataDescriptor::FundamentalType::boolean;
386 BOOST_TEST(isNumericOrBool);
389 cleanup({dummyFile, dummyFile +
".stdout", dummyFile +
".stderr"});
397 auto outputFile = generateFromLMap();
398 auto catalogue = openGenerated(outputFile);
406 BOOST_TEST(catalogue.
hasRegister(
"/ArrayConstant"));
410 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
418 auto outputFile = generateFromLMap();
419 auto catalogue = openGenerated(outputFile);
420 BOOST_TEST(catalogue.
hasRegister(
"/ArrayVariable"));
423 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
431 auto outputFile = generateFromLMap();
432 auto catalogue = openGenerated(outputFile);
433 BOOST_TEST(catalogue.
hasRegister(
"/MyModule/SomeSubmodule/Variable"));
435 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
442 auto dummyFile = generateFromDummy(
"devToJmap_dummy_access.jmap");
443 auto catalogue = openGenerated(dummyFile);
445 BOOST_TEST(catalogue.
hasRegister(
"/SomeTopLevelRegister"));
451 BOOST_TEST(catalogue.
hasRegister(
"/APP/SomeTable"));
455 cleanup({dummyFile, dummyFile +
".stdout", dummyFile +
".stderr"});
464 auto outputFile = generateFromLMap();
466 device.
open(
"(dummy?map=" + outputFile +
")");
468 for(
const std::string& path :
469 {std::string(
"/var_int32"), std::string(
"/var_float64"), std::string(
"/ArrayVariable")}) {
470 auto registerInfo = fullCatalogue.
getRegister(path);
471 BOOST_TEST(!registerInfo.isWriteable());
472 BOOST_TEST(registerInfo.isReadable());
476 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
483 std::string emptySource =
"devToJmap_empty_source.jmap";
485 std::ofstream stream(emptySource);
487 "mapFormatVersion": "1.0",
489 "interruptHandler": {},
493 std::string outputFile = "devToJmap_empty_out.jmap";
495 BOOST_REQUIRE(runTool(std::format(R
"(--device "{}" --output {})", "(dummy?map=" + emptySource +
")", outputFile),
496 outputFile +
".stdout", outputFile +
".stderr", exitCode));
497 BOOST_TEST(exitCode == 0);
500 auto stderrContent = readFile(outputFile +
".stderr");
501 BOOST_TEST(stderrContent.find(
"Warning:") == std::string::npos);
502 BOOST_TEST(stderrContent.find(
"Error:") == std::string::npos);
504 auto catalogue = openGenerated(outputFile);
507 cleanup({emptySource, outputFile, outputFile +
".stdout", outputFile +
".stderr"});
516 source.
open(dummySourceCdd);
519 auto dummyFile = generateFromDummy(
"devToJmap_dummy_2d.jmap");
520 BOOST_TEST(!affected.empty());
521 BOOST_TEST(readFile(dummyFile +
".stderr").find(
"two-dimensional registers") != std::string::npos);
523 auto catalogue = openGenerated(dummyFile);
525 for(
const auto& path : affected) {
528 BOOST_TEST(catalogue.
hasRegister(
"/DAQ/DOUBLE_BUF/ENA"));
530 cleanup({dummyFile, dummyFile +
".stdout", dummyFile +
".stderr"});
538 std::string sourceFile =
"devToJmap_2donly_source.jmap";
542 using nlohmann::json;
543 json source = json::parse(readFile(
"muxedDataAccessor.jmap"));
544 std::ofstream stream(sourceFile);
545 stream <<
json{{
"mapFormatVersion", source[
"mapFormatVersion"]}, {
"metadata", json::object()},
546 {
"interruptHandler", json::object()},
548 json{{
"TEST",
json{{
"children",
json{{
"DMA", source[
"addressSpace"][
"TEST"][
"children"][
"DMA"]}}}}}}}};
550 std::string outputFile =
"devToJmap_2donly_out.jmap";
552 BOOST_REQUIRE(runTool(std::format(R
"(--device '(dummy?map={})' --output {})", sourceFile, outputFile),
553 outputFile + ".stdout", outputFile +
".stderr", exitCode));
554 BOOST_TEST(exitCode == 0);
555 BOOST_TEST(readFile(outputFile +
".stderr").find(
"two-dimensional registers") != std::string::npos);
557 auto catalogue = openGenerated(outputFile);
563 cleanup({sourceFile, outputFile, outputFile +
".stdout", outputFile +
".stderr"});
571 auto outputFile = generateFromLMap();
574 source.
open(
"LMAP0");
576 BOOST_TEST(!affected.empty());
577 BOOST_TEST(readFile(outputFile +
".stderr").find(
"writable registers") != std::string::npos);
579 cleanup({outputFile, outputFile +
".stdout", outputFile +
".stderr"});
586 std::string stdoutFile =
"devToJmap_noout.stdout";
587 std::string stderrFile =
"devToJmap_noout.stderr";
589 BOOST_REQUIRE(runTool(std::format(R
"(--device "{}")", dummySourceCdd), stdoutFile, stderrFile, exitCode));
590 BOOST_TEST(exitCode != 0);
591 BOOST_TEST(readFile(stdoutFile).empty());
592 cleanup({stdoutFile, stderrFile});
599 std::string stdoutFile =
"devToJmap_help.stdout";
600 std::string stderrFile =
"devToJmap_help.stderr";
602 BOOST_REQUIRE(runTool(
"--help", stdoutFile, stderrFile, exitCode));
603 BOOST_TEST(exitCode == 0);
604 BOOST_TEST(readFile(stdoutFile).find(
"Usage:") != std::string::npos);
605 cleanup({stdoutFile, stderrFile});
612 auto firstFile = generateFromDummy(
"devToJmap_det_a.jmap");
613 std::string first = readFile(firstFile);
615 std::string secondFile =
"devToJmap_det_b.jmap";
616 BOOST_REQUIRE(runTool(std::format(R
"(--device "{}" --output {})", dummySourceCdd, secondFile), secondFile + ".stdout",
617 secondFile +
".stderr", exitCode));
618 BOOST_TEST(exitCode == 0);
619 BOOST_TEST(first == readFile(secondFile));
620 cleanup({firstFile, firstFile +
".stdout", firstFile +
".stderr", secondFile, secondFile +
".stdout",
621 secondFile +
".stderr"});
629 std::string aliasDmap =
"devToJmap_id.dmap";
631 std::ofstream stream(aliasDmap);
632 stream <<
"DUMMYJ (dummy?map=simpleJsonFile.jmap)\n";
635 std::string cddFile =
"devToJmap_id_cdd.jmap";
636 std::string aliasFile =
"devToJmap_id_alias.jmap";
638 BOOST_REQUIRE(runTool(std::format(R
"(--device "{}" --output {})", dummySourceCdd, cddFile), cddFile + ".stdout",
639 cddFile +
".stderr", exitCode));
640 BOOST_TEST(exitCode == 0);
641 BOOST_REQUIRE(runTool(std::format(R
"(--device DUMMYJ --dmap {} --output {})", aliasDmap, aliasFile),
642 aliasFile + ".stdout", aliasFile +
".stderr", exitCode));
643 BOOST_TEST(exitCode == 0);
645 BOOST_TEST(readFile(cddFile) == readFile(aliasFile));
647 cleanup({aliasDmap, cddFile, cddFile +
".stdout", cddFile +
".stderr", aliasFile, aliasFile +
".stdout",
648 aliasFile +
".stderr"});
656 std::string outputFile =
"devToJmap_openfail.jmap";
657 std::string stdoutFile =
"devToJmap_openfail.stdout";
658 std::string stderrFile =
"devToJmap_openfail.stderr";
660 BOOST_REQUIRE(runTool(std::format(R
"(--device "{}" --output {})", "(noSuchBackend?x=1)", outputFile), stdoutFile,
661 stderrFile, exitCode));
662 BOOST_TEST(exitCode == 1);
663 BOOST_TEST(!readFile(stderrFile).empty());
664 cleanup({outputFile, stdoutFile, stderrFile});
669BOOST_AUTO_TEST_SUITE_END()
static BackendFactory & getInstance()
Static function to get an instance of factory.
void setDMapFilePath(std::string dMapFilePath)
This function sets the _DMapFilePath.
Class describing the actual payload data format of a register in an abstract manner.
FundamentalType fundamentalType() const
Get the fundamental data type.
bool isIntegral() const
Return whether the data is integral or not (e.g.
DataType minimumDataType() const
Get the minimum data type required to represent the described data type in the host CPU.
bool isSigned() const
Return whether the data is signed or not.
size_t getNumberOfBytes() const
Return the number of bytes per element for the data type.
Class allows to read/write registers from device.
RegisterCatalogue getRegisterCatalogue() const
Return the register catalogue with detailed information on all registers.
void open(std::string const &aliasName)
Open a device by the given alias name from the DMAP file.
Catalogue of register information.
RegisterInfo getRegister(const RegisterPath ®isterPathName) const
Get register information for a given full path name.
size_t getNumberOfRegisters() const
Get number of registers in the catalogue.
bool hasRegister(const RegisterPath ®isterPathName) const
Check if register with the given path name exists.
bool isWriteable() const
Return whether the register is writeable.
const DataDescriptor & getDataDescriptor() const
Return desciption of the actual payload data for this register.
unsigned int getNumberOfElements() const
Return number of elements per channel.
bool isReadable() const
Return whether the register is readable.
This header collects some conversion routines which should be useful both for ApplicationCore Python ...
@ wait_for_new_data
Make any read blocking until new data has arrived since the last read.
BOOST_AUTO_TEST_CASE(TestRoundTripViaNumericAddressedDevice)
#define CHIMERATK_DEVICE_TO_JMAP