1 // Copyright 2023 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
15 #pragma once
16 #include <cpp-string/string_printf.h>
17
18 #include <algorithm>
19 #include <array>
20 #include <iostream>
21 #include <type_traits>
22
23 #include "gmock/gmock.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
25
26 // Run |statement| and return if a fatal test error occurred. Include the file
27 // name and line number in the output.
28 //
29 // This is useful for running test helpers in subroutines. For example, if a
30 // test helper posted ASSERTs checks inside of a dispatcher task:
31 //
32 // RETURN_IF_FATAL(RunLoopUntilIdle());
33 //
34 // would return if any of the tasks had an ASSERT failure.
35 //
36 // Fatal failures in Google Test such as ASSERT_* failures and calls to FAIL()
37 // set a global flag for the failure (see testing::Test::HasFatalFailure) and
38 // return from the function where they occur. However, the change in flow
39 // control is limited to that subroutine scope. Test code higher up the stack
40 // must propagate the failure in order to exit the test.
41 //
42 // Note in the above example, if a task in the test loop has a fatal failure, it
43 // does not prevent the remaining due tasks in the loop from running. The test
44 // case would not exit until RunLoopFromIdle returns.
45 #define RETURN_IF_FATAL(statement) \
46 do { \
47 SCOPED_TRACE(""); \
48 ASSERT_NO_FATAL_FAILURE(statement); \
49 } while (false)
50
51 namespace bt {
52
53 template <class InputIt>
ByteContainerToString(InputIt begin,InputIt end)54 std::string ByteContainerToString(InputIt begin, InputIt end) {
55 std::string bytes_string;
56 for (InputIt iter = begin; iter != end; ++iter) {
57 bytes_string += bt_lib_cpp_string::StringPrintf("0x%.2x ", *iter);
58 }
59 return bytes_string;
60 }
61
62 template <class Container>
ByteContainerToString(const Container & c)63 std::string ByteContainerToString(const Container& c) {
64 return ByteContainerToString(c.begin(), c.end());
65 }
66
67 template <class InputIt>
PrintByteContainer(InputIt begin,InputIt end)68 void PrintByteContainer(InputIt begin, InputIt end) {
69 std::cout << ByteContainerToString(begin, end);
70 }
71
72 // Prints the contents of a container as a string.
73 template <class Container>
PrintByteContainer(const Container & c)74 void PrintByteContainer(const Container& c) {
75 PrintByteContainer(c.begin(), c.end());
76 }
77
78 // Function-template for comparing contents of two iterable byte containers for
79 // equality. If the contents are not equal, this logs a GTEST-style error
80 // message to stdout. Meant to be used from unit tests.
81 template <class InputIt1, class InputIt2>
ContainersEqual(InputIt1 expected_begin,InputIt1 expected_end,InputIt2 actual_begin,InputIt2 actual_end)82 bool ContainersEqual(InputIt1 expected_begin,
83 InputIt1 expected_end,
84 InputIt2 actual_begin,
85 InputIt2 actual_end) {
86 if (std::equal(expected_begin, expected_end, actual_begin, actual_end))
87 return true;
88 std::cout << "Expected: (" << (expected_end - expected_begin) << " bytes) { ";
89 PrintByteContainer(expected_begin, expected_end);
90 std::cout << "}\n Found: (" << (actual_end - actual_begin) << " bytes) { ";
91 PrintByteContainer(actual_begin, actual_end);
92 std::cout << "}" << std::endl;
93 return false;
94 }
95
96 template <class Container1, class Container2>
ContainersEqual(const Container1 & expected,const Container2 & actual)97 bool ContainersEqual(const Container1& expected, const Container2& actual) {
98 return ContainersEqual(
99 expected.begin(), expected.end(), actual.begin(), actual.end());
100 }
101
102 template <class Container1>
ContainersEqual(const Container1 & expected,const uint8_t * actual_bytes,size_t actual_num_bytes)103 bool ContainersEqual(const Container1& expected,
104 const uint8_t* actual_bytes,
105 size_t actual_num_bytes) {
106 return ContainersEqual(expected.begin(),
107 expected.end(),
108 actual_bytes,
109 actual_bytes + actual_num_bytes);
110 }
111
112 // Returns a managed pointer to a heap allocated MutableByteBuffer.
113 template <typename... T>
NewBuffer(T...bytes)114 MutableByteBufferPtr NewBuffer(T... bytes) {
115 return std::make_unique<StaticByteBuffer<sizeof...(T)>>(
116 std::forward<T>(bytes)...);
117 }
118
119 // Returns the value of |x| as a little-endian array, i.e. the first byte of the
120 // array has the value of the least significant byte of |x|.
121 template <typename T>
ToBytes(T x)122 constexpr std::array<uint8_t, sizeof(T)> ToBytes(T x) {
123 static_assert(std::is_integral_v<T>,
124 "Must use integral types for safe bytewise access");
125 std::array<uint8_t, sizeof(T)> bytes;
126 for (auto& byte : bytes) {
127 byte = static_cast<uint8_t>(x);
128 x >>= 8;
129 }
130 return bytes;
131 }
132
133 // Returns the Upper/Lower bits of a uint16_t
UpperBits(const uint16_t x)134 constexpr uint8_t UpperBits(const uint16_t x) { return ToBytes(x).back(); }
LowerBits(const uint16_t x)135 constexpr uint8_t LowerBits(const uint16_t x) { return ToBytes(x).front(); }
136
137 // Wraps ContainerEq, which doesn't support comparing different ByteBuffer types
138 MATCHER_P(BufferEq, b, "") {
139 return ::testing::ExplainMatchResult(
140 ::testing::ContainerEq(bt::BufferView(b)),
141 bt::BufferView(arg),
142 result_listener);
143 }
144
145 } // namespace bt
146