osquery-1/osquery/dispatcher/tests/dispatcher.cpp

147 lines
3.4 KiB
C++
Raw Normal View History

/**
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed in accordance with the terms specified in
* the LICENSE file found in the root directory of this source tree.
*/
2018-08-02 15:57:02 +00:00
#include <chrono>
#include <gtest/gtest.h>
2016-03-21 22:27:51 +00:00
#include <osquery/dispatcher.h>
#include <osquery/utils/status/status.h>
namespace osquery {
class DispatcherTests : public testing::Test {
void TearDown() override {
Dispatcher::instance().resetStopping();
}
};
TEST_F(DispatcherTests, test_singleton) {
2015-05-04 03:02:01 +00:00
auto& one = Dispatcher::instance();
auto& two = Dispatcher::instance();
EXPECT_EQ(&one, &two);
}
InterruptableRunnable RunnerInterruptPoint redesign (#4545) * InterruptableRunnable RunnerInterruptPoint redesign There were several inefficiencies in the old version of RunnerInterruptPoint and InterruptableRunnable. 1) RunnerInterruptPoint was throwing the exception when interrupted, however, the exception was always ignored. 2) InterruptableRunnable used the read-write lock, however only write lock was used. 3) InterruptableRunnable InterruptableRunnable, stored almost similar variable stop_, interrupted_. 4) std::atomic<bool> interrupted_ was used with locks, even though it was accessed by default safest access mode memory_order_seq_cst. So no additional cache invalidation was needed. 5) InterruptableRunnable contained code(in method interrupted() and variables bypass_check_, checked) just for testing. Which was slowing down method interrupted(). 6) Some more confusing things. notify_all was not needed, as only one thread could be waiting for the conditional variable. RunnerInterruptPoint:: pause(void) looks ambiguous and that's why was not used anywhere. I resolved all these problems by merging InterruptableRunnable and RunnerInterruptPoint into the InterruptableRunnable. 1) No use of the exception. 2) 4) Simple mutex, which is only used for pauseMilli. InterruptableRunnable::interrupted and InterruptableRunnable::interrupt function lock-free. 3) Single variable interrupted_. 5) Made InterruptableRunnable::interrupt virtual. Tests override interrupt to make things testable. 6) change to notify_one and removed pause without the specific time.
2018-06-15 15:15:43 +00:00
class InternalTestableRunnable : public InternalRunnable {
public:
InterruptableRunnable RunnerInterruptPoint redesign (#4545) * InterruptableRunnable RunnerInterruptPoint redesign There were several inefficiencies in the old version of RunnerInterruptPoint and InterruptableRunnable. 1) RunnerInterruptPoint was throwing the exception when interrupted, however, the exception was always ignored. 2) InterruptableRunnable used the read-write lock, however only write lock was used. 3) InterruptableRunnable InterruptableRunnable, stored almost similar variable stop_, interrupted_. 4) std::atomic<bool> interrupted_ was used with locks, even though it was accessed by default safest access mode memory_order_seq_cst. So no additional cache invalidation was needed. 5) InterruptableRunnable contained code(in method interrupted() and variables bypass_check_, checked) just for testing. Which was slowing down method interrupted(). 6) Some more confusing things. notify_all was not needed, as only one thread could be waiting for the conditional variable. RunnerInterruptPoint:: pause(void) looks ambiguous and that's why was not used anywhere. I resolved all these problems by merging InterruptableRunnable and RunnerInterruptPoint into the InterruptableRunnable. 1) No use of the exception. 2) 4) Simple mutex, which is only used for pauseMilli. InterruptableRunnable::interrupted and InterruptableRunnable::interrupt function lock-free. 3) Single variable interrupted_. 5) Made InterruptableRunnable::interrupt virtual. Tests override interrupt to make things testable. 6) change to notify_one and removed pause without the specific time.
2018-06-15 15:15:43 +00:00
InternalTestableRunnable(const std::string& name) : InternalRunnable(name) {}
bool interrupted() override {
// A small conditional to force-skip an interruption check, used in testing.
if (!checked_) {
checked_ = true;
return false;
} else {
return InternalRunnable::interrupted();
}
}
private:
/// Testing only, track the interruptible check for interruption.
bool checked_{false};
};
class TestRunnable : public InternalTestableRunnable {
public:
explicit TestRunnable() : InternalTestableRunnable("TestRunnable") {}
virtual void start() override {
++i;
}
void reset() {
i = 0;
}
size_t count() {
return i;
}
private:
static std::atomic<size_t> i;
};
std::atomic<size_t> TestRunnable::i{0};
TEST_F(DispatcherTests, test_service_count) {
auto runnable = std::make_shared<TestRunnable>();
auto service_count = Dispatcher::instance().serviceCount();
// The service exits after incrementing.
auto s = Dispatcher::addService(runnable);
EXPECT_TRUE(s);
// Wait for the service to stop.
Dispatcher::joinServices();
// Make sure the service is removed.
EXPECT_EQ(service_count, Dispatcher::instance().serviceCount());
}
TEST_F(DispatcherTests, test_run) {
auto runnable = std::make_shared<TestRunnable>();
runnable->reset();
// The service exits after incrementing.
Dispatcher::addService(runnable);
Dispatcher::joinServices();
EXPECT_EQ(1U, runnable->count());
EXPECT_TRUE(runnable->hasRun());
// This runnable cannot be executed again.
auto s = Dispatcher::addService(runnable);
EXPECT_FALSE(s);
Dispatcher::joinServices();
EXPECT_EQ(1U, runnable->count());
}
TEST_F(DispatcherTests, test_independent_run) {
// Nothing stops two instances of the same service from running.
auto r1 = std::make_shared<TestRunnable>();
auto r2 = std::make_shared<TestRunnable>();
r1->reset();
Dispatcher::addService(r1);
Dispatcher::addService(r2);
Dispatcher::joinServices();
EXPECT_EQ(2U, r1->count());
}
InterruptableRunnable RunnerInterruptPoint redesign (#4545) * InterruptableRunnable RunnerInterruptPoint redesign There were several inefficiencies in the old version of RunnerInterruptPoint and InterruptableRunnable. 1) RunnerInterruptPoint was throwing the exception when interrupted, however, the exception was always ignored. 2) InterruptableRunnable used the read-write lock, however only write lock was used. 3) InterruptableRunnable InterruptableRunnable, stored almost similar variable stop_, interrupted_. 4) std::atomic<bool> interrupted_ was used with locks, even though it was accessed by default safest access mode memory_order_seq_cst. So no additional cache invalidation was needed. 5) InterruptableRunnable contained code(in method interrupted() and variables bypass_check_, checked) just for testing. Which was slowing down method interrupted(). 6) Some more confusing things. notify_all was not needed, as only one thread could be waiting for the conditional variable. RunnerInterruptPoint:: pause(void) looks ambiguous and that's why was not used anywhere. I resolved all these problems by merging InterruptableRunnable and RunnerInterruptPoint into the InterruptableRunnable. 1) No use of the exception. 2) 4) Simple mutex, which is only used for pauseMilli. InterruptableRunnable::interrupted and InterruptableRunnable::interrupt function lock-free. 3) Single variable interrupted_. 5) Made InterruptableRunnable::interrupt virtual. Tests override interrupt to make things testable. 6) change to notify_one and removed pause without the specific time.
2018-06-15 15:15:43 +00:00
class BlockingTestRunnable : public InternalTestableRunnable {
public:
InterruptableRunnable RunnerInterruptPoint redesign (#4545) * InterruptableRunnable RunnerInterruptPoint redesign There were several inefficiencies in the old version of RunnerInterruptPoint and InterruptableRunnable. 1) RunnerInterruptPoint was throwing the exception when interrupted, however, the exception was always ignored. 2) InterruptableRunnable used the read-write lock, however only write lock was used. 3) InterruptableRunnable InterruptableRunnable, stored almost similar variable stop_, interrupted_. 4) std::atomic<bool> interrupted_ was used with locks, even though it was accessed by default safest access mode memory_order_seq_cst. So no additional cache invalidation was needed. 5) InterruptableRunnable contained code(in method interrupted() and variables bypass_check_, checked) just for testing. Which was slowing down method interrupted(). 6) Some more confusing things. notify_all was not needed, as only one thread could be waiting for the conditional variable. RunnerInterruptPoint:: pause(void) looks ambiguous and that's why was not used anywhere. I resolved all these problems by merging InterruptableRunnable and RunnerInterruptPoint into the InterruptableRunnable. 1) No use of the exception. 2) 4) Simple mutex, which is only used for pauseMilli. InterruptableRunnable::interrupted and InterruptableRunnable::interrupt function lock-free. 3) Single variable interrupted_. 5) Made InterruptableRunnable::interrupt virtual. Tests override interrupt to make things testable. 6) change to notify_one and removed pause without the specific time.
2018-06-15 15:15:43 +00:00
explicit BlockingTestRunnable()
: InternalTestableRunnable("BlockingTestRunnable") {}
virtual void start() override {
// Wow that's a long sleep!
2018-08-02 15:57:02 +00:00
pause(std::chrono::seconds(100));
}
};
TEST_F(DispatcherTests, test_interruption) {
auto r1 = std::make_shared<BlockingTestRunnable>();
Dispatcher::addService(r1);
// This service would normally wait for 100 seconds.
r1->interrupt();
Dispatcher::joinServices();
EXPECT_TRUE(r1->hasRun());
}
TEST_F(DispatcherTests, test_stop_dispatcher) {
Dispatcher::stopServices();
auto r1 = std::make_shared<TestRunnable>();
auto s = Dispatcher::addService(r1);
EXPECT_FALSE(s);
}
}