From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../transport/test/runnable_utils_unittest.cpp | 353 +++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 dom/media/webrtc/transport/test/runnable_utils_unittest.cpp (limited to 'dom/media/webrtc/transport/test/runnable_utils_unittest.cpp') diff --git a/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp b/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp new file mode 100644 index 0000000000..70707b148f --- /dev/null +++ b/dom/media/webrtc/transport/test/runnable_utils_unittest.cpp @@ -0,0 +1,353 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Original author: ekr@rtfm.com +#include + +#include "nsCOMPtr.h" +#include "nsNetCID.h" + +#include "mozilla/RefPtr.h" +#include "mozilla/UniquePtr.h" + +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" + +#include "runnable_utils.h" + +#define GTEST_HAS_RTTI 0 +#include "gtest/gtest.h" +#include "gtest_utils.h" + +using namespace mozilla; + +namespace { + +// Helper used to make sure args are properly copied and/or moved. +struct CtorDtorState { + enum class State { Empty, Copy, Explicit, Move, Moved }; + + static const char* ToStr(const State& state) { + switch (state) { + case State::Empty: + return "empty"; + case State::Copy: + return "copy"; + case State::Explicit: + return "explicit"; + case State::Move: + return "move"; + case State::Moved: + return "moved"; + default: + return "unknown"; + } + } + + void DumpState() const { std::cerr << ToStr(state_) << std::endl; } + + CtorDtorState() { DumpState(); } + + explicit CtorDtorState(int* destroyed) + : dtor_count_(destroyed), state_(State::Explicit) { + DumpState(); + } + + CtorDtorState(const CtorDtorState& other) + : dtor_count_(other.dtor_count_), state_(State::Copy) { + DumpState(); + } + + // Clear the other's dtor counter so it's not counted if moved. + CtorDtorState(CtorDtorState&& other) + : dtor_count_(std::exchange(other.dtor_count_, nullptr)), + state_(State::Move) { + other.state_ = State::Moved; + DumpState(); + } + + ~CtorDtorState() { + const char* const state = ToStr(state_); + std::cerr << "Destructor called with end state: " << state << std::endl; + + if (dtor_count_) { + ++*dtor_count_; + } + } + + int* dtor_count_ = nullptr; + State state_ = State::Empty; +}; + +class Destructor { + private: + ~Destructor() { + std::cerr << "Destructor called" << std::endl; + *destroyed_ = true; + } + + public: + explicit Destructor(bool* destroyed) : destroyed_(destroyed) {} + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Destructor) + + private: + bool* destroyed_; +}; + +class TargetClass { + public: + explicit TargetClass(int* ran) : ran_(ran) {} + + void m1(int x) { + std::cerr << __FUNCTION__ << " " << x << std::endl; + *ran_ = 1; + } + + void m2(int x, int y) { + std::cerr << __FUNCTION__ << " " << x << " " << y << std::endl; + *ran_ = 2; + } + + void m1set(bool* z) { + std::cerr << __FUNCTION__ << std::endl; + *z = true; + } + int return_int(int x) { + std::cerr << __FUNCTION__ << std::endl; + return x; + } + + void destructor_target_ref(RefPtr destructor) {} + + int* ran_; +}; + +class RunnableArgsTest : public MtransportTest { + public: + RunnableArgsTest() : MtransportTest(), ran_(0), cl_(&ran_) {} + + void Test1Arg() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1); + r->Run(); + ASSERT_EQ(1, ran_); + } + + void Test2Args() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2); + r->Run(); + ASSERT_EQ(2, ran_); + } + + private: + int ran_; + TargetClass cl_; +}; + +class DispatchTest : public MtransportTest { + public: + DispatchTest() : MtransportTest(), ran_(0), cl_(&ran_) {} + + void SetUp() { + MtransportTest::SetUp(); + + nsresult rv; + target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + } + + void Test1Arg() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m1, 1); + NS_DispatchAndSpinEventLoopUntilComplete("DispatchTest::Test1Arg"_ns, + target_, do_AddRef(r)); + ASSERT_EQ(1, ran_); + } + + void Test2Args() { + Runnable* r = WrapRunnable(&cl_, &TargetClass::m2, 1, 2); + NS_DispatchAndSpinEventLoopUntilComplete("DispatchTest::Test2Args"_ns, + target_, do_AddRef(r)); + ASSERT_EQ(2, ran_); + } + + void Test1Set() { + bool x = false; + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::Test1Set"_ns, target_, + do_AddRef(WrapRunnable(&cl_, &TargetClass::m1set, &x))); + ASSERT_TRUE(x); + } + + void TestRet() { + int z; + int x = 10; + + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestRet"_ns, target_, + do_AddRef(WrapRunnableRet(&z, &cl_, &TargetClass::return_int, x))); + ASSERT_EQ(10, z); + } + + protected: + int ran_; + TargetClass cl_; + nsCOMPtr target_; +}; + +TEST_F(RunnableArgsTest, OneArgument) { Test1Arg(); } + +TEST_F(RunnableArgsTest, TwoArguments) { Test2Args(); } + +TEST_F(DispatchTest, OneArgument) { Test1Arg(); } + +TEST_F(DispatchTest, TwoArguments) { Test2Args(); } + +TEST_F(DispatchTest, Test1Set) { Test1Set(); } + +TEST_F(DispatchTest, TestRet) { TestRet(); } + +void SetNonMethod(TargetClass* cl, int x) { cl->m1(x); } + +int SetNonMethodRet(TargetClass* cl, int x) { + cl->m1(x); + + return x; +} + +TEST_F(DispatchTest, TestNonMethod) { + test_utils_->SyncDispatchToSTS(WrapRunnableNM(SetNonMethod, &cl_, 10)); + + ASSERT_EQ(1, ran_); +} + +TEST_F(DispatchTest, TestNonMethodRet) { + int z; + + test_utils_->SyncDispatchToSTS( + WrapRunnableNMRet(&z, SetNonMethodRet, &cl_, 10)); + + ASSERT_EQ(1, ran_); + ASSERT_EQ(10, z); +} + +TEST_F(DispatchTest, TestDestructorRef) { + bool destroyed = false; + { + RefPtr destructor = new Destructor(&destroyed); + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestDestructorRef"_ns, target_, + do_AddRef(WrapRunnable(&cl_, &TargetClass::destructor_target_ref, + destructor))); + ASSERT_FALSE(destroyed); + } + ASSERT_TRUE(destroyed); + + // Now try with a move. + destroyed = false; + { + RefPtr destructor = new Destructor(&destroyed); + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestDestructorRef"_ns, target_, + do_AddRef(WrapRunnable(&cl_, &TargetClass::destructor_target_ref, + std::move(destructor)))); + ASSERT_TRUE(destroyed); + } +} + +TEST_F(DispatchTest, TestMove) { + int destroyed = 0; + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - moved arg + // - by-val capture in function should consume a move + // - expect destruction in the function scope + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestMove"_ns, target_, + do_AddRef(WrapRunnableNM([](CtorDtorState s) {}, std::move(state)))); + ASSERT_EQ(1, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(1, destroyed); + + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - copied arg + // - by-val capture in function should consume a move + // - expect destruction in the function scope and call scope + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestMove"_ns, target_, + do_AddRef(WrapRunnableNM([](CtorDtorState s) {}, state))); + ASSERT_EQ(2, destroyed); + } + // Original state should be destroyed + ASSERT_EQ(3, destroyed); + + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - moved arg + // - by-ref in function should accept a moved arg + // - expect destruction in the wrapper invocation scope + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestMove"_ns, target_, + do_AddRef( + WrapRunnableNM([](const CtorDtorState& s) {}, std::move(state)))); + ASSERT_EQ(4, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(4, destroyed); + + { + CtorDtorState state(&destroyed); + + // Dispatch with: + // - moved arg + // - r-value function should accept a moved arg + // - expect destruction in the wrapper invocation scope + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestMove"_ns, target_, + do_AddRef(WrapRunnableNM([](CtorDtorState&& s) {}, std::move(state)))); + ASSERT_EQ(5, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(5, destroyed); +} + +TEST_F(DispatchTest, TestUniquePtr) { + // Test that holding the class in UniquePtr works + int ran = 0; + auto cl = MakeUnique(&ran); + + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestUniquePtr"_ns, target_, + do_AddRef(WrapRunnable(std::move(cl), &TargetClass::m1, 1))); + ASSERT_EQ(1, ran); + + // Test that UniquePtr works as a param to the runnable + int destroyed = 0; + { + auto state = MakeUnique(&destroyed); + + // Dispatch with: + // - moved arg + // - Function should move construct from arg + // - expect destruction in the wrapper invocation scope + NS_DispatchAndSpinEventLoopUntilComplete( + "DispatchTest::TestUniquePtr"_ns, target_, + do_AddRef(WrapRunnableNM([](UniquePtr s) {}, + std::move(state)))); + ASSERT_EQ(1, destroyed); + } + // Still shouldn't count when we go out of scope as it was moved. + ASSERT_EQ(1, destroyed); +} + +} // end of namespace -- cgit v1.2.3