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