diff options
author | Brooks Davis <brooks@FreeBSD.org> | 2020-03-17 16:56:50 +0000 |
---|---|---|
committer | Brooks Davis <brooks@FreeBSD.org> | 2020-03-17 16:56:50 +0000 |
commit | 08334c51dbb99d9ecd2bb86a2d94ed06da9e167a (patch) | |
tree | c43eb24d59bd5c963583a5190caef80fc8387322 /utils/signals/timer_test.cpp | |
download | src-08334c51dbb99d9ecd2bb86a2d94ed06da9e167a.tar.gz src-08334c51dbb99d9ecd2bb86a2d94ed06da9e167a.zip |
Import the kyua testing framework for infrastructure softwarevendor/kyua/0.13-a685f91
Imported at 0.13 plus assumulated changes to git hash a685f91.
Obtained from: https://github.com/jmmv/kyua
Sponsored by: DARPA
Notes
Notes:
svn path=/vendor/kyua/dist/; revision=359042
svn path=/vendor/kyua/0.13-a685f91/; revision=359043; tag=vendor/kyua/0.13-a685f91
Diffstat (limited to 'utils/signals/timer_test.cpp')
-rw-r--r-- | utils/signals/timer_test.cpp | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/utils/signals/timer_test.cpp b/utils/signals/timer_test.cpp new file mode 100644 index 000000000000..61e9cac6b088 --- /dev/null +++ b/utils/signals/timer_test.cpp @@ -0,0 +1,426 @@ +// Copyright 2010 The Kyua Authors. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of Google Inc. nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/signals/timer.hpp" + +extern "C" { +#include <signal.h> +#include <unistd.h> +} + +#include <cstddef> +#include <iostream> +#include <vector> + +#include <atf-c++.hpp> + +#include "utils/datetime.hpp" +#include "utils/defs.hpp" +#include "utils/format/containers.ipp" +#include "utils/format/macros.hpp" +#include "utils/signals/interrupts.hpp" +#include "utils/signals/programmer.hpp" + +namespace datetime = utils::datetime; +namespace signals = utils::signals; + + +namespace { + + +/// A timer that inserts an element into a vector on activation. +class delayed_inserter : public signals::timer { + /// Vector into which to insert the element. + std::vector< int >& _destination; + + /// Element to insert into _destination on activation. + const int _item; + + /// Timer activation callback. + void + callback(void) + { + signals::interrupts_inhibiter inhibiter; + _destination.push_back(_item); + } + +public: + /// Constructor. + /// + /// \param delta Time to the timer activation. + /// \param destination Vector into which to insert the element. + /// \param item Element to insert into destination on activation. + delayed_inserter(const datetime::delta& delta, + std::vector< int >& destination, const int item) : + signals::timer(delta), _destination(destination), _item(item) + { + } +}; + + +/// Signal handler that does nothing. +static void +null_handler(const int /* signo */) +{ +} + + +/// Waits for the activation of all given timers. +/// +/// \param timers Pointers to all the timers to wait for. +static void +wait_timers(const std::vector< signals::timer* >& timers) +{ + std::size_t n_fired, old_n_fired = 0; + do { + n_fired = 0; + for (std::vector< signals::timer* >::const_iterator + iter = timers.begin(); iter != timers.end(); ++iter) { + const signals::timer* timer = *iter; + if (timer->fired()) + ++n_fired; + } + if (old_n_fired < n_fired) { + std::cout << "Waiting; " << n_fired << " timers fired so far\n"; + old_n_fired = n_fired; + } + ::usleep(100); + } while (n_fired < timers.size()); +} + + +} // anonymous namespace + + +ATF_TEST_CASE(program_seconds); +ATF_TEST_CASE_HEAD(program_seconds) +{ + set_md_var("timeout", "10"); +} +ATF_TEST_CASE_BODY(program_seconds) +{ + signals::timer timer(datetime::delta(1, 0)); + ATF_REQUIRE(!timer.fired()); + while (!timer.fired()) + ::usleep(1000); +} + + +ATF_TEST_CASE(program_useconds); +ATF_TEST_CASE_HEAD(program_useconds) +{ + set_md_var("timeout", "10"); +} +ATF_TEST_CASE_BODY(program_useconds) +{ + signals::timer timer(datetime::delta(0, 500000)); + ATF_REQUIRE(!timer.fired()); + while (!timer.fired()) + ::usleep(1000); +} + + +ATF_TEST_CASE(multiprogram_ordered); +ATF_TEST_CASE_HEAD(multiprogram_ordered) +{ + set_md_var("timeout", "20"); +} +ATF_TEST_CASE_BODY(multiprogram_ordered) +{ + static const std::size_t n_timers = 100; + + std::vector< signals::timer* > timers; + std::vector< int > items, exp_items; + + const int initial_delay_ms = 1000000; + for (std::size_t i = 0; i < n_timers; ++i) { + exp_items.push_back(i); + + timers.push_back(new delayed_inserter( + datetime::delta(0, initial_delay_ms + (i + 1) * 10000), + items, i)); + ATF_REQUIRE(!timers[i]->fired()); + } + + wait_timers(timers); + + ATF_REQUIRE_EQ(exp_items, items); +} + + +ATF_TEST_CASE(multiprogram_reorder_next_activations); +ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations) +{ + set_md_var("timeout", "20"); +} +ATF_TEST_CASE_BODY(multiprogram_reorder_next_activations) +{ + std::vector< signals::timer* > timers; + std::vector< int > items; + + // First timer with an activation in the future. + timers.push_back(new delayed_inserter( + datetime::delta(0, 100000), items, 1)); + ATF_REQUIRE(!timers[timers.size() - 1]->fired()); + + // Timer with an activation earlier than the previous one. + timers.push_back(new delayed_inserter( + datetime::delta(0, 50000), items, 2)); + ATF_REQUIRE(!timers[timers.size() - 1]->fired()); + + // Timer with an activation later than all others. + timers.push_back(new delayed_inserter( + datetime::delta(0, 200000), items, 3)); + ATF_REQUIRE(!timers[timers.size() - 1]->fired()); + + // Timer with an activation in between. + timers.push_back(new delayed_inserter( + datetime::delta(0, 150000), items, 4)); + ATF_REQUIRE(!timers[timers.size() - 1]->fired()); + + wait_timers(timers); + + std::vector< int > exp_items; + exp_items.push_back(2); + exp_items.push_back(1); + exp_items.push_back(4); + exp_items.push_back(3); + ATF_REQUIRE_EQ(exp_items, items); +} + + +ATF_TEST_CASE(multiprogram_and_cancel_some); +ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some) +{ + set_md_var("timeout", "20"); +} +ATF_TEST_CASE_BODY(multiprogram_and_cancel_some) +{ + std::vector< signals::timer* > timers; + std::vector< int > items; + + // First timer with an activation in the future. + timers.push_back(new delayed_inserter( + datetime::delta(0, 100000), items, 1)); + + // Timer with an activation earlier than the previous one. + timers.push_back(new delayed_inserter( + datetime::delta(0, 50000), items, 2)); + + // Timer with an activation later than all others. + timers.push_back(new delayed_inserter( + datetime::delta(0, 200000), items, 3)); + + // Timer with an activation in between. + timers.push_back(new delayed_inserter( + datetime::delta(0, 150000), items, 4)); + + // Cancel the first timer to reprogram next activation. + timers[1]->unprogram(); delete timers[1]; timers.erase(timers.begin() + 1); + + // Cancel another timer without reprogramming next activation. + timers[2]->unprogram(); delete timers[2]; timers.erase(timers.begin() + 2); + + wait_timers(timers); + + std::vector< int > exp_items; + exp_items.push_back(1); + exp_items.push_back(3); + ATF_REQUIRE_EQ(exp_items, items); +} + + +ATF_TEST_CASE(multiprogram_and_expire_before_activations); +ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations) +{ + set_md_var("timeout", "20"); +} +ATF_TEST_CASE_BODY(multiprogram_and_expire_before_activations) +{ + std::vector< signals::timer* > timers; + std::vector< int > items; + + { + signals::interrupts_inhibiter inhibiter; + + // First timer with an activation in the future. + timers.push_back(new delayed_inserter( + datetime::delta(0, 100000), items, 1)); + ATF_REQUIRE(!timers[timers.size() - 1]->fired()); + + // Timer with an activation earlier than the previous one. + timers.push_back(new delayed_inserter( + datetime::delta(0, 50000), items, 2)); + ATF_REQUIRE(!timers[timers.size() - 1]->fired()); + + ::sleep(1); + + // Timer with an activation later than all others. + timers.push_back(new delayed_inserter( + datetime::delta(0, 200000), items, 3)); + + ::sleep(1); + } + + wait_timers(timers); + + std::vector< int > exp_items; + exp_items.push_back(2); + exp_items.push_back(1); + exp_items.push_back(3); + ATF_REQUIRE_EQ(exp_items, items); +} + + +ATF_TEST_CASE(expire_before_firing); +ATF_TEST_CASE_HEAD(expire_before_firing) +{ + set_md_var("timeout", "20"); +} +ATF_TEST_CASE_BODY(expire_before_firing) +{ + std::vector< int > items; + + // The code below causes a signal to go pending. Make sure we ignore it + // when we unblock signals. + signals::programmer sigalrm(SIGALRM, null_handler); + + { + signals::interrupts_inhibiter inhibiter; + + delayed_inserter* timer = new delayed_inserter( + datetime::delta(0, 1000), items, 1234); + ::sleep(1); + // Interrupts are inhibited so we never got a chance to execute the + // timer before it was destroyed. However, the handler should run + // regardless at some point, possibly during deletion. + timer->unprogram(); + delete timer; + } + + std::vector< int > exp_items; + exp_items.push_back(1234); + ATF_REQUIRE_EQ(exp_items, items); +} + + +ATF_TEST_CASE(reprogram_from_scratch); +ATF_TEST_CASE_HEAD(reprogram_from_scratch) +{ + set_md_var("timeout", "20"); +} +ATF_TEST_CASE_BODY(reprogram_from_scratch) +{ + std::vector< int > items; + + delayed_inserter* timer1 = new delayed_inserter( + datetime::delta(0, 100000), items, 1); + timer1->unprogram(); delete timer1; + + // All constructed timers are now dead, so the interval timer should have + // been reprogrammed. Let's start over. + + delayed_inserter* timer2 = new delayed_inserter( + datetime::delta(0, 200000), items, 2); + while (!timer2->fired()) + ::usleep(1000); + timer2->unprogram(); delete timer2; + + std::vector< int > exp_items; + exp_items.push_back(2); + ATF_REQUIRE_EQ(exp_items, items); +} + + +ATF_TEST_CASE(unprogram); +ATF_TEST_CASE_HEAD(unprogram) +{ + set_md_var("timeout", "10"); +} +ATF_TEST_CASE_BODY(unprogram) +{ + signals::timer timer(datetime::delta(0, 500000)); + timer.unprogram(); + usleep(500000); + ATF_REQUIRE(!timer.fired()); +} + + +ATF_TEST_CASE(infinitesimal); +ATF_TEST_CASE_HEAD(infinitesimal) +{ + set_md_var("descr", "Ensure that the ordering in which the signal, the " + "timer and the global state are programmed is correct; do so " + "by setting an extremely small delay for the timer hoping that " + "it can trigger such conditions"); + set_md_var("timeout", "10"); +} +ATF_TEST_CASE_BODY(infinitesimal) +{ + const std::size_t rounds = 100; + const std::size_t exp_good = 90; + + std::size_t good = 0; + for (std::size_t i = 0; i < rounds; i++) { + signals::timer timer(datetime::delta(0, 1)); + + // From the setitimer(2) documentation: + // + // Time values smaller than the resolution of the system clock are + // rounded up to this resolution (typically 10 milliseconds). + // + // We don't know what this resolution is but we must wait for longer + // than we programmed; do a rough guess and hope it is good. This may + // be obviously wrong and thus lead to mysterious test failures in some + // systems, hence why we only expect a percentage of successes below. + // Still, we can fail... + ::usleep(1000); + + if (timer.fired()) + ++good; + timer.unprogram(); + } + std::cout << F("Ran %s tests, %s passed; threshold is %s\n") + % rounds % good % exp_good; + ATF_REQUIRE(good >= exp_good); +} + + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, program_seconds); + ATF_ADD_TEST_CASE(tcs, program_useconds); + ATF_ADD_TEST_CASE(tcs, multiprogram_ordered); + ATF_ADD_TEST_CASE(tcs, multiprogram_reorder_next_activations); + ATF_ADD_TEST_CASE(tcs, multiprogram_and_cancel_some); + ATF_ADD_TEST_CASE(tcs, multiprogram_and_expire_before_activations); + ATF_ADD_TEST_CASE(tcs, expire_before_firing); + ATF_ADD_TEST_CASE(tcs, reprogram_from_scratch); + ATF_ADD_TEST_CASE(tcs, unprogram); + ATF_ADD_TEST_CASE(tcs, infinitesimal); +} |