UntypedFunction: Add unit tests and fix a few issues
Bug: webrtc:11943 Change-Id: I1f3c0495612148546ec399a800f97fe88b439c83 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/184260 Commit-Queue: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32116}
This commit is contained in:
parent
818be15e4d
commit
78e9acd967
1
BUILD.gn
1
BUILD.gn
@ -538,6 +538,7 @@ if (rtc_include_tests) {
|
||||
"call:fake_network_pipe_unittests",
|
||||
"p2p:libstunprober_unittests",
|
||||
"p2p:rtc_p2p_unittests",
|
||||
"rtc_base:function_unittest",
|
||||
"rtc_base:rtc_base_approved_unittests",
|
||||
"rtc_base:rtc_base_unittests",
|
||||
"rtc_base:rtc_json_unittests",
|
||||
|
||||
@ -1158,6 +1158,15 @@ if (rtc_include_tests) {
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("function_unittest") {
|
||||
testonly = true
|
||||
sources = [ "function_unittest.cc" ]
|
||||
deps = [
|
||||
":function",
|
||||
"../test:test_support",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("rtc_base_nonparallel_tests") {
|
||||
testonly = true
|
||||
|
||||
|
||||
@ -102,9 +102,10 @@ class UntypedFunction final {
|
||||
} else {
|
||||
// The callable is either nontrivial or too large, so we can't keep it
|
||||
// in the inline storage; use the heap instead.
|
||||
webrtc_function_impl::VoidUnion vu;
|
||||
vu.void_ptr = new F_deref(std::forward<F>(f));
|
||||
return UntypedFunction(
|
||||
webrtc_function_impl::VoidUnion{.void_ptr =
|
||||
new F_deref(std::forward<F>(f))},
|
||||
vu,
|
||||
reinterpret_cast<webrtc_function_impl::FunVoid*>(
|
||||
webrtc_function_impl::CallHelpers<
|
||||
Signature>::template CallVoidPtr<F_deref>),
|
||||
@ -123,8 +124,10 @@ class UntypedFunction final {
|
||||
// the result is an empty UntypedFunction.
|
||||
template <typename Signature>
|
||||
static UntypedFunction Create(Signature* f) {
|
||||
webrtc_function_impl::VoidUnion vu;
|
||||
vu.fun_ptr = reinterpret_cast<webrtc_function_impl::FunVoid*>(f);
|
||||
return UntypedFunction(
|
||||
reinterpret_cast<webrtc_function_impl::FunVoid*>(f),
|
||||
vu,
|
||||
f ? reinterpret_cast<webrtc_function_impl::FunVoid*>(
|
||||
webrtc_function_impl::CallHelpers<Signature>::CallFunPtr)
|
||||
: nullptr,
|
||||
@ -134,6 +137,18 @@ class UntypedFunction final {
|
||||
// Default constructor. Creates an empty UntypedFunction.
|
||||
UntypedFunction() : call_(nullptr), delete_(nullptr) {}
|
||||
|
||||
// Nullptr constructor and assignment. Creates an empty UntypedFunction.
|
||||
UntypedFunction(std::nullptr_t) // NOLINT(runtime/explicit)
|
||||
: call_(nullptr), delete_(nullptr) {}
|
||||
UntypedFunction& operator=(std::nullptr_t) {
|
||||
call_ = nullptr;
|
||||
if (delete_) {
|
||||
delete_(&f_);
|
||||
delete_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Not copyable.
|
||||
UntypedFunction(const UntypedFunction&) = delete;
|
||||
UntypedFunction& operator=(const UntypedFunction&) = delete;
|
||||
@ -144,6 +159,9 @@ class UntypedFunction final {
|
||||
other.delete_ = nullptr;
|
||||
}
|
||||
UntypedFunction& operator=(UntypedFunction&& other) {
|
||||
if (delete_) {
|
||||
delete_(&f_);
|
||||
}
|
||||
f_ = other.f_;
|
||||
call_ = other.call_;
|
||||
delete_ = other.delete_;
|
||||
@ -169,7 +187,7 @@ class UntypedFunction final {
|
||||
|
||||
template <typename Signature, typename... ArgT>
|
||||
typename webrtc_function_impl::CallHelpers<Signature>::return_type Call(
|
||||
ArgT... args) {
|
||||
ArgT&&... args) {
|
||||
return webrtc_function_impl::CallHelpers<Signature>::DoCall(
|
||||
call_, &f_, std::forward<ArgT>(args)...);
|
||||
}
|
||||
|
||||
307
rtc_base/function_unittest.cc
Normal file
307
rtc_base/function_unittest.cc
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/function.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
using ::testing::Pointee;
|
||||
|
||||
TEST(UntypedFunction, Empty1) {
|
||||
UntypedFunction uf;
|
||||
EXPECT_FALSE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, Empty2) {
|
||||
UntypedFunction uf = nullptr;
|
||||
EXPECT_FALSE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, Empty3) {
|
||||
UntypedFunction uf = UntypedFunction::Create<int(int)>(nullptr);
|
||||
EXPECT_FALSE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallTrivialWithInt) {
|
||||
auto uf = UntypedFunction::Create<int(int)>([](int x) { return x + 5; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int(int)>(17), 22);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallTrivialWithPointer) {
|
||||
auto uf = UntypedFunction::Create<int(int*)>([](int* x) { return *x; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
int x = 12;
|
||||
EXPECT_EQ(uf.Call<int(int*)>(&x), 12);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallTrivialWithReference) {
|
||||
auto uf = UntypedFunction::Create<void(int&)>([](int& x) { x = 3; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
int x = 12;
|
||||
uf.Call<void(int&)>(x);
|
||||
EXPECT_EQ(x, 3);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallTrivialWithRvalueReference) {
|
||||
auto uf = UntypedFunction::Create<int(int&&)>([](int&& x) { return x - 2; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int(int&&)>(34), 32);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallNontrivialWithInt) {
|
||||
std::vector<int> list;
|
||||
auto uf = UntypedFunction::Create<int(int)>([list](int x) mutable {
|
||||
list.push_back(x);
|
||||
return list.size();
|
||||
});
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int(int)>(17), 1);
|
||||
EXPECT_EQ(uf.Call<int(int)>(17), 2);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallNontrivialWithPointer) {
|
||||
std::vector<int> list;
|
||||
auto uf = UntypedFunction::Create<int*(int*)>([list](int* x) mutable {
|
||||
list.push_back(*x);
|
||||
return list.data();
|
||||
});
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
int x = 12;
|
||||
EXPECT_THAT(uf.Call<int*(int*)>(&x), Pointee(12));
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallNontrivialWithReference) {
|
||||
std::vector<int> list = {34, 35, 36};
|
||||
auto uf =
|
||||
UntypedFunction::Create<void(int&)>([list](int& x) { x = list[1]; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
int x = 12;
|
||||
uf.Call<void(int&)>(x);
|
||||
EXPECT_EQ(x, 35);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallNontrivialWithRvalueReference) {
|
||||
std::vector<int> list;
|
||||
auto uf = UntypedFunction::Create<int(int&&)>([list](int&& x) mutable {
|
||||
list.push_back(x);
|
||||
return list.size();
|
||||
});
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int(int&&)>(34), 1);
|
||||
EXPECT_EQ(uf.Call<int(int&&)>(34), 2);
|
||||
}
|
||||
|
||||
int AddFive(int x) {
|
||||
return x + 5;
|
||||
}
|
||||
int DereferencePointer(int* x) {
|
||||
return *x;
|
||||
}
|
||||
void AssignThree(int& x) {
|
||||
x = 3;
|
||||
}
|
||||
int SubtractTwo(int&& x) {
|
||||
return x - 2;
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallFunctionPointerWithInt) {
|
||||
auto uf = UntypedFunction::Create<int(int)>(AddFive);
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int(int)>(17), 22);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallFunctionPointerWithPointer) {
|
||||
auto uf = UntypedFunction::Create<int(int*)>(DereferencePointer);
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
int x = 12;
|
||||
EXPECT_EQ(uf.Call<int(int*)>(&x), 12);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallFunctionPointerWithReference) {
|
||||
auto uf = UntypedFunction::Create<void(int&)>(AssignThree);
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
int x = 12;
|
||||
uf.Call<void(int&)>(x);
|
||||
EXPECT_EQ(x, 3);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallFunctionPointerWithRvalueReference) {
|
||||
auto uf = UntypedFunction::Create<int(int&&)>(SubtractTwo);
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int(int&&)>(34), 32);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallTrivialWithNoArgs) {
|
||||
int arr[] = {1, 2, 3};
|
||||
auto uf = UntypedFunction::Create<int()>([arr] { return arr[1]; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int()>(), 2);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, CallLargeTrivialWithNoArgs) {
|
||||
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
|
||||
auto uf = UntypedFunction::Create<int()>([arr] { return arr[4]; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int()>(), 5);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, MoveonlyReturnValue) {
|
||||
auto uf = UntypedFunction::Create<std::unique_ptr<int>()>(
|
||||
[] { return std::make_unique<int>(567); });
|
||||
EXPECT_THAT(uf.Call<std::unique_ptr<int>()>(), Pointee(567));
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, MoveonlyArgument) {
|
||||
auto uf = UntypedFunction::Create<int(std::unique_ptr<int>)>(
|
||||
[](std::unique_ptr<int> x) { return *x + 19; });
|
||||
EXPECT_EQ(uf.Call<int(std::unique_ptr<int>)>(std::make_unique<int>(40)), 59);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, MoveOnlyCallable) {
|
||||
auto uf = UntypedFunction::Create<int()>(
|
||||
[x = std::make_unique<int>(17)] { return ++*x; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int()>(), 18);
|
||||
EXPECT_EQ(uf.Call<int()>(), 19);
|
||||
UntypedFunction uf2 = std::move(uf);
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_FALSE(uf2.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int()>(), 20);
|
||||
EXPECT_EQ(uf.Call<int()>(), 21);
|
||||
}
|
||||
|
||||
class Destroyer {
|
||||
public:
|
||||
explicit Destroyer(int& destroy_count) : destroy_count_(&destroy_count) {}
|
||||
~Destroyer() { ++*destroy_count_; }
|
||||
int operator()() { return 72; }
|
||||
int* destroy_count_;
|
||||
};
|
||||
|
||||
TEST(UntypedFunction, CallableIsDestroyed) {
|
||||
int destroy_count = 0;
|
||||
{
|
||||
auto uf = UntypedFunction::Create<int()>(Destroyer(destroy_count));
|
||||
// Destruction count is 1 here, because the temporary we created above was
|
||||
// destroyed.
|
||||
EXPECT_EQ(destroy_count, 1);
|
||||
{
|
||||
auto uf2 = std::move(uf);
|
||||
EXPECT_EQ(destroy_count, 1);
|
||||
}
|
||||
// `uf2` was destroyed.
|
||||
EXPECT_EQ(destroy_count, 2);
|
||||
}
|
||||
// `uf` was destroyed, but it didn't contain a Destroyer since we moved it to
|
||||
// `uf2` above.
|
||||
EXPECT_EQ(destroy_count, 2);
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, MoveAssign) {
|
||||
int destroy_count = 0;
|
||||
auto uf = UntypedFunction::Create<int()>(Destroyer(destroy_count));
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
// Destruction count is 1 here, because the temporary we created above was
|
||||
// destroyed.
|
||||
EXPECT_EQ(destroy_count, 1);
|
||||
UntypedFunction uf2 = nullptr;
|
||||
EXPECT_FALSE(uf2);
|
||||
EXPECT_TRUE(uf2.IsTriviallyDestructible());
|
||||
|
||||
uf2 = std::move(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_TRUE(uf2);
|
||||
EXPECT_FALSE(uf2.IsTriviallyDestructible());
|
||||
EXPECT_EQ(destroy_count, 1); // The callable was not destroyed.
|
||||
EXPECT_EQ(uf2.Call<int()>(), 72);
|
||||
|
||||
UntypedFunction uf3 = nullptr;
|
||||
uf2 = std::move(uf3);
|
||||
EXPECT_FALSE(uf2);
|
||||
EXPECT_TRUE(uf2.IsTriviallyDestructible());
|
||||
EXPECT_EQ(destroy_count, 2); // The callable was destroyed by the assignment.
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, NullptrAssign) {
|
||||
int destroy_count = 0;
|
||||
auto uf = UntypedFunction::Create<int()>(Destroyer(destroy_count));
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
// Destruction count is 1 here, because the temporary we created above was
|
||||
// destroyed.
|
||||
EXPECT_EQ(destroy_count, 1);
|
||||
|
||||
uf = nullptr;
|
||||
EXPECT_FALSE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_EQ(destroy_count, 2); // The callable was destroyed by the assignment.
|
||||
}
|
||||
|
||||
TEST(UntypedFunction, Swap) {
|
||||
int x = 13;
|
||||
auto uf = UntypedFunction::Create<int()>([x]() mutable { return ++x; });
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
auto y = std::make_unique<int>(113);
|
||||
auto uf2 =
|
||||
UntypedFunction::Create<int()>([y = std::move(y)] { return ++*y; });
|
||||
EXPECT_TRUE(uf2);
|
||||
EXPECT_FALSE(uf2.IsTriviallyDestructible());
|
||||
UntypedFunction uf3 = nullptr;
|
||||
EXPECT_FALSE(uf3);
|
||||
EXPECT_TRUE(uf3.IsTriviallyDestructible());
|
||||
|
||||
EXPECT_EQ(uf.Call<int()>(), 14);
|
||||
swap(uf, uf2);
|
||||
EXPECT_TRUE(uf);
|
||||
EXPECT_FALSE(uf.IsTriviallyDestructible());
|
||||
EXPECT_TRUE(uf2);
|
||||
EXPECT_TRUE(uf2.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf.Call<int()>(), 114);
|
||||
EXPECT_EQ(uf2.Call<int()>(), 15);
|
||||
|
||||
swap(uf, uf3);
|
||||
EXPECT_FALSE(uf);
|
||||
EXPECT_TRUE(uf.IsTriviallyDestructible());
|
||||
EXPECT_TRUE(uf3);
|
||||
EXPECT_FALSE(uf3.IsTriviallyDestructible());
|
||||
EXPECT_EQ(uf3.Call<int()>(), 115);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc
|
||||
Loading…
x
Reference in New Issue
Block a user