xref: /aosp_15_r20/external/pigweed/pw_i2c/public/pw_i2c/initiator_mock.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <array>
17 #include <cstddef>
18 #include <optional>
19 
20 #include "pw_bytes/span.h"
21 #include "pw_containers/to_array.h"
22 #include "pw_i2c/initiator.h"
23 
24 namespace pw::i2c {
25 
26 /// Base class for creating transaction instances. For read-only,
27 /// write-only, or probe transactions, improve code readability
28 /// by using one of the following helpers instead:
29 ///
30 /// * `pw::i2c::ReadTransaction`
31 /// * `pw::i2c::WriteTransaction`
32 /// * `pw::i2c::ProbeTransaction`
33 ///
34 /// If you need to create a write-then-read transaction, you can
35 /// use this class.
36 class Transaction {
37  public:
38   /// Constructor for creating write-only, read-only, or write-then-read
39   /// transactions.
40   constexpr Transaction(
41       Status expected_return_value,
42       Address device_address,
43       ConstByteSpan write_buffer,
44       ConstByteSpan read_buffer,
45       std::optional<chrono::SystemClock::duration> timeout = std::nullopt)
return_value_(expected_return_value)46       : return_value_(expected_return_value),
47         read_buffer_(read_buffer),
48         write_buffer_(write_buffer),
49         address_(device_address),
50         timeout_(timeout) {}
51 
52   /// Alternative constructor for creating probe transactions.
53   constexpr Transaction(
54       Status expected_return_value,
55       Address device_address,
56       std::optional<chrono::SystemClock::duration> timeout = std::nullopt)
Transaction(expected_return_value,device_address,ConstByteSpan (),ignored_buffer_,timeout)57       : Transaction(expected_return_value,
58                     device_address,
59                     ConstByteSpan(),
60                     ignored_buffer_,
61                     timeout) {}
62 
63   /// Gets the buffer that is virtually read.
read_buffer()64   ConstByteSpan read_buffer() const { return read_buffer_; }
65 
66   /// Gets the buffer that the I2C device should write to.
write_buffer()67   ConstByteSpan write_buffer() const { return write_buffer_; }
68 
69   /// Gets the minimum duration to wait for a blocking I2C transaction.
timeout()70   std::optional<chrono::SystemClock::duration> timeout() const {
71     return timeout_;
72   }
73 
74   /// Gets the I2C address that the I2C transaction is targeting.
address()75   Address address() const { return address_; }
76 
77   /// Gets the expected return value for the transaction.
return_value()78   Status return_value() const { return return_value_; }
79 
80  private:
81   const Status return_value_;
82   const ConstByteSpan read_buffer_;
83   const ConstByteSpan write_buffer_;
84   static constexpr std::array<std::byte, 1> ignored_buffer_ = {};
85   const Address address_;
86   const std::optional<chrono::SystemClock::duration> timeout_;
87 };
88 
89 /// A helper that constructs a read-only I2C transaction.
90 /// Used for testing read transactions with `pw::i2c::MockInitiator`.
91 constexpr Transaction ReadTransaction(
92     Status expected_return_value,
93     Address device_address,
94     ConstByteSpan read_buffer,
95     std::optional<chrono::SystemClock::duration> timeout = std::nullopt) {
96   return Transaction(expected_return_value,
97                      device_address,
98                      ConstByteSpan(),
99                      read_buffer,
100                      timeout);
101 }
102 
103 /// A helper that constructs a write-only I2C transaction.
104 /// Used for testing write transactions with `pw::i2c::MockInitiator`.
105 constexpr Transaction WriteTransaction(
106     Status expected_return_value,
107     Address device_address,
108     ConstByteSpan write_buffer,
109     std::optional<chrono::SystemClock::duration> timeout = std::nullopt) {
110   return Transaction(expected_return_value,
111                      device_address,
112                      write_buffer,
113                      ConstByteSpan(),
114                      timeout);
115 }
116 
117 /// A helper that constructs a one-byte read I2C transaction.
118 /// Used for testing probe transactions with `pw::i2c::MockInitiator`.
119 constexpr Transaction ProbeTransaction(
120     Status expected_return_value,
121     Address device_address,
122     std::optional<chrono::SystemClock::duration> timeout = std::nullopt) {
123   return Transaction(expected_return_value, device_address, timeout);
124 }
125 
126 /// A generic mocked backend for `pw::i2c::Initiator` that's specifically
127 /// designed to make it easier to develop I2C device drivers.
128 /// `pw::i2c::MockInitiator` compares actual I2C transactions against expected
129 /// transactions. The expected transactions are represented as a list of
130 /// `pw::i2c::Transaction` instances that are passed as arguments in the
131 /// `pw::i2c::MockInitiator` constructor. Each consecutive call to
132 /// `pw::i2c::MockInitiator` iterates to the next expected transaction.
133 /// `pw::i2c::MockInitiator::Finalize()` indicates whether the actual
134 /// transactions matched the expected transactions.
135 ///
136 /// `pw::i2c::MockInitiator` is intended to be used within GoogleTest tests.
137 /// See @rstref{module-pw_unit_test}.
138 ///
139 /// @code{.cpp}
140 ///   #include <chrono>
141 ///
142 ///   #include "pw_bytes/array.h"
143 ///   #include "pw_i2c/address.h"
144 ///   #include "pw_i2c/initiator_mock.h"
145 ///   #include "pw_result/result.h"
146 ///   #include "pw_unit_test/framework.h"
147 ///
148 ///   using namespace std::chrono_literals;
149 ///
150 ///   namespace {
151 ///
152 ///   TEST(I2CTestSuite, I2CWriteTestCase) {
153 ///     constexpr pw::i2c::Address kAddress =
154 ///     pw::i2c::Address::SevenBit<0x01>(); constexpr auto kExpectedWrite =
155 ///     pw::bytes::Array<1, 2, 3>(); auto expected_transactions =
156 ///     pw::i2c::MakeExpectedTransactionArray(
157 ///       {pw::i2c::WriteTransaction(pw::OkStatus(), kAddress, kExpectedWrite,
158 ///       1ms)}
159 ///     );
160 ///     pw::i2c::MockInitiator initiator(expected_transactions);
161 ///     pw::ConstByteSpan kActualWrite = pw::bytes::Array<1, 2, 3>();
162 ///     pw::Status status = initiator.WriteFor(kAddress, kActualWrite, 1ms);
163 ///     EXPECT_EQ(initiator.Finalize(), pw::OkStatus());
164 ///   }
165 ///
166 ///   }
167 /// @endcode
168 class MockInitiator : public Initiator {
169  public:
MockInitiator(span<Transaction> transaction_list)170   explicit constexpr MockInitiator(span<Transaction> transaction_list)
171       : expected_transactions_(transaction_list),
172         expected_transaction_index_(0) {}
173 
174   /// Indicates whether the actual I2C transactions matched the expected
175   /// transactions. Should be called at the end of the test.
176   ///
177   /// @returns @rst
178   ///
179   /// .. pw-status-codes::
180   ///
181   ///    OK: The actual transactions matched the expected transactions.
182   ///
183   ///    OUT_OF_RANGE: The mocked set of transactions hasn't been exhausted.
184   ///
185   /// @endrst
Finalize()186   Status Finalize() const {
187     if (expected_transaction_index_ != expected_transactions_.size()) {
188       return Status::OutOfRange();
189     }
190     return Status();
191   }
192 
193   /// Runs `pw::i2c::MockInitiator::Finalize()` regardless of whether it was
194   /// already optionally finalized.
195   ~MockInitiator() override;
196 
197  private:
198   // Implements a mocked backend for the i2c initiator.
199   //
200   // Expects (via Gtest):
201   // tx_buffer == expected_transaction_tx_buffer
202   // tx_buffer.size() == expected_transaction_tx_buffer.size()
203   // rx_buffer.size() == expected_transaction_rx_buffer.size()
204   //
205   // Asserts:
206   // When the number of calls to this method exceed the number of expected
207   //    transactions.
208   //
209   // Returns:
210   // Specified transaction return type
211   Status DoWriteReadFor(Address device_address,
212                         ConstByteSpan tx_buffer,
213                         ByteSpan rx_buffer,
214                         chrono::SystemClock::duration timeout) override;
215 
216   span<Transaction> expected_transactions_;
217   size_t expected_transaction_index_;
218 };
219 
220 // Makes a new i2c transactions list.
221 template <size_t kSize>
MakeExpectedTransactionArray(const Transaction (& transactions)[kSize])222 constexpr std::array<Transaction, kSize> MakeExpectedTransactionArray(
223     const Transaction (&transactions)[kSize]) {
224   return containers::to_array(transactions);
225 }
226 
227 }  // namespace pw::i2c
228