// 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_preprocessor/util.h"
#include "pw_unit_test/framework.h"

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) {}

  pw::Status SetSource(ElementType& new_source,
                       uint32_t new_selector_enable,
                       uint32_t new_selector_disable,
                       bool permit_change_if_in_use) {
    // Store a copy of the current `selector_enable_` variable in case
    // that the update fails, since we need to update `selector_enable_`
    // to its new value, since `UpdateSource` might call the `DoEnable`
    // member function.
    uint32_t old_selector_enable = selector_enable_;
    selector_enable_ = new_selector_enable;
    pw::Status status = this->UpdateSource(new_source, permit_change_if_in_use);
    if (status.ok()) {
      selector_disable_ = new_selector_disable;
    } else {
      // Restore the old selector value.
      selector_enable_ = old_selector_enable;
    }

    return status;
  }

 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_;
  friend class ClockTreeSetSource;
};

using ClockSelectorTestBlocking = ClockSelectorTest<ElementBlocking>;
using ClockSelectorTestNonBlockingMightFail =
    ClockSelectorTest<ElementNonBlockingMightFail>;

class ClockTreeSetSource : public ClockTree {
 public:
  pw::Status SetSource(ClockSelectorTestBlocking& element,
                       ElementBlocking& new_source,
                       uint32_t selector_enable,
                       uint32_t selector_disable,
                       bool permit_change_if_in_use) {
    std::lock_guard lock(mutex_);
    return element.SetSource(
        new_source, selector_enable, selector_disable, permit_change_if_in_use);
  }

  pw::Status SetSource(ClockSelectorTestNonBlockingMightFail& element,
                       ElementNonBlockingMightFail& new_source,
                       uint32_t selector_enable,
                       uint32_t selector_disable,
                       bool permit_change_if_in_use) {
    std::lock_guard lock(interrupt_spin_lock_);
    return element.SetSource(
        new_source, selector_enable, selector_disable, permit_change_if_in_use);
  }
};

struct clock_source_state_test_call_data {
  uint32_t value;
  ClockOperation op;
};

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:
  void ValidateClockAction(ClockOperation op) {
    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);
    }
    test_data_.num_calls++;
  }

  pw::Status DoEnable() final {
    *clock_state_ |= value_;
    ValidateClockAction(ClockOperation::kAcquire);
    return pw::OkStatus();
  }

  pw::Status DoDisable() final {
    *clock_state_ &= ~value_;
    ValidateClockAction(ClockOperation::kRelease);
    return pw::OkStatus();
  }

  uint32_t value_;
  uint32_t* clock_state_;
  struct clock_source_state_test_data& test_data_;
};
using ClockSourceStateTestBlocking = ClockSelectorTest<ElementBlocking>;
using ClockSourceStateTestNonBlocking =
    ClockSelectorTest<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() {
  ClockTree clock_tree;
  pw::Status status;
  ClockSourceTest<ElementType> clock_a;

  EXPECT_EQ(clock_a.ref_count(), 0u);

  status = clock_tree.Acquire(clock_a);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);

  status = clock_tree.Acquire(clock_a);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 2u);

  status = clock_tree.Release(clock_a);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);

  status = clock_tree.Release(clock_a);
  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);
  ClockTree clock_tree;

  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_tree.Acquire(clock_divider_b);
  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_tree.Acquire(clock_divider_b_element);
  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_tree.Acquire(clock_divider_c);
  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_tree.Release(clock_divider_b);
  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_tree.Release(clock_divider_b_element);
  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_tree.Release(clock_divider_c);
  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);
  ClockTree clock_tree;
  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_tree.Acquire(clock_divider_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_divider_b.ref_count(), 1u);

  status = clock_tree.SetDividerValue(clock_divider_b_abstract, 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_tree.Release(clock_divider_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_divider_b.ref_count(), 0u);

  status = clock_tree.SetDividerValue(clock_divider_b, 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_tree.Acquire(clock_divider_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_divider_b.ref_count(), 1u);

  status = clock_tree.Release(clock_divider_b);
  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>();
}

// 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);
  ClockTree clock_tree;
  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_tree.Acquire(clock_divider_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_divider_b.ref_count(), 1u);

  status = clock_tree.SetDividerValue(clock_divider_b, 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_tree.Release(clock_divider_b);
  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);
  ClockTree clock_tree;
  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_tree.Acquire(clock_selector_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  status = clock_tree.Acquire(clock_selector_b_element);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 2u);

  status = clock_tree.Release(clock_selector_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_b_element);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_selector_b.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_b);
  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>();
}

// Validate that we can update the source of a selector.
template <typename ElementType>
static void TestClockSelectorUpdateSource() {
  const bool kPermitUpdateWhileInUse = true;
  const bool kProhibitUpdateWhileInUse = false;
  const uint32_t kSelector = 41;
  struct clock_selector_test_call_data call_data[] = {
      {kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()},
      {kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 4, ClockOperation::kRelease, pw::OkStatus()},
      {kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 4, ClockOperation::kRelease, pw::OkStatus()},
      {kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()},
      {kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 4, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data test_data;
  INIT_TEST_DATA(test_data, call_data);
  ClockTreeSetSource clock_tree;
  pw::Status status;

  ClockSourceTest<ElementType> clock_a;
  ClockSourceTest<ElementType> clock_b;
  ClockSelectorTest<ElementType> clock_selector_c(
      clock_a, kSelector, 1, 8, test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Validate that we cannot change the source when the reference count is held,
  // while we are prohibited from changing the source with an active reference
  // count.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 20, 40, kProhibitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_FAILED_PRECONDITION);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Validate that we can change the source when the reference count is held,
  // while we are permitted to change the source with an active reference count.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 2u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Validate that we are re-enabling clock_b.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Validate that we can change the source when no reference count is held,
  // while we are prohibited from changing the source with an active reference
  // count.
  status = clock_tree.SetSource(
      clock_selector_c, clock_a, 1, 8, kProhibitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Validate that we are enabling clock_a.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Validate that we can change the source when no reference count is held,
  // while we are permitted to change the source with an active reference count.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Validate that we are enabling clock_b.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);
  EXPECT_EQ(test_data.num_calls, test_data.num_expected_calls);
}

TEST(ClockTree, ClockSelectorUpdateSourceBlocking) {
  TestClockSelectorUpdateSource<ElementBlocking>();
}

TEST(ClockTree, ClockSelectorUpdateSourceNonBlocking) {
  TestClockSelectorUpdateSource<ElementNonBlockingMightFail>();
}

// Validate that `ClockSource` and current configured selector remain
// unchanged if updating clock source fails when acquiring reference
// to new source.
template <typename ElementType>
static void TestClockSelectorUpdateSourceFailure1() {
  const bool kPermitUpdateWhileInUse = true;

  struct clock_source_failure_test_call_data clock_a_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()},
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_a_test_data;
  INIT_TEST_DATA(clock_a_test_data, clock_a_call_data);
  ClockSourceFailureTest<ElementType> clock_a(clock_a_test_data);
  ;

  struct clock_source_failure_test_call_data clock_b_call_data[] = {
      {ClockOperation::kAcquire, pw::Status::Internal()}};

  struct clock_source_failure_test_data clock_b_test_data;
  INIT_TEST_DATA(clock_b_test_data, clock_b_call_data);
  ClockSourceFailureTest<ElementType> clock_b(clock_b_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()},
      {kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data selector_c_test_data;
  INIT_TEST_DATA(selector_c_test_data, selector_call_data);

  ClockTreeSetSource clock_tree;
  pw::Status status;

  ClockSelectorTest<ElementType> clock_selector_c(
      clock_a, kSelector, 1, 8, selector_c_test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Try to acquire a reference to the new source, which will fail. Then
  // validate that everything remained in place, and that the selector
  // configuration hasn't changed by releasing and reacquiring the
  // `clock_selector_c`.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Release the selector and verify that the correct selector value gets
  // configured.
  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Acquire and release the selector and verify that the correct selector
  // values get configured again.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  EXPECT_EQ(clock_a_test_data.num_calls, clock_a_test_data.num_expected_calls);
  EXPECT_EQ(clock_b_test_data.num_calls, clock_b_test_data.num_expected_calls);
  EXPECT_EQ(selector_c_test_data.num_calls,
            selector_c_test_data.num_expected_calls);
}
TEST(ClockTree, ClockSelectorUpdateSourceFailure1Blocking) {
  TestClockSelectorUpdateSourceFailure1<ElementBlocking>();
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure1NonBlocking) {
  TestClockSelectorUpdateSourceFailure1<ElementNonBlockingMightFail>();
}

// Validate that `ClockSource` and current configured selector remain
// unchanged if `DoDisable` call fails of current selector. The
// new source reference count should remain unchanged at the end.
template <typename ElementType>
static void TestClockSelectorUpdateSourceFailure2() {
  const bool kPermitUpdateWhileInUse = true;

  struct clock_source_failure_test_call_data clock_a_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()},
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_a_test_data;
  INIT_TEST_DATA(clock_a_test_data, clock_a_call_data);
  ClockSourceFailureTest<ElementType> clock_a(clock_a_test_data);
  ;

  struct clock_source_failure_test_call_data clock_b_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_b_test_data;
  INIT_TEST_DATA(clock_b_test_data, clock_b_call_data);
  ClockSourceFailureTest<ElementType> clock_b(clock_b_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()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()},
      {kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data selector_c_test_data;
  INIT_TEST_DATA(selector_c_test_data, selector_call_data);

  ClockTreeSetSource clock_tree;
  pw::Status status;

  ClockSelectorTest<ElementType> clock_selector_c(
      clock_a, kSelector, 1, 8, selector_c_test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Try to disable the old source, which will fail. Then validate that
  // everything remained in place, and that the selector configuration hasn't
  // changed by releasing and reacquiring the `clock_selector_c`.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Release the selector and verify that the correct selector value gets
  // configured.
  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Acquire and release the selector and verify that the correct selector
  // values get configured again.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  EXPECT_EQ(clock_a_test_data.num_calls, clock_a_test_data.num_expected_calls);
  EXPECT_EQ(clock_b_test_data.num_calls, clock_b_test_data.num_expected_calls);
  EXPECT_EQ(selector_c_test_data.num_calls,
            selector_c_test_data.num_expected_calls);
}
TEST(ClockTree, ClockSelectorUpdateSourceFailure2Blocking) {
  TestClockSelectorUpdateSourceFailure2<ElementBlocking>();
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure2NonBlocking) {
  TestClockSelectorUpdateSourceFailure2<ElementNonBlockingMightFail>();
}

// Validate that `ClockSource` and current configured selector remain
// unchanged if `DoDisable` call fails of current selector.
// The `DoDisable` call of the new source will fail as well, so validate
// that the new source got enabled as well.
template <typename ElementType>
static void TestClockSelectorUpdateSourceFailure3() {
  const bool kPermitUpdateWhileInUse = true;

  struct clock_source_failure_test_call_data clock_a_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()},
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_a_test_data;
  INIT_TEST_DATA(clock_a_test_data, clock_a_call_data);
  ClockSourceFailureTest<ElementType> clock_a(clock_a_test_data);
  ;

  struct clock_source_failure_test_call_data clock_b_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::Status::FailedPrecondition()}};

  struct clock_source_failure_test_data clock_b_test_data;
  INIT_TEST_DATA(clock_b_test_data, clock_b_call_data);
  ClockSourceFailureTest<ElementType> clock_b(clock_b_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()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()},
      {kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data selector_c_test_data;
  INIT_TEST_DATA(selector_c_test_data, selector_call_data);

  ClockTreeSetSource clock_tree;
  pw::Status status;

  ClockSelectorTest<ElementType> clock_selector_c(
      clock_a, kSelector, 1, 8, selector_c_test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Try to disable the old source, which will fail, and try to disable the new
  // source which will fail as well. Then validate that everything remained in
  // place, and that the selector configuration hasn't changed by releasing and
  // reacquiring the `clock_selector_c`, but also that the new source got
  // acquired.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Release the selector and verify that the correct selector value gets
  // configured.
  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Acquire and release the selector and verify that the correct selector
  // values get configured again.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  EXPECT_EQ(clock_a_test_data.num_calls, clock_a_test_data.num_expected_calls);
  EXPECT_EQ(clock_b_test_data.num_calls, clock_b_test_data.num_expected_calls);
  EXPECT_EQ(selector_c_test_data.num_calls,
            selector_c_test_data.num_expected_calls);
}
TEST(ClockTree, ClockSelectorUpdateSourceFailure3Blocking) {
  TestClockSelectorUpdateSourceFailure3<ElementBlocking>();
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure3NonBlocking) {
  TestClockSelectorUpdateSourceFailure3<ElementNonBlockingMightFail>();
}

// Validate that `ClockSource` gets disabled, if new clock source's `DoEnable`
// call fails.
template <typename ElementType>
static void TestClockSelectorUpdateSourceFailure4() {
  const bool kPermitUpdateWhileInUse = true;

  struct clock_source_failure_test_call_data clock_a_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()},
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_a_test_data;
  INIT_TEST_DATA(clock_a_test_data, clock_a_call_data);
  ClockSourceFailureTest<ElementType> clock_a(clock_a_test_data);
  ;

  struct clock_source_failure_test_call_data clock_b_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_b_test_data;
  INIT_TEST_DATA(clock_b_test_data, clock_b_call_data);
  ClockSourceFailureTest<ElementType> clock_b(clock_b_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()},
      {kSelector, 2, ClockOperation::kAcquire, pw::Status::Internal()},
      {kSelector, 1, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 8, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data selector_c_test_data;
  INIT_TEST_DATA(selector_c_test_data, selector_call_data);

  ClockTreeSetSource clock_tree;
  pw::Status status;

  ClockSelectorTest<ElementType> clock_selector_c(
      clock_a, kSelector, 1, 8, selector_c_test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Try to enable the new source, which will fail. Since the new source failed
  // to enable after we disabled the old source, everything should be disabled
  // at this point. When we enable the selector again, the old source should get
  // re-enabled again.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  // Acquire and release the selector and verify that the correct selector
  // values get configured again.
  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  EXPECT_EQ(clock_a_test_data.num_calls, clock_a_test_data.num_expected_calls);
  EXPECT_EQ(clock_b_test_data.num_calls, clock_b_test_data.num_expected_calls);
  EXPECT_EQ(selector_c_test_data.num_calls,
            selector_c_test_data.num_expected_calls);
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure4Blocking) {
  TestClockSelectorUpdateSourceFailure4<ElementBlocking>();
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure4NonBlocking) {
  TestClockSelectorUpdateSourceFailure4<ElementNonBlockingMightFail>();
}

// Validate that we try to release `ClockSource` if new clock source gets
// enabled, and that the failure of release has no impact on newly conifgured
// selector setting.
template <typename ElementType>
static void TestClockSelectorUpdateSourceFailure5() {
  const bool kPermitUpdateWhileInUse = true;

  struct clock_source_failure_test_call_data clock_a_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::Status::Internal()}};

  struct clock_source_failure_test_data clock_a_test_data;
  INIT_TEST_DATA(clock_a_test_data, clock_a_call_data);
  ClockSourceFailureTest<ElementType> clock_a(clock_a_test_data);
  ;

  struct clock_source_failure_test_call_data clock_b_call_data[] = {
      {ClockOperation::kAcquire, pw::OkStatus()},
      {ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_source_failure_test_data clock_b_test_data;
  INIT_TEST_DATA(clock_b_test_data, clock_b_call_data);
  ClockSourceFailureTest<ElementType> clock_b(clock_b_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()},
      {kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 4, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data selector_c_test_data;
  INIT_TEST_DATA(selector_c_test_data, selector_call_data);

  ClockTreeSetSource clock_tree;
  pw::Status status;

  ClockSelectorTest<ElementType> clock_selector_c(
      clock_a, kSelector, 1, 8, selector_c_test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  // Enable the new source, but releasing the old source fails. The new source
  // should be active, but the old source will keep its reference.
  status = clock_tree.SetSource(
      clock_selector_c, clock_b, 2, 4, kPermitUpdateWhileInUse);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 1u);
  EXPECT_EQ(clock_selector_c.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_c);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_b.ref_count(), 0u);
  EXPECT_EQ(clock_selector_c.ref_count(), 0u);

  EXPECT_EQ(clock_a_test_data.num_calls, clock_a_test_data.num_expected_calls);
  EXPECT_EQ(clock_b_test_data.num_calls, clock_b_test_data.num_expected_calls);
  EXPECT_EQ(selector_c_test_data.num_calls,
            selector_c_test_data.num_expected_calls);
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure5Blocking) {
  TestClockSelectorUpdateSourceFailure5<ElementBlocking>();
}

TEST(ClockTree, ClockSelectorUpdateSourceFailure5NonBlocking) {
  TestClockSelectorUpdateSourceFailure5<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},
      {4, ClockOperation::kAcquire},
      {2, ClockOperation::kAcquire},
      {1, ClockOperation::kRelease},
      {2, ClockOperation::kRelease},
      {4, ClockOperation::kRelease}};

  struct clock_source_state_test_data test_data;
  INIT_TEST_DATA(test_data, call_data);
  ClockTree clock_tree;
  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_tree.Acquire(clock_a);
  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_tree.Acquire(clock_c_element);
  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_tree.Acquire(clock_b);
  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_tree.Release(clock_a);
  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_tree.Release(clock_b);
  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_tree.Release(clock_c_element);
  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);

  ClockTree clock_tree;
  pw::Status status;

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_selector_b.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_b);
  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);

  ClockTree clock_tree;
  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_tree.Acquire(clock_divider_c);
  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);

  ClockTree clock_tree;
  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_tree.Acquire(clock_divider_c);
  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);

  ClockTree clock_tree;
  pw::Status status;

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_selector_b.ref_count(), 0u);

  // Acquire initial references
  status = clock_tree.Acquire(clock_selector_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_b);
  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);

  ClockTree clock_tree;
  pw::Status status;

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_selector_b.ref_count(), 0u);

  status = clock_tree.Acquire(clock_selector_b);
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  status = clock_tree.Release(clock_selector_b);
  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.may_block());

  ClockSourceTest<ElementNonBlockingMightFail> clock_non_blocking_might_fail;
  EXPECT_FALSE(clock_non_blocking_might_fail.may_block());

  ClockSourceTest<ElementBlocking> clock_blocking;
  EXPECT_TRUE(clock_blocking.may_block());
}

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.may_block());

  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.may_block());

  ClockDividerTest<ElementBlocking> clock_divider_blocking(
      clock_blocking, 1, 1, test_data);
  EXPECT_TRUE(clock_divider_blocking.may_block());
}

// Validate that the ElementController performs the correct
// clock operations and returns the expected status codes.
template <typename ElementType>
static void TestElementController() {
  const uint32_t kSelector = 41;
  struct clock_selector_test_call_data call_data[] = {
      {kSelector, 2, ClockOperation::kAcquire, pw::Status::Internal()},
      {kSelector, 2, ClockOperation::kAcquire, pw::OkStatus()},
      {kSelector, 7, ClockOperation::kRelease, pw::Status::Internal()},
      {kSelector, 7, ClockOperation::kRelease, pw::OkStatus()}};

  struct clock_selector_test_data test_data;
  INIT_TEST_DATA(test_data, call_data);
  ClockTree clock_tree;
  pw::Status status;

  ClockSourceTest<ElementType> clock_a;
  ClockSelectorTest<ElementType> clock_selector_b(
      clock_a, kSelector, 2, 7, test_data);

  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_selector_b.ref_count(), 0u);

  // Specify an element controller with valid pointers.
  ElementController clock_tree_element_controller(&clock_tree,
                                                  &clock_selector_b);

  // First acquire call should fail.
  status = clock_tree_element_controller.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
  EXPECT_EQ(clock_a.ref_count(), 0u);
  EXPECT_EQ(clock_selector_b.ref_count(), 0u);

  // Second acquire call should succeed
  status = clock_tree_element_controller.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  // Third acquire call should succeed
  status = clock_tree_element_controller.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 2u);

  // First release call should succeed, since this only changes the reference
  // count of `clock_selector_b`.
  status = clock_tree_element_controller.Release();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  // Second release call should fail and not change the reference counts.
  status = clock_tree_element_controller.Release();
  EXPECT_EQ(status.code(), PW_STATUS_INTERNAL);
  EXPECT_EQ(clock_a.ref_count(), 1u);
  EXPECT_EQ(clock_selector_b.ref_count(), 1u);

  // Third release call should succeed.
  status = clock_tree_element_controller.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, ElementControllerBlocking) {
  TestElementController<ElementBlocking>();
}

TEST(ClockTree, ElementControllerNonBlocking) {
  TestElementController<ElementNonBlockingMightFail>();
}

// Validate that the ElementController performs clock operations
// for ElementNonBlockingCannotFail elements.
TEST(ClockTree, ElementControllerCannotFail) {
  ClockTree clock_tree;
  pw::Status status;

  ClockSourceTest<ElementNonBlockingCannotFail> clock_a;

  EXPECT_EQ(clock_a.ref_count(), 0u);

  // Specify an element controller with valid pointers.
  ElementController clock_tree_element_controller(&clock_tree, &clock_a);

  // Acquire call should succeed
  status = clock_tree_element_controller.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);

  // Acquire call should succeed
  status = clock_tree_element_controller.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 2u);

  // Release call should succeed.
  status = clock_tree_element_controller.Release();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 1u);

  // Release call should succeed.
  status = clock_tree_element_controller.Release();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);
}

// Validate that the ElementController performs no clock operations
// if not both clock tree and element are specified.
TEST(ClockTree, ElementControllerNoClockOperations) {
  ClockTree clock_tree;
  pw::Status status;

  ClockSourceTest<ElementNonBlockingCannotFail> clock_a;

  EXPECT_EQ(clock_a.ref_count(), 0u);

  // Specify an element controller with no clock_tree pointer.
  ElementController clock_tree_element_controller_no_clock_tree(nullptr,
                                                                &clock_a);

  // Acquire shouldn't acquire a reference to `clock_a`
  // due to the missing `clock_tree`.
  status = clock_tree_element_controller_no_clock_tree.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);

  // Release shouldn't release a reference to `clock_a`
  // due to the missing `clock_tree`.
  status = clock_tree_element_controller_no_clock_tree.Release();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
  EXPECT_EQ(clock_a.ref_count(), 0u);

  // Specify an element controller with no element pointer.
  ElementController clock_tree_element_controller_no_element(&clock_tree,
                                                             nullptr);

  status = clock_tree_element_controller_no_element.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);

  status = clock_tree_element_controller_no_clock_tree.Release();
  EXPECT_EQ(status.code(), PW_STATUS_OK);

  // Specify an element controller with two null pointers.
  ElementController clock_tree_element_controller_nullptrs;

  status = clock_tree_element_controller_nullptrs.Acquire();
  EXPECT_EQ(status.code(), PW_STATUS_OK);

  status = clock_tree_element_controller_nullptrs.Release();
  EXPECT_EQ(status.code(), PW_STATUS_OK);
}

// 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);

  ClockTree clock_tree;

  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_tree.Acquire(clock_divider_a);
  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_tree.Acquire(clock_divider_a);
  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_tree.Acquire(clock_divider_b);
  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_tree.Release(clock_divider_b);
  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_tree.Release(clock_divider_a);
  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_tree.Release(clock_divider_a);
  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);
}

}  // namespace
}  // namespace pw::clock_tree
