1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <object/dispatcher.h>
8
9#include <lib/unittest/unittest.h>
10#include <object/state_observer.h>
11
12namespace {
13
14class TestDispatcher final : public SoloDispatcher<TestDispatcher> {
15public:
16    TestDispatcher() {}
17    ~TestDispatcher() final = default;
18    zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_NONE; }
19    bool has_state_tracker() const final { return true; }
20
21    // Heler: Causes OnStateChange() to be called.
22    void CallUpdateState() {
23        UpdateState(0, 1);
24    }
25
26    // Helper: Causes most On*() hooks (except for OnInitialized) to
27    // be called on all of |st|'s observers.
28    void CallAllOnHooks() {
29        UpdateState(0, 7);
30        Cancel(/* handle= */ nullptr);
31        CancelByKey(/* handle= */ nullptr, /* port= */ nullptr, /* key= */ 2u);
32    }
33};
34
35} // namespace
36
37// Tests for observer removal
38namespace removal {
39
40class RemovableObserver : public StateObserver {
41public:
42    RemovableObserver() = default;
43
44    // The number of times OnRemoved() has been called.
45    int removals() const { return removals_; }
46
47private:
48    // No-op overrides of pure virtuals.
49    Flags OnInitialize(zx_signals_t initial_state,
50                       const StateObserver::CountInfo* cinfo) override {
51        return 0;
52    }
53    Flags OnStateChange(zx_signals_t new_state) override { return 0; }
54    Flags OnCancel(const Handle* handle) override { return 0; }
55    Flags OnCancelByKey(const Handle* handle, const void* port, uint64_t key)
56        override { return 0; }
57
58    void OnRemoved() override { removals_++; }
59
60    int removals_ = 0;
61};
62
63bool on_initialize() {
64    BEGIN_TEST;
65
66    class RmOnInitialize : public RemovableObserver {
67    public:
68        Flags OnInitialize(zx_signals_t initial_state,
69                           const StateObserver::CountInfo* cinfo) override {
70            return kNeedRemoval;
71        }
72    };
73
74    RmOnInitialize obs;
75    EXPECT_EQ(0, obs.removals(), "");
76
77    // Cause OnInitialize() to be called.
78    TestDispatcher st;
79    st.AddObserver(&obs, nullptr);
80
81    // Should have been removed.
82    EXPECT_EQ(1, obs.removals(), "");
83
84    // Further On hook calls should not re-remove.
85    st.CallAllOnHooks();
86    EXPECT_EQ(1, obs.removals(), "");
87
88    END_TEST;
89}
90
91class RmOnStateChange : public RemovableObserver {
92public:
93    Flags OnStateChange(zx_signals_t new_state) override {
94        return kNeedRemoval;
95    }
96};
97
98bool on_state_change_via_update_state() {
99    BEGIN_TEST;
100
101    RmOnStateChange obs;
102    EXPECT_EQ(0, obs.removals(), "");
103
104    TestDispatcher st;
105    st.AddObserver(&obs, nullptr);
106    EXPECT_EQ(0, obs.removals(), ""); // Not removed yet.
107
108    // Cause OnStateChange() to be called.
109    st.CallUpdateState();
110
111    // Should have been removed.
112    EXPECT_EQ(1, obs.removals(), "");
113
114    // Further On hook calls should not re-remove.
115    st.CallAllOnHooks();
116    EXPECT_EQ(1, obs.removals(), "");
117
118    END_TEST;
119}
120
121bool on_cancel() {
122    BEGIN_TEST;
123
124    class RmOnCancel : public RemovableObserver {
125    public:
126        Flags OnCancel(const Handle* handle) override {
127            return kNeedRemoval;
128        }
129    };
130
131    RmOnCancel obs;
132    EXPECT_EQ(0, obs.removals(), "");
133
134    TestDispatcher st;
135    st.AddObserver(&obs, nullptr);
136    EXPECT_EQ(0, obs.removals(), ""); // Not removed yet.
137
138    // Cause OnCancel() to be called.
139    st.Cancel(/* handle= */ nullptr);
140
141    // Should have been removed.
142    EXPECT_EQ(1, obs.removals(), "");
143
144    // Further On hook calls should not re-remove.
145    st.CallAllOnHooks();
146    EXPECT_EQ(1, obs.removals(), "");
147
148    END_TEST;
149}
150
151bool on_cancel_by_key() {
152    BEGIN_TEST;
153
154    class RmOnCancelByKey : public RemovableObserver {
155    public:
156        Flags OnCancelByKey(const Handle* handle, const void* port, uint64_t key)
157            override {
158            return kNeedRemoval;
159        }
160    };
161
162    RmOnCancelByKey obs;
163    EXPECT_EQ(0, obs.removals(), "");
164
165    TestDispatcher st;
166    st.AddObserver(&obs, nullptr);
167    EXPECT_EQ(0, obs.removals(), ""); // Not removed yet.
168
169    // Cause OnCancelByKey() to be called.
170    st.CancelByKey(/* handle= */ nullptr, /* port= */ nullptr, /* key= */ 2u);
171
172    // Should have been removed.
173    EXPECT_EQ(1, obs.removals(), "");
174
175    // Further On hook calls should not re-remove.
176    st.CallAllOnHooks();
177    EXPECT_EQ(1, obs.removals(), "");
178
179    END_TEST;
180}
181
182} // namespace removal
183
184#define ST_UNITTEST(fname) UNITTEST(#fname, fname)
185
186UNITTEST_START_TESTCASE(state_tracker_tests)
187
188ST_UNITTEST(removal::on_initialize)
189ST_UNITTEST(removal::on_state_change_via_update_state)
190ST_UNITTEST(removal::on_cancel)
191ST_UNITTEST(removal::on_cancel_by_key)
192
193UNITTEST_END_TESTCASE(
194    state_tracker_tests, "statetracker", "StateTracker test");
195