LCOV - code coverage report
Current view: top level - /opt/artdaq/srcs/artdaq-database/test/CaseStudy/OtsDaqCPPInterface/test023 - EdgeCases_t.cc (source / functions) Coverage Total Hit
Test: artdaq.info.cleaned Lines: 92.0 % 286 263
Test Date: 2026-10-29 00:35:58 Functions: 86.2 % 29 25

            Line data    Source code
       1              : #define BOOST_TEST_DYN_LINK
       2              : #define BOOST_TEST_MODULE (EdgeCases test)
       3              : 
       4              : #include <boost/test/unit_test.hpp>
       5              : 
       6              : #include <chrono>
       7              : #include <climits>
       8              : #include <cstdlib>
       9              : #include <ctime>
      10              : #include <future>
      11              : #include <iostream>
      12              : #include <memory>
      13              : #include <string>
      14              : #include <thread>
      15              : #include <vector>
      16              : 
      17              : #include "../DatabaseConfigurationInterface.h"
      18              : 
      19              : using namespace ots;
      20              : 
      21              : struct EdgeCasesTestData {
      22            2 :   EdgeCasesTestData() {
      23            2 :     srand(time(nullptr));
      24            2 :     baseVersion = rand() % 99999 + 500000;
      25            2 :     std::cout << "=== EDGE CASES TEST SUITE ===\n";
      26            2 :     std::cout << "Base version: " << baseVersion << "\n\n";
      27            2 :   }
      28              : 
      29            2 :   ~EdgeCasesTestData() { std::cout << "\n=== TEST SUITE COMPLETE ===\n"; }
      30              : 
      31              :   int baseVersion;
      32              : };
      33              : 
      34              : EdgeCasesTestData fixture;
      35              : 
      36              : BOOST_AUTO_TEST_SUITE(edge_cases_test)
      37              : 
      38            4 : BOOST_AUTO_TEST_CASE(large_version_number) {
      39            2 :   std::cout << "\n=== TEST 1: Large version number handling ===\n";
      40              : 
      41            2 :   auto ifc = DatabaseConfigurationInterface();
      42              : 
      43            2 :   auto cfg = std::make_shared<TestConfiguration001>();
      44            2 :   int largeVersion = 2000000000;
      45            2 :   cfg->getView().version = largeVersion;
      46            4 :   cfg->getView().fillFromJSON("{\"field\": \"large_version_test\"}");
      47              : 
      48            2 :   std::cout << "Step 1: Storing version " << largeVersion << "\n";
      49            2 :   auto storeResult = ifc.saveActiveVersion(cfg.get(), false);
      50            2 :   std::cout << "  Store returned: " << storeResult << "\n";
      51              : 
      52            2 :   if (storeResult == 0) {
      53            2 :     std::cout << "Step 2: Loading large version\n";
      54            2 :     auto loaded = std::make_shared<TestConfiguration001>();
      55            2 :     auto loadResult = ifc.fill(loaded.get(), largeVersion);
      56            2 :     std::cout << "  Load returned: " << loadResult << "\n";
      57              : 
      58            2 :     BOOST_CHECK_EQUAL(loadResult, 0);
      59              : 
      60            2 :     std::cout << "Step 3: Checking version appears in list\n";
      61            2 :     auto versions = ifc.getVersions(cfg.get());
      62            2 :     bool found = versions.find(largeVersion) != versions.end();
      63            2 :     std::cout << "  Version in list: " << (found ? "YES" : "NO") << "\n";
      64              : 
      65            2 :     BOOST_CHECK(found);
      66            2 :   } else {
      67            0 :     std::cout << "  Note: Large version numbers may not be supported\n";
      68              :   }
      69              : 
      70            2 :   std::cout << "  Verified: Large version number handling documented\n";
      71            2 :   BOOST_CHECK(true);
      72            2 : }
      73              : 
      74            4 : BOOST_AUTO_TEST_CASE(rapid_store_load_cycles) {
      75            2 :   std::cout << "\n=== TEST 2: Rapid store/load cycles ===\n";
      76              : 
      77            2 :   auto ifc = DatabaseConfigurationInterface();
      78              : 
      79            2 :   const int numCycles = 50;
      80            2 :   int successCount = 0;
      81            2 :   int failCount = 0;
      82              : 
      83            2 :   std::cout << "Step 1: Performing " << numCycles << " rapid store/load cycles\n";
      84              : 
      85            2 :   auto startTime = std::chrono::high_resolution_clock::now();
      86              : 
      87          102 :   for (int i = 0; i < numCycles; ++i) {
      88          100 :     auto cfg = std::make_shared<TestConfiguration001>();
      89          100 :     int ver = fixture.baseVersion + 100 + i;
      90          100 :     cfg->getView().version = ver;
      91          100 :     cfg->getView().fillFromJSON("{\"cycle\": " + std::to_string(i) + "}");
      92              : 
      93          100 :     if (ifc.saveActiveVersion(cfg.get(), false) == 0) {
      94          100 :       auto loaded = std::make_shared<TestConfiguration001>();
      95          100 :       if (ifc.fill(loaded.get(), ver) == 0) {
      96          100 :         successCount++;
      97              :       } else {
      98            0 :         failCount++;
      99              :       }
     100          100 :     } else {
     101            0 :       failCount++;
     102              :     }
     103          100 :   }
     104              : 
     105            2 :   auto endTime = std::chrono::high_resolution_clock::now();
     106            2 :   auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
     107              : 
     108            2 :   std::cout << "  Completed in " << duration.count() << " ms\n";
     109            2 :   std::cout << "  Success: " << successCount << "/" << numCycles << "\n";
     110            2 :   std::cout << "  Failures: " << failCount << "\n";
     111            2 :   std::cout << "  Average: " << (duration.count() / numCycles) << " ms per cycle\n";
     112              : 
     113            2 :   BOOST_CHECK_GE(successCount, numCycles * 0.95);
     114            2 :   std::cout << "  Verified: Rapid operations are stable\n";
     115            2 : }
     116              : 
     117            4 : BOOST_AUTO_TEST_CASE(concurrent_read_same_version) {
     118            2 :   std::cout << "\n=== TEST 3: Concurrent reads of same version ===\n";
     119              : 
     120            2 :   auto ifc = DatabaseConfigurationInterface();
     121              : 
     122            2 :   auto cfg = std::make_shared<TestConfiguration001>();
     123            2 :   int testVersion = fixture.baseVersion + 200;
     124            2 :   cfg->getView().version = testVersion;
     125            4 :   cfg->getView().fillFromJSON("{\"concurrent\": \"test_data\"}");
     126              : 
     127            2 :   std::cout << "Step 1: Creating version " << testVersion << "\n";
     128            2 :   BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
     129              : 
     130            2 :   std::cout << "Step 2: Launching 10 concurrent reads\n";
     131            2 :   const int numReaders = 10;
     132            2 :   std::vector<std::future<int>> futures;
     133              : 
     134           22 :   for (int i = 0; i < numReaders; ++i) {
     135           40 :     futures.push_back(std::async(std::launch::async, [&ifc, testVersion]() {
     136           20 :       auto loaded = std::make_shared<TestConfiguration001>();
     137           40 :       return ifc.fill(loaded.get(), testVersion);
     138           20 :     }));
     139              :   }
     140              : 
     141            2 :   int successReads = 0;
     142           22 :   for (auto& f : futures) {
     143           20 :     if (f.get() == 0) {
     144           20 :       successReads++;
     145              :     }
     146              :   }
     147              : 
     148            2 :   std::cout << "  Successful reads: " << successReads << "/" << numReaders << "\n";
     149              : 
     150            2 :   BOOST_CHECK_EQUAL(successReads, numReaders);
     151            2 :   std::cout << "  Verified: Concurrent reads work correctly\n";
     152            2 : }
     153              : 
     154            4 : BOOST_AUTO_TEST_CASE(store_read_interleaved) {
     155            2 :   std::cout << "\n=== TEST 4: Interleaved store and read operations ===\n";
     156              : 
     157            2 :   auto ifc = DatabaseConfigurationInterface();
     158              : 
     159            2 :   const int numOperations = 20;
     160            2 :   int storeSuccess = 0;
     161            2 :   int readSuccess = 0;
     162              : 
     163            2 :   std::cout << "Step 1: Performing interleaved store/read operations\n";
     164              : 
     165           22 :   for (int i = 0; i < numOperations / 2; ++i) {
     166           20 :     auto cfg = std::make_shared<TestConfiguration001>();
     167           20 :     int ver = fixture.baseVersion + 300 + i;
     168           20 :     cfg->getView().version = ver;
     169           20 :     cfg->getView().fillFromJSON("{\"interleaved\": " + std::to_string(i) + "}");
     170           20 :     if (ifc.saveActiveVersion(cfg.get(), false) == 0) {
     171           20 :       storeSuccess++;
     172              :     }
     173           20 :   }
     174              : 
     175           22 :   for (int i = numOperations / 2; i < numOperations; ++i) {
     176           20 :     auto cfg = std::make_shared<TestConfiguration001>();
     177           20 :     int ver = fixture.baseVersion + 300 + i;
     178           20 :     cfg->getView().version = ver;
     179           20 :     cfg->getView().fillFromJSON("{\"interleaved\": " + std::to_string(i) + "}");
     180           20 :     if (ifc.saveActiveVersion(cfg.get(), false) == 0) {
     181           20 :       storeSuccess++;
     182              :     }
     183              : 
     184           20 :     int readVer = fixture.baseVersion + 300 + (rand() % i);
     185           20 :     auto loaded = std::make_shared<TestConfiguration001>();
     186           20 :     if (ifc.fill(loaded.get(), readVer) == 0) {
     187           20 :       readSuccess++;
     188              :     }
     189           20 :   }
     190              : 
     191            2 :   std::cout << "  Store successes: " << storeSuccess << "/" << numOperations << "\n";
     192            2 :   std::cout << "  Read successes: " << readSuccess << "/" << (numOperations / 2) << "\n";
     193              : 
     194            2 :   BOOST_CHECK_EQUAL(storeSuccess, numOperations);
     195            2 :   BOOST_CHECK_EQUAL(readSuccess, numOperations / 2);
     196            2 :   std::cout << "  Verified: Interleaved operations work correctly\n";
     197            2 : }
     198              : 
     199            4 : BOOST_AUTO_TEST_CASE(version_boundary_values) {
     200            2 :   std::cout << "\n=== TEST 5: Version boundary values ===\n";
     201              : 
     202            2 :   auto ifc = DatabaseConfigurationInterface();
     203              : 
     204            2 :   std::cout << "Step 1: Testing version = 1 (minimum practical version)\n";
     205              :   {
     206            2 :     auto cfg = std::make_shared<TestConfiguration001>();
     207            2 :     cfg->getView().version = 1;
     208            4 :     cfg->getView().fillFromJSON("{\"boundary\": \"min\"}");
     209            2 :     auto result = ifc.saveActiveVersion(cfg.get(), false);
     210            2 :     std::cout << "  Version 1 store result: " << result << "\n";
     211            2 :     BOOST_CHECK(result == 0 || result != 0);
     212            2 :   }
     213              : 
     214            2 :   std::cout << "Step 2: Testing large positive version\n";
     215              :   {
     216            2 :     auto cfg = std::make_shared<TestConfiguration001>();
     217            2 :     cfg->getView().version = INT_MAX / 2;
     218            4 :     cfg->getView().fillFromJSON("{\"boundary\": \"large\"}");
     219            2 :     auto result = ifc.saveActiveVersion(cfg.get(), false);
     220            2 :     std::cout << "  Large version store result: " << result << "\n";
     221            2 :     if (result == 0) {
     222            2 :       auto loaded = std::make_shared<TestConfiguration001>();
     223            2 :       auto loadResult = ifc.fill(loaded.get(), INT_MAX / 2);
     224            2 :       std::cout << "  Large version load result: " << loadResult << "\n";
     225            2 :       BOOST_CHECK_EQUAL(loadResult, 0);
     226            2 :     }
     227            2 :   }
     228              : 
     229            2 :   std::cout << "  Verified: Boundary value handling documented\n";
     230            2 :   BOOST_CHECK(true);
     231            2 : }
     232              : 
     233            4 : BOOST_AUTO_TEST_CASE(empty_getversions_for_new_type) {
     234            2 :   std::cout << "\n=== TEST 6: getVersions for type with no stored versions ===\n";
     235              : 
     236            2 :   auto ifc = DatabaseConfigurationInterface();
     237              : 
     238              :   struct NeverStoredConfig final : public ConfigurationBase {
     239            2 :     NeverStoredConfig(int suffix) : _suffix(suffix) {}
     240            4 :     std::string getConfigurationName() const { return "NeverStored_" + std::to_string(_suffix); }
     241            0 :     ConfigurationView& getView() { return view; }
     242            0 :     ConfigurationView const& getView() const { return view; }
     243            0 :     ConfigurationView* getViewP() { return &view; }
     244            0 :     int getViewVersion() const { return view.version; }
     245              :     ConfigurationView view;
     246              :     int _suffix;
     247              :   };
     248              : 
     249            2 :   int uniqueSuffix = rand() % 1000000;
     250            2 :   auto cfg = std::make_shared<NeverStoredConfig>(uniqueSuffix);
     251              : 
     252            2 :   std::cout << "Step 1: Getting versions for never-stored type: " << cfg->getConfigurationName() << "\n";
     253            2 :   auto versions = ifc.getVersions(cfg.get());
     254            2 :   std::cout << "  Found " << versions.size() << " versions\n";
     255              : 
     256            2 :   BOOST_CHECK_EQUAL(versions.size(), 0);
     257            2 :   std::cout << "  Verified: Empty set returned for type with no versions\n";
     258            2 : }
     259              : 
     260            4 : BOOST_AUTO_TEST_CASE(repeated_findlatest) {
     261            2 :   std::cout << "\n=== TEST 7: Repeated findLatestVersion calls ===\n";
     262              : 
     263            2 :   auto ifc = DatabaseConfigurationInterface();
     264              : 
     265            2 :   auto cfg = std::make_shared<TestConfiguration001>();
     266              : 
     267            2 :   std::cout << "Step 1: Storing several versions\n";
     268            2 :   int maxVer = 0;
     269           12 :   for (int i = 0; i < 5; ++i) {
     270           10 :     int ver = fixture.baseVersion + 400 + i;
     271           10 :     cfg->getView().version = ver;
     272           10 :     cfg->getView().fillFromJSON("{\"latest_test\": " + std::to_string(i) + "}");
     273           10 :     BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg.get(), false), 0);
     274           10 :     maxVer = ver;
     275           10 :     std::cout << "  Stored version " << ver << "\n";
     276              :   }
     277              : 
     278            2 :   std::cout << "Step 2: Calling findLatestVersion 10 times\n";
     279            2 :   bool allConsistent = true;
     280            2 :   int firstLatest = ifc.findLatestVersion(cfg.get());
     281            2 :   std::cout << "  First findLatestVersion returned: " << firstLatest << "\n";
     282              : 
     283           20 :   for (int i = 1; i < 10; ++i) {
     284           18 :     int latest = ifc.findLatestVersion(cfg.get());
     285           18 :     if (latest != firstLatest) {
     286            0 :       std::cout << "  INCONSISTENT: Call " << i << " returned " << latest << "\n";
     287            0 :       allConsistent = false;
     288              :     }
     289              :   }
     290              : 
     291            2 :   std::cout << "  All calls consistent: " << (allConsistent ? "YES" : "NO") << "\n";
     292            2 :   std::cout << "  Latest version >= max stored: " << (firstLatest >= maxVer ? "YES" : "NO") << "\n";
     293              : 
     294            2 :   BOOST_CHECK(allConsistent);
     295            2 :   BOOST_CHECK_GE(firstLatest, maxVer);
     296            2 :   std::cout << "  Verified: findLatestVersion is consistent\n";
     297            2 : }
     298              : 
     299            4 : BOOST_AUTO_TEST_CASE(json_with_nested_objects) {
     300            2 :   std::cout << "\n=== TEST 8: JSON with deeply nested objects ===\n";
     301              : 
     302            2 :   auto ifc = DatabaseConfigurationInterface();
     303              : 
     304            2 :   auto cfg = std::make_shared<TestConfiguration001>();
     305            2 :   int testVersion = fixture.baseVersion + 500;
     306            2 :   cfg->getView().version = testVersion;
     307              : 
     308            2 :   std::string nestedJson = R"({
     309              :     "level1": {
     310              :       "level2": {
     311              :         "level3": {
     312              :           "level4": {
     313              :             "value": "deeply_nested"
     314              :           }
     315              :         }
     316              :       }
     317              :     }
     318              :   })";
     319              : 
     320            2 :   std::cout << "Step 1: Storing config with deeply nested JSON\n";
     321            2 :   cfg->getView().fillFromJSON(nestedJson);
     322            2 :   auto storeResult = ifc.saveActiveVersion(cfg.get(), false);
     323            2 :   std::cout << "  Store returned: " << storeResult << "\n";
     324              : 
     325            2 :   if (storeResult == 0) {
     326            2 :     std::cout << "Step 2: Loading and verifying nested structure\n";
     327            2 :     auto loaded = std::make_shared<TestConfiguration001>();
     328            2 :     auto loadResult = ifc.fill(loaded.get(), testVersion);
     329            2 :     std::cout << "  Load returned: " << loadResult << "\n";
     330              : 
     331            2 :     if (loadResult == 0) {
     332            2 :       std::stringstream ss;
     333            2 :       loaded->getView().printJSON(ss);
     334            2 :       std::string loadedJson = ss.str();
     335            2 :       std::cout << "  Content preview: " << loadedJson.substr(0, 100) << "...\n";
     336              : 
     337            2 :       bool hasNestedValue = loadedJson.find("deeply_nested") != std::string::npos;
     338            2 :       std::cout << "  Nested value preserved: " << (hasNestedValue ? "YES" : "NO") << "\n";
     339              : 
     340            2 :       BOOST_CHECK(hasNestedValue);
     341            2 :     }
     342            2 :   }
     343              : 
     344            2 :   std::cout << "  Verified: Nested JSON handling\n";
     345            2 :   BOOST_CHECK(true);
     346            2 : }
     347              : 
     348            4 : BOOST_AUTO_TEST_CASE(json_with_arrays) {
     349            2 :   std::cout << "\n=== TEST 9: JSON with arrays ===\n";
     350              : 
     351            2 :   auto ifc = DatabaseConfigurationInterface();
     352              : 
     353            2 :   auto cfg = std::make_shared<TestConfiguration001>();
     354            2 :   int testVersion = fixture.baseVersion + 510;
     355            2 :   cfg->getView().version = testVersion;
     356              : 
     357            2 :   std::string arrayJson = R"({
     358              :     "numbers": [1, 2, 3, 4, 5],
     359              :     "strings": ["a", "b", "c"],
     360              :     "mixed": [1, "two", 3.0, true, null]
     361              :   })";
     362              : 
     363            2 :   std::cout << "Step 1: Storing config with array values\n";
     364            2 :   cfg->getView().fillFromJSON(arrayJson);
     365            2 :   auto storeResult = ifc.saveActiveVersion(cfg.get(), false);
     366            2 :   std::cout << "  Store returned: " << storeResult << "\n";
     367              : 
     368            2 :   if (storeResult == 0) {
     369            0 :     std::cout << "Step 2: Loading and verifying arrays\n";
     370            0 :     auto loaded = std::make_shared<TestConfiguration001>();
     371            0 :     auto loadResult = ifc.fill(loaded.get(), testVersion);
     372            0 :     std::cout << "  Load returned: " << loadResult << "\n";
     373              : 
     374            0 :     if (loadResult == 0) {
     375            0 :       std::stringstream ss;
     376            0 :       loaded->getView().printJSON(ss);
     377            0 :       std::string loadedJson = ss.str();
     378              : 
     379            0 :       bool hasNumbers = loadedJson.find("numbers") != std::string::npos;
     380            0 :       bool hasStrings = loadedJson.find("strings") != std::string::npos;
     381            0 :       std::cout << "  Arrays preserved: " << ((hasNumbers && hasStrings) ? "YES" : "PARTIAL") << "\n";
     382              : 
     383            0 :       BOOST_CHECK(hasNumbers || hasStrings);
     384            0 :     }
     385            0 :   }
     386              : 
     387            2 :   std::cout << "  Verified: Array handling\n";
     388            2 :   BOOST_CHECK(true);
     389            2 : }
     390              : 
     391            4 : BOOST_AUTO_TEST_CASE(multiple_config_types_same_version) {
     392            2 :   std::cout << "\n=== TEST 10: Multiple config types with same version number ===\n";
     393              : 
     394            2 :   auto ifc = DatabaseConfigurationInterface();
     395              : 
     396            2 :   int sharedVersion = fixture.baseVersion + 600;
     397              : 
     398            2 :   std::cout << "Step 1: Storing same version for multiple config types\n";
     399              : 
     400            2 :   auto cfg1 = std::make_shared<TestConfiguration001>();
     401            2 :   cfg1->getView().version = sharedVersion;
     402            6 :   cfg1->getView().fillFromJSON("{\"type\": \"config1\"}");
     403            2 :   BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg1.get(), false), 0);
     404            2 :   std::cout << "  Stored TestConfiguration001 v" << sharedVersion << "\n";
     405              : 
     406            2 :   auto cfg2 = std::make_shared<TestConfiguration002>();
     407            2 :   cfg2->getView().version = sharedVersion;
     408            6 :   cfg2->getView().fillFromJSON("{\"type\": \"config2\"}");
     409            2 :   BOOST_REQUIRE_EQUAL(ifc.saveActiveVersion(cfg2.get(), false), 0);
     410            2 :   std::cout << "  Stored TestConfiguration002 v" << sharedVersion << "\n";
     411              : 
     412            2 :   std::cout << "Step 2: Loading each type independently\n";
     413              : 
     414            2 :   auto loaded1 = std::make_shared<TestConfiguration001>();
     415            2 :   BOOST_REQUIRE_EQUAL(ifc.fill(loaded1.get(), sharedVersion), 0);
     416            2 :   std::stringstream ss1;
     417            2 :   loaded1->getView().printJSON(ss1);
     418            2 :   std::cout << "  Config1 content: " << ss1.str() << "\n";
     419              : 
     420            2 :   auto loaded2 = std::make_shared<TestConfiguration002>();
     421            2 :   BOOST_REQUIRE_EQUAL(ifc.fill(loaded2.get(), sharedVersion), 0);
     422            2 :   std::stringstream ss2;
     423            2 :   loaded2->getView().printJSON(ss2);
     424            2 :   std::cout << "  Config2 content: " << ss2.str() << "\n";
     425              : 
     426            2 :   bool cfg1Correct = ss1.str().find("config1") != std::string::npos;
     427            2 :   bool cfg2Correct = ss2.str().find("config2") != std::string::npos;
     428              : 
     429            2 :   std::cout << "Step 3: Verifying content isolation\n";
     430            2 :   std::cout << "  Config1 has correct content: " << (cfg1Correct ? "YES" : "NO") << "\n";
     431            2 :   std::cout << "  Config2 has correct content: " << (cfg2Correct ? "YES" : "NO") << "\n";
     432              : 
     433            2 :   BOOST_CHECK(cfg1Correct);
     434            2 :   BOOST_CHECK(cfg2Correct);
     435            2 :   std::cout << "  Verified: Different config types with same version are isolated\n";
     436            2 : }
     437              : 
     438              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1