Alexei Frolov | 99de52d | 2021-05-11 19:58:01 -0700 | [diff] [blame] | 1 | // Copyright 2021 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 | #include "pw_function/function.h" |
| 16 | |
| 17 | #include "gtest/gtest.h" |
Wyatt Hepler | f5cdd93 | 2021-06-14 13:53:23 -0700 | [diff] [blame] | 18 | #include "pw_polyfill/language_feature_macros.h" |
Alexei Frolov | 99de52d | 2021-05-11 19:58:01 -0700 | [diff] [blame] | 19 | |
| 20 | namespace pw { |
| 21 | namespace { |
| 22 | |
Wyatt Hepler | f5cdd93 | 2021-06-14 13:53:23 -0700 | [diff] [blame] | 23 | // Ensure that Function can be constant initialized. |
| 24 | [[maybe_unused]] PW_CONSTINIT Function<void()> can_be_constant_initialized; |
| 25 | |
Alexei Frolov | 99de52d | 2021-05-11 19:58:01 -0700 | [diff] [blame] | 26 | int Multiply(int a, int b) { return a * b; } |
| 27 | |
| 28 | TEST(Function, OperatorCall) { |
| 29 | Function<int(int, int)> multiply(Multiply); |
| 30 | EXPECT_EQ(multiply(3, 7), 21); |
| 31 | } |
| 32 | |
| 33 | void CallbackAdd(int a, int b, pw::Function<void(int sum)> callback) { |
| 34 | callback(a + b); |
| 35 | } |
| 36 | |
| 37 | int add_result = -1; |
| 38 | |
| 39 | void free_add_callback(int sum) { add_result = sum; } |
| 40 | |
| 41 | TEST(Function, ConstructInPlace_FreeFunction) { |
| 42 | add_result = -1; |
| 43 | CallbackAdd(25, 17, free_add_callback); |
| 44 | EXPECT_EQ(add_result, 42); |
| 45 | } |
| 46 | |
| 47 | TEST(Function, ConstructInPlace_NonCapturingLambda) { |
| 48 | add_result = -1; |
| 49 | CallbackAdd(25, 18, [](int sum) { add_result = sum; }); |
| 50 | EXPECT_EQ(add_result, 43); |
| 51 | } |
| 52 | |
| 53 | TEST(Function, ConstructInPlace_CapturingLambda) { |
| 54 | int result = -1; |
| 55 | CallbackAdd(25, 19, [&](int sum) { result = sum; }); |
| 56 | EXPECT_EQ(result, 44); |
| 57 | } |
| 58 | |
| 59 | class CallableObject { |
| 60 | public: |
| 61 | CallableObject(int* result) : result_(result) {} |
| 62 | |
| 63 | CallableObject(CallableObject&& other) = default; |
| 64 | CallableObject& operator=(CallableObject&& other) = default; |
| 65 | |
| 66 | void operator()(int sum) { *result_ = sum; } |
| 67 | |
| 68 | private: |
| 69 | int* result_; |
| 70 | }; |
| 71 | |
| 72 | TEST(Function, ConstructInPlace_CallableObject) { |
| 73 | int result = -1; |
| 74 | CallbackAdd(25, 20, CallableObject(&result)); |
| 75 | EXPECT_EQ(result, 45); |
| 76 | } |
| 77 | |
| 78 | class MemberFunctionTest : public ::testing::Test { |
| 79 | protected: |
| 80 | MemberFunctionTest() : result_(-1) {} |
| 81 | |
| 82 | void set_result(int result) { result_ = result; } |
| 83 | |
| 84 | int result_; |
| 85 | }; |
| 86 | |
| 87 | TEST_F(MemberFunctionTest, ConstructInPlace_Lambda) { |
| 88 | CallbackAdd(25, 21, [this](int sum) { set_result(sum); }); |
| 89 | EXPECT_EQ(result_, 46); |
| 90 | } |
| 91 | |
| 92 | TEST(Function, Null_OperatorBool) { |
| 93 | Closure implicit_null; |
| 94 | Closure explicit_null(nullptr); |
| 95 | Closure assigned_null = nullptr; |
| 96 | Closure not_null([]() {}); |
| 97 | |
| 98 | EXPECT_FALSE(bool(implicit_null)); |
| 99 | EXPECT_FALSE(bool(explicit_null)); |
| 100 | EXPECT_FALSE(bool(assigned_null)); |
| 101 | EXPECT_TRUE(bool(not_null)); |
| 102 | |
| 103 | EXPECT_TRUE(!implicit_null); |
| 104 | EXPECT_TRUE(!explicit_null); |
| 105 | EXPECT_TRUE(!assigned_null); |
| 106 | EXPECT_FALSE(!not_null); |
| 107 | } |
| 108 | |
| 109 | TEST(Function, Null_OperatorEquals) { |
| 110 | Closure implicit_null; |
| 111 | Closure explicit_null(nullptr); |
| 112 | Closure assigned_null = nullptr; |
| 113 | Closure not_null([]() {}); |
| 114 | |
| 115 | EXPECT_TRUE(implicit_null == nullptr); |
| 116 | EXPECT_TRUE(explicit_null == nullptr); |
| 117 | EXPECT_TRUE(assigned_null == nullptr); |
| 118 | EXPECT_TRUE(not_null != nullptr); |
| 119 | |
| 120 | EXPECT_FALSE(implicit_null != nullptr); |
| 121 | EXPECT_FALSE(explicit_null != nullptr); |
| 122 | EXPECT_FALSE(assigned_null != nullptr); |
| 123 | EXPECT_FALSE(not_null == nullptr); |
| 124 | } |
| 125 | |
| 126 | TEST(Function, Null_Set) { |
| 127 | Closure function = []() {}; |
| 128 | EXPECT_NE(function, nullptr); |
| 129 | function = nullptr; |
| 130 | EXPECT_EQ(function, nullptr); |
| 131 | } |
| 132 | |
| 133 | void DoNothing() {} |
| 134 | |
| 135 | TEST(Function, Null_FunctionPointer) { |
| 136 | void (*ptr)() = DoNothing; |
| 137 | Closure not_null(ptr); |
| 138 | EXPECT_NE(not_null, nullptr); |
| 139 | ptr = nullptr; |
| 140 | Closure is_null(ptr); |
| 141 | EXPECT_EQ(is_null, nullptr); |
| 142 | } |
| 143 | |
| 144 | TEST(Function, Move_Null) { |
| 145 | Closure moved; |
| 146 | EXPECT_EQ(moved, nullptr); |
| 147 | Closure function(std::move(moved)); |
| 148 | EXPECT_EQ(function, nullptr); |
| 149 | |
| 150 | // Ignore use-after-move. |
| 151 | #ifndef __clang_analyzer__ |
| 152 | EXPECT_EQ(moved, nullptr); |
| 153 | #endif // __clang_analyzer__ |
| 154 | } |
| 155 | |
| 156 | TEST(Function, MoveAssign_Null) { |
| 157 | Closure moved; |
| 158 | EXPECT_EQ(moved, nullptr); |
| 159 | Closure function = std::move(moved); |
| 160 | EXPECT_EQ(function, nullptr); |
| 161 | |
| 162 | // Ignore use-after-move. |
| 163 | #ifndef __clang_analyzer__ |
| 164 | EXPECT_EQ(moved, nullptr); |
| 165 | #endif // __clang_analyzer__ |
| 166 | } |
| 167 | |
| 168 | TEST(Function, Move_Inline) { |
| 169 | Function<int(int, int)> moved(Multiply); |
| 170 | EXPECT_NE(moved, nullptr); |
| 171 | Function<int(int, int)> multiply(std::move(moved)); |
| 172 | EXPECT_EQ(multiply(3, 3), 9); |
| 173 | |
| 174 | // Ignore use-after-move. |
| 175 | #ifndef __clang_analyzer__ |
| 176 | EXPECT_EQ(moved, nullptr); |
| 177 | #endif // __clang_analyzer__ |
| 178 | } |
| 179 | |
| 180 | TEST(Function, MoveAssign_Inline) { |
| 181 | Function<int(int, int)> moved(Multiply); |
| 182 | EXPECT_NE(moved, nullptr); |
| 183 | Function<int(int, int)> multiply = std::move(moved); |
| 184 | EXPECT_EQ(multiply(3, 3), 9); |
| 185 | |
| 186 | // Ignore use-after-move. |
| 187 | #ifndef __clang_analyzer__ |
| 188 | EXPECT_EQ(moved, nullptr); |
| 189 | #endif // __clang_analyzer__ |
| 190 | } |
| 191 | |
| 192 | class MoveTracker { |
| 193 | public: |
| 194 | MoveTracker() : move_count_(0) {} |
| 195 | |
| 196 | MoveTracker(MoveTracker&& other) : move_count_(other.move_count_ + 1) {} |
| 197 | MoveTracker& operator=(MoveTracker&& other) = default; |
| 198 | |
| 199 | int operator()() const { return move_count_; } |
| 200 | |
| 201 | private: |
| 202 | int move_count_; |
| 203 | }; |
| 204 | |
| 205 | TEST(Function, Move_CustomObject) { |
| 206 | Function<int()> moved((MoveTracker())); |
| 207 | EXPECT_EQ(moved(), 2); // internally moves twice on construction |
| 208 | Function<int()> tracker(std::move(moved)); |
| 209 | EXPECT_EQ(tracker(), 3); |
| 210 | |
| 211 | // Ignore use-after-move. |
| 212 | #ifndef __clang_analyzer__ |
| 213 | EXPECT_EQ(moved, nullptr); |
| 214 | #endif // __clang_analyzer__ |
| 215 | } |
| 216 | |
| 217 | TEST(Function, MoveAssign_CustomObject) { |
| 218 | Function<int()> moved((MoveTracker())); |
| 219 | EXPECT_EQ(moved(), 2); // internally moves twice on construction |
| 220 | Function<int()> tracker = std::move(moved); |
| 221 | EXPECT_EQ(tracker(), 3); |
| 222 | |
| 223 | // Ignore use-after-move. |
| 224 | #ifndef __clang_analyzer__ |
| 225 | EXPECT_EQ(moved, nullptr); |
| 226 | #endif // __clang_analyzer__ |
| 227 | } |
| 228 | |
| 229 | } // namespace |
| 230 | } // namespace pw |
Ewout van Bekkum | c4a786a | 2021-07-27 17:05:12 -0700 | [diff] [blame] | 231 | |
| 232 | namespace obscure_different_namespace_which_should_never_collide { |
| 233 | namespace { |
| 234 | |
| 235 | TEST(Function, Null_OperatorEquals_DifferentNamespace) { |
| 236 | pw::Closure implicit_null; |
| 237 | pw::Closure explicit_null(nullptr); |
| 238 | pw::Closure assigned_null = nullptr; |
| 239 | pw::Closure not_null([]() {}); |
| 240 | |
| 241 | EXPECT_TRUE(implicit_null == nullptr); |
| 242 | EXPECT_TRUE(explicit_null == nullptr); |
| 243 | EXPECT_TRUE(assigned_null == nullptr); |
| 244 | EXPECT_TRUE(not_null != nullptr); |
| 245 | |
| 246 | EXPECT_FALSE(implicit_null != nullptr); |
| 247 | EXPECT_FALSE(explicit_null != nullptr); |
| 248 | EXPECT_FALSE(assigned_null != nullptr); |
| 249 | EXPECT_FALSE(not_null == nullptr); |
| 250 | } |
| 251 | |
| 252 | } // namespace |
| 253 | } // namespace obscure_different_namespace_which_should_never_collide |