LCOV - code coverage report
Current view: top level - iomanager/unittest - FollyQueue_test.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 71.6 % 88 63
Test Date: 2025-12-21 13:07:08 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /**
       2              :  *
       3              :  * @file FollyQueue_test.cxx FollyQueue class Unit Tests
       4              :  *
       5              :  * This is part of the DUNE DAQ Application Framework, copyright 2020.
       6              :  * Licensing/copyright details are in the COPYING file that you should have
       7              :  * received with this code.
       8              :  */
       9              : 
      10              : #include "iomanager/queue/FollyQueue.hpp"
      11              : 
      12              : #define BOOST_TEST_MODULE FollyQueue_test // NOLINT
      13              : #include "boost/test/unit_test.hpp"
      14              : 
      15              : #include <chrono>
      16              : #include <utility>
      17              : 
      18              : // For a first look at the code, you may want to skip past the
      19              : // contents of the unnamed namespace and move ahead to the actual test
      20              : // cases
      21              : 
      22              : namespace {
      23              : 
      24              : constexpr int max_testable_capacity = 1'000'000'000; ///< The maximum capacity this test will attempt to check
      25              : constexpr double fractional_timeout_tolerance =
      26              :   0.5; ///< The fraction of the timeout which the timing is allowed to be off by
      27              : 
      28              : /**
      29              :  * @brief Timeout to use for tests
      30              :  *
      31              :  * Don't set the timeout to zero, otherwise the tests will fail since they'd
      32              :  * expect the push/pop functions to execute instananeously
      33              :  */
      34              : constexpr auto timeout = std::chrono::milliseconds(5);
      35              : /**
      36              :  * @brief Timeout expressed in microseconds
      37              :  */
      38              : constexpr auto timeout_in_us = std::chrono::duration_cast<std::chrono::microseconds>(timeout).count();
      39              : 
      40              : dunedaq::iomanager::FollySPSCQueue<int> queue("FollyQueue", 10); ///< Queue instance for the test
      41              : 
      42              : } // namespace ""
      43              : 
      44              : // This test case should run first. Make sure all other test cases depend on
      45              : // this.
      46              : 
      47            2 : BOOST_AUTO_TEST_CASE(sanity_checks)
      48              : {
      49            1 :   BOOST_REQUIRE(!queue.can_pop());
      50              : 
      51            1 :   BOOST_REQUIRE_EQUAL(queue.get_capacity(), 10);
      52            1 :   BOOST_REQUIRE_EQUAL(queue.get_num_elements(), 0);
      53              : 
      54            1 :   auto start_time = std::chrono::steady_clock::now();
      55            1 :   try {
      56            1 :     BOOST_REQUIRE(queue.can_push());
      57            1 :     queue.push(42, timeout);
      58            0 :   } catch (const dunedaq::iomanager::QueueTimeoutExpired& ex) {
      59            0 :     BOOST_TEST_REQUIRE(false, "Test failure: unexpected timeout exception throw from push");
      60            0 :   } catch (...) { // NOLINT
      61            0 :     BOOST_TEST_REQUIRE(false, "Test failure: unexpected exception (non-timeout-related) thrown");
      62            0 :   }
      63              : 
      64            1 :   auto push_time = std::chrono::steady_clock::now() - start_time;
      65              : 
      66            1 :   if (push_time > timeout) {
      67            0 :     auto push_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(push_time).count();
      68              : 
      69            0 :     BOOST_TEST_REQUIRE(false,
      70              :                        "Test failure: pushing element onto empty Queue "
      71              :                        "resulted in a timeout (function exited after "
      72              :                          << push_time_in_us << " microseconds, timeout is " << timeout_in_us << " microseconds)");
      73              :   }
      74              : 
      75            1 :   BOOST_REQUIRE(queue.can_pop());
      76            1 :   BOOST_REQUIRE_EQUAL(queue.get_num_elements(), 1);
      77              : 
      78            1 :   start_time = std::chrono::steady_clock::now();
      79            1 :   int popped_value = -999;
      80            1 :   try {
      81            1 :     queue.pop(popped_value, timeout);
      82            0 :   } catch (const dunedaq::iomanager::QueueTimeoutExpired& ex) {
      83            0 :     BOOST_TEST_REQUIRE(false, "Test failure: unexpected timeout exception throw from pop");
      84            0 :   } catch (...) { // NOLINT
      85            0 :     BOOST_TEST_REQUIRE(false, "Test failure: unexpected exception (non-timeout-related) thrown");
      86            0 :   }
      87              : 
      88            1 :   auto pop_time = std::chrono::steady_clock::now() - start_time;
      89              : 
      90            1 :   if (pop_time > timeout) {
      91            0 :     auto pop_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(pop_time).count();
      92            0 :     BOOST_TEST_REQUIRE(false,
      93              :                        "Test failure: popping element off Queue "
      94              :                        "resulted in a timeout without an exception throw (function exited after "
      95              :                          << pop_time_in_us << " microseconds, timeout is " << timeout_in_us << " microseconds)");
      96              :   }
      97              : 
      98            1 :   BOOST_REQUIRE_EQUAL(popped_value, 42);
      99            1 : }
     100              : 
     101            2 : BOOST_AUTO_TEST_CASE(empty_checks, *boost::unit_test::depends_on("sanity_checks"))
     102              : {
     103            1 :   int popped_value = -999;
     104              : 
     105            1 :   while (queue.can_pop()) {
     106              : 
     107            0 :     try {
     108            0 :       queue.pop(popped_value, timeout);
     109            0 :     } catch (const dunedaq::iomanager::QueueTimeoutExpired& ex) {
     110            0 :       BOOST_TEST(false,
     111              :                  "Timeout exception thrown in call to FollyQueue::pop(); unable "
     112              :                  "to empty the Queue");
     113            0 :       break;
     114            0 :     }
     115              :   }
     116              : 
     117            1 :   BOOST_REQUIRE(!queue.can_pop());
     118              : 
     119              :   // pop off of an empty Queue
     120              : 
     121            1 :   auto start_time = std::chrono::steady_clock::now();
     122            2 :   BOOST_CHECK_THROW(queue.pop(popped_value, timeout), dunedaq::iomanager::QueueTimeoutExpired);
     123            1 :   auto pop_duration = std::chrono::steady_clock::now() - start_time;
     124              : 
     125            1 :   const double fraction_of_pop_timeout_used =
     126            1 :     static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(pop_duration).count()) /
     127            1 :     std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
     128              : 
     129            1 :   BOOST_TEST_MESSAGE("Attempted pop_duration divided by timeout is " << fraction_of_pop_timeout_used);
     130              : 
     131            1 :   BOOST_CHECK_GT(fraction_of_pop_timeout_used, 1 - fractional_timeout_tolerance);
     132            1 :   BOOST_CHECK_LT(fraction_of_pop_timeout_used, 1 + fractional_timeout_tolerance);
     133            1 : }
     134              : 
     135            2 : BOOST_AUTO_TEST_CASE(full_checks, *boost::unit_test::depends_on("empty_checks"))
     136              : {
     137            1 :   int push_value = 0;
     138              : 
     139           11 :   while (queue.can_push()) {
     140              : 
     141           10 :     try {
     142           10 :       int push_value_tmp = push_value;
     143           10 :       queue.push(std::move(push_value_tmp), timeout);
     144           10 :       push_value++;
     145            0 :     } catch (const dunedaq::iomanager::QueueTimeoutExpired& ex) {
     146            0 :       BOOST_TEST(false,
     147              :                  "Timeout exception thrown in call to FollyQueue::push(); unable "
     148              :                  "to fill the Queue");
     149            0 :       break;
     150            0 :     }
     151              :   }
     152              : 
     153            1 :   BOOST_REQUIRE(!queue.can_push());
     154            1 :   BOOST_REQUIRE_EQUAL(push_value, queue.get_capacity());
     155              : 
     156            1 :   int test_max_capacity = 1000000;
     157            2 :   while (push_value < test_max_capacity) {
     158              :     // push to a full Queue
     159            2 :     auto start_time = std::chrono::steady_clock::now();
     160            2 :     try {
     161            2 :       int push_value_tmp = push_value;
     162            2 :       queue.push(std::move(push_value_tmp), timeout);
     163            1 :       push_value++;
     164            1 :     } catch (dunedaq::iomanager::QueueTimeoutExpired&) {
     165            1 :       auto push_duration = std::chrono::steady_clock::now() - start_time;
     166            1 :       BOOST_TEST_MESSAGE("Timeout occurred. Capacity is " << queue.get_capacity() << ", current occupancy is "
     167              :                                                           << queue.get_num_elements());
     168              : 
     169            1 :       const double fraction_of_push_timeout_used =
     170            1 :         static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(push_duration).count()) /
     171            1 :         std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count();
     172              : 
     173            1 :       BOOST_TEST_MESSAGE("Attempted push_duration divided by timeout is " << fraction_of_push_timeout_used);
     174              : 
     175            1 :       BOOST_CHECK_GT(fraction_of_push_timeout_used, 1 - fractional_timeout_tolerance);
     176            1 :       BOOST_CHECK_LT(fraction_of_push_timeout_used, 1 + fractional_timeout_tolerance);
     177            1 :       break;
     178            1 :     }
     179              :   }
     180            1 :   if (push_value == test_max_capacity) {
     181            0 :     BOOST_TEST_MESSAGE("Unable to cause push timeout in " << test_max_capacity << " pushes");
     182              :   }
     183            1 : }
        

Generated by: LCOV version 2.0-1