Index: chrome/browser/profiles/profile_destroyer_unittest.cc |
diff --git a/chrome/browser/profiles/profile_destroyer_unittest.cc b/chrome/browser/profiles/profile_destroyer_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c9777a4893a0e0ed78e36e746fca9b3b5378d2f1 |
--- /dev/null |
+++ b/chrome/browser/profiles/profile_destroyer_unittest.cc |
@@ -0,0 +1,138 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/profiles/profile_destroyer.h" |
+ |
+#include "chrome/test/base/browser_with_test_window_test.h" |
+#include "chrome/test/base/testing_profile.h" |
+#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/site_instance.h" |
+ |
+class TestingOffTheRecordDestructionProfile : public TestingProfile { |
+ public: |
+ TestingOffTheRecordDestructionProfile() : destroyed_otr_profile_(false) { |
+ set_incognito(true); |
+ } |
+ virtual void DestroyOffTheRecordProfile() OVERRIDE { |
+ destroyed_otr_profile_ = true; |
+ } |
+ bool destroyed_otr_profile_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestingOffTheRecordDestructionProfile); |
+}; |
+ |
+class TestingOriginalDestructionProfile : public TestingProfile { |
+ public: |
+ TestingOriginalDestructionProfile() : destroyed_otr_profile_(false) { |
+ DCHECK_EQ(kNull, living_instance_); |
+ living_instance_ = this; |
+ } |
+ virtual ~TestingOriginalDestructionProfile() { |
+ DCHECK_EQ(this, living_instance_); |
+ living_instance_ = NULL; |
+ } |
+ virtual void DestroyOffTheRecordProfile() OVERRIDE { |
+ SetOffTheRecordProfile(NULL); |
+ destroyed_otr_profile_ = true; |
+ } |
+ bool destroyed_otr_profile_; |
+ static TestingOriginalDestructionProfile* living_instance_; |
+ |
+ // This is to avoid type casting in DCHECK_EQ & EXPECT_NE. |
+ static const TestingOriginalDestructionProfile* kNull; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestingOriginalDestructionProfile); |
+}; |
+const TestingOriginalDestructionProfile* |
+ TestingOriginalDestructionProfile::kNull = NULL; |
+ |
+TestingOriginalDestructionProfile* |
+ TestingOriginalDestructionProfile::living_instance_ = NULL; |
+ |
+class ProfileDestroyerTest : public BrowserWithTestWindowTest { |
+ public: |
+ ProfileDestroyerTest() : off_the_record_profile_(NULL) {} |
+ |
+ protected: |
+ virtual TestingProfile* CreateProfile() OVERRIDE { |
+ if (off_the_record_profile_ == NULL) |
+ off_the_record_profile_ = new TestingOffTheRecordDestructionProfile(); |
+ return off_the_record_profile_; |
+ } |
+ TestingOffTheRecordDestructionProfile* off_the_record_profile_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ProfileDestroyerTest); |
+}; |
+ |
+TEST_F(ProfileDestroyerTest, DelayProfileDestruction) { |
+ scoped_refptr<content::SiteInstance> instance1( |
+ content::SiteInstance::Create(off_the_record_profile_)); |
+ scoped_ptr<content::RenderProcessHost> render_process_host1; |
+ render_process_host1.reset(instance1->GetProcess()); |
+ ASSERT_TRUE(render_process_host1.get() != NULL); |
+ |
+ scoped_refptr<content::SiteInstance> instance2( |
+ content::SiteInstance::Create(off_the_record_profile_)); |
+ scoped_ptr<content::RenderProcessHost> render_process_host2; |
+ render_process_host2.reset(instance2->GetProcess()); |
+ ASSERT_TRUE(render_process_host2.get() != NULL); |
+ |
+ // destroying the browser should not destroy the off the record profile... |
+ set_browser(NULL); |
+ EXPECT_FALSE(off_the_record_profile_->destroyed_otr_profile_); |
+ |
+ // until we destroy the render process host holding on to it... |
+ render_process_host1.release()->Cleanup(); |
+ |
+ // And asynchronicity kicked in properly. |
+ MessageLoop::current()->RunAllPending(); |
+ EXPECT_FALSE(off_the_record_profile_->destroyed_otr_profile_); |
+ |
+ // I meant, ALL the render process hosts... :-) |
+ render_process_host2.release()->Cleanup(); |
+ MessageLoop::current()->RunAllPending(); |
+ EXPECT_TRUE(off_the_record_profile_->destroyed_otr_profile_); |
+} |
+ |
+TEST_F(ProfileDestroyerTest, DelayOriginalProfileDestruction) { |
+ TestingOriginalDestructionProfile* original_profile = |
+ new TestingOriginalDestructionProfile; |
+ |
+ TestingOffTheRecordDestructionProfile* off_the_record_profile = |
+ new TestingOffTheRecordDestructionProfile; |
+ |
+ original_profile->SetOffTheRecordProfile(off_the_record_profile); |
+ |
+ scoped_refptr<content::SiteInstance> instance1( |
+ content::SiteInstance::Create(off_the_record_profile)); |
+ scoped_ptr<content::RenderProcessHost> render_process_host1; |
+ render_process_host1.reset(instance1->GetProcess()); |
+ ASSERT_TRUE(render_process_host1.get() != NULL); |
+ |
+ // Trying to destroy the original profile should be delayed until associated |
+ // off the record profile is released by all render process hosts. |
+ ProfileDestroyer::DestroyProfileWhenAppropriate(original_profile); |
+ EXPECT_NE(TestingOriginalDestructionProfile::kNull, |
+ TestingOriginalDestructionProfile::living_instance_); |
+ EXPECT_FALSE(original_profile->destroyed_otr_profile_); |
+ |
+ render_process_host1.release()->Cleanup(); |
+ MessageLoop::current()->RunAllPending(); |
+ EXPECT_EQ(NULL, TestingOriginalDestructionProfile::living_instance_); |
+ |
+ // And the same protection should apply to the main profile. |
+ TestingOriginalDestructionProfile* main_profile = |
+ new TestingOriginalDestructionProfile; |
+ scoped_refptr<content::SiteInstance> instance2( |
+ content::SiteInstance::Create(main_profile)); |
+ scoped_ptr<content::RenderProcessHost> render_process_host2; |
+ render_process_host2.reset(instance2->GetProcess()); |
+ ASSERT_TRUE(render_process_host2.get() != NULL); |
+ |
+ ProfileDestroyer::DestroyProfileWhenAppropriate(main_profile); |
+ EXPECT_EQ(main_profile, TestingOriginalDestructionProfile::living_instance_); |
+ render_process_host2.release()->Cleanup(); |
+ MessageLoop::current()->RunAllPending(); |
+ EXPECT_EQ(NULL, TestingOriginalDestructionProfile::living_instance_); |
+} |