upm/tests/unit/utilities/utilities_tests.cxx
Noel Eck db3c73cbd1 utilities: Update time/r methods for LINUX
Default to MONOTONIC clock for timer methods to avoid falling victim to
clock corrections.  Changed signatures from accepting pointers since
this is not needed an complicates calls and Java/JS/Python bindings.

    * Switched from nanosleep to clock_nanosleep to allow developers to
      provide a clock for LINUX
    * Default upm_clock_init to CLOCK_MONOTONIC
    * Updated logic to calculating delay and elapsed to be more readable
    * Added ns flavors for completeness
    * Refactored all upm_* delay/timer methods
    * Added #else for preprocessor cases w/o an #else
    * Added test for AQI
    * Added test fixture with logic to identify a minimum delay time
      which is used as a metric for testing all delay methods
    * Much more lenient unit testing of delays to minimize false CI
      failures

Signed-off-by: Noel Eck <noel.eck@intel.com>
2018-07-31 14:20:30 -07:00

169 lines
4.9 KiB
C++

/*
* Author: Noel Eck <noel.eck@intel.com>
* Copyright (c) 2018 Intel Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <chrono>
#include <thread>
#include "gtest/gtest.h"
#include "upm_utilities.h"
#include "upm_utilities.hpp"
/* Average over AVG_CNT iterations */
#define AVG_CNT 5
/* Specify a delay for all tests under 1s */
#define ms_50 std::chrono::milliseconds(50)
/* Specify a +/- value for all tests under 1s. Since the delay methods in
* non-realtime operating systems can vary greatly, use a lenient value for
* testing these methods. */
#define time_range std::chrono::milliseconds(5)
/* Helper defines */
#define to_ms std::chrono::duration_cast<std::chrono::milliseconds>
#define to_us std::chrono::duration_cast<std::chrono::microseconds>
#define to_ns std::chrono::duration_cast<std::chrono::nanoseconds>
/* Utilities test fixture */
class utilities_unit : public ::testing::Test
{
protected:
/* One-time setup logic if needed */
utilities_unit() {}
/* One-time tear-down logic if needed */
virtual ~utilities_unit() {}
/* Per-test setup logic if needed */
virtual void SetUp() {}
/* Per-test tear-down logic if needed */
virtual void TearDown() {}
};
/* Sanity check on min_delay_ns */
TEST_F(utilities_unit, min_delay_LT_500us)
{
/* Determine a rough-average for the minimum delay using chrono */
std::chrono::nanoseconds min_delay_ns = std::chrono::nanoseconds::zero();
for (int i = 0; i < AVG_CNT; i++)
{
auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::nanoseconds(1));
auto end = std::chrono::steady_clock::now();
min_delay_ns += to_ns(end-start);
}
min_delay_ns /= AVG_CNT;
ASSERT_LT(to_us(min_delay_ns).count(), 500);
}
/* Test the second delay method */
TEST_F(utilities_unit, test_upm_delay)
{
upm_clock_t clock = upm_clock_init();
/* Test a corner case */
upm_delay(0);
/* +- check for 0s */
EXPECT_EQ(upm_elapsed_ms(&clock), 0);
clock = upm_clock_init();
upm_delay(1);
/* +- check near 1s */
EXPECT_NEAR(upm_elapsed_ms(&clock), 1000, time_range.count());
}
/* Test the millisecond delay method */
TEST_F(utilities_unit, test_upm_delay_ms)
{
upm_clock_t clock = upm_clock_init();
/* Test a corner case */
upm_delay_ms(0);
/* +- check for 0ms */
EXPECT_EQ(upm_elapsed_ms(&clock), 0);
clock = upm_clock_init();
upm_delay_ms(ms_50.count() * AVG_CNT);
/* +- check near 50ms */
EXPECT_NEAR(upm_elapsed_ms(&clock)/AVG_CNT, ms_50.count(), time_range.count());
}
/* Test the microsecond delay method */
TEST_F(utilities_unit, test_upm_delay_us)
{
upm_clock_t clock = upm_clock_init();
/* Test a corner case */
upm_delay_us(0);
/* +- check for 0us +/- 100us */
EXPECT_NEAR(upm_elapsed_us(&clock), 0, 100);
clock = upm_clock_init();
upm_delay_us(to_us(ms_50).count() * AVG_CNT);
/* +- check near 50ms */
EXPECT_NEAR(upm_elapsed_us(&clock)/AVG_CNT, to_us(ms_50).count(),
to_us(time_range).count());
}
/* Test the nanosecond delay method */
TEST_F(utilities_unit, test_upm_delay_ns)
{
upm_clock_t clock = upm_clock_init();
/* Test a corner case */
upm_delay_ns(0);
/* +- check for 0us +/- 100us */
EXPECT_NEAR(upm_elapsed_ns(&clock), 0, 100000);
clock = upm_clock_init();
upm_delay_ns(to_ns(ms_50).count() * AVG_CNT);
/* +- check near 50ms */
EXPECT_NEAR(upm_elapsed_ns(&clock)/AVG_CNT, to_ns(ms_50).count(),
to_ns(time_range).count());
}
/* Test the max us delay (default to disabled) */
TEST_F(utilities_unit, DISABLED_test_upm_delay_us_max)
{
upm_clock_t clock = upm_clock_init();
upm_delay_us(4294967295);
EXPECT_NEAR(upm_elapsed_us(&clock), 4294967295, 150);
}
/* Test the Air Quality Index method */
TEST_F(utilities_unit, test_upm_ugm3_to_aqi)
{
EXPECT_EQ(upm_ugm3_to_aqi(10), 41);
}