| // Copyright 2017 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <object/dispatcher.h> |
| |
| #include <lib/unittest/unittest.h> |
| #include <object/state_observer.h> |
| |
| class TestDispatcher; |
| template <> struct CanaryTag<TestDispatcher> { |
| static constexpr uint32_t magic = 0; |
| }; |
| |
| class TestDispatcher final : public SoloDispatcher<TestDispatcher, ZX_RIGHTS_BASIC> { |
| public: |
| TestDispatcher() {} |
| ~TestDispatcher() final = default; |
| zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_NONE; } |
| |
| // Heler: Causes OnStateChange() to be called. |
| void CallUpdateState() { |
| UpdateState(0, 1); |
| } |
| |
| // Helper: Causes most On*() hooks (except for OnInitialized) to |
| // be called on all of |st|'s observers. |
| void CallAllOnHooks() { |
| UpdateState(0, 7); |
| Cancel(/* handle= */ nullptr); |
| CancelByKey(/* handle= */ nullptr, /* port= */ nullptr, /* key= */ 2u); |
| } |
| }; |
| |
| // Tests for observer removal |
| namespace removal { |
| |
| class RemovableObserver : public StateObserver { |
| public: |
| RemovableObserver() = default; |
| |
| // The number of times OnRemoved() has been called. |
| int removals() const { return removals_; } |
| |
| private: |
| // No-op overrides of pure virtuals. |
| Flags OnInitialize(zx_signals_t initial_state, |
| const StateObserver::CountInfo* cinfo) override { |
| return 0; |
| } |
| Flags OnStateChange(zx_signals_t new_state) override { return 0; } |
| Flags OnCancel(const Handle* handle) override { return 0; } |
| Flags OnCancelByKey(const Handle* handle, const void* port, uint64_t key) |
| override { return 0; } |
| |
| void OnRemoved() override { removals_++; } |
| |
| int removals_ = 0; |
| }; |
| |
| bool on_initialize() { |
| BEGIN_TEST; |
| |
| class RmOnInitialize : public RemovableObserver { |
| public: |
| Flags OnInitialize(zx_signals_t initial_state, |
| const StateObserver::CountInfo* cinfo) override { |
| return kNeedRemoval; |
| } |
| }; |
| |
| RmOnInitialize obs; |
| EXPECT_EQ(0, obs.removals(), ""); |
| |
| // Cause OnInitialize() to be called. |
| TestDispatcher st; |
| ASSERT_EQ(ZX_OK, st.AddObserver(&obs), ""); |
| |
| // Should have been removed. |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| // Further On hook calls should not re-remove. |
| st.CallAllOnHooks(); |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| END_TEST; |
| } |
| |
| class RmOnStateChange : public RemovableObserver { |
| public: |
| Flags OnStateChange(zx_signals_t new_state) override { |
| return kNeedRemoval; |
| } |
| }; |
| |
| bool on_state_change_via_update_state() { |
| BEGIN_TEST; |
| |
| RmOnStateChange obs; |
| EXPECT_EQ(0, obs.removals(), ""); |
| |
| TestDispatcher st; |
| ASSERT_EQ(ZX_OK, st.AddObserver(&obs), ""); |
| EXPECT_EQ(0, obs.removals(), ""); // Not removed yet. |
| |
| // Cause OnStateChange() to be called. |
| st.CallUpdateState(); |
| |
| // Should have been removed. |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| // Further On hook calls should not re-remove. |
| st.CallAllOnHooks(); |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| END_TEST; |
| } |
| |
| bool on_cancel() { |
| BEGIN_TEST; |
| |
| class RmOnCancel : public RemovableObserver { |
| public: |
| Flags OnCancel(const Handle* handle) override { |
| return kNeedRemoval; |
| } |
| }; |
| |
| RmOnCancel obs; |
| EXPECT_EQ(0, obs.removals(), ""); |
| |
| TestDispatcher st; |
| ASSERT_EQ(ZX_OK, st.AddObserver(&obs), ""); |
| EXPECT_EQ(0, obs.removals(), ""); // Not removed yet. |
| |
| // Cause OnCancel() to be called. |
| st.Cancel(/* handle= */ nullptr); |
| |
| // Should have been removed. |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| // Further On hook calls should not re-remove. |
| st.CallAllOnHooks(); |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| END_TEST; |
| } |
| |
| bool on_cancel_by_key() { |
| BEGIN_TEST; |
| |
| class RmOnCancelByKey : public RemovableObserver { |
| public: |
| Flags OnCancelByKey(const Handle* handle, const void* port, uint64_t key) |
| override { |
| return kNeedRemoval; |
| } |
| }; |
| |
| RmOnCancelByKey obs; |
| EXPECT_EQ(0, obs.removals(), ""); |
| |
| TestDispatcher st; |
| ASSERT_EQ(ZX_OK, st.AddObserver(&obs), ""); |
| EXPECT_EQ(0, obs.removals(), ""); // Not removed yet. |
| |
| // Cause OnCancelByKey() to be called. |
| st.CancelByKey(/* handle= */ nullptr, /* port= */ nullptr, /* key= */ 2u); |
| |
| // Should have been removed. |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| // Further On hook calls should not re-remove. |
| st.CallAllOnHooks(); |
| EXPECT_EQ(1, obs.removals(), ""); |
| |
| END_TEST; |
| } |
| |
| } // namespace removal |
| |
| #define ST_UNITTEST(fname) UNITTEST(#fname, fname) |
| |
| UNITTEST_START_TESTCASE(state_tracker_tests) |
| |
| ST_UNITTEST(removal::on_initialize) |
| ST_UNITTEST(removal::on_state_change_via_update_state) |
| ST_UNITTEST(removal::on_cancel) |
| ST_UNITTEST(removal::on_cancel_by_key) |
| |
| UNITTEST_END_TESTCASE( |
| state_tracker_tests, "statetracker", "StateTracker test"); |