aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen <benlawson@google.com>2023-03-15 21:20:36 +0000
committerCQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-03-15 21:20:36 +0000
commit11436fd01d7622c539ce2a8deecc9039caf8b390 (patch)
treefb020e1658a4effa8fd36e5769a1c6baf9eeec63
parent5b670fb60053a170375f76a46a088db6b827bdd1 (diff)
downloadpigweed-11436fd01d7622c539ce2a8deecc9039caf8b390.tar.gz
pw_async: Create FakeDispatcherFixture
Create FakeDispatcherFixture, a simple wrapper around a FakeDispatcher that makes it more convenient to run the dispatcher in tests. Create fake_dispatcher_fixture gni template to facilitate writing unit tests against FakeDispatcherFixture with the pw_async_basic backend when no FakeDispatcher backend is configured. Change-Id: Ibabb387b0039a61211a423f0e72b6fb62b2bbad8 Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/130927 Commit-Queue: Ben Lawson <benlawson@google.com> Reviewed-by: Erik Gilling <konkers@google.com>
-rw-r--r--docs/BUILD.gn1
-rw-r--r--pw_async/BUILD.bazel1
-rw-r--r--pw_async/BUILD.gn14
-rw-r--r--pw_async/docs.rst32
-rw-r--r--pw_async/fake_dispatcher_fixture.gni38
-rw-r--r--pw_async/fake_dispatcher_test.gni2
-rw-r--r--pw_async/public/pw_async/fake_dispatcher_fixture.h66
-rw-r--r--pw_async_basic/BUILD.bazel1
-rw-r--r--pw_async_basic/BUILD.gn12
-rw-r--r--pw_async_basic/fake_dispatcher_fixture_test.cc36
10 files changed, 177 insertions, 26 deletions
diff --git a/docs/BUILD.gn b/docs/BUILD.gn
index c43470f80..eaf29be52 100644
--- a/docs/BUILD.gn
+++ b/docs/BUILD.gn
@@ -111,6 +111,7 @@ group("third_party_docs") {
_doxygen_input_files = [
# All sources with doxygen comment blocks.
"$dir_pw_async/public/pw_async/dispatcher.h",
+ "$dir_pw_async/public/pw_async/fake_dispatcher_fixture.h",
"$dir_pw_async/public/pw_async/task.h",
"$dir_pw_bluetooth/public/pw_bluetooth/gatt/client.h",
"$dir_pw_bluetooth/public/pw_bluetooth/gatt/server.h",
diff --git a/pw_async/BUILD.bazel b/pw_async/BUILD.bazel
index 7ec78637e..2ae446d6b 100644
--- a/pw_async/BUILD.bazel
+++ b/pw_async/BUILD.bazel
@@ -18,6 +18,7 @@ filegroup(
"fake_dispatcher_test.cc",
"public/pw_async/dispatcher.h",
"public/pw_async/fake_dispatcher.h",
+ "public/pw_async/fake_dispatcher_fixture.h",
"public/pw_async/internal/types.h",
"public/pw_async/task.h",
],
diff --git a/pw_async/BUILD.gn b/pw_async/BUILD.gn
index ab1846ff0..483b9b5e6 100644
--- a/pw_async/BUILD.gn
+++ b/pw_async/BUILD.gn
@@ -16,6 +16,7 @@ import("//build_overrides/pigweed.gni")
import("$dir_pw_async/async.gni")
import("$dir_pw_async/backend.gni")
+import("$dir_pw_async/fake_dispatcher_fixture.gni")
import("$dir_pw_async/fake_dispatcher_test.gni")
import("$dir_pw_build/facade.gni")
import("$dir_pw_build/target_types.gni")
@@ -68,6 +69,14 @@ pw_facade("fake_dispatcher") {
] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
}
+fake_dispatcher_fixture("fake_dispatcher_fixture") {
+ backend = ":fake_dispatcher"
+ visibility = [
+ ":*",
+ "$dir_pw_async_basic:*",
+ ] + pw_async_EXPERIMENTAL_MODULE_VISIBILITY
+}
+
pw_test_group("tests") {
}
@@ -77,6 +86,9 @@ pw_doc_group("docs") {
# Satisfy source_is_in_build_files presubmit step
pw_source_set("fake_dispatcher_test") {
- sources = [ "fake_dispatcher_test.cc" ]
+ sources = [
+ "fake_dispatcher_test.cc",
+ "public/pw_async/fake_dispatcher_fixture.h",
+ ]
visibility = []
}
diff --git a/pw_async/docs.rst b/pw_async/docs.rst
index 178066b43..a892be7f5 100644
--- a/pw_async/docs.rst
+++ b/pw_async/docs.rst
@@ -133,15 +133,9 @@ the current/main thread:
#include "pw_async_basic/dispatcher.h"
- BasicDispatcher dispatcher;
-
- void interrupt_handler() {
- dispatcher.PostTask([](pw::async::Context& ctx){
- // Handle interrupt
- });
- }
-
int main() {
+ BasicDispatcher dispatcher;
+
Task task([](pw::async::Context& ctx){
printf("hello world\n");
});
@@ -156,23 +150,12 @@ the current/main thread:
Fake Dispatcher
===============
To test async code, FakeDispatcher should be dependency injected in place of
-Dispatcher. Then, time should be driven in unit tests.
-
-.. code-block:: cpp
-
- TEST(Example) {
- FakeDispatcher dispatcher;
-
- MyClass obj(&dispatcher);
+Dispatcher. Then, time should be driven in unit tests using the ``Run*()``
+methods. For convenience, you can use the test fixture
+FakeDispatcherFixture.
- obj.ScheduleSomeTasks();
- dispatcher.RunUntilIdle();
- EXPECT_TRUE(some condition);
-
- obj.ScheduleTaskToRunIn30Seconds();
- dispatcher.RunFor(30s);
- EXPECT_TRUE(task ran);
- }
+.. doxygenclass:: pw::async::test::FakeDispatcherFixture
+ :members:
.. attention::
@@ -185,7 +168,6 @@ Dispatcher. Then, time should be driven in unit tests.
Roadmap
-------
- Stabilize Task cancellation API
-- Create test fixture for FakeDispatcher
- Utility for dynamically allocated Tasks
- Bazel support
- CMake support
diff --git a/pw_async/fake_dispatcher_fixture.gni b/pw_async/fake_dispatcher_fixture.gni
new file mode 100644
index 000000000..a58be16ac
--- /dev/null
+++ b/pw_async/fake_dispatcher_fixture.gni
@@ -0,0 +1,38 @@
+# Copyright 2023 The Pigweed Authors
+#
+# 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.
+
+import("//build_overrides/pigweed.gni")
+import("$dir_pw_chrono/backend.gni")
+import("$dir_pw_sync/backend.gni")
+import("$dir_pw_thread/backend.gni")
+import("$dir_pw_unit_test/test.gni")
+
+# Creates a pw_source_set that provides a concrete FakeDispatcherFixture.
+#
+# Parameters
+#
+# backend (required)
+# [target] The FakeDispatcher backend to use.
+template("fake_dispatcher_fixture") {
+ assert(defined(invoker.backend))
+
+ pw_source_set(target_name) {
+ public = [ "$dir_pw_async/public/pw_async/fake_dispatcher_fixture.h" ]
+ public_deps = [
+ "$dir_pw_unit_test",
+ invoker.backend,
+ ]
+ forward_variables_from(invoker, [ "visibility" ])
+ }
+}
diff --git a/pw_async/fake_dispatcher_test.gni b/pw_async/fake_dispatcher_test.gni
index c55e849f1..33a32ee33 100644
--- a/pw_async/fake_dispatcher_test.gni
+++ b/pw_async/fake_dispatcher_test.gni
@@ -26,6 +26,8 @@ import("$dir_pw_unit_test/test.gni")
# backend (required)
# [target] The FakeDispatcher backend to test.
template("fake_dispatcher_test") {
+ assert(defined(invoker.backend))
+
pw_test(target_name) {
enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != "" &&
pw_sync_TIMED_THREAD_NOTIFICATION_BACKEND != "" &&
diff --git a/pw_async/public/pw_async/fake_dispatcher_fixture.h b/pw_async/public/pw_async/fake_dispatcher_fixture.h
new file mode 100644
index 000000000..b32efeb92
--- /dev/null
+++ b/pw_async/public/pw_async/fake_dispatcher_fixture.h
@@ -0,0 +1,66 @@
+// Copyright 2023 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "gtest/gtest.h"
+#include "pw_async/fake_dispatcher.h"
+
+namespace pw::async::test {
+
+/// Test fixture that is a simple wrapper around a FakeDispatcher.
+///
+/// Example:
+/// @code{.cpp}
+/// using ExampleTest = pw::async::test::FakeDispatcherFixture;
+///
+/// TEST_F(ExampleTest, Example) {
+/// MyClass obj(dispatcher());
+///
+/// obj.ScheduleSomeTasks();
+/// RunUntilIdle();
+/// EXPECT_TRUE(some condition);
+///
+/// obj.ScheduleTaskToRunIn30Seconds();
+/// RunFor(30s);
+/// EXPECT_TRUE(task ran);
+/// }
+/// @endcode
+class FakeDispatcherFixture : public ::testing::Test {
+ public:
+ /// Returns the FakeDispatcher that should be used for dependency injection.
+ FakeDispatcher& dispatcher() { return dispatcher_; }
+
+ /// Returns the current fake time.
+ chrono::SystemClock::time_point now() { return dispatcher_.now(); }
+
+ /// Dispatches all tasks with due times up until `now()`.
+ void RunUntilIdle() { dispatcher_.RunUntilIdle(); }
+
+ /// Dispatches all tasks with due times up to `end_time`, progressively
+ /// advancing the fake clock.
+ void RunUntil(chrono::SystemClock::time_point end_time) {
+ dispatcher_.RunUntil(end_time);
+ }
+
+ /// Dispatches all tasks with due times up to `now() + duration`,
+ /// progressively advancing the fake clock.
+ void RunFor(chrono::SystemClock::duration duration) {
+ dispatcher_.RunFor(duration);
+ }
+
+ private:
+ FakeDispatcher dispatcher_;
+};
+
+} // namespace pw::async::test
diff --git a/pw_async_basic/BUILD.bazel b/pw_async_basic/BUILD.bazel
index 214299ff6..8a1fb3aca 100644
--- a/pw_async_basic/BUILD.bazel
+++ b/pw_async_basic/BUILD.bazel
@@ -18,6 +18,7 @@ filegroup(
"dispatcher.cc",
"dispatcher_test.cc",
"fake_dispatcher.cc",
+ "fake_dispatcher_fixture_test.cc",
"public/pw_async_basic/dispatcher.h",
"public/pw_async_basic/fake_dispatcher.h",
"public/pw_async_basic/task.h",
diff --git a/pw_async_basic/BUILD.gn b/pw_async_basic/BUILD.gn
index e228eddef..9ccce5e85 100644
--- a/pw_async_basic/BUILD.gn
+++ b/pw_async_basic/BUILD.gn
@@ -15,6 +15,7 @@
import("//build_overrides/pigweed.gni")
import("$dir_pw_async/async.gni")
+import("$dir_pw_async/fake_dispatcher_fixture.gni")
import("$dir_pw_async/fake_dispatcher_test.gni")
import("$dir_pw_bloat/bloat.gni")
import("$dir_pw_build/target_types.gni")
@@ -81,6 +82,16 @@ fake_dispatcher_test("fake_dispatcher_test") {
backend = ":fake_dispatcher"
}
+fake_dispatcher_fixture("fake_dispatcher_fixture") {
+ backend = ":fake_dispatcher"
+}
+
+pw_test("fake_dispatcher_fixture_test") {
+ enable_if = pw_chrono_SYSTEM_CLOCK_BACKEND != ""
+ sources = [ "fake_dispatcher_fixture_test.cc" ]
+ deps = [ ":fake_dispatcher_fixture" ]
+}
+
pw_source_set("dispatcher") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_async_basic/dispatcher.h" ]
@@ -115,6 +126,7 @@ pw_test_group("tests") {
tests = [
":dispatcher_test",
":fake_dispatcher_test",
+ ":fake_dispatcher_fixture_test",
]
}
diff --git a/pw_async_basic/fake_dispatcher_fixture_test.cc b/pw_async_basic/fake_dispatcher_fixture_test.cc
new file mode 100644
index 000000000..e7835bd96
--- /dev/null
+++ b/pw_async_basic/fake_dispatcher_fixture_test.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 The Pigweed Authors
+//
+// 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 "pw_async/fake_dispatcher_fixture.h"
+
+#include "gtest/gtest.h"
+
+namespace pw::async {
+namespace {
+
+using FakeDispatcherFixture = test::FakeDispatcherFixture;
+
+TEST_F(FakeDispatcherFixture, PostTasks) {
+ int count = 0;
+ auto inc_count = [&count](Context& /*c*/, Status /*status*/) { ++count; };
+
+ Task task(inc_count);
+ dispatcher().PostTask(task);
+
+ ASSERT_EQ(count, 0);
+ RunUntilIdle();
+ ASSERT_EQ(count, 1);
+}
+
+} // namespace
+} // namespace pw::async