| /// Copyright 2014 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 "athena/activity/public/activity_factory.h" |
| #include "athena/activity/public/activity_manager.h" |
| #include "athena/content/app_activity.h" |
| #include "athena/content/app_activity_registry.h" |
| #include "athena/content/public/app_registry.h" |
| #include "athena/extensions/public/extensions_delegate.h" |
| #include "athena/resource_manager/public/resource_manager.h" |
| #include "athena/test/base/athena_test_base.h" |
| #include "extensions/browser/install/extension_install_ui.h" |
| #include "extensions/common/extension_set.h" |
| #include "ui/aura/window.h" |
| #include "ui/views/view.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace content { |
| class BrowserContext; |
| } |
| |
| namespace athena { |
| namespace test { |
| |
| namespace { |
| |
| // An identifier for the running apps. |
| const char kDummyApp1[] = "aaaaaaa"; |
| const char kDummyApp2[] = "bbbbbbb"; |
| |
| // A dummy test app activity which works without content / ShellAppWindow. |
| class TestAppActivity : public AppActivity { |
| public: |
| explicit TestAppActivity(const std::string& app_id) |
| : AppActivity(app_id), |
| view_(new views::View()), |
| current_state_(ACTIVITY_VISIBLE) { |
| app_activity_registry_ = |
| AppRegistry::Get()->GetAppActivityRegistry(app_id, nullptr); |
| app_activity_registry_->RegisterAppActivity(this); |
| } |
| ~TestAppActivity() override { |
| app_activity_registry_->UnregisterAppActivity(this); |
| } |
| |
| AppActivityRegistry* app_activity_registry() { |
| return app_activity_registry_; |
| } |
| |
| // Activity: |
| ActivityViewModel* GetActivityViewModel() override { return this; } |
| void SetCurrentState(Activity::ActivityState state) override { |
| current_state_ = state; |
| if (state == ACTIVITY_UNLOADED) |
| app_activity_registry_->Unload(); |
| } |
| ActivityState GetCurrentState() override { return current_state_; } |
| bool IsVisible() override { return true; } |
| ActivityMediaState GetMediaState() override { |
| return Activity::ACTIVITY_MEDIA_STATE_NONE; |
| } |
| aura::Window* GetWindow() override { |
| return view_->GetWidget() ? view_->GetWidget()->GetNativeWindow() : nullptr; |
| } |
| |
| // ActivityViewModel: |
| void Init() override {} |
| SkColor GetRepresentativeColor() const override { return 0; } |
| base::string16 GetTitle() const override { return title_; } |
| bool UsesFrame() const override { return true; } |
| views::View* GetContentsView() override { return view_; } |
| gfx::ImageSkia GetOverviewModeImage() override { return gfx::ImageSkia(); } |
| |
| private: |
| // If known the registry which holds all activities for the associated app. |
| AppActivityRegistry* app_activity_registry_; |
| |
| // The title of the activity. |
| base::string16 title_; |
| |
| // Our view. |
| views::View* view_; |
| |
| // The current state for this activity. |
| ActivityState current_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestAppActivity); |
| }; |
| |
| // An AppContentDelegateClass which we can query for call stats. |
| class TestExtensionsDelegate : public ExtensionsDelegate { |
| public: |
| TestExtensionsDelegate() : unload_called_(0), restart_called_(0) {} |
| ~TestExtensionsDelegate() override {} |
| |
| int unload_called() const { return unload_called_; } |
| int restart_called() const { return restart_called_; } |
| |
| // ExtensionsDelegate: |
| content::BrowserContext* GetBrowserContext() const override { |
| return nullptr; |
| } |
| const extensions::ExtensionSet& GetInstalledExtensions() override { |
| return extension_set_; |
| } |
| // Unload an application. Returns true when unloaded. |
| bool UnloadApp(const std::string& app_id) override { |
| unload_called_++; |
| // Since we did not close anything we let the framework clean up. |
| return false; |
| } |
| // Restarts an application. Returns true when the restart was initiated. |
| bool LaunchApp(const std::string& app_id) override { |
| restart_called_++; |
| return true; |
| } |
| scoped_ptr<extensions::ExtensionInstallUI> CreateExtensionInstallUI() |
| override { |
| return scoped_ptr<extensions::ExtensionInstallUI>(); |
| } |
| |
| private: |
| int unload_called_; |
| int restart_called_; |
| |
| extensions::ExtensionSet extension_set_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TestExtensionsDelegate); |
| }; |
| |
| } // namespace |
| |
| // Our testing base. |
| class AppActivityTest : public AthenaTestBase { |
| public: |
| AppActivityTest() : test_extensions_delegate_(nullptr) {} |
| ~AppActivityTest() override {} |
| |
| // AthenaTestBase: |
| void SetUp() override { |
| AthenaTestBase::SetUp(); |
| // Create and install our TestAppContentDelegate with instrumentation. |
| ExtensionsDelegate::Shutdown(); |
| // The instance will be deleted by ExtensionsDelegate::Shutdown(). |
| test_extensions_delegate_ = new TestExtensionsDelegate(); |
| } |
| |
| // A function to create an Activity. |
| TestAppActivity* CreateAppActivity(const std::string& app_id) { |
| TestAppActivity* activity = new TestAppActivity(app_id); |
| ActivityManager::Get()->AddActivity(activity); |
| return activity; |
| } |
| |
| void DeleteActivity(Activity* activity) { |
| Activity::Delete(activity); |
| RunAllPendingInMessageLoop(); |
| } |
| |
| // Get the position of the activity in the navigation history. |
| int GetActivityPosition(Activity* activity) { |
| aura::Window* window = activity->GetActivityViewModel()->GetContentsView() |
| ->GetWidget()->GetNativeWindow(); |
| aura::Window::Windows windows = activity->GetWindow()->parent()->children(); |
| for (size_t i = 0; i < windows.size(); i++) { |
| if (windows[i] == window) |
| return i; |
| } |
| return -1; |
| } |
| |
| // To avoid interference of the ResourceManager in these AppActivity |
| // framework tests, we disable the ResourceManager for some tests. |
| // Every use/interference of this function gets explained. |
| void DisableResourceManager() { |
| ResourceManager::Get()->Pause(true); |
| } |
| |
| protected: |
| TestExtensionsDelegate* test_extensions_delegate() { |
| return test_extensions_delegate_; |
| } |
| |
| private: |
| TestExtensionsDelegate* test_extensions_delegate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AppActivityTest); |
| }; |
| |
| // Only creates one activity and destroys it. |
| TEST_F(AppActivityTest, OneAppActivity) { |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| { |
| TestAppActivity* app_activity = CreateAppActivity(kDummyApp1); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, app_activity->app_activity_registry()->NumberOfActivities()); |
| EXPECT_EQ(AppRegistry::Get()->GetAppActivityRegistry(kDummyApp1, nullptr), |
| app_activity->app_activity_registry()); |
| DeleteActivity(app_activity); |
| } |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(0, test_extensions_delegate()->unload_called()); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| } |
| |
| // Test running of two applications. |
| TEST_F(AppActivityTest, TwoAppsWithOneActivityEach) { |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| { |
| TestAppActivity* app_activity1 = CreateAppActivity(kDummyApp1); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, app_activity1->app_activity_registry()->NumberOfActivities()); |
| TestAppActivity* app_activity2 = CreateAppActivity(kDummyApp2); |
| EXPECT_EQ(2, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, app_activity2->app_activity_registry()->NumberOfActivities()); |
| EXPECT_EQ(1, app_activity1->app_activity_registry()->NumberOfActivities()); |
| DeleteActivity(app_activity1); |
| DeleteActivity(app_activity2); |
| } |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(0, test_extensions_delegate()->unload_called()); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| } |
| |
| // Create and destroy two activities for the same application. |
| TEST_F(AppActivityTest, TwoAppActivities) { |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| { |
| TestAppActivity* app_activity1 = CreateAppActivity(kDummyApp1); |
| TestAppActivity* app_activity2 = CreateAppActivity(kDummyApp1); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(2, app_activity1->app_activity_registry()->NumberOfActivities()); |
| EXPECT_EQ(app_activity1->app_activity_registry(), |
| app_activity2->app_activity_registry()); |
| DeleteActivity(app_activity1); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, app_activity2->app_activity_registry()->NumberOfActivities()); |
| DeleteActivity(app_activity2); |
| } |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| { |
| TestAppActivity* app_activity1 = CreateAppActivity(kDummyApp1); |
| TestAppActivity* app_activity2 = CreateAppActivity(kDummyApp1); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(2, app_activity1->app_activity_registry()->NumberOfActivities()); |
| EXPECT_EQ(app_activity1->app_activity_registry(), |
| app_activity2->app_activity_registry()); |
| DeleteActivity(app_activity2); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, app_activity1->app_activity_registry()->NumberOfActivities()); |
| DeleteActivity(app_activity1); |
| } |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(0, test_extensions_delegate()->unload_called()); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| } |
| |
| // Test unload and the creation of the proxy, then "closing the activity". |
| TEST_F(AppActivityTest, TestUnloadFollowedByClose) { |
| // We do not want the ResourceManager to interfere with this test. In this |
| // case it would (dependent on its current internal implementation) |
| // automatically re-load the unloaded activity if it is in an "active" |
| // position. |
| DisableResourceManager(); |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| |
| TestAppActivity* app_activity = CreateAppActivity(kDummyApp1); |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| AppActivityRegistry* app_activity_registry = |
| app_activity->app_activity_registry(); |
| EXPECT_EQ(1, app_activity_registry->NumberOfActivities()); |
| EXPECT_EQ(Activity::ACTIVITY_VISIBLE, app_activity->GetCurrentState()); |
| |
| // Calling Unload now should not do anything since at least one activity in |
| // the registry is still visible. |
| app_activity_registry->Unload(); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(0, test_extensions_delegate()->unload_called()); |
| |
| // After setting our activity to unloaded however the application should get |
| // unloaded as requested. |
| app_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(1, test_extensions_delegate()->unload_called()); |
| |
| // Check that our created application is gone, and instead a proxy got |
| // created. |
| ASSERT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| ASSERT_EQ(app_activity_registry, |
| AppRegistry::Get()->GetAppActivityRegistry(kDummyApp1, nullptr)); |
| EXPECT_EQ(0, app_activity_registry->NumberOfActivities()); |
| Activity* activity_proxy = app_activity_registry->unloaded_activity_proxy(); |
| ASSERT_TRUE(activity_proxy); |
| EXPECT_NE(app_activity, activity_proxy); |
| EXPECT_EQ(Activity::ACTIVITY_UNLOADED, activity_proxy->GetCurrentState()); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| |
| // Close the proxy object and make sure that nothing bad happens. |
| DeleteActivity(activity_proxy); |
| |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, test_extensions_delegate()->unload_called()); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| } |
| |
| // Test that when unloading an app while multiple apps / activities are present, |
| // the proxy gets created in the correct location. |
| TEST_F(AppActivityTest, TestUnloadProxyLocation) { |
| // Disable the resource manager since some build bots run this test for an |
| // extended amount of time which allows the MemoryPressureNotifier to fire. |
| DisableResourceManager(); |
| // Set up some activities for some applications. |
| TestAppActivity* app_activity1a = CreateAppActivity(kDummyApp1); |
| TestAppActivity* app_activity2a = CreateAppActivity(kDummyApp2); |
| TestAppActivity* app_activity2b = CreateAppActivity(kDummyApp2); |
| TestAppActivity* app_activity1b = CreateAppActivity(kDummyApp1); |
| EXPECT_EQ(3, GetActivityPosition(app_activity1b)); |
| EXPECT_EQ(2, GetActivityPosition(app_activity2b)); |
| EXPECT_EQ(1, GetActivityPosition(app_activity2a)); |
| EXPECT_EQ(0, GetActivityPosition(app_activity1a)); |
| |
| // Unload an app and make sure that the proxy is in the newest activity slot. |
| AppActivityRegistry* app_activity_registry = |
| app_activity2a->app_activity_registry(); |
| app_activity2a->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| app_activity2b->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(0, app_activity_registry->NumberOfActivities()); |
| Activity* activity_proxy = app_activity_registry->unloaded_activity_proxy(); |
| RunAllPendingInMessageLoop(); |
| |
| EXPECT_EQ(2, GetActivityPosition(app_activity1b)); |
| EXPECT_EQ(1, GetActivityPosition(activity_proxy)); |
| EXPECT_EQ(0, GetActivityPosition(app_activity1a)); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| |
| DeleteActivity(activity_proxy); |
| DeleteActivity(app_activity1b); |
| DeleteActivity(app_activity1a); |
| } |
| |
| // Test that an unload with multiple activities of the same app will only unload |
| // when all activities were marked for unloading. |
| TEST_F(AppActivityTest, TestMultipleActivityUnloadLock) { |
| // Disable the resource manager since some build bots run this test for an |
| // extended amount of time which allows the MemoryPressureNotifier to fire. |
| DisableResourceManager(); |
| |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| |
| TestAppActivity* app_activity1 = CreateAppActivity(kDummyApp1); |
| TestAppActivity* app_activity2 = CreateAppActivity(kDummyApp1); |
| TestAppActivity* app_activity3 = CreateAppActivity(kDummyApp1); |
| |
| // Check that we have 3 activities of the same application. |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| AppActivityRegistry* app_activity_registry = |
| app_activity1->app_activity_registry(); |
| EXPECT_EQ(app_activity_registry, app_activity2->app_activity_registry()); |
| EXPECT_EQ(app_activity_registry, app_activity3->app_activity_registry()); |
| EXPECT_EQ(3, app_activity_registry->NumberOfActivities()); |
| EXPECT_EQ(Activity::ACTIVITY_VISIBLE, app_activity1->GetCurrentState()); |
| EXPECT_EQ(Activity::ACTIVITY_VISIBLE, app_activity2->GetCurrentState()); |
| EXPECT_EQ(Activity::ACTIVITY_VISIBLE, app_activity3->GetCurrentState()); |
| |
| // After setting all activities to UNLOADED the application should unload. |
| app_activity1->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(0, test_extensions_delegate()->unload_called()); |
| app_activity2->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(0, test_extensions_delegate()->unload_called()); |
| app_activity3->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(1, test_extensions_delegate()->unload_called()); |
| |
| // Now there should only be the proxy activity left. |
| ASSERT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| ASSERT_EQ(app_activity_registry, |
| AppRegistry::Get()->GetAppActivityRegistry(kDummyApp1, nullptr)); |
| EXPECT_EQ(0, app_activity_registry->NumberOfActivities()); |
| Activity* activity_proxy = app_activity_registry->unloaded_activity_proxy(); |
| ASSERT_TRUE(activity_proxy); |
| EXPECT_NE(app_activity1, activity_proxy); |
| EXPECT_NE(app_activity2, activity_proxy); |
| EXPECT_NE(app_activity3, activity_proxy); |
| EXPECT_EQ(Activity::ACTIVITY_UNLOADED, activity_proxy->GetCurrentState()); |
| |
| // Close the proxy object and make sure that nothing bad happens. |
| DeleteActivity(activity_proxy); |
| |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_EQ(1, test_extensions_delegate()->unload_called()); |
| EXPECT_EQ(0, test_extensions_delegate()->restart_called()); |
| } |
| |
| // Test that activating the proxy will reload the application. |
| TEST_F(AppActivityTest, TestUnloadWithReload) { |
| // We do not want the ResourceManager to interfere with this test. In this |
| // case it would (dependent on its current internal implementation) |
| // automatically re-load the unloaded activity if it is in an "active" |
| // position. |
| DisableResourceManager(); |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| |
| TestAppActivity* app_activity = CreateAppActivity(kDummyApp1); |
| AppActivityRegistry* app_activity_registry = |
| app_activity->app_activity_registry(); |
| |
| // Unload the activity. |
| app_activity->SetCurrentState(Activity::ACTIVITY_UNLOADED); |
| RunAllPendingInMessageLoop(); |
| EXPECT_EQ(1, test_extensions_delegate()->unload_called()); |
| |
| // Try to activate the activity again. This will force the application to |
| // reload. |
| Activity* activity_proxy = app_activity_registry->unloaded_activity_proxy(); |
| activity_proxy->SetCurrentState(Activity::ACTIVITY_VISIBLE); |
| EXPECT_EQ(1, test_extensions_delegate()->restart_called()); |
| |
| // However - the restart in this test framework does not really restart and |
| // all objects should be still there.. |
| EXPECT_EQ(1, AppRegistry::Get()->NumberOfApplications()); |
| EXPECT_TRUE(app_activity_registry->unloaded_activity_proxy()); |
| Activity::Delete(app_activity_registry->unloaded_activity_proxy()); |
| EXPECT_EQ(0, AppRegistry::Get()->NumberOfApplications()); |
| } |
| |
| } // namespace test |
| } // namespace athena |