blob: 266b940fd4e9ae817b4d98feb64ffd8aec80c50a [file] [log] [blame]
/// 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