blob: 866a78f00675afc38cd6b5824cd06312b0805915 [file] [log] [blame]
// 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");