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 | f4bac50 | 2021-10-15 14:48:01 -0700 | [diff] [blame] | 23 | // TODO(pwbug/47): Convert this to a compilation failure test. |
| 24 | #if defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithNonFunction) |
| 25 | |
| 26 | [[maybe_unused]] Function<int> function_pointer; |
| 27 | |
| 28 | #elif defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithFunctionPointer1) |
| 29 | |
| 30 | [[maybe_unused]] Function<void (*)()> function_pointer; |
| 31 | |
| 32 | #elif defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithFunctionPointer2) |
| 33 | |
| 34 | [[maybe_unused]] void SomeFunction(int); |
| 35 | |
| 36 | [[maybe_unused]] Function<decltype(&SomeFunction)> function_pointer; |
| 37 | |
| 38 | #elif defined(PW_COMPILE_FAIL_TEST_CannotInstantiateWithFunctionReference) |
| 39 | |
| 40 | [[maybe_unused]] Function<void (&)()> function_pointer; |
| 41 | |
| 42 | #endif // compile fail tests |
| 43 | |
Wyatt Hepler | f5cdd93 | 2021-06-14 13:53:23 -0700 | [diff] [blame] | 44 | // Ensure that Function can be constant initialized. |
| 45 | [[maybe_unused]] PW_CONSTINIT Function<void()> can_be_constant_initialized; |
| 46 | |
Alexei Frolov | 99de52d | 2021-05-11 19:58:01 -0700 | [diff] [blame] | 47 | int Multiply(int a, int b) { return a * b; } |
| 48 | |
| 49 | TEST(Function, OperatorCall) { |
| 50 | Function<int(int, int)> multiply(Multiply); |
| 51 | EXPECT_EQ(multiply(3, 7), 21); |
| 52 | } |
| 53 | |
| 54 | void CallbackAdd(int a, int b, pw::Function<void(int sum)> callback) { |
| 55 | callback(a + b); |
| 56 | } |
| 57 | |
| 58 | int add_result = -1; |
| 59 | |
| 60 | void free_add_callback(int sum) { add_result = sum; } |
| 61 | |
| 62 | TEST(Function, ConstructInPlace_FreeFunction) { |
| 63 | add_result = -1; |
| 64 | CallbackAdd(25, 17, free_add_callback); |
| 65 | EXPECT_EQ(add_result, 42); |
| 66 | } |
| 67 | |
| 68 | TEST(Function, ConstructInPlace_NonCapturingLambda) { |
| 69 | add_result = -1; |
| 70 | CallbackAdd(25, 18, [](int sum) { add_result = sum; }); |
| 71 | EXPECT_EQ(add_result, 43); |
| 72 | } |
| 73 | |
| 74 | TEST(Function, ConstructInPlace_CapturingLambda) { |
| 75 | int result = -1; |
| 76 | CallbackAdd(25, 19, [&](int sum) { result = sum; }); |
| 77 | EXPECT_EQ(result, 44); |
| 78 | } |
| 79 | |
| 80 | class CallableObject { |
| 81 | public: |
| 82 | CallableObject(int* result) : result_(result) {} |
| 83 | |
| 84 | CallableObject(CallableObject&& other) = default; |
| 85 | CallableObject& operator=(CallableObject&& other) = default; |
| 86 | |
| 87 | void operator()(int sum) { *result_ = sum; } |
| 88 | |
| 89 | private: |
| 90 | int* result_; |
| 91 | }; |
| 92 | |
| 93 | TEST(Function, ConstructInPlace_CallableObject) { |
| 94 | int result = -1; |
| 95 | CallbackAdd(25, 20, CallableObject(&result)); |
| 96 | EXPECT_EQ(result, 45); |
| 97 | } |
| 98 | |
| 99 | class MemberFunctionTest : public ::testing::Test { |
| 100 | protected: |
| 101 | MemberFunctionTest() : result_(-1) {} |
| 102 | |
| 103 | void set_result(int result) { result_ = result; } |
| 104 | |
| 105 | int result_; |
| 106 | }; |
| 107 | |
| 108 | TEST_F(MemberFunctionTest, ConstructInPlace_Lambda) { |
| 109 | CallbackAdd(25, 21, [this](int sum) { set_result(sum); }); |
| 110 | EXPECT_EQ(result_, 46); |
| 111 | } |
| 112 | |
| 113 | TEST(Function, Null_OperatorBool) { |
| 114 | Closure implicit_null; |
| 115 | Closure explicit_null(nullptr); |
| 116 | Closure assigned_null = nullptr; |
| 117 | Closure not_null([]() {}); |
| 118 | |
| 119 | EXPECT_FALSE(bool(implicit_null)); |
| 120 | EXPECT_FALSE(bool(explicit_null)); |
| 121 | EXPECT_FALSE(bool(assigned_null)); |
| 122 | EXPECT_TRUE(bool(not_null)); |
| 123 | |
| 124 | EXPECT_TRUE(!implicit_null); |
| 125 | EXPECT_TRUE(!explicit_null); |
| 126 | EXPECT_TRUE(!assigned_null); |
| 127 | EXPECT_FALSE(!not_null); |
| 128 | } |
| 129 | |
| 130 | TEST(Function, Null_OperatorEquals) { |
| 131 | Closure implicit_null; |
| 132 | Closure explicit_null(nullptr); |
| 133 | Closure assigned_null = nullptr; |
| 134 | Closure not_null([]() {}); |
| 135 | |
| 136 | EXPECT_TRUE(implicit_null == nullptr); |
| 137 | EXPECT_TRUE(explicit_null == nullptr); |
| 138 | EXPECT_TRUE(assigned_null == nullptr); |
| 139 | EXPECT_TRUE(not_null != nullptr); |
| 140 | |
| 141 | EXPECT_FALSE(implicit_null != nullptr); |
| 142 | EXPECT_FALSE(explicit_null != nullptr); |
| 143 | EXPECT_FALSE(assigned_null != nullptr); |
| 144 | EXPECT_FALSE(not_null == nullptr); |
| 145 | } |
| 146 | |
| 147 | TEST(Function, Null_Set) { |
| 148 | Closure function = []() {}; |
| 149 | EXPECT_NE(function, nullptr); |
| 150 | function = nullptr; |
| 151 | EXPECT_EQ(function, nullptr); |
| 152 | } |
| 153 | |
| 154 | void DoNothing() {} |
| 155 | |
| 156 | TEST(Function, Null_FunctionPointer) { |
| 157 | void (*ptr)() = DoNothing; |
| 158 | Closure not_null(ptr); |
| 159 | EXPECT_NE(not_null, nullptr); |
| 160 | ptr = nullptr; |
| 161 | Closure is_null(ptr); |
| 162 | EXPECT_EQ(is_null, nullptr); |
| 163 | } |
| 164 | |
| 165 | TEST(Function, Move_Null) { |
| 166 | Closure moved; |
| 167 | EXPECT_EQ(moved, nullptr); |
| 168 | Closure function(std::move(moved)); |
| 169 | EXPECT_EQ(function, nullptr); |
| 170 | |
| 171 | // Ignore use-after-move. |
| 172 | #ifndef __clang_analyzer__ |
| 173 | EXPECT_EQ(moved, nullptr); |
| 174 | #endif // __clang_analyzer__ |
| 175 | } |
| 176 | |
| 177 | TEST(Function, MoveAssign_Null) { |
| 178 | Closure moved; |
| 179 | EXPECT_EQ(moved, nullptr); |
| 180 | Closure function = std::move(moved); |
| 181 | EXPECT_EQ(function, nullptr); |
| 182 | |
| 183 | // Ignore use-after-move. |
| 184 | #ifndef __clang_analyzer__ |
| 185 | EXPECT_EQ(moved, nullptr); |
| 186 | #endif // __clang_analyzer__ |
| 187 | } |
| 188 | |
| 189 | TEST(Function, Move_Inline) { |
| 190 | Function<int(int, int)> moved(Multiply); |
| 191 | EXPECT_NE(moved, nullptr); |
| 192 | Function<int(int, int)> multiply(std::move(moved)); |
| 193 | EXPECT_EQ(multiply(3, 3), 9); |
| 194 | |
| 195 | // Ignore use-after-move. |
| 196 | #ifndef __clang_analyzer__ |
| 197 | EXPECT_EQ(moved, nullptr); |
| 198 | #endif // __clang_analyzer__ |
| 199 | } |
| 200 | |
| 201 | TEST(Function, MoveAssign_Inline) { |
| 202 | Function<int(int, int)> moved(Multiply); |
| 203 | EXPECT_NE(moved, nullptr); |
| 204 | Function<int(int, int)> multiply = std::move(moved); |
| 205 | EXPECT_EQ(multiply(3, 3), 9); |
| 206 | |
| 207 | // Ignore use-after-move. |
| 208 | #ifndef __clang_analyzer__ |
| 209 | EXPECT_EQ(moved, nullptr); |
| 210 | #endif // __clang_analyzer__ |
| 211 | } |
| 212 | |
Ewout van Bekkum | 87ca34c | 2022-02-10 09:41:12 -0800 | [diff] [blame] | 213 | TEST(Function, MoveAssign_Callable) { |
| 214 | Function<int(int, int)> operation = Multiply; |
| 215 | EXPECT_EQ(operation(3, 3), 9); |
| 216 | operation = [](int a, int b) -> int { return a + b; }; |
| 217 | EXPECT_EQ(operation(3, 3), 6); |
| 218 | } |
| 219 | |
Alexei Frolov | 99de52d | 2021-05-11 19:58:01 -0700 | [diff] [blame] | 220 | class MoveTracker { |
| 221 | public: |
| 222 | MoveTracker() : move_count_(0) {} |
| 223 | |
| 224 | MoveTracker(MoveTracker&& other) : move_count_(other.move_count_ + 1) {} |
| 225 | MoveTracker& operator=(MoveTracker&& other) = default; |
| 226 | |
| 227 | int operator()() const { return move_count_; } |
| 228 | |
| 229 | private: |
| 230 | int move_count_; |
| 231 | }; |
| 232 | |
| 233 | TEST(Function, Move_CustomObject) { |
| 234 | Function<int()> moved((MoveTracker())); |
| 235 | EXPECT_EQ(moved(), 2); // internally moves twice on construction |
| 236 | Function<int()> tracker(std::move(moved)); |
| 237 | EXPECT_EQ(tracker(), 3); |
| 238 | |
| 239 | // Ignore use-after-move. |
| 240 | #ifndef __clang_analyzer__ |
| 241 | EXPECT_EQ(moved, nullptr); |
| 242 | #endif // __clang_analyzer__ |
| 243 | } |
| 244 | |
| 245 | TEST(Function, MoveAssign_CustomObject) { |
| 246 | Function<int()> moved((MoveTracker())); |
| 247 | EXPECT_EQ(moved(), 2); // internally moves twice on construction |
| 248 | Function<int()> tracker = std::move(moved); |
| 249 | EXPECT_EQ(tracker(), 3); |
| 250 | |
| 251 | // Ignore use-after-move. |
| 252 | #ifndef __clang_analyzer__ |
| 253 | EXPECT_EQ(moved, nullptr); |
| 254 | #endif // __clang_analyzer__ |
| 255 | } |
| 256 | |
Wyatt Hepler | 1af214d | 2022-01-19 10:42:47 -0800 | [diff] [blame] | 257 | TEST(Function, MoveOnlyType) { |
| 258 | class MoveOnlyType { |
| 259 | public: |
| 260 | MoveOnlyType() = default; |
| 261 | |
| 262 | MoveOnlyType(const MoveOnlyType& other) = delete; |
| 263 | MoveOnlyType& operator=(const MoveOnlyType& other) = delete; |
| 264 | |
| 265 | MoveOnlyType(MoveOnlyType&&) = default; |
| 266 | MoveOnlyType& operator=(MoveOnlyType&&) = default; |
| 267 | |
| 268 | bool ItsWorking() const { return true; } |
| 269 | }; |
| 270 | |
| 271 | pw::Function<bool(MoveOnlyType)> function = [](MoveOnlyType value) { |
| 272 | return value.ItsWorking(); |
| 273 | }; |
| 274 | |
| 275 | MoveOnlyType move_only; |
| 276 | EXPECT_TRUE(function(std::move(move_only))); |
| 277 | } |
| 278 | |
Alexei Frolov | 99de52d | 2021-05-11 19:58:01 -0700 | [diff] [blame] | 279 | } // namespace |
| 280 | } // namespace pw |
Ewout van Bekkum | c4a786a | 2021-07-27 17:05:12 -0700 | [diff] [blame] | 281 | |
| 282 | namespace obscure_different_namespace_which_should_never_collide { |
| 283 | namespace { |
| 284 | |
| 285 | TEST(Function, Null_OperatorEquals_DifferentNamespace) { |
| 286 | pw::Closure implicit_null; |
| 287 | pw::Closure explicit_null(nullptr); |
| 288 | pw::Closure assigned_null = nullptr; |
| 289 | pw::Closure not_null([]() {}); |
| 290 | |
| 291 | EXPECT_TRUE(implicit_null == nullptr); |
| 292 | EXPECT_TRUE(explicit_null == nullptr); |
| 293 | EXPECT_TRUE(assigned_null == nullptr); |
| 294 | EXPECT_TRUE(not_null != nullptr); |
| 295 | |
| 296 | EXPECT_FALSE(implicit_null != nullptr); |
| 297 | EXPECT_FALSE(explicit_null != nullptr); |
| 298 | EXPECT_FALSE(assigned_null != nullptr); |
| 299 | EXPECT_FALSE(not_null == nullptr); |
| 300 | } |
| 301 | |
| 302 | } // namespace |
| 303 | } // namespace obscure_different_namespace_which_should_never_collide |