1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "inplace_function_tests"
18 
19 #include <mediautils/InPlaceFunction.h>
20 
21 #include <type_traits>
22 
23 #include <gtest/gtest.h>
24 #include <log/log.h>
25 
26 using namespace android;
27 using namespace android::mediautils;
28 
29 struct BigCallable {
BigCallableBigCallable30     BigCallable(size_t* x, size_t val1, size_t val2) : ptr(x), a(val1), b(val2) {}
31     size_t* ptr;
32     size_t a;
33     size_t b;
operator ()BigCallable34     size_t operator()(size_t input) const {
35         *ptr += a * 100 + b * 10 + input;
36         return 8;
37     }
38 };
39 
TEST(InPlaceFunctionTests,Basic)40 TEST(InPlaceFunctionTests, Basic) {
41     size_t x = 5;
42     InPlaceFunction<size_t(size_t)> func;
43     {
44         BigCallable test{&x, 2, 3};
45         func = test;
46     }
47     EXPECT_EQ(func(2), 8ull);
48     EXPECT_EQ(x, 232ull + 5);
49 }
50 
TEST(InPlaceFunctionTests,Invalid)51 TEST(InPlaceFunctionTests, Invalid) {
52     InPlaceFunction<size_t(size_t)> func;
53     EXPECT_TRUE(!func);
54     InPlaceFunction<size_t(size_t)> func2{nullptr};
55     EXPECT_TRUE(!func2);
56     InPlaceFunction<size_t(size_t)> func3 = [](size_t x) { return x; };
57     EXPECT_TRUE(!(!func3));
58     func3 = nullptr;
59     EXPECT_TRUE(!func3);
60 }
61 
TEST(InPlaceFunctionTests,MultiArg)62 TEST(InPlaceFunctionTests, MultiArg) {
63     InPlaceFunction<size_t(size_t, size_t, size_t)> func = [](size_t a, size_t b, size_t c) {
64         return a + b + c;
65     };
66     EXPECT_EQ(func(2, 3, 5), 2ull + 3 + 5);
67 }
68 struct Record {
RecordRecord69     Record(size_t m, size_t c, size_t d) : move_called(m), copy_called(c), dtor_called(d) {}
RecordRecord70     Record() {}
71     size_t move_called = 0;
72     size_t copy_called = 0;
73     size_t dtor_called = 0;
operator <<(std::ostream & os,const Record & record)74     friend std::ostream& operator<<(std::ostream& os, const Record& record) {
75         return os << "Record, moves: " << record.move_called << ", copies: " << record.copy_called
76                   << ", dtor: " << record.dtor_called << '\n';
77     }
78 };
79 
operator ==(const Record & lhs,const Record & rhs)80 bool operator==(const Record& lhs, const Record& rhs) {
81     return lhs.move_called == rhs.move_called && lhs.copy_called == rhs.copy_called &&
82            lhs.dtor_called == rhs.dtor_called;
83 }
84 
85 struct Noisy {
86     Record& ref;
87     size_t state;
NoisyNoisy88     Noisy(Record& record, size_t val) : ref(record), state(val) {}
NoisyNoisy89     Noisy(const Noisy& other) : ref(other.ref), state(other.state) { ref.copy_called++; }
90 
NoisyNoisy91     Noisy(Noisy&& other) : ref(other.ref), state(other.state) { ref.move_called++; }
~NoisyNoisy92     ~Noisy() { ref.dtor_called++; }
93 
operator ()Noisy94     size_t operator()() { return state; }
95 };
96 
TEST(InPlaceFunctionTests,CtorForwarding)97 TEST(InPlaceFunctionTests, CtorForwarding) {
98     Record record;
99     Noisy noisy{record, 17};
100     InPlaceFunction<size_t()> func{noisy};
101     EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
102     EXPECT_EQ(func(), 17ull);
103     Record record2;
104     Noisy noisy2{record2, 13};
105     InPlaceFunction<size_t()> func2{std::move(noisy2)};
106     EXPECT_EQ(record2, Record(1, 0, 0));  // move, copy, dtor
107     EXPECT_EQ(func2(), 13ull);
108 }
109 
TEST(InPlaceFunctionTests,FunctionCtorForwarding)110 TEST(InPlaceFunctionTests, FunctionCtorForwarding) {
111     {
112         Record record;
113         Noisy noisy{record, 17};
114         InPlaceFunction<size_t()> func{noisy};
115         EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
116         EXPECT_EQ(func(), 17ull);
117         InPlaceFunction<size_t()> func2{func};
118         EXPECT_EQ(record, Record(0, 2, 0));  // move, copy, dtor
119         EXPECT_EQ(func2(), 17ull);
120     }
121     Record record;
122     Noisy noisy{record, 13};
123     InPlaceFunction<size_t()> func{noisy};
124     EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
125     EXPECT_EQ(func(), 13ull);
126     InPlaceFunction<size_t()> func2{std::move(func)};
127     EXPECT_EQ(record, Record(1, 1, 0));  // move, copy, dtor
128     EXPECT_EQ(func2(), 13ull);
129     // We expect moved from functions to still be valid
130     EXPECT_TRUE(!(!func));
131     EXPECT_EQ(static_cast<bool>(func), static_cast<bool>(func2));
132     EXPECT_EQ(func(), 13ull);
133 }
134 
TEST(InPlaceFunctionTests,Dtor)135 TEST(InPlaceFunctionTests, Dtor) {
136     Record record;
137     {
138         InPlaceFunction<size_t()> func;
139         {
140             Noisy noisy{record, 17};
141             func = noisy;
142         }
143         EXPECT_EQ(func(), 17ull);
144         EXPECT_EQ(record.dtor_called, 1ull);
145     }
146     EXPECT_EQ(record.dtor_called, 2ull);
147 }
148 
TEST(InPlaceFunctionTests,Assignment)149 TEST(InPlaceFunctionTests, Assignment) {
150     {
151         Record record;
152         Record record2;
153         Noisy noisy{record, 17};
154         Noisy noisy2{record2, 5};
155         InPlaceFunction<size_t()> func{noisy};
156         EXPECT_EQ(func(), 17ull);
157         EXPECT_EQ(record.dtor_called, 0ull);
158         func = noisy2;
159         EXPECT_EQ(record.dtor_called, 1ull);
160         EXPECT_EQ(record2, Record(0, 1, 0));  // move, copy, dtor
161         EXPECT_EQ(func(), 5ull);
162     }
163     {
164         Record record;
165         Record record2;
166         Noisy noisy{record, 17};
167         Noisy noisy2{record2, 5};
168         InPlaceFunction<size_t()> func{noisy};
169         EXPECT_EQ(func(), 17ull);
170         EXPECT_EQ(record.dtor_called, 0ull);
171         func = std::move(noisy2);
172         EXPECT_EQ(record.dtor_called, 1ull);
173         EXPECT_EQ(record2, Record(1, 0, 0));  // move, copy, dtor
174         EXPECT_EQ(func(), 5ull);
175     }
176 
177     {
178         Record record;
179         Record record2;
180         Noisy noisy{record, 17};
181         Noisy noisy2{record2, 13};
182         {
183             InPlaceFunction<size_t()> func{noisy};
184             EXPECT_EQ(func(), 17ull);
185             InPlaceFunction<size_t()> func2{noisy2};
186             EXPECT_EQ(record2, Record(0, 1, 0));  // move, copy, dtor
187             EXPECT_EQ(record.dtor_called, 0ull);
188             func = func2;
189             EXPECT_EQ(record.dtor_called, 1ull);
190             EXPECT_EQ(func(), 13ull);
191             EXPECT_EQ(record2, Record(0, 2, 0));  // move, copy, dtor
192             EXPECT_TRUE(static_cast<bool>(func2));
193             EXPECT_EQ(func2(), 13ull);
194         }
195         EXPECT_EQ(record2, Record(0, 2, 2));  // move, copy, dtor
196     }
197 
198     {
199         Record record;
200         Record record2;
201         Noisy noisy{record, 17};
202         Noisy noisy2{record2, 13};
203         {
204             InPlaceFunction<size_t()> func{noisy};
205             EXPECT_EQ(func(), 17ull);
206             InPlaceFunction<size_t()> func2{noisy2};
207             EXPECT_EQ(record.dtor_called, 0ull);
208             EXPECT_EQ(record2, Record(0, 1, 0));  // move, copy, dtor
209             func = std::move(func2);
210             EXPECT_EQ(record.dtor_called, 1ull);
211             EXPECT_EQ(func(), 13ull);
212             EXPECT_EQ(record2, Record(1, 1, 0));  // move, copy, dtor
213             // Moved from function is still valid
214             EXPECT_TRUE(static_cast<bool>(func2));
215             EXPECT_EQ(func2(), 13ull);
216         }
217         EXPECT_EQ(record2, Record(1, 1, 2));  // move, copy, dtor
218     }
219 }
220 
TEST(InPlaceFunctionTests,Swap)221 TEST(InPlaceFunctionTests, Swap) {
222     Record record1;
223     Record record2;
224     InPlaceFunction<size_t()> func1 = Noisy{record1, 5};
225     InPlaceFunction<size_t()> func2 = Noisy{record2, 7};
226     EXPECT_EQ(record1, Record(1, 0, 1));  // move, copy, dtor
227     EXPECT_EQ(record2, Record(1, 0, 1));  // move, copy, dtor
228     EXPECT_EQ(func1(), 5ull);
229     EXPECT_EQ(func2(), 7ull);
230     func1.swap(func2);
231     EXPECT_EQ(record1, Record(2, 0, 2));  // move, copy, dtor
232     // An additional move and destroy into the temporary object
233     EXPECT_EQ(record2, Record(3, 0, 3));  // move, copy, dtor
234     EXPECT_EQ(func1(), 7ull);
235     EXPECT_EQ(func2(), 5ull);
236 }
237 
TEST(InPlaceFunctionTests,Conversion)238 TEST(InPlaceFunctionTests, Conversion) {
239     Record record;
240     Noisy noisy{record, 15};
241     {
242         InPlaceFunction<size_t(), 16> func2 = noisy;
243         EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
244         {
245             InPlaceFunction<size_t(), 32> func{func2};
246             EXPECT_EQ(record, Record(0, 2, 0));  // move, copy, dtor
247             EXPECT_EQ(func2(), func());
248         }
249         EXPECT_EQ(record, Record(0, 2, 1));  // move, copy, dtor
250     }
251     EXPECT_EQ(record, Record(0, 2, 2));  // move, copy, dtor
252 }
253 
TEST(InPlaceFunctionTests,ConversionMove)254 TEST(InPlaceFunctionTests, ConversionMove) {
255     Record record;
256     Noisy noisy{record, 15};
257     {
258         InPlaceFunction<size_t(), 16> func2 = noisy;
259         EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
260         {
261             InPlaceFunction<size_t(), 32> func{std::move(func2)};
262             EXPECT_EQ(record, Record(1, 1, 0));  // move, copy, dtor
263             EXPECT_EQ(func2(), func());
264         }
265         EXPECT_EQ(record, Record(1, 1, 1));  // move, copy, dtor
266     }
267     EXPECT_EQ(record, Record(1, 1, 2));  // move, copy, dtor
268 }
269 
TEST(InPlaceFunctionTests,ConversionAssign)270 TEST(InPlaceFunctionTests, ConversionAssign) {
271     Record record;
272     Noisy noisy{record, 15};
273     {
274         InPlaceFunction<size_t(), 32> func;
275         {
276             InPlaceFunction<size_t(), 16> func2 = noisy;
277             EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
278             func = func2;
279             EXPECT_EQ(record, Record(0, 2, 0));  // move, copy, dtor
280             EXPECT_EQ(func2(), func());
281         }
282         EXPECT_EQ(record, Record(0, 2, 1));  // move, copy, dtor
283     }
284     EXPECT_EQ(record, Record(0, 2, 2));  // move, copy, dtor
285 }
286 
TEST(InPlaceFunctionTests,ConversionAssignMove)287 TEST(InPlaceFunctionTests, ConversionAssignMove) {
288     Record record;
289     Noisy noisy{record, 15};
290     {
291         InPlaceFunction<size_t(), 32> func;
292         {
293             InPlaceFunction<size_t(), 16> func2 = noisy;
294             EXPECT_EQ(record, Record(0, 1, 0));  // move, copy, dtor
295             func = std::move(func2);
296             EXPECT_EQ(record, Record(1, 1, 0));  // move, copy, dtor
297             EXPECT_EQ(func2(), func());
298         }
299         EXPECT_EQ(record, Record(1, 1, 1));  // move, copy, dtor
300     }
301     EXPECT_EQ(record, Record(1, 1, 2));  // move, copy, dtor
302 }
303 
304 struct NoMoveCopy {
305     NoMoveCopy() = default;
306     NoMoveCopy(const NoMoveCopy&) = delete;
307     NoMoveCopy(NoMoveCopy&&) = delete;
308 };
309 struct TestCallable {
operator ()TestCallable310     NoMoveCopy& operator()(NoMoveCopy& x) { return x; }
311 };
312 
TEST(InPlaceFunctionTests,ArgumentForwarding)313 TEST(InPlaceFunctionTests, ArgumentForwarding) {
314     const auto lambd = [](NoMoveCopy& x) -> NoMoveCopy& { return x; };
315     InPlaceFunction<NoMoveCopy&(NoMoveCopy&)> func = lambd;
316     const auto lambd2 = [](NoMoveCopy&& x) -> NoMoveCopy&& { return std::move(x); };
317     InPlaceFunction<NoMoveCopy && (NoMoveCopy &&)> func2 = lambd2;
318     auto lvalue = NoMoveCopy{};
319     func(lvalue);
320     func2(NoMoveCopy{});
321     InPlaceFunction<void(NoMoveCopy&)> func3 = [](const NoMoveCopy&) {};
322     func3(lvalue);
323     InPlaceFunction<void(NoMoveCopy &&)> func4 = [](const NoMoveCopy&) {};
324     func4(std::move(lvalue));
325     InPlaceFunction<void(const NoMoveCopy&)> func5 = [](const NoMoveCopy&) {};
326     func5(lvalue);
327     InPlaceFunction<void(const NoMoveCopy&&)> func6 = [](const NoMoveCopy&) {};
328     func6(std::move(lvalue));
329     InPlaceFunction<void(const NoMoveCopy&&)> func7 = [](const NoMoveCopy&&) {};
330     func7(std::move(lvalue));
331     InPlaceFunction<void(NoMoveCopy &&)> func8 = [](const NoMoveCopy&&) {};
332     func8(std::move(lvalue));
333 
334     {
335         Record record;
336         Noisy noisy{record, 5};
337         const auto lambd3 = [](Noisy) {};
338         InPlaceFunction<void(Noisy)> func3{lambd3};
339         EXPECT_EQ(record, Record(0, 0, 0));  // move, copy, dtor
340         func3(std::move(noisy));
341         EXPECT_EQ(record, Record(2, 0, 2));  // move, copy, dtor
342     }
343 
344     {
345         Record record;
346         Noisy noisy{record, 5};
347         const auto lambd3 = [](Noisy) {};
348         InPlaceFunction<void(Noisy)> func3{lambd3};
349         EXPECT_EQ(record, Record(0, 0, 0));  // move, copy, dtor
350         func3(noisy);
351         EXPECT_EQ(record, Record(1, 1, 2));  // move, copy, dtor
352     }
353 }
354 
TEST(InPlaceFunctionTests,VoidFunction)355 TEST(InPlaceFunctionTests, VoidFunction) {
356     InPlaceFunction<void(size_t)> func = [](size_t x) -> size_t { return x; };
357     func(5);
358     InPlaceFunction<void(void)> func2 = []() -> size_t { return 5; };
359     func2();
360 }
foo()361 NoMoveCopy foo() {
362     return NoMoveCopy();
363 }
364 struct Test {
operator ()Test365     NoMoveCopy operator()() { return NoMoveCopy{}; }
366 };
367 
TEST(InPlaceFunctionTests,FullElision)368 TEST(InPlaceFunctionTests, FullElision) {
369     InPlaceFunction<NoMoveCopy()> func = foo;
370 }
371 
TEST(InPlaceFunctionTests,ReturnConversion)372 TEST(InPlaceFunctionTests, ReturnConversion) {
373     const auto lambd = [](int&& x) -> int&& { return std::move(x); };
374     InPlaceFunction<int && (int&& x)> func = lambd;
375     func(5);
376     InPlaceFunction<void(int)> func3 = [](double) {};
377     func3(5);
378     InPlaceFunction<double()> func4 = []() -> int { return 5; };
379     func4();
380 }
381 
382 struct Overloaded {
operator ()Overloaded383     int operator()() & { return 2; }
operator ()Overloaded384     int operator()() const& { return 3; }
operator ()Overloaded385     int operator()() && { return 4; }
operator ()Overloaded386     int operator()() const&& { return 5; }
387 };
388 
TEST(InPlaceFunctionTests,OverloadResolution)389 TEST(InPlaceFunctionTests, OverloadResolution) {
390     InPlaceFunction<int()> func = Overloaded{};
391     EXPECT_EQ(func(), 2);
392     EXPECT_EQ(std::move(func()), 2);
393 }
394 
395 template <class T, class U, class = void>
396 struct can_assign : std::false_type {};
397 
398 template <class T, class U>
399 struct can_assign<T, U, typename std::void_t<decltype(T().operator=(U()))>> : std::true_type {};
400 
401 template <class From, class To, bool Expected>
402 static constexpr bool Convertible =
403         (can_assign<To, From>::value ==
404          std::is_constructible_v<To, From>)&&(std::is_constructible_v<To, From> == Expected);
405 
406 struct TooBig {
407     std::array<uint64_t, 5> big = {1, 2, 3, 4, 5};
operator ()TooBig408     size_t operator()() { return static_cast<size_t>(big[0] + big[1] + big[2] + big[3] + big[4]); }
409 };
410 static_assert(sizeof(TooBig) == 40);
411 struct NotCallable {};
412 struct WrongArg {
operator ()WrongArg413     void operator()(NotCallable) {}
414 };
415 struct WrongRet {
operator ()WrongRet416     NotCallable operator()(size_t) { return NotCallable{}; }
417 };
418 
419 static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 32>, true>);
420 static_assert(
421         Convertible<InPlaceFunction<size_t(size_t), 32>, InPlaceFunction<size_t(), 32>, false>);
422 static_assert(Convertible<InPlaceFunction<void(), 32>, InPlaceFunction<size_t(), 32>, false>);
423 static_assert(Convertible<TooBig, InPlaceFunction<size_t(), 32>, false>);
424 static_assert(Convertible<TooBig, InPlaceFunction<size_t(), 40>, true>);
425 static_assert(Convertible<NotCallable, InPlaceFunction<size_t(), 40>, false>);
426 static_assert(Convertible<WrongArg, InPlaceFunction<void(size_t), 40>, false>);
427 static_assert(Convertible<WrongRet, InPlaceFunction<size_t(size_t), 40>, false>);
428 // Void returning functions are modelled by any return type
429 static_assert(Convertible<WrongRet, InPlaceFunction<void(size_t), 40>, true>);
430 
431 // Check constructibility/assignability from smaller function types
432 static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 24>, false>);
433 static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 40>, true>);
434 static_assert(
435         Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(size_t), 40>, false>);
436 static_assert(
437         Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<NotCallable(), 40>, false>);
438 
439 struct BadLambd {
operator ()BadLambd440     int operator()(int&& x) { return std::move(x); }
441 };
442 
443 static_assert(Convertible<BadLambd, InPlaceFunction<int(int&&), 32>, true>);
444 static_assert(Convertible<BadLambd, InPlaceFunction<int&(int&&), 32>, false>);
445 static_assert(Convertible<BadLambd, InPlaceFunction<const int&(int&&), 32>, false>);
446 static_assert(Convertible<BadLambd, InPlaceFunction<int && (int&&), 32>, false>);
447 static_assert(Convertible<BadLambd, InPlaceFunction<const int && (int&&), 32>, false>);
448 
449 struct Base {};
450 struct Derived : Base {};
451 struct Converted {
ConvertedConverted452     Converted(const Derived&) {}
453 };
454 
455 struct ConvertCallable {
operator ()ConvertCallable456     Derived operator()() { return Derived{}; }
operator ()ConvertCallable457     Derived& operator()(Derived& x) { return x; }
operator ()ConvertCallable458     Derived&& operator()(Derived&& x) { return std::move(x); }
operator ()ConvertCallable459     const Derived& operator()(const Derived& x) { return x; }
operator ()ConvertCallable460     const Derived&& operator()(const Derived&& x) { return std::move(x); }
461 };
462 
463 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived&()>, false>);
464 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base&()>, false>);
465 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived()>, true>);
466 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base()>, true>);
467 static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted()>, true>);
468 static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted&()>, false>);
469 static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted && ()>, false>);
470 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Converted&()>, false>);
471 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Converted && ()>, false>);
472 
473 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived&(Derived&)>, true>);
474 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base&(Derived&)>, true>);
475 
476 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived && (Derived &&)>, true>);
477 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base && (Derived &&)>, true>);
478 
479 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(const Derived&)>, true>);
480 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(const Derived&)>, true>);
481 
482 static_assert(
483         Convertible<ConvertCallable, InPlaceFunction<const Derived && (const Derived&&)>, true>);
484 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base && (const Derived&&)>, true>);
485 
486 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(Derived&)>, true>);
487 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(Derived&)>, true>);
488 
489 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived && (Derived &&)>, true>);
490 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base && (Derived &&)>, true>);
491 
492 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(Derived&&)>, true>);
493 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(Derived&&)>, true>);
494