diff options
author | Ben <benlawson@google.com> | 2023-03-15 21:20:36 +0000 |
---|---|---|
committer | CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-03-15 21:20:36 +0000 |
commit | 11436fd01d7622c539ce2a8deecc9039caf8b390 (patch) | |
tree | fb020e1658a4effa8fd36e5769a1c6baf9eeec63 | |
parent | 5b670fb60053a170375f76a46a088db6b827bdd1 (diff) | |
download | pigweed-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.gn | 1 | ||||
-rw-r--r-- | pw_async/BUILD.bazel | 1 | ||||
-rw-r--r-- | pw_async/BUILD.gn | 14 | ||||
-rw-r--r-- | pw_async/docs.rst | 32 | ||||
-rw-r--r-- | pw_async/fake_dispatcher_fixture.gni | 38 | ||||
-rw-r--r-- | pw_async/fake_dispatcher_test.gni | 2 | ||||
-rw-r--r-- | pw_async/public/pw_async/fake_dispatcher_fixture.h | 66 | ||||
-rw-r--r-- | pw_async_basic/BUILD.bazel | 1 | ||||
-rw-r--r-- | pw_async_basic/BUILD.gn | 12 | ||||
-rw-r--r-- | pw_async_basic/fake_dispatcher_fixture_test.cc | 36 |
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 |