Path: blob/main/contrib/kyua/utils/signals/timer_test.cpp
48199 views
// Copyright 2010 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/signals/timer.hpp"2930extern "C" {31#include <signal.h>32#include <unistd.h>33}3435#include <cstddef>36#include <iostream>37#include <vector>3839#include <atf-c++.hpp>4041#include "utils/datetime.hpp"42#include "utils/defs.hpp"43#include "utils/format/containers.ipp"44#include "utils/format/macros.hpp"45#include "utils/signals/interrupts.hpp"46#include "utils/signals/programmer.hpp"4748namespace datetime = utils::datetime;49namespace signals = utils::signals;505152namespace {535455/// A timer that inserts an element into a vector on activation.56class delayed_inserter : public signals::timer {57/// Vector into which to insert the element.58std::vector< int >& _destination;5960/// Element to insert into _destination on activation.61const int _item;6263/// Timer activation callback.64void65callback(void)66{67signals::interrupts_inhibiter inhibiter;68_destination.push_back(_item);69}7071public:72/// Constructor.73///74/// \param delta Time to the timer activation.75/// \param destination Vector into which to insert the element.76/// \param item Element to insert into destination on activation.77delayed_inserter(const datetime::delta& delta,78std::vector< int >& destination, const int item) :79signals::timer(delta), _destination(destination), _item(item)80{81}82};838485/// Signal handler that does nothing.86static void87null_handler(const int /* signo */)88{89}909192/// Waits for the activation of all given timers.93///94/// \param timers Pointers to all the timers to wait for.95static void96wait_timers(const std::vector< signals::timer* >& timers)97{98std::size_t n_fired, old_n_fired = 0;99do {100n_fired = 0;101for (std::vector< signals::timer* >::const_iterator102iter = timers.begin(); iter != timers.end(); ++iter) {103const signals::timer* timer = *iter;104if (timer->fired())105++n_fired;106}107if (old_n_fired < n_fired) {108std::cout << "Waiting; " << n_fired << " timers fired so far\n";109old_n_fired = n_fired;110}111::usleep(100);112} while (n_fired < timers.size());113}114115116} // anonymous namespace117118119ATF_TEST_CASE(program_seconds);120ATF_TEST_CASE_HEAD(program_seconds)121{122set_md_var("timeout", "10");123}124ATF_TEST_CASE_BODY(program_seconds)125{126signals::timer timer(datetime::delta(1, 0));127ATF_REQUIRE(!timer.fired());128while (!timer.fired())129::usleep(1000);130}131132133ATF_TEST_CASE(program_useconds);134ATF_TEST_CASE_HEAD(program_useconds)135{136set_md_var("timeout", "10");137}138ATF_TEST_CASE_BODY(program_useconds)139{140signals::timer timer(datetime::delta(0, 500000));141ATF_REQUIRE(!timer.fired());142while (!timer.fired())143::usleep(1000);144}145146147ATF_TEST_CASE(multiprogram_ordered);148ATF_TEST_CASE_HEAD(multiprogram_ordered)149{150set_md_var("timeout", "20");151}152ATF_TEST_CASE_BODY(multiprogram_ordered)153{154static const std::size_t n_timers = 100;155156std::vector< signals::timer* > timers;157std::vector< int > items, exp_items;158159const int initial_delay_ms = 1000000;160for (std::size_t i = 0; i < n_timers; ++i) {161exp_items.push_back(i);162163timers.push_back(new delayed_inserter(164datetime::delta(0, initial_delay_ms + (i + 1) * 10000),165items, i));166ATF_REQUIRE(!timers[i]->fired());167}168169wait_timers(timers);170171ATF_REQUIRE_EQ(exp_items, items);172}173174175ATF_TEST_CASE(multiprogram_reorder_next_activations);176ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations)177{178set_md_var("timeout", "20");179}180ATF_TEST_CASE_BODY(multiprogram_reorder_next_activations)181{182std::vector< signals::timer* > timers;183std::vector< int > items;184185// First timer with an activation in the future.186timers.push_back(new delayed_inserter(187datetime::delta(0, 100000), items, 1));188ATF_REQUIRE(!timers[timers.size() - 1]->fired());189190// Timer with an activation earlier than the previous one.191timers.push_back(new delayed_inserter(192datetime::delta(0, 50000), items, 2));193ATF_REQUIRE(!timers[timers.size() - 1]->fired());194195// Timer with an activation later than all others.196timers.push_back(new delayed_inserter(197datetime::delta(0, 200000), items, 3));198ATF_REQUIRE(!timers[timers.size() - 1]->fired());199200// Timer with an activation in between.201timers.push_back(new delayed_inserter(202datetime::delta(0, 150000), items, 4));203ATF_REQUIRE(!timers[timers.size() - 1]->fired());204205wait_timers(timers);206207std::vector< int > exp_items;208exp_items.push_back(2);209exp_items.push_back(1);210exp_items.push_back(4);211exp_items.push_back(3);212ATF_REQUIRE_EQ(exp_items, items);213}214215216ATF_TEST_CASE(multiprogram_and_cancel_some);217ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some)218{219set_md_var("timeout", "20");220}221ATF_TEST_CASE_BODY(multiprogram_and_cancel_some)222{223std::vector< signals::timer* > timers;224std::vector< int > items;225226// First timer with an activation in the future.227timers.push_back(new delayed_inserter(228datetime::delta(0, 100000), items, 1));229230// Timer with an activation earlier than the previous one.231timers.push_back(new delayed_inserter(232datetime::delta(0, 50000), items, 2));233234// Timer with an activation later than all others.235timers.push_back(new delayed_inserter(236datetime::delta(0, 200000), items, 3));237238// Timer with an activation in between.239timers.push_back(new delayed_inserter(240datetime::delta(0, 150000), items, 4));241242// Cancel the first timer to reprogram next activation.243timers[1]->unprogram(); delete timers[1]; timers.erase(timers.begin() + 1);244245// Cancel another timer without reprogramming next activation.246timers[2]->unprogram(); delete timers[2]; timers.erase(timers.begin() + 2);247248wait_timers(timers);249250std::vector< int > exp_items;251exp_items.push_back(1);252exp_items.push_back(3);253ATF_REQUIRE_EQ(exp_items, items);254}255256257ATF_TEST_CASE(multiprogram_and_expire_before_activations);258ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations)259{260set_md_var("timeout", "20");261}262ATF_TEST_CASE_BODY(multiprogram_and_expire_before_activations)263{264std::vector< signals::timer* > timers;265std::vector< int > items;266267{268signals::interrupts_inhibiter inhibiter;269270// First timer with an activation in the future.271timers.push_back(new delayed_inserter(272datetime::delta(0, 100000), items, 1));273ATF_REQUIRE(!timers[timers.size() - 1]->fired());274275// Timer with an activation earlier than the previous one.276timers.push_back(new delayed_inserter(277datetime::delta(0, 50000), items, 2));278ATF_REQUIRE(!timers[timers.size() - 1]->fired());279280::sleep(1);281282// Timer with an activation later than all others.283timers.push_back(new delayed_inserter(284datetime::delta(0, 200000), items, 3));285286::sleep(1);287}288289wait_timers(timers);290291std::vector< int > exp_items;292exp_items.push_back(2);293exp_items.push_back(1);294exp_items.push_back(3);295ATF_REQUIRE_EQ(exp_items, items);296}297298299ATF_TEST_CASE(expire_before_firing);300ATF_TEST_CASE_HEAD(expire_before_firing)301{302set_md_var("timeout", "20");303}304ATF_TEST_CASE_BODY(expire_before_firing)305{306std::vector< int > items;307308// The code below causes a signal to go pending. Make sure we ignore it309// when we unblock signals.310signals::programmer sigalrm(SIGALRM, null_handler);311312{313signals::interrupts_inhibiter inhibiter;314315delayed_inserter* timer = new delayed_inserter(316datetime::delta(0, 1000), items, 1234);317::sleep(1);318// Interrupts are inhibited so we never got a chance to execute the319// timer before it was destroyed. However, the handler should run320// regardless at some point, possibly during deletion.321timer->unprogram();322delete timer;323}324325std::vector< int > exp_items;326exp_items.push_back(1234);327ATF_REQUIRE_EQ(exp_items, items);328}329330331ATF_TEST_CASE(reprogram_from_scratch);332ATF_TEST_CASE_HEAD(reprogram_from_scratch)333{334set_md_var("timeout", "20");335}336ATF_TEST_CASE_BODY(reprogram_from_scratch)337{338std::vector< int > items;339340delayed_inserter* timer1 = new delayed_inserter(341datetime::delta(0, 100000), items, 1);342timer1->unprogram(); delete timer1;343344// All constructed timers are now dead, so the interval timer should have345// been reprogrammed. Let's start over.346347delayed_inserter* timer2 = new delayed_inserter(348datetime::delta(0, 200000), items, 2);349while (!timer2->fired())350::usleep(1000);351timer2->unprogram(); delete timer2;352353std::vector< int > exp_items;354exp_items.push_back(2);355ATF_REQUIRE_EQ(exp_items, items);356}357358359ATF_TEST_CASE(unprogram);360ATF_TEST_CASE_HEAD(unprogram)361{362set_md_var("timeout", "10");363}364ATF_TEST_CASE_BODY(unprogram)365{366signals::timer timer(datetime::delta(0, 500000));367timer.unprogram();368usleep(500000);369ATF_REQUIRE(!timer.fired());370}371372373ATF_TEST_CASE(infinitesimal);374ATF_TEST_CASE_HEAD(infinitesimal)375{376set_md_var("descr", "Ensure that the ordering in which the signal, the "377"timer and the global state are programmed is correct; do so "378"by setting an extremely small delay for the timer hoping that "379"it can trigger such conditions");380set_md_var("timeout", "10");381}382ATF_TEST_CASE_BODY(infinitesimal)383{384const std::size_t rounds = 100;385const std::size_t exp_good = 90;386387std::size_t good = 0;388for (std::size_t i = 0; i < rounds; i++) {389signals::timer timer(datetime::delta(0, 1));390391// From the setitimer(2) documentation:392//393// Time values smaller than the resolution of the system clock are394// rounded up to this resolution (typically 10 milliseconds).395//396// We don't know what this resolution is but we must wait for longer397// than we programmed; do a rough guess and hope it is good. This may398// be obviously wrong and thus lead to mysterious test failures in some399// systems, hence why we only expect a percentage of successes below.400// Still, we can fail...401::usleep(1000);402403if (timer.fired())404++good;405timer.unprogram();406}407std::cout << F("Ran %s tests, %s passed; threshold is %s\n")408% rounds % good % exp_good;409ATF_REQUIRE(good >= exp_good);410}411412413ATF_INIT_TEST_CASES(tcs)414{415ATF_ADD_TEST_CASE(tcs, program_seconds);416ATF_ADD_TEST_CASE(tcs, program_useconds);417ATF_ADD_TEST_CASE(tcs, multiprogram_ordered);418ATF_ADD_TEST_CASE(tcs, multiprogram_reorder_next_activations);419ATF_ADD_TEST_CASE(tcs, multiprogram_and_cancel_some);420ATF_ADD_TEST_CASE(tcs, multiprogram_and_expire_before_activations);421ATF_ADD_TEST_CASE(tcs, expire_before_firing);422ATF_ADD_TEST_CASE(tcs, reprogram_from_scratch);423ATF_ADD_TEST_CASE(tcs, unprogram);424ATF_ADD_TEST_CASE(tcs, infinitesimal);425}426427428