| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromecast/base/component/component.h" |
| |
| #include <memory> |
| |
| #include "base/functional/bind.h" |
| #include "base/run_loop.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/test/task_environment.h" |
| #include "build/build_config.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace chromecast { |
| |
| class ComponentTest : public ::testing::Test { |
| protected: |
| base::test::SingleThreadTaskEnvironment task_environment_; |
| }; |
| |
| using ComponentDeathTest = ComponentTest; |
| |
| class ComponentB; |
| class ComponentC; |
| class ComponentA : public Component<ComponentA> { |
| public: |
| void MakeSelfDependency() { |
| a_.reset(new Component<ComponentA>::Dependency(GetRef(), this)); |
| } |
| |
| void MakeCircularDependency(const Component<ComponentB>::WeakRef& b) { |
| b_.reset(new Component<ComponentB>::Dependency(b, this)); |
| } |
| |
| void MakeTransitiveCircularDependency( |
| const Component<ComponentC>::WeakRef& c) { |
| c_.reset(new Component<ComponentC>::Dependency(c, this)); |
| } |
| |
| void OnEnable() override { |
| if (!fail_enable_) { |
| enabled_ = true; |
| Test(); |
| } |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&ComponentA::OnEnableComplete, |
| base::Unretained(this), !fail_enable_)); |
| } |
| |
| void OnDisable() override { |
| if (enabled_) |
| Test(); |
| enabled_ = false; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ComponentA::OnDisableComplete, base::Unretained(this))); |
| } |
| |
| void Test() { |
| EXPECT_TRUE(enabled_); |
| EXPECT_FALSE(fail_enable_); |
| } |
| |
| bool enabled() const { return enabled_; } |
| void FailEnable() { fail_enable_ = true; } |
| |
| private: |
| bool enabled_ = false; |
| bool fail_enable_ = false; |
| |
| std::unique_ptr<Component<ComponentA>::Dependency> a_; |
| std::unique_ptr<Component<ComponentB>::Dependency> b_; |
| std::unique_ptr<Component<ComponentC>::Dependency> c_; |
| }; |
| |
| class ComponentB : public Component<ComponentB> { |
| public: |
| explicit ComponentB(const ComponentA::WeakRef& a) : a_(a, this) {} |
| |
| void OnEnable() override { |
| if (!fail_enable_) { |
| enabled_ = true; |
| Test(); |
| } |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&ComponentB::OnEnableComplete, |
| base::Unretained(this), !fail_enable_)); |
| } |
| |
| void OnDisable() override { |
| if (enabled_) |
| Test(); |
| enabled_ = false; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ComponentB::OnDisableComplete, base::Unretained(this))); |
| } |
| |
| void Test() { |
| EXPECT_TRUE(enabled_); |
| EXPECT_FALSE(fail_enable_); |
| a_->Test(); |
| } |
| |
| bool enabled() const { return enabled_; } |
| void FailEnable() { fail_enable_ = true; } |
| |
| private: |
| bool enabled_ = false; |
| bool fail_enable_ = false; |
| |
| ComponentA::Dependency a_; |
| }; |
| |
| class ComponentC : public Component<ComponentC> { |
| public: |
| explicit ComponentC(const ComponentB::WeakRef& b) : b_(b, this) {} |
| |
| void OnEnable() override { |
| if (!fail_enable_) { |
| enabled_ = true; |
| Test(); |
| } |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(&ComponentC::OnEnableComplete, |
| base::Unretained(this), !fail_enable_)); |
| } |
| |
| void OnDisable() override { |
| if (enabled_) |
| Test(); |
| enabled_ = false; |
| base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&ComponentC::OnDisableComplete, base::Unretained(this))); |
| } |
| |
| void Test() { |
| EXPECT_TRUE(enabled_); |
| EXPECT_FALSE(fail_enable_); |
| b_->Test(); |
| } |
| |
| bool enabled() const { return enabled_; } |
| void FailEnable() { fail_enable_ = true; } |
| |
| private: |
| bool enabled_ = false; |
| bool fail_enable_ = false; |
| |
| ComponentB::Dependency b_; |
| }; |
| |
| std::string DeathRegex(const std::string& regex) { |
| #if BUILDFLAG(IS_ANDROID) |
| return ""; |
| #else |
| return regex; |
| #endif |
| } |
| |
| #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST |
| TEST_F(ComponentDeathTest, SelfDependency) { |
| GTEST_FLAG_SET(death_test_style, "threadsafe"); |
| ComponentA a; |
| EXPECT_DEATH(a.MakeSelfDependency(), DeathRegex("Circular dependency")); |
| } |
| |
| TEST_F(ComponentDeathTest, CircularDependency) { |
| GTEST_FLAG_SET(death_test_style, "threadsafe"); |
| ComponentA a; |
| ComponentB b(a.GetRef()); |
| EXPECT_DEATH(a.MakeCircularDependency(b.GetRef()), |
| DeathRegex("Circular dependency")); |
| } |
| |
| TEST_F(ComponentDeathTest, TransitiveCircularDependency) { |
| GTEST_FLAG_SET(death_test_style, "threadsafe"); |
| ComponentA a; |
| ComponentB b(a.GetRef()); |
| ComponentC c(b.GetRef()); |
| EXPECT_DEATH(a.MakeTransitiveCircularDependency(c.GetRef()), |
| DeathRegex("Circular dependency")); |
| } |
| #endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && |
| // GTEST_HAS_DEATH_TEST |
| |
| TEST_F(ComponentTest, SimpleEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, TransitiveEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| std::unique_ptr<ComponentB> b(new ComponentB(a->GetRef())); |
| std::unique_ptr<ComponentC> c(new ComponentC(b->GetRef())); |
| c->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| EXPECT_TRUE(b->enabled()); |
| EXPECT_TRUE(c->enabled()); |
| a.release()->Destroy(); |
| b.release()->Destroy(); |
| c.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, FailEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->FailEnable(); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, TransitiveFailEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| std::unique_ptr<ComponentB> b(new ComponentB(a->GetRef())); |
| std::unique_ptr<ComponentC> c(new ComponentC(b->GetRef())); |
| a->FailEnable(); |
| c->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| EXPECT_FALSE(b->enabled()); |
| EXPECT_FALSE(c->enabled()); |
| a.release()->Destroy(); |
| b.release()->Destroy(); |
| c.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, DisableWhileEnabling) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Enable(); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, EnableTwice) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Enable(); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, DisableTwice) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, DisableAfterFailedEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->FailEnable(); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, DisableAfterNeverEnabled) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, DisableDependencyWhileEnabling) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| std::unique_ptr<ComponentB> b(new ComponentB(a->GetRef())); |
| std::unique_ptr<ComponentC> c(new ComponentC(b->GetRef())); |
| b->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| c->Enable(); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| EXPECT_FALSE(b->enabled()); |
| EXPECT_FALSE(c->enabled()); |
| a.release()->Destroy(); |
| b.release()->Destroy(); |
| c.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, EnableDisableEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Enable(); |
| a->Disable(); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, DisableEnableDisable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| a->Disable(); |
| a->Enable(); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, TransitiveEnableDisableEnable) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| std::unique_ptr<ComponentB> b(new ComponentB(a->GetRef())); |
| std::unique_ptr<ComponentC> c(new ComponentC(b->GetRef())); |
| a->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| c->Enable(); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| EXPECT_FALSE(b->enabled()); |
| EXPECT_FALSE(c->enabled()); |
| c->Enable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(a->enabled()); |
| EXPECT_TRUE(b->enabled()); |
| EXPECT_TRUE(c->enabled()); |
| a.release()->Destroy(); |
| b.release()->Destroy(); |
| c.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, WeakRefs) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| ComponentA::WeakRef weak = a->GetRef(); |
| EXPECT_FALSE(weak.Try()); |
| a->Enable(); |
| EXPECT_FALSE(weak.Try()); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_TRUE(weak.Try()); |
| weak.Try()->Test(); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(weak.Try()); |
| a.release()->Destroy(); |
| } |
| |
| TEST_F(ComponentTest, WeakRefsKeepEnabled) { |
| std::unique_ptr<ComponentA> a(new ComponentA()); |
| ComponentA::WeakRef weak = a->GetRef(); |
| EXPECT_FALSE(weak.Try()); |
| a->Enable(); |
| EXPECT_FALSE(weak.Try()); |
| base::RunLoop().RunUntilIdle(); |
| { |
| auto held_ref = weak.Try(); |
| EXPECT_TRUE(held_ref); |
| held_ref->Test(); |
| a->Disable(); |
| base::RunLoop().RunUntilIdle(); |
| // The held ref keeps |a| enabled until it goes out of scope. |
| EXPECT_TRUE(a->enabled()); |
| } |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(a->enabled()); |
| EXPECT_FALSE(weak.Try()); |
| a.release()->Destroy(); |
| } |
| |
| } // namespace chromecast |