Files
platform-external-webrtc/test/single_threaded_task_queue_unittest.cc
Danil Chapovalov 71037a8e99 Implement TaskQueueBase interface by SingleThreadedTaskQueueForTesting
that allows to use SingleThreadedTaskQueueForTesting as regular TaskQueue.
which allows components that currently depend on SingleThreadedTaskQueueForTesting
to depend on TaskQueueBase interface instead.
Those updates can be done one-by-one and in the end would allow to stop
using SingleThreadedTaskQueueForTesting in favor of other TaskQueue implementations.

Bug: webrtc:10933
Change-Id: I3e642c88c968012588b9d9c09918340f37bbedbd
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/154352
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Elad Alon <eladalon@webrtc.org>
Reviewed-by: Yves Gerey <yvesg@google.com>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29307}
2019-09-25 15:58:17 +00:00

375 lines
12 KiB
C++

/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "test/single_threaded_task_queue.h"
#include <atomic>
#include <memory>
#include <vector>
#include "api/task_queue/task_queue_test.h"
#include "rtc_base/event.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
using TaskId = DEPRECATED_SingleThreadedTaskQueueForTesting::TaskId;
// Test should not rely on the object under test not being faulty. If the task
// queue ever blocks forever, we want the tests to fail, rather than hang.
constexpr int kMaxWaitTimeMs = 10000;
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
SanityConstructionDestruction) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest, ExecutesPostedTasks) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> executed(false);
rtc::Event done;
task_queue.PostTask([&executed, &done]() {
executed.store(true);
done.Set();
});
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
EXPECT_TRUE(executed.load());
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
PostMultipleTasksFromSameExternalThread) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
constexpr size_t kCount = 3;
std::atomic<bool> executed[kCount];
for (std::atomic<bool>& exec : executed) {
exec.store(false);
}
std::vector<std::unique_ptr<rtc::Event>> done_events;
for (size_t i = 0; i < kCount; i++) {
done_events.emplace_back(std::make_unique<rtc::Event>());
}
// To avoid the tasks which comprise the actual test from running before they
// have all be posted, which could result in only one task ever being in the
// queue at any given time, post one waiting task that would block the
// task-queue, and unblock only after all tasks have been posted.
rtc::Event rendezvous;
task_queue.PostTask(
[&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
// Post the tasks which comprise the test.
for (size_t i = 0; i < kCount; i++) {
task_queue.PostTask([&executed, &done_events, i]() { // |i| by value.
executed[i].store(true);
done_events[i]->Set();
});
}
rendezvous.Set(); // Release the task-queue.
// Wait until the task queue has executed all the tasks.
for (size_t i = 0; i < kCount; i++) {
ASSERT_TRUE(done_events[i]->Wait(kMaxWaitTimeMs));
}
for (size_t i = 0; i < kCount; i++) {
EXPECT_TRUE(executed[i].load());
}
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
PostToTaskQueueFromOwnThread) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> executed(false);
rtc::Event done;
auto internally_posted_task = [&executed, &done]() {
executed.store(true);
done.Set();
};
auto externally_posted_task = [&task_queue, &internally_posted_task]() {
task_queue.PostTask(internally_posted_task);
};
task_queue.PostTask(externally_posted_task);
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
EXPECT_TRUE(executed.load());
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
TasksExecutedInSequence) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
// The first task would perform:
// accumulator = 10 * accumulator + i
// Where |i| is 1, 2 and 3 for the 1st, 2nd and 3rd tasks, respectively.
// The result would be 123 if and only iff the tasks were executed in order.
size_t accumulator = 0;
size_t expected_value = 0; // Updates to the correct value.
// Prevent the chain from being set in motion before we've had time to
// schedule it all, lest the queue only contain one task at a time.
rtc::Event rendezvous;
task_queue.PostTask(
[&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
for (size_t i = 0; i < 3; i++) {
task_queue.PostTask([&accumulator, i]() { // |i| passed by value.
accumulator = 10 * accumulator + i;
});
expected_value = 10 * expected_value + i;
}
// The test will wait for the task-queue to finish.
rtc::Event done;
task_queue.PostTask([&done]() { done.Set(); });
rendezvous.Set(); // Set the chain in motion.
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
EXPECT_EQ(accumulator, expected_value);
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
ExecutesPostedDelayedTask) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> executed(false);
rtc::Event done;
constexpr int64_t delay_ms = 20;
static_assert(delay_ms < kMaxWaitTimeMs / 2, "Delay too long for tests.");
task_queue.PostDelayedTask(
[&executed, &done]() {
executed.store(true);
done.Set();
},
delay_ms);
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
EXPECT_TRUE(executed.load());
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
DoesNotExecuteDelayedTaskTooSoon) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> executed(false);
constexpr int64_t delay_ms = 2000;
static_assert(delay_ms < kMaxWaitTimeMs / 2, "Delay too long for tests.");
task_queue.PostDelayedTask([&executed]() { executed.store(true); }, delay_ms);
// Wait less than is enough, make sure the task was not yet executed.
rtc::Event not_done;
ASSERT_FALSE(not_done.Wait(delay_ms / 2));
EXPECT_FALSE(executed.load());
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
TaskWithLesserDelayPostedAfterFirstDelayedTaskExectuedBeforeFirst) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> earlier_executed(false);
constexpr int64_t earlier_delay_ms = 500;
std::atomic<bool> later_executed(false);
constexpr int64_t later_delay_ms = 1000;
static_assert(earlier_delay_ms + later_delay_ms < kMaxWaitTimeMs / 2,
"Delay too long for tests.");
rtc::Event done;
auto earlier_task = [&earlier_executed, &later_executed]() {
EXPECT_FALSE(later_executed.load());
earlier_executed.store(true);
};
auto later_task = [&earlier_executed, &later_executed, &done]() {
EXPECT_TRUE(earlier_executed.load());
later_executed.store(true);
done.Set();
};
task_queue.PostDelayedTask(later_task, later_delay_ms);
task_queue.PostDelayedTask(earlier_task, earlier_delay_ms);
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
ASSERT_TRUE(earlier_executed);
ASSERT_TRUE(later_executed);
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
TaskWithGreaterDelayPostedAfterFirstDelayedTaskExectuedAfterFirst) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> earlier_executed(false);
constexpr int64_t earlier_delay_ms = 500;
std::atomic<bool> later_executed(false);
constexpr int64_t later_delay_ms = 1000;
static_assert(earlier_delay_ms + later_delay_ms < kMaxWaitTimeMs / 2,
"Delay too long for tests.");
rtc::Event done;
auto earlier_task = [&earlier_executed, &later_executed]() {
EXPECT_FALSE(later_executed.load());
earlier_executed.store(true);
};
auto later_task = [&earlier_executed, &later_executed, &done]() {
EXPECT_TRUE(earlier_executed.load());
later_executed.store(true);
done.Set();
};
task_queue.PostDelayedTask(earlier_task, earlier_delay_ms);
task_queue.PostDelayedTask(later_task, later_delay_ms);
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
ASSERT_TRUE(earlier_executed);
ASSERT_TRUE(later_executed);
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
ExternalThreadCancelsTask) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
rtc::Event done;
// Prevent the to-be-cancelled task from being executed before we've had
// time to cancel it.
rtc::Event rendezvous;
task_queue.PostTask(
[&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
TaskId cancelled_task_id = task_queue.PostTask([]() { EXPECT_TRUE(false); });
task_queue.PostTask([&done]() { done.Set(); });
task_queue.CancelTask(cancelled_task_id);
// Set the tasks in motion; the cancelled task does not run (otherwise the
// test would fail). The last task ends the test, showing that the queue
// progressed beyond the cancelled task.
rendezvous.Set();
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
}
// In this test, we'll set off a chain where the first task cancels the second
// task, then a third task runs (showing that we really cancelled the task,
// rather than just halted the task-queue).
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
InternalThreadCancelsTask) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
rtc::Event done;
// Prevent the chain from being set-off before we've set everything up.
rtc::Event rendezvous;
task_queue.PostTask(
[&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
// This is the canceller-task. It takes cancelled_task_id by reference,
// because the ID will only become known after the cancelled task is
// scheduled.
TaskId cancelled_task_id;
auto canceller_task = [&task_queue, &cancelled_task_id]() {
task_queue.CancelTask(cancelled_task_id);
};
task_queue.PostTask(canceller_task);
// This task will be cancelled by the task before it.
auto cancelled_task = []() { EXPECT_TRUE(false); };
cancelled_task_id = task_queue.PostTask(cancelled_task);
// When this task runs, it will allow the test to be finished.
auto completion_marker_task = [&done]() { done.Set(); };
task_queue.PostTask(completion_marker_task);
rendezvous.Set(); // Set the chain in motion.
ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest, SendTask) {
DEPRECATED_SingleThreadedTaskQueueForTesting task_queue("task_queue");
std::atomic<bool> executed(false);
task_queue.SendTask([&executed]() {
// Intentionally delay, so that if SendTask didn't block, the sender thread
// would have time to read |executed|.
rtc::Event delay;
ASSERT_FALSE(delay.Wait(1000));
executed.store(true);
});
EXPECT_TRUE(executed);
}
TEST(DEPRECATED_SingleThreadedTaskQueueForTestingTest,
DestructTaskQueueWhileTasksPending) {
auto task_queue =
std::make_unique<DEPRECATED_SingleThreadedTaskQueueForTesting>(
"task_queue");
std::atomic<size_t> counter(0);
constexpr size_t tasks = 10;
for (size_t i = 0; i < tasks; i++) {
task_queue->PostTask([&counter]() {
std::atomic_fetch_add(&counter, static_cast<size_t>(1));
rtc::Event delay;
ASSERT_FALSE(delay.Wait(500));
});
}
task_queue.reset();
EXPECT_LT(counter, tasks);
}
class SingleThreadedTaskQueueForTestingFactory : public TaskQueueFactory {
public:
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
absl::string_view /* name */,
Priority /*priority*/) const override {
return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>(
new DEPRECATED_SingleThreadedTaskQueueForTesting("noname"));
}
};
INSTANTIATE_TEST_SUITE_P(
DeprecatedSingleThreadedTaskQueueForTesting,
TaskQueueTest,
::testing::Values(
std::make_unique<SingleThreadedTaskQueueForTestingFactory>));
} // namespace
} // namespace test
} // namespace webrtc