blob: 08dad69e130a9a49c34a6aac023fc9fd9fd6f196 [file]
// Copyright 2024 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_clock_tree/clock_tree.h"
#include "pw_clock_tree/external_source.h"
#include "pw_preprocessor/util.h"
#include "pw_unit_test/framework.h"
using namespace std::chrono_literals;
namespace pw::clock_tree {
namespace {
#define INIT_TEST_DATA(test_data, call_data) \
test_data.num_expected_calls = PW_ARRAY_SIZE(call_data); \
test_data.num_calls = 0; \
test_data.data = call_data
enum class ClockOperation {
kAcquire,
kRelease,
};
struct clock_divider_test_call_data {
uint32_t divider_name;
uint32_t divider;
ClockOperation op;
pw::Status status;
};
struct clock_divider_test_data {
uint32_t num_expected_calls;
uint32_t num_calls;
struct clock_divider_test_call_data* data;
};
template <typename ElementType>
class ClockDividerTest : public ClockDividerElement<ElementType> {
public:
constexpr ClockDividerTest(ElementType& source,
uint32_t divider_name,
uint32_t divider,
struct clock_divider_test_data& test_data)
: ClockDividerElement<ElementType>(source, divider),
divider_name_(divider_name),
test_data_(test_data) {}
private:
pw::Status ValidateClockAction(ClockOperation op) {
pw::Status status = pw::Status::OutOfRange();
if (test_data_.num_calls < test_data_.num_expected_calls) {
uint32_t i = test_data_.num_calls;
EXPECT_EQ(test_data_.data[i].divider_name, divider_name_);
EXPECT_EQ(test_data_.data[i].divider, this->divider());
EXPECT_EQ(test_data_.data[i].op, op);
status = test_data_.data[i].status;
}
test_data_.num_calls++;
return status;
}
pw::Status DoEnable() final {
return ValidateClockAction(ClockOperation::kAcquire);
}
pw::Status DoDisable() final {
return ValidateClockAction(ClockOperation::kRelease);
}
uint32_t divider_name_;
struct clock_divider_test_data& test_data_;
};
using ClockDividerTestBlocking = ClockDividerTest<ElementBlocking>;
using ClockDividerTestNonBlocking =
ClockDividerTest<ElementNonBlockingMightFail>;
template <typename ElementType>
class ClockDividerNoDoDisableTest : public ClockDividerElement<ElementType> {
public:
constexpr ClockDividerNoDoDisableTest(
ElementType& source,
uint32_t divider_name,
uint32_t divider,
struct clock_divider_test_data& test_data)
: ClockDividerElement<ElementType>(source, divider),
divider_name_(divider_name),
test_data_(test_data) {}
private:
pw::Status ValidateClockAction(ClockOperation op) {
pw::Status status = pw::Status::OutOfRange();
if (test_data_.num_calls < test_data_.num_expected_calls) {
uint32_t i = test_data_.num_calls;
EXPECT_EQ(test_data_.data[i].divider_name, divider_name_);
EXPECT_EQ(test_data_.data[i].divider, this->divider());
EXPECT_EQ(test_data_.data[i].op, op);
status = test_data_.data[i].status;
}
test_data_.num_calls++;
return status;
}
pw::Status DoEnable() final {
return ValidateClockAction(ClockOperation::kAcquire);
}
uint32_t divider_name_;
struct clock_divider_test_data& test_data_;
};
using ClockDividerNoDoDisableTestBlocking =
ClockDividerNoDoDisableTest<ElementBlocking>;
using ClockDividerNoDoDisableTestNonBlocking =
ClockDividerNoDoDisableTest<ElementNonBlockingMightFail>;
struct clock_selector_test_call_data {
uint32_t selector;
uint32_t value;
ClockOperation op;
pw::Status status;
};
struct clock_selector_test_data {
uint32_t num_expected_calls;
uint32_t num_calls;
struct clock_selector_test_call_data* data;
};
template <typename ElementType>
class ClockSelectorTest : public DependentElement<ElementType> {
public:
constexpr ClockSelectorTest(ElementType& source,
uint32_t selector,
uint32_t selector_enable,
uint32_t selector_disable,
struct clock_selector_test_data& test_data)
: DependentElement<ElementType>(source),
selector_(selector),
selector_enable_(selector_enable),
selector_disable_(selector_disable),
test_data_(test_data) {}
private:
pw::Status ValidateClockAction(ClockOperation op) {
pw::Status status = pw::Status::OutOfRange();
if (test_data_.num_calls < test_data_.num_expected_calls) {
uint32_t i = test_data_.num_calls;
uint32_t value = (op == ClockOperation::kAcquire) ? selector_enable_
: selector_disable_;
EXPECT_EQ(test_data_.data[i].selector, selector_);
EXPECT_EQ(test_data_.data[i].value, value);
EXPECT_EQ(test_data_.data[i].op, op);
status = test_data_.data[i].status;
}
test_data_.num_calls++;
return status;
}
pw::Status DoEnable() final {
return ValidateClockAction(ClockOperation::kAcquire);
}
pw::Status DoDisable() final {
return ValidateClockAction(ClockOperation::kRelease);
}
uint32_t selector_;
uint32_t selector_enable_;
uint32_t selector_disable_;
struct clock_selector_test_data& test_data_;
};
using ClockSelectorTestBlocking = ClockSelectorTest<ElementBlocking>;
using ClockSelectorTestNonBlockingMightFail =
ClockSelectorTest<ElementNonBlockingMightFail>;
struct clock_source_state_test_call_data {
uint32_t value;
ClockOperation op;
pw::Status status;
};
struct clock_source_state_test_data {
uint32_t num_expected_calls;
uint32_t num_calls;
struct clock_source_state_test_call_data* data;
};
template <typename ElementType>
class ClockSourceStateTest : public ClockSource<ElementType> {
public:
constexpr ClockSourceStateTest(uint32_t value,
uint32_t* clock_state,
struct clock_source_state_test_data& test_data)
: value_(value), clock_state_(clock_state), test_data_(test_data) {}
private:
pw::Status ValidateClockAction(ClockOperation op) {
pw::Status status = pw::Status::OutOfRange();
if (test_data_.num_calls < test_data_.num_expected_calls) {
uint32_t i = test_data_.num_calls;
EXPECT_EQ(test_data_.data[i].value, value_);
EXPECT_EQ(test_data_.data[i].op, op);
status = test_data_.data[i].status;
}
test_data_.num_calls++;
return status;
}
pw::Status DoEnable() final {
PW_TRY(ValidateClockAction(ClockOperation::kAcquire));
*clock_state_ |= value_;
return pw::OkStatus();
}
pw::Status DoDisable() final {
PW_TRY(ValidateClockAction(ClockOperation::kRelease));
*clock_state_ &= ~value_;
return pw::OkStatus();
}
uint32_t value_;
uint32_t* clock_state_;
struct clock_source_state_test_data& test_data_;
};
using ClockSourceStateTestBlocking = ClockSourceStateTest<ElementBlocking>;
using ClockSourceStateTestNonBlocking =
ClockSourceStateTest<ElementNonBlockingMightFail>;
template <typename ElementType>
class ClockSourceTest : public ClockSource<ElementType> {
private:
pw::Status DoEnable() final { return pw::OkStatus(); }
pw::Status DoDisable() final { return pw::OkStatus(); }
};
using ClockSourceTestBlocking = ClockSourceTest<ElementBlocking>;
using ClockSourceTestNonBlocking = ClockSourceTest<ElementNonBlockingMightFail>;
struct clock_source_failure_test_call_data {
ClockOperation op;
pw::Status status;
};
struct clock_source_failure_test_data {
uint32_t num_expected_calls;
uint32_t num_calls;
struct clock_source_failure_test_call_data* data;
};
template <typename ElementType>
class ClockSourceFailureTest : public ClockSource<ElementType> {
public:
constexpr ClockSourceFailureTest(
struct clock_source_failure_test_data& test_data)
: test_data_(test_data) {}
private:
pw::Status ValidateClockAction(ClockOperation op) {
pw::Status status = pw::Status::OutOfRange();
if (test_data_.num_calls < test_data_.num_expected_calls) {
uint32_t i = test_data_.num_calls;
EXPECT_EQ(test_data_.data[i].op, op);
status = test_data_.data[i].status;
}
test_data_.num_calls++;
return status;
}
pw::Status DoEnable() final {
return ValidateClockAction(ClockOperation::kAcquire);
}
pw::Status DoDisable() final {
return ValidateClockAction(ClockOperation::kRelease);
}
struct clock_source_failure_test_data& test_data_;
};
using ClockSourceFailureTestBlocking = ClockSourceFailureTest<ElementBlocking>;
using ClockSourceFailureTestNonBlocking =
ClockSourceFailureTest<ElementNonBlockingMightFail>;
template <typename ElementType>
static void TestClock() {
pw::Status status;
ClockSourceTest<ElementType> clock_a;
EXPECT_EQ(clock_a.ref_count(), 0u);
status = clock_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
status = clock_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 2u);
status = clock_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
status = clock_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
}
TEST(ClockTree, ClockBlocking) { TestClock<ElementBlocking>(); }
TEST(ClockTree, ClockNonBlocking) { TestClock<ElementNonBlockingMightFail>(); }
// Validate that the correct divider values are getting set.
// The `clock_divider_b` doesn't override the `DoDisable` function,
// so only the ClockDividerNoDoDisableTest's `DoEnable` method will be called.
template <typename ElementType>
static void TestClockDivider() {
const uint32_t kClockDividerB = 23;
const uint32_t kClockDividerC = 42;
struct clock_divider_test_call_data call_data[] = {
{kClockDividerB, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerC, 4, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerC, 4, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_divider_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceTest<ElementType> clock_a;
ClockDividerNoDoDisableTest<ElementType> clock_divider_b(
clock_a, kClockDividerB, 2, test_data);
ClockDividerTest<ElementType> clock_divider_c(
clock_a, kClockDividerC, 4, test_data);
ClockDivider& clock_divider_b_abstract = clock_divider_b;
Element& clock_divider_b_element = clock_divider_b_abstract.element();
pw::Status status;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
status = clock_divider_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
status = clock_divider_b_element.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 2u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
status = clock_divider_c.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 2u);
EXPECT_EQ(clock_divider_c.ref_count(), 1u);
status = clock_divider_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
EXPECT_EQ(clock_divider_c.ref_count(), 1u);
// Releasing `clock_divider_b` won't be tracked, since
// only the base class `DoDisable` method will be called.
status = clock_divider_b_element.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 1u);
status = clock_divider_c.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, DividerBlocking) { TestClockDivider<ElementBlocking>(); }
TEST(ClockTree, DividerNonBlocking) {
TestClockDivider<ElementNonBlockingMightFail>();
}
// Validate that different divider values can be set.
template <typename ElementType>
static void TestClockDividerSet() {
const uint32_t kClockDivider = 23;
struct clock_divider_test_call_data call_data[] = {
{kClockDivider, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDivider, 4, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDivider, 4, ClockOperation::kRelease, pw::OkStatus()},
{kClockDivider, 6, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDivider, 6, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_divider_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
pw::Status status;
ClockSourceTest<ElementType> clock_a;
ClockDividerTest<ElementType> clock_divider_b(
clock_a, kClockDivider, 2, test_data);
ClockDivider& clock_divider_b_abstract = clock_divider_b;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b_abstract.SetDivider(4);
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_b.SetDivider(6);
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, ClockDividerSetBlocking) {
TestClockDividerSet<ElementBlocking>();
}
TEST(ClockTree, ClockDividerSetNonBlocking) {
TestClockDividerSet<ElementNonBlockingMightFail>();
}
template <typename ElementType>
class DependentElementTest : public DependentElement<ElementType> {
public:
template <typename SourceType>
constexpr DependentElementTest(SourceType& source)
: DependentElement<ElementType>(source) {}
using DependentElement<ElementType>::SetSource;
private:
pw::Status DoEnable() final { return pw::OkStatus(); }
pw::Status DoDisable() final { return pw::OkStatus(); }
};
TEST(ClockTree, DependentElementSetSource) {
ClockSourceTest<ElementNonBlockingCannotFail> source_a;
ClockSourceTest<ElementBlocking> source_b;
DependentElementTest<ElementBlocking> dependent(source_a);
EXPECT_EQ(source_a.ref_count(), 0u);
EXPECT_EQ(source_b.ref_count(), 0u);
// Set source to a and acquire
dependent.SetSource(source_a);
PW_TEST_EXPECT_OK(dependent.Acquire());
EXPECT_EQ(source_a.ref_count(), 1u);
// Release
PW_TEST_EXPECT_OK(dependent.Release());
EXPECT_EQ(source_a.ref_count(), 0u);
// Set source to b and acquire
dependent.SetSource(source_b);
PW_TEST_EXPECT_OK(dependent.Acquire());
EXPECT_EQ(source_a.ref_count(), 0u);
EXPECT_EQ(source_b.ref_count(), 1u);
// Release
PW_TEST_EXPECT_OK(dependent.Release());
EXPECT_EQ(source_b.ref_count(), 0u);
}
// Validate that if the `DoEnable` function fails that gets called as part
// of a divider update, that the state of the divider doesn't change.
template <typename ElementType>
static void TestClockDividerSetFailure() {
const uint32_t kClockDivider = 23;
struct clock_divider_test_call_data call_data[] = {
{kClockDivider, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDivider, 4, ClockOperation::kAcquire, pw::Status::Internal()},
{kClockDivider, 2, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_divider_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
pw::Status status;
ClockSourceTest<ElementType> clock_a;
ClockDividerTest<ElementType> clock_divider_b(
clock_a, kClockDivider, 2, test_data);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b.SetDivider(4);
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, ClockDividerSetFailureBlocking) {
TestClockDividerSetFailure<ElementBlocking>();
}
TEST(ClockTree, ClockDividerSetFailureNonBlocking) {
TestClockDividerSetFailure<ElementNonBlockingMightFail>();
}
// Validate that a selector enables and disables correctly.
template <typename ElementType>
static void TestClockSelector() {
const uint32_t kSelector = 41;
struct clock_selector_test_call_data call_data[] = {
{kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kSelector, 7, ClockOperation::kRelease, pw::OkStatus()},
{kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kSelector, 7, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_selector_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
pw::Status status;
ClockSourceTest<ElementType> clock_a;
ClockSelectorTest<ElementType> clock_selector_b(
clock_a, kSelector, 2, 7, test_data);
Element& clock_selector_b_element = clock_selector_b;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
status = clock_selector_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 1u);
status = clock_selector_b_element.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 2u);
status = clock_selector_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 1u);
status = clock_selector_b_element.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
status = clock_selector_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 1u);
status = clock_selector_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, ClockSelectorBlocking) { TestClockSelector<ElementBlocking>(); }
TEST(ClockTree, ClockSelectorNonBlocking) {
TestClockSelector<ElementNonBlockingMightFail>();
}
template <typename ElementType>
static void TestClockSource() {
uint32_t shared_clock_value = 0;
uint32_t exclusive_clock_value = 0;
struct clock_source_state_test_call_data call_data[] = {
{1, ClockOperation::kAcquire, pw::OkStatus()},
{4, ClockOperation::kAcquire, pw::OkStatus()},
{2, ClockOperation::kAcquire, pw::OkStatus()},
{1, ClockOperation::kRelease, pw::OkStatus()},
{2, ClockOperation::kRelease, pw::OkStatus()},
{4, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_source_state_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
pw::Status status;
ClockSourceStateTest<ElementType> clock_a(1, &shared_clock_value, test_data);
ClockSourceStateTest<ElementType> clock_b(2, &shared_clock_value, test_data);
ClockSourceStateTest<ElementType> clock_c(
4, &exclusive_clock_value, test_data);
Element& clock_c_element = clock_c;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_b.ref_count(), 0u);
EXPECT_EQ(clock_c.ref_count(), 0u);
EXPECT_EQ(shared_clock_value, 0u);
EXPECT_EQ(exclusive_clock_value, 0u);
status = clock_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_b.ref_count(), 0u);
EXPECT_EQ(clock_c.ref_count(), 0u);
EXPECT_EQ(shared_clock_value, 1u);
EXPECT_EQ(exclusive_clock_value, 0u);
status = clock_c_element.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_b.ref_count(), 0u);
EXPECT_EQ(clock_c.ref_count(), 1u);
EXPECT_EQ(shared_clock_value, 1u);
EXPECT_EQ(exclusive_clock_value, 4u);
status = clock_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_b.ref_count(), 1u);
EXPECT_EQ(clock_c.ref_count(), 1u);
EXPECT_EQ(shared_clock_value, 3u);
EXPECT_EQ(exclusive_clock_value, 4u);
status = clock_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_b.ref_count(), 1u);
EXPECT_EQ(clock_c.ref_count(), 1u);
EXPECT_EQ(shared_clock_value, 2u);
EXPECT_EQ(exclusive_clock_value, 4u);
status = clock_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_b.ref_count(), 0u);
EXPECT_EQ(clock_c.ref_count(), 1u);
EXPECT_EQ(shared_clock_value, 0u);
EXPECT_EQ(exclusive_clock_value, 4u);
status = clock_c_element.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_b.ref_count(), 0u);
EXPECT_EQ(clock_c.ref_count(), 0u);
EXPECT_EQ(shared_clock_value, 0u);
EXPECT_EQ(exclusive_clock_value, 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, ClockSourceBlocking) { TestClockSource<ElementBlocking>(); }
TEST(ClockTree, ClockSourceNonBlocking) {
TestClockSource<ElementNonBlockingMightFail>();
}
// Validate that no references have been acquired when ClockSource
// fails in `DoEnable`.
template <typename ElementType>
static void TestFailureAcquire1() {
struct clock_source_failure_test_call_data clock_call_data[] = {
{ClockOperation::kAcquire, pw::Status::Internal()}};
struct clock_source_failure_test_data clock_test_data;
INIT_TEST_DATA(clock_test_data, clock_call_data);
ClockSourceFailureTest<ElementType> clock_a(clock_test_data);
const uint32_t kSelector = 41;
struct clock_selector_test_data selector_test_data = {};
ClockSelectorTest<ElementType> clock_selector_b(
clock_a, kSelector, 1, 8, selector_test_data);
pw::Status status;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
status = clock_selector_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(clock_test_data.num_calls, clock_test_data.num_expected_calls);
EXPECT_EQ(selector_test_data.num_calls,
selector_test_data.num_expected_calls);
}
TEST(ClockTree, ClockFailureAcquire1Blocking) {
TestFailureAcquire1<ElementBlocking>();
}
TEST(ClockTree, ClockFailureAcquire1NonBlocking) {
TestFailureAcquire1<ElementNonBlockingMightFail>();
}
// Validate that `ClockSource` reference gets released correctly, when
// dependent clock element fails to enable in `DoEnable`, and that
// `DependentElement` doesn't get enabled if dependent
// clock tree element doesn't get enabled successfully.
template <typename ElementType>
static void TestFailureAcquire2() {
struct clock_source_failure_test_call_data clock_call_data[] = {
{ClockOperation::kAcquire, pw::OkStatus()},
{ClockOperation::kRelease, pw::OkStatus()}};
struct clock_source_failure_test_data clock_test_data;
INIT_TEST_DATA(clock_test_data, clock_call_data);
ClockSourceFailureTest<ElementType> clock_a(clock_test_data);
const uint32_t kSelector = 41;
struct clock_selector_test_call_data selector_call_data[] = {
{kSelector, 1, ClockOperation::kAcquire, pw::Status::Internal()}};
struct clock_selector_test_data selector_test_data;
INIT_TEST_DATA(selector_test_data, selector_call_data);
ClockSelectorTest<ElementType> clock_selector_b(
clock_a, kSelector, 1, 8, selector_test_data);
const uint32_t kClockDividerC = 42;
struct clock_divider_test_data divider_test_data = {};
ClockDividerTest<ElementType> clock_divider_c(
clock_selector_b, kClockDividerC, 4, divider_test_data);
pw::Status status;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
status = clock_divider_c.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
EXPECT_EQ(clock_test_data.num_calls, clock_test_data.num_expected_calls);
EXPECT_EQ(selector_test_data.num_calls,
selector_test_data.num_expected_calls);
EXPECT_EQ(divider_test_data.num_calls, divider_test_data.num_expected_calls);
}
TEST(ClockTree, ClockFailureAcquire2Blocking) {
TestFailureAcquire2<ElementBlocking>();
}
TEST(ClockTree, ClockFailureAcquire2NonBlocking) {
TestFailureAcquire2<ElementNonBlockingMightFail>();
}
// Validate that `ClockSource` and `DependentElement` references
// gets released correctly, when dependent clock element fails to enable
// in `DoEnable`.
template <typename ElementType>
static void TestFailureAcquire3() {
struct clock_source_failure_test_call_data clock_call_data[] = {
{ClockOperation::kAcquire, pw::OkStatus()},
{ClockOperation::kRelease, pw::OkStatus()}};
struct clock_source_failure_test_data clock_test_data;
INIT_TEST_DATA(clock_test_data, clock_call_data);
ClockSourceFailureTest<ElementType> clock_a(clock_test_data);
const uint32_t kSelector = 41;
struct clock_selector_test_call_data selector_call_data[] = {
{kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
{kSelector, 8, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_selector_test_data selector_test_data;
INIT_TEST_DATA(selector_test_data, selector_call_data);
ClockSelectorTest<ElementType> clock_selector_b(
clock_a, kSelector, 1, 8, selector_test_data);
const uint32_t kClockDividerC = 42;
struct clock_divider_test_call_data divider_call_data[] = {
{kClockDividerC, 4, ClockOperation::kAcquire, pw::Status::Internal()}};
struct clock_divider_test_data divider_test_data;
INIT_TEST_DATA(divider_test_data, divider_call_data);
ClockDividerTest<ElementType> clock_divider_c(
clock_selector_b, kClockDividerC, 4, divider_test_data);
pw::Status status;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
status = clock_divider_c.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(clock_divider_c.ref_count(), 0u);
EXPECT_EQ(clock_test_data.num_calls, clock_test_data.num_expected_calls);
EXPECT_EQ(selector_test_data.num_calls,
selector_test_data.num_expected_calls);
EXPECT_EQ(divider_test_data.num_calls, divider_test_data.num_expected_calls);
}
TEST(ClockTree, ClockFailureAcquire3Blocking) {
TestFailureAcquire3<ElementBlocking>();
}
TEST(ClockTree, ClockFailureAcquire3NonBlocking) {
TestFailureAcquire3<ElementNonBlockingMightFail>();
}
// Validate that reference counts are correct when a ClockSource derived class
// fails in `DoDisable` during `Release.
template <typename ElementType>
static void TestFailureRelease1() {
struct clock_source_failure_test_call_data clock_call_data[] = {
{ClockOperation::kAcquire, pw::OkStatus()},
{ClockOperation::kRelease, pw::Status::Internal()}};
struct clock_source_failure_test_data clock_test_data;
INIT_TEST_DATA(clock_test_data, clock_call_data);
ClockSourceFailureTest<ElementType> clock_a(clock_test_data);
const uint32_t kSelector = 41;
struct clock_selector_test_call_data selector_call_data[] = {
{kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
{kSelector, 8, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_selector_test_data selector_test_data;
INIT_TEST_DATA(selector_test_data, selector_call_data);
ClockSelectorTest<ElementType> clock_selector_b(
clock_a, kSelector, 1, 8, selector_test_data);
pw::Status status;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
// Acquire initial references
status = clock_selector_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 1u);
status = clock_selector_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
EXPECT_EQ(clock_test_data.num_calls, clock_test_data.num_expected_calls);
EXPECT_EQ(selector_test_data.num_calls,
selector_test_data.num_expected_calls);
}
TEST(ClockTree, ClockFailureRelease1Blocking) {
TestFailureRelease1<ElementBlocking>();
}
TEST(ClockTree, ClockFailureRelease1NonBlocking) {
TestFailureRelease1<ElementNonBlockingMightFail>();
}
// Validate that the reference is kept alive if a `DoDisable` call
// fails when releasing a reference for a DependentElement derived
// class.
template <typename ElementType>
static void TestFailureRelease2() {
struct clock_source_failure_test_call_data clock_call_data[] = {
{ClockOperation::kAcquire, pw::OkStatus()}};
struct clock_source_failure_test_data clock_test_data;
INIT_TEST_DATA(clock_test_data, clock_call_data);
ClockSourceFailureTest<ElementType> clock_a(clock_test_data);
const uint32_t kSelector = 41;
struct clock_selector_test_call_data selector_call_data[] = {
{kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
{kSelector, 8, ClockOperation::kRelease, pw::Status::Internal()}};
struct clock_selector_test_data selector_test_data;
INIT_TEST_DATA(selector_test_data, selector_call_data);
ClockSelectorTest<ElementType> clock_selector_b(
clock_a, kSelector, 1, 8, selector_test_data);
pw::Status status;
EXPECT_EQ(clock_a.ref_count(), 0u);
EXPECT_EQ(clock_selector_b.ref_count(), 0u);
status = clock_selector_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 1u);
status = clock_selector_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_a.ref_count(), 1u);
EXPECT_EQ(clock_selector_b.ref_count(), 1u);
EXPECT_EQ(clock_test_data.num_calls, clock_test_data.num_expected_calls);
EXPECT_EQ(selector_test_data.num_calls,
selector_test_data.num_expected_calls);
}
TEST(ClockTree, ClockFailureRelease2Blocking) {
TestFailureRelease2<ElementBlocking>();
}
TEST(ClockTree, ClockFailureRelease2NonBlocking) {
TestFailureRelease2<ElementNonBlockingMightFail>();
}
TEST(ClockTree, ElementMayBlock) {
ClockSourceTest<ElementNonBlockingCannotFail> clock_non_blocking_cannot_fail;
EXPECT_FALSE(clock_non_blocking_cannot_fail.kMayBlock);
ClockSourceTest<ElementNonBlockingMightFail> clock_non_blocking_might_fail;
EXPECT_FALSE(clock_non_blocking_might_fail.kMayBlock);
ClockSourceTest<ElementBlocking> clock_blocking;
EXPECT_TRUE(clock_blocking.kMayBlock);
Element& element_non_blocking_cannot_fail = clock_non_blocking_cannot_fail;
EXPECT_TRUE(element_non_blocking_cannot_fail.kMayBlock);
Element& element_non_blocking_might_fail = clock_non_blocking_might_fail;
EXPECT_TRUE(element_non_blocking_might_fail.kMayBlock);
Element& element_blocking = clock_blocking;
EXPECT_TRUE(element_blocking.kMayBlock);
}
TEST(ClockTree, ElementMayFail) {
ClockSourceTest<ElementNonBlockingCannotFail> clock_non_blocking_cannot_fail;
EXPECT_FALSE(clock_non_blocking_cannot_fail.kMayFail);
ClockSourceTest<ElementNonBlockingMightFail> clock_non_blocking_might_fail;
EXPECT_TRUE(clock_non_blocking_might_fail.kMayFail);
ClockSourceTest<ElementBlocking> clock_blocking;
EXPECT_TRUE(clock_blocking.kMayFail);
Element& element_non_blocking_cannot_fail = clock_non_blocking_cannot_fail;
EXPECT_TRUE(element_non_blocking_cannot_fail.kMayFail);
Element& element_non_blocking_might_fail = clock_non_blocking_might_fail;
EXPECT_TRUE(element_non_blocking_might_fail.kMayFail);
Element& element_blocking = clock_blocking;
EXPECT_TRUE(element_blocking.kMayFail);
}
TEST(ClockTree, ClockDividerMayBlock) {
struct clock_divider_test_data test_data;
ClockSourceTest<ElementNonBlockingCannotFail> clock_non_blocking_cannot_fail;
ClockSourceTest<ElementNonBlockingMightFail> clock_non_blocking_might_fail;
ClockSourceTest<ElementBlocking> clock_blocking;
ClockDividerTest<ElementNonBlockingCannotFail>
clock_divider_non_blocking_cannot_fail(
clock_non_blocking_cannot_fail, 1, 1, test_data);
EXPECT_FALSE(clock_divider_non_blocking_cannot_fail.kMayBlock);
ClockDividerTest<ElementNonBlockingMightFail>
clock_divider_non_blocking_might_fail(
clock_non_blocking_might_fail, 1, 1, test_data);
EXPECT_FALSE(clock_divider_non_blocking_might_fail.kMayBlock);
ClockDividerTest<ElementBlocking> clock_divider_blocking(
clock_blocking, 1, 1, test_data);
EXPECT_TRUE(clock_divider_blocking.kMayBlock);
}
TEST(ClockTree, ClockDividerMayFail) {
struct clock_divider_test_data test_data;
ClockSourceTest<ElementNonBlockingCannotFail> clock_non_blocking_cannot_fail;
ClockSourceTest<ElementNonBlockingMightFail> clock_non_blocking_might_fail;
ClockSourceTest<ElementBlocking> clock_blocking;
ClockDividerTest<ElementNonBlockingCannotFail>
clock_divider_non_blocking_cannot_fail(
clock_non_blocking_cannot_fail, 1, 1, test_data);
EXPECT_FALSE(clock_divider_non_blocking_cannot_fail.kMayFail);
ClockDividerTest<ElementNonBlockingMightFail>
clock_divider_non_blocking_might_fail(
clock_non_blocking_might_fail, 1, 1, test_data);
EXPECT_TRUE(clock_divider_non_blocking_might_fail.kMayFail);
ClockDividerTest<ElementBlocking> clock_divider_blocking(
clock_blocking, 1, 1, test_data);
EXPECT_TRUE(clock_divider_blocking.kMayFail);
}
// Validate the behavior of the ClockSourceNoOp class
TEST(ClockTree, ClockSourceNoOp) {
const uint32_t kClockDividerA = 23;
const uint32_t kClockDividerB = 42;
struct clock_divider_test_call_data call_data[] = {
{kClockDividerA, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerB, 4, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerB, 4, ClockOperation::kRelease, pw::OkStatus()},
{kClockDividerA, 2, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_divider_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceNoOp clock_source_no_op;
ClockDividerTest<ElementNonBlockingCannotFail> clock_divider_a(
clock_source_no_op, kClockDividerA, 2, test_data);
ClockDividerTest<ElementNonBlockingCannotFail> clock_divider_b(
clock_source_no_op, kClockDividerB, 4, test_data);
EXPECT_EQ(clock_source_no_op.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
clock_divider_a.Acquire();
EXPECT_EQ(clock_source_no_op.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
clock_divider_a.Acquire();
EXPECT_EQ(clock_source_no_op.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
clock_divider_b.Acquire();
EXPECT_EQ(clock_source_no_op.ref_count(), 2u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
clock_divider_b.Release();
EXPECT_EQ(clock_source_no_op.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
clock_divider_a.Release();
EXPECT_EQ(clock_source_no_op.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
clock_divider_a.Release();
EXPECT_EQ(clock_source_no_op.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
// Validate the behavior of the ClockSourceNoOpBlocking class
TEST(ClockTree, ClockSourceNoOpBlocking) {
const uint32_t kClockDividerA = 23;
const uint32_t kClockDividerB = 42;
struct clock_divider_test_call_data call_data[] = {
{kClockDividerA, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerB, 4, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerB, 4, ClockOperation::kRelease, pw::OkStatus()},
{kClockDividerA, 2, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_divider_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceNoOpBlocking clock_source_no_op_blocking;
ClockDividerTest<ElementBlocking> clock_divider_a(
clock_source_no_op_blocking, kClockDividerA, 2, test_data);
ClockDividerTest<ElementBlocking> clock_divider_b(
clock_source_no_op_blocking, kClockDividerB, 4, test_data);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
pw::Status status;
status = clock_divider_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 2u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_source_no_op_blocking.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
// Validate that AcquireWith acquires the element_with during
// acquisition of element.
TEST(ClockTree, AcquireWith) {
uint32_t element_with_value = 0;
uint32_t element_value = 0;
// The order of acquisitions validates that we are
// acquiring `element_with` before acquring `element`,
// and releasing `element_with` after acquiring `element`.
struct clock_source_state_test_call_data call_data[] = {
// AcquireWith(element, element_with)
{1, ClockOperation::kAcquire, pw::OkStatus()},
{2, ClockOperation::kAcquire, pw::OkStatus()},
{1, ClockOperation::kRelease, pw::OkStatus()},
// Release(element)
{2, ClockOperation::kRelease, pw::OkStatus()},
// Acquire(element_with)
{1, ClockOperation::kAcquire, pw::OkStatus()},
// AcquireWith(element, element_with)
{2, ClockOperation::kAcquire, pw::OkStatus()}};
struct clock_source_state_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceStateTestBlocking clock_element_with(
1, &element_with_value, test_data);
ClockSourceStateTestBlocking clock_element(2, &element_value, test_data);
pw::Status status;
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
status = clock_element.AcquireWith(clock_element_with);
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_element.ref_count(), 1u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
EXPECT_EQ(element_with_value, 0u);
EXPECT_EQ(element_value, 2u);
status = clock_element.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
EXPECT_EQ(element_with_value, 0u);
EXPECT_EQ(element_value, 0u);
status = clock_element_with.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 1u);
EXPECT_EQ(element_with_value, 1u);
EXPECT_EQ(element_value, 0u);
status = clock_element.AcquireWith(clock_element_with);
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_element.ref_count(), 1u);
EXPECT_EQ(clock_element_with.ref_count(), 1u);
EXPECT_EQ(element_with_value, 1u);
EXPECT_EQ(element_value, 2u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, AcquireWithFailure1) {
uint32_t element_with_value = 0;
uint32_t element_value = 0;
struct clock_source_state_test_call_data call_data[] = {
// AcquireWith(element, element_with)
{1, ClockOperation::kAcquire, pw::Status::Internal()}};
struct clock_source_state_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceStateTestBlocking clock_element_with(
1, &element_with_value, test_data);
ClockSourceStateTestBlocking clock_element(2, &element_value, test_data);
pw::Status status;
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
status = clock_element.AcquireWith(clock_element_with);
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
EXPECT_EQ(element_with_value, 0u);
EXPECT_EQ(element_value, 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, AcquireWithFailure2) {
uint32_t element_with_value = 0;
uint32_t element_value = 0;
struct clock_source_state_test_call_data call_data[] = {
// AcquireWith(element, element_with)
{1, ClockOperation::kAcquire, pw::OkStatus()},
{2, ClockOperation::kAcquire, pw::Status::Internal()},
{1, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_source_state_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceStateTestBlocking clock_element_with(
1, &element_with_value, test_data);
ClockSourceStateTestBlocking clock_element(2, &element_value, test_data);
pw::Status status;
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
status = clock_element.AcquireWith(clock_element_with);
EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
EXPECT_EQ(element_with_value, 0u);
EXPECT_EQ(element_value, 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
TEST(ClockTree, AcquireWithFailure3) {
uint32_t element_with_value = 0;
uint32_t element_value = 0;
struct clock_source_state_test_call_data call_data[] = {
// AcquireWith(element, element_with)
{1, ClockOperation::kAcquire, pw::OkStatus()},
{2, ClockOperation::kAcquire, pw::OkStatus()},
{1, ClockOperation::kRelease, pw::Status::Internal()}};
struct clock_source_state_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockSourceStateTestBlocking clock_element_with(
1, &element_with_value, test_data);
ClockSourceStateTestBlocking clock_element(2, &element_value, test_data);
pw::Status status;
EXPECT_EQ(clock_element.ref_count(), 0u);
EXPECT_EQ(clock_element_with.ref_count(), 0u);
status = clock_element.AcquireWith(clock_element_with);
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(clock_element.ref_count(), 1u);
EXPECT_EQ(clock_element_with.ref_count(), 1u);
EXPECT_EQ(element_with_value, 1u);
EXPECT_EQ(element_value, 2u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}
// OptionalElement
class TestElement : public ElementBlocking {
public:
uint32_t acquire_count() const { return acquire_count_; }
uint32_t release_count() const { return release_count_; }
void set_acquire_status(Status status) { acquire_status_ = status; }
void set_release_status(Status status) { release_status_ = status; }
private:
Status DoAcquireLocked() final {
++acquire_count_;
return acquire_status_;
}
Status DoReleaseLocked() final {
++release_count_;
return release_status_;
}
Status DoEnable() final { return OkStatus(); }
uint32_t acquire_count_ = 0;
uint32_t release_count_ = 0;
Status acquire_status_ = OkStatus();
Status release_status_ = OkStatus();
};
TEST(OptionalElement, SuccessWhenEmpty) {
OptionalElement op;
PW_TEST_EXPECT_OK(op.Acquire());
PW_TEST_EXPECT_OK(op.Release());
}
TEST(OptionalElement, CallsAcquireRelease) {
TestElement element;
OptionalElement op(element);
PW_TEST_EXPECT_OK(op.Acquire());
EXPECT_EQ(element.acquire_count(), 1u);
EXPECT_EQ(element.release_count(), 0u);
PW_TEST_EXPECT_OK(op.Release());
EXPECT_EQ(element.acquire_count(), 1u);
EXPECT_EQ(element.release_count(), 1u);
}
TEST(OptionalElement, PassesThroughStatus) {
TestElement element;
OptionalElement op(element);
element.set_acquire_status(Status::Internal());
EXPECT_EQ(op.Acquire(), Status::Internal());
element.set_release_status(Status::Unavailable());
EXPECT_EQ(op.Release(), Status::Unavailable());
}
TEST(ClockTreeDeathTest, ReleaseWithoutAcquireCrashes) {
ClockSourceTest<ElementBlocking> clock_a;
EXPECT_DEATH_IF_SUPPORTED(clock_a.Release().IgnoreError(), ".*");
ClockSourceTest<ElementNonBlockingMightFail> clock_b;
EXPECT_DEATH_IF_SUPPORTED(clock_b.Release().IgnoreError(), ".*");
}
class TestDigitalOut : public pw::digital_io::DigitalOut {
public:
TestDigitalOut() {}
private:
Status DoEnable(bool) override { return OkStatus(); }
Status DoSetState(pw::digital_io::State) override { return OkStatus(); }
};
static void TestExternalClockSource(
ExternalClockSource& external_clock_source) {
const uint32_t kClockDividerA = 23;
const uint32_t kClockDividerB = 42;
struct clock_divider_test_call_data call_data[] = {
{kClockDividerA, 2, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerB, 4, ClockOperation::kAcquire, pw::OkStatus()},
{kClockDividerB, 4, ClockOperation::kRelease, pw::OkStatus()},
{kClockDividerA, 2, ClockOperation::kRelease, pw::OkStatus()}};
struct clock_divider_test_data test_data;
INIT_TEST_DATA(test_data, call_data);
ClockDividerTest<ElementBlocking> clock_divider_a(
external_clock_source, kClockDividerA, 2, test_data);
ClockDividerTest<ElementBlocking> clock_divider_b(
external_clock_source, kClockDividerB, 4, test_data);
EXPECT_EQ(external_clock_source.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
pw::Status status;
status = clock_divider_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(external_clock_source.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_a.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(external_clock_source.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_b.Acquire();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(external_clock_source.ref_count(), 2u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 1u);
status = clock_divider_b.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(external_clock_source.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 2u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(external_clock_source.ref_count(), 1u);
EXPECT_EQ(clock_divider_a.ref_count(), 1u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
status = clock_divider_a.Release();
EXPECT_EQ(status.code(), PW_STATUS_OK);
EXPECT_EQ(external_clock_source.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
EXPECT_EQ(external_clock_source.ref_count(), 0u);
EXPECT_EQ(clock_divider_a.ref_count(), 0u);
EXPECT_EQ(clock_divider_b.ref_count(), 0u);
}
TEST(ExternalClockSource, AcquireRelease) {
TestDigitalOut digital_io_line;
EXPECT_EQ(digital_io_line.Enable(), PW_STATUS_OK);
EXPECT_EQ(digital_io_line.SetStateInactive(), PW_STATUS_OK);
ExternalClockSource external_clock_source(digital_io_line, 10ms, 5ms);
TestExternalClockSource(external_clock_source);
}
TEST(ExternalClockSource, SetOutLineAcquireRelease) {
TestDigitalOut digital_io_line;
EXPECT_EQ(digital_io_line.Enable(), PW_STATUS_OK);
EXPECT_EQ(digital_io_line.SetStateInactive(), PW_STATUS_OK);
ExternalClockSource external_clock_source(10ms, 5ms);
external_clock_source.SetOutLine(digital_io_line);
TestExternalClockSource(external_clock_source);
}
TEST(ExternalClockSource, NoOutLineSet) {
ExternalClockSource external_clock_source;
EXPECT_EQ(external_clock_source.ref_count(), 0u);
EXPECT_EQ(external_clock_source.Acquire(), PW_STATUS_FAILED_PRECONDITION);
EXPECT_EQ(external_clock_source.ref_count(), 0u);
}
} // namespace
} // namespace pw::clock_tree