2017-12-19 00:04:06 +00:00
|
|
|
/**
|
2016-02-11 19:48:58 +00:00
|
|
|
* Copyright (c) 2014-present, Facebook, Inc.
|
2014-12-18 18:50:47 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
2019-02-19 18:52:19 +00:00
|
|
|
* This source code is licensed in accordance with the terms specified in
|
|
|
|
* the LICENSE file found in the root directory of this source tree.
|
2014-12-18 18:50:47 +00:00
|
|
|
*/
|
2014-09-12 04:44:10 +00:00
|
|
|
|
2018-08-02 15:57:02 +00:00
|
|
|
#include <chrono>
|
|
|
|
|
2014-09-12 04:44:10 +00:00
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2016-03-21 22:27:51 +00:00
|
|
|
#include <osquery/dispatcher.h>
|
2019-01-14 11:31:17 +00:00
|
|
|
#include <osquery/utils/status/status.h>
|
2014-09-12 04:44:10 +00:00
|
|
|
|
|
|
|
namespace osquery {
|
|
|
|
|
2016-03-12 09:23:09 +00:00
|
|
|
class DispatcherTests : public testing::Test {
|
2017-10-25 02:55:05 +00:00
|
|
|
void TearDown() override {
|
|
|
|
Dispatcher::instance().resetStopping();
|
|
|
|
}
|
2016-03-12 09:23:09 +00:00
|
|
|
};
|
2014-09-12 04:44:10 +00:00
|
|
|
|
|
|
|
TEST_F(DispatcherTests, test_singleton) {
|
2015-05-04 03:02:01 +00:00
|
|
|
auto& one = Dispatcher::instance();
|
|
|
|
auto& two = Dispatcher::instance();
|
2016-03-12 09:23:09 +00:00
|
|
|
EXPECT_EQ(&one, &two);
|
2014-09-12 04:44:10 +00:00
|
|
|
}
|
|
|
|
|
2018-06-15 15:15:43 +00:00
|
|
|
class InternalTestableRunnable : public InternalRunnable {
|
2014-09-12 04:44:10 +00:00
|
|
|
public:
|
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") {}
|
2016-08-14 22:41:53 +00:00
|
|
|
|
|
|
|
virtual void start() override {
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
void reset() {
|
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t count() {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-09-21 17:52:52 +00:00
|
|
|
static std::atomic<size_t> i;
|
2014-09-12 04:44:10 +00:00
|
|
|
};
|
2016-08-14 22:41:53 +00:00
|
|
|
|
2016-09-21 17:52:52 +00:00
|
|
|
std::atomic<size_t> TestRunnable::i{0};
|
2016-08-14 22:41:53 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2018-06-15 15:15:43 +00:00
|
|
|
class BlockingTestRunnable : public InternalTestableRunnable {
|
2016-08-14 22:41:53 +00:00
|
|
|
public:
|
2018-06-15 15:15:43 +00:00
|
|
|
explicit BlockingTestRunnable()
|
|
|
|
: InternalTestableRunnable("BlockingTestRunnable") {}
|
2016-08-14 22:41:53 +00:00
|
|
|
|
|
|
|
virtual void start() override {
|
|
|
|
// Wow that's a long sleep!
|
2018-08-02 15:57:02 +00:00
|
|
|
pause(std::chrono::seconds(100));
|
2016-08-14 22:41:53 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-09-12 04:44:10 +00:00
|
|
|
}
|