Files
nearby/cpp/platform/impl/windows/executor_test.cc
T
2021-09-16 13:19:03 -07:00

372 lines
12 KiB
C++

// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "platform/impl/windows/executor.h"
#include <algorithm>
#include <utility>
#include "gtest/gtest.h"
TEST(ExecutorTests, SingleThreadedExecutorSucceeds) {
// Arrange
std::string expected("runnable 1");
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>();
std::string output = std::string();
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
threadIds->push_back(GetCurrentThreadId());
// Act
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 1");
});
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 2);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
// We should've run all runnables on the worker thread
ASSERT_EQ(output, expected);
}
TEST(ExecutorTests, SingleThreadedExecutorAfterShutdownFails) {
// Arrange
std::string expected("");
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>();
std::unique_ptr<std::string> output = std::make_unique<std::string>();
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
threadIds->push_back(GetCurrentThreadId());
executor->Shutdown();
// Act
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 1");
});
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 1);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
// We should've run all runnables on the worker thread
ASSERT_EQ(*output.get(), expected);
}
TEST(ExecutorTests, SingleThreadedExecutorExecuteNullSucceeds) {
// Arrange
std::string expected("runnable 1");
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>();
std::string output = std::string();
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
threadIds->push_back(GetCurrentThreadId());
// Act
executor->Execute(nullptr);
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 1");
});
executor->Execute(nullptr);
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 2);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
// We should've run all runnables on the worker thread
ASSERT_EQ(output, expected);
}
TEST(ExecutorTests, SingleThreadedExecutorMultipleTasksSucceeds) {
// Arrange
std::string expected(
"runnable 1, runnable 2, runnable 3, runnable 4, runnable 5");
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>();
std::string output = std::string();
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
threadIds->push_back(GetCurrentThreadId());
// Act
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 1, ");
});
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 2, ");
});
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 3, ");
});
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 4, ");
});
executor->Execute([&output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output.append("runnable 5");
});
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 6);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
// We should've run all runnables on the worker thread
auto workerThreadId = threadIds->at(1);
for (int index = 1; index < threadIds->size(); index++) {
ASSERT_EQ(threadIds->at(index), workerThreadId);
}
// We should of run them in the order submitted
ASSERT_EQ(output, expected);
}
TEST(ExecutorTests, MultiThreadedExecutorSingleTaskSucceeds) {
// Arrange
std::string expected("runnable 1");
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>(2);
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds->push_back(GetCurrentThreadId());
// Act
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 1");
});
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 2);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
// We should've run the task
ASSERT_EQ(*output.get(), expected);
}
TEST(ExecutorTests, MultiThreadedExecutorMultipleTasksSucceeds) {
// Arrange
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>(2);
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds->push_back(GetCurrentThreadId());
// Act
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 1, ");
});
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 2, ");
});
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 3, ");
});
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 4, ");
});
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 5");
});
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 6);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
}
TEST(ExecutorTests, MultiThreadedExecutorSingleTaskAfterShutdownFails) {
// Arrange
std::string expected("");
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>(2);
// Container to note threads that ran
std::unique_ptr<std::vector<DWORD>> threadIds =
std::make_unique<std::vector<DWORD>>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds->push_back(GetCurrentThreadId());
executor->Shutdown();
// Act
executor->Execute([output, &threadIds]() {
threadIds->push_back(GetCurrentThreadId());
output->append("runnable 1");
});
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 1);
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds->at(0));
// We should've run the task
ASSERT_EQ(*output.get(), expected);
}
TEST(ExecutorTests, MultiThreadedExecutorNegativeThreadsThrows) {
// Arrange
// Act
// Assert
EXPECT_THROW(
{
try {
auto result =
std::make_unique<location::nearby::windows::Executor>(-1);
} catch (const std::invalid_argument::exception& e) {
// and this tests that it has the correct message
EXPECT_STREQ("max_concurrency", e.what());
throw;
}
},
std::invalid_argument);
}
TEST(ExecutorTests, MultiThreadedExecutorTooManyThreadsThrows) {
// Arrange
// Act
// Assert
EXPECT_THROW(
{
try {
auto result =
std::make_unique<location::nearby::windows::Executor>(65);
} catch (const location::nearby::windows::ThreadPoolException& e) {
// and this tests that it has the correct message
EXPECT_STREQ("Thread pool max size exceeded.", e.what());
throw;
}
},
location::nearby::windows::ThreadPoolException);
}
TEST(ExecutorTests,
MultiThreadedExecutorMultipleTasksLargeNumberOfThreadsSucceeds) {
// Arrange
std::unique_ptr<location::nearby::windows::Executor> executor =
std::make_unique<location::nearby::windows::Executor>(
MAXIMUM_WAIT_OBJECTS - 1);
// Container to note threads that ran
std::vector<DWORD> threadIds = std::vector<DWORD>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds.push_back(GetCurrentThreadId());
CRITICAL_SECTION testCriticalSection;
InitializeCriticalSection(&testCriticalSection);
// Act
for (int index = 0; index < 250; index++) {
executor->Execute(
[output, &threadIds, index, &testCriticalSection]() mutable {
DWORD id = GetCurrentThreadId();
EnterCriticalSection(&testCriticalSection);
threadIds.push_back(id);
output->append("runnable ");
output->append(std::to_string(index));
output->append(", ");
LeaveCriticalSection(&testCriticalSection);
// Using rand since this is in a critical section
// and windows doesn't have a rand_r anyway
auto sleepTime = (std::rand() % 101) + 1; // NOLINT
Sleep(sleepTime);
});
}
executor->Shutdown();
DeleteCriticalSection(&testCriticalSection);
// Assert
// We should still be on the main thread
ASSERT_EQ(GetCurrentThreadId(), threadIds.at(0));
std::sort(threadIds.begin(), threadIds.end());
int64_t uniqueIds =
std::unique(threadIds.begin(), threadIds.end()) - threadIds.begin();
ASSERT_EQ(uniqueIds, 64);
// We should've run 1 time on the main thread, and 200 times on the
// workerThreads
ASSERT_EQ(threadIds.size(), 251);
}