зеркало из https://github.com/microsoft/napajs.git
Merged PR 316411: enable unit tests in npm
enable unit tests in npm
This commit is contained in:
Родитель
ac94741fe2
Коммит
a665942332
|
@ -5,6 +5,8 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
|
||||
#define UNUSED(var) (void)(var)
|
||||
|
||||
/// <summary> The maximum string length of a single log call. Anything over will be truncated. </summary>
|
||||
const size_t LOG_MAX_SIZE = 512;
|
||||
|
||||
|
@ -38,7 +40,12 @@ inline void LogFormattedMessage(
|
|||
|
||||
#else
|
||||
|
||||
#define LOG(section, level, traceId, format, ...)
|
||||
#define LOG(section, level, traceId, format, ...) \
|
||||
UNUSED(section); \
|
||||
UNUSED(level); \
|
||||
UNUSED(traceId); \
|
||||
UNUSED(format); \
|
||||
UNUSED(__VA_ARGS__);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"install": "cmake-js compile",
|
||||
"prepublish": "tsc -p lib",
|
||||
"pretest": "tsc -p test",
|
||||
"test": "mocha test --recursive"
|
||||
"test": "mocha test --recursive",
|
||||
"unittest": "cmake-js compile -d unittest && node unittest/run.js"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace string {
|
|||
if (len != 0 || !compress) {
|
||||
list.push_back(str.substr(start, len));
|
||||
}
|
||||
start = i;
|
||||
start = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ namespace string {
|
|||
|
||||
/// <summary> Return a trimmed copy of the string. </summary>
|
||||
inline std::string TrimCopy(const std::string &str) {
|
||||
std::string copy;
|
||||
std::string copy = str;
|
||||
Trim(copy);
|
||||
return copy;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
|
||||
|
||||
project("napa-unittest")
|
||||
|
||||
set(NAPA_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
|
||||
# Test Files
|
||||
file(GLOB_RECURSE TEST_FILES
|
||||
main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/settings/*.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/zone/*.cpp)
|
||||
|
||||
# Source files under test
|
||||
file(GLOB_RECURSE SOURCE_FILES
|
||||
${NAPA_ROOT}/src/settings/settings-parser.cpp
|
||||
${NAPA_ROOT}/src/zone/simple-thread-pool.cpp)
|
||||
|
||||
# The target name
|
||||
set(TARGET_NAME ${PROJECT_NAME})
|
||||
|
||||
# The generated test executable
|
||||
add_executable(${TARGET_NAME} ${TEST_FILES} ${SOURCE_FILES})
|
||||
|
||||
# Compiler definitions
|
||||
target_compile_definitions(${TARGET_NAME} PRIVATE NAPA_LOG_DISABLED)
|
||||
|
||||
# Include directories
|
||||
target_include_directories(${TARGET_NAME}
|
||||
PRIVATE
|
||||
${NAPA_ROOT}/inc
|
||||
${NAPA_ROOT}/src
|
||||
${NAPA_ROOT}/third-party)
|
||||
|
||||
# Set output directory for dll/libs
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/bin
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/bin
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch/catch.hpp>
|
|
@ -0,0 +1,4 @@
|
|||
var path = require('path');
|
||||
var childProcess = require('child_process');
|
||||
|
||||
childProcess.execFileSync(path.join(__dirname, 'bin/napa-unittest.exe'), [], { stdio: 'inherit' });
|
|
@ -0,0 +1,66 @@
|
|||
#include <catch/catch.hpp>
|
||||
|
||||
#include <settings/settings-parser.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace napa;
|
||||
|
||||
TEST_CASE("Parsing nothing doesn't fail", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
|
||||
REQUIRE(settings::ParseFromString("", settings));
|
||||
REQUIRE(settings::ParseFromConsole(0, nullptr, settings));
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing from string", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
settings.loggingProvider = "";
|
||||
|
||||
REQUIRE(settings::ParseFromString("--loggingProvider myProvider", settings));
|
||||
REQUIRE(settings.loggingProvider == "myProvider");
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing from console", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
settings.loggingProvider = "";
|
||||
|
||||
std::vector<char*> args = { "--loggingProvider", "myProvider" };
|
||||
|
||||
REQUIRE(settings::ParseFromConsole(static_cast<int>(args.size()), args.data(), settings));
|
||||
REQUIRE(settings.loggingProvider == "myProvider");
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing non existing setting fails", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
|
||||
REQUIRE(settings::ParseFromString("--thisSettingDoesNotExist noValue", settings) == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing does not change defaults if setting is not provided", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
settings.metricProvider = "myMetricProvider";
|
||||
|
||||
REQUIRE(settings::ParseFromString("--loggingProvider myProvider", settings));
|
||||
REQUIRE(settings.metricProvider == "myMetricProvider");
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing with extra white spaces succeeds", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
settings.loggingProvider = "";
|
||||
|
||||
REQUIRE(settings::ParseFromString(" --loggingProvider \t myProvider \t\t ", settings));
|
||||
REQUIRE(settings.loggingProvider == "myProvider");
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing with empty string succeeds", "[settings-parser]") {
|
||||
settings::PlatformSettings settings;
|
||||
|
||||
REQUIRE(settings::ParseFromString("", settings) == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Parsing with different value type fails", "[settings-parser]") {
|
||||
settings::ZoneSettings settings;
|
||||
|
||||
REQUIRE(settings::ParseFromString("--workers five", settings) == false);
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
#include <catch/catch.hpp>
|
||||
#include <zone/scheduler.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
|
||||
using namespace napa;
|
||||
using namespace napa::zone;
|
||||
using namespace napa::settings;
|
||||
|
||||
class TestTask : public Task {
|
||||
public:
|
||||
TestTask(std::function<void()> callback = []() {}) :
|
||||
numberOfExecutions(0),
|
||||
lastExecutedWorkerId(99),
|
||||
_callback(std::move(callback)) {}
|
||||
|
||||
void SetCurrentWorkerId(WorkerId id) {
|
||||
lastExecutedWorkerId = id;
|
||||
}
|
||||
|
||||
virtual void Execute() override
|
||||
{
|
||||
numberOfExecutions++;
|
||||
_callback();
|
||||
}
|
||||
|
||||
std::atomic<uint32_t> numberOfExecutions;
|
||||
std::atomic<WorkerId> lastExecutedWorkerId;
|
||||
|
||||
private:
|
||||
std::function<void()> _callback;
|
||||
};
|
||||
|
||||
|
||||
template <uint32_t I>
|
||||
class TestWorker {
|
||||
public:
|
||||
|
||||
TestWorker(WorkerId id,
|
||||
const ZoneSettings &settings,
|
||||
std::function<void(WorkerId)> idleCallback) : _id(id) {
|
||||
numberOfWorkers++;
|
||||
_idleNotificationCallback = idleCallback;
|
||||
}
|
||||
|
||||
~TestWorker() {
|
||||
for (auto& fut : _futures) {
|
||||
fut.get();
|
||||
}
|
||||
}
|
||||
|
||||
void Schedule(std::shared_ptr<Task> task) {
|
||||
auto testTask = std::dynamic_pointer_cast<TestTask>(task);
|
||||
testTask->SetCurrentWorkerId(_id);
|
||||
|
||||
_futures.emplace_back(std::async(std::launch::async, [this, task]() {
|
||||
task->Execute();
|
||||
_idleNotificationCallback(_id);
|
||||
}));
|
||||
}
|
||||
|
||||
static uint32_t numberOfWorkers;
|
||||
|
||||
private:
|
||||
WorkerId _id;
|
||||
std::vector<std::shared_future<void>> _futures;
|
||||
std::function<void(WorkerId)> _idleNotificationCallback;
|
||||
};
|
||||
|
||||
template <uint32_t I>
|
||||
uint32_t TestWorker<I>::numberOfWorkers = 0;
|
||||
|
||||
|
||||
TEST_CASE("scheduler creates correct number of worker", "[scheduler]") {
|
||||
ZoneSettings settings;
|
||||
settings.workers = 3;
|
||||
|
||||
auto scheduler = std::make_unique<SchedulerImpl<TestWorker<1>>>(settings);
|
||||
|
||||
REQUIRE(TestWorker<1>::numberOfWorkers == settings.workers);
|
||||
}
|
||||
|
||||
TEST_CASE("scheduler assigns tasks correctly", "[scheduler]") {
|
||||
ZoneSettings settings;
|
||||
settings.workers = 3;
|
||||
|
||||
auto scheduler = std::make_unique<SchedulerImpl<TestWorker<2>>>(settings);
|
||||
auto task = std::make_shared<TestTask>();
|
||||
|
||||
SECTION("schedules on exactly one worker") {
|
||||
scheduler->Schedule(task);
|
||||
scheduler = nullptr; // force draining all scheduled tasks
|
||||
|
||||
REQUIRE(task->numberOfExecutions == 1);
|
||||
}
|
||||
|
||||
SECTION("schedule on a specific worker") {
|
||||
scheduler->ScheduleOnWorker(2, task);
|
||||
scheduler = nullptr; // force draining all scheduled tasks
|
||||
|
||||
REQUIRE(task->numberOfExecutions == 1);
|
||||
REQUIRE(task->lastExecutedWorkerId == 2);
|
||||
}
|
||||
|
||||
SECTION("schedule on all workers") {
|
||||
scheduler->ScheduleOnAllWorkers(task);
|
||||
scheduler = nullptr; // force draining all scheduled tasks
|
||||
|
||||
REQUIRE(task->numberOfExecutions == settings.workers);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("scheduler distributes and schedules all tasks", "[scheduler]") {
|
||||
ZoneSettings settings;
|
||||
settings.workers = 4;
|
||||
|
||||
auto scheduler = std::make_unique<SchedulerImpl<TestWorker<3>>>(settings);
|
||||
|
||||
std::vector<std::shared_ptr<TestTask>> tasks;
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
auto task = std::make_shared<TestTask>();
|
||||
tasks.push_back(task);
|
||||
scheduler->Schedule(task);
|
||||
}
|
||||
|
||||
scheduler = nullptr; // force draining all scheduled tasks
|
||||
|
||||
std::vector<bool> scheduledWorkersFlags = { false, false, false, false };
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
// Make sure that each task was executed once
|
||||
REQUIRE(tasks[i]->numberOfExecutions == 1);
|
||||
scheduledWorkersFlags[tasks[i]->lastExecutedWorkerId] = true;
|
||||
}
|
||||
|
||||
// Make sure that all workers were participating
|
||||
for (auto flag: scheduledWorkersFlags) {
|
||||
REQUIRE(flag);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче