// 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 "chrome/browser/apps/ephemeral_app_browsertest.h"
#include <vector>
#include "apps/saved_files_service.h"
#include "base/files/scoped_temp_dir.h"
#include "base/scoped_observer.h"
#include "base/stl_util.h"
#include "chrome/browser/apps/app_browsertest_util.h"
#include "chrome/browser/extensions/api/file_system/file_system_api.h"
#include "chrome/browser/extensions/app_sync_data.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_sync_service.h"
#include "chrome/browser/extensions/extension_test_message_listener.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/notifications/desktop_notification_service.h"
#include "chrome/browser/notifications/desktop_notification_service_factory.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/api/alarms.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/app_sorting.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_observer.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/process_manager.h"
#include "extensions/common/extension.h"
#include "extensions/common/switches.h"
#include "sync/api/fake_sync_change_processor.h"
#include "sync/api/sync_change_processor_wrapper_for_test.h"
#include "sync/api/sync_error_factory_mock.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/notifier_settings.h"
using extensions::AppSyncData;
using extensions::Event;
using extensions::EventRouter;
using extensions::Extension;
using extensions::ExtensionPrefs;
using extensions::ExtensionRegistry;
using extensions::ExtensionRegistryObserver;
using extensions::ExtensionSystem;
using extensions::Manifest;
namespace {
namespace alarms = extensions::api::alarms;
const char kDispatchEventTestApp[] = "ephemeral_apps/dispatch_event";
const char kNotificationsTestApp[] = "ephemeral_apps/notification_settings";
const char kFileSystemTestApp[] = "ephemeral_apps/filesystem_retain_entries";
typedef std::vector<message_center::Notifier*> NotifierList;
bool IsNotifierInList(const message_center::NotifierId& notifier_id,
const NotifierList& notifiers) {
for (NotifierList::const_iterator it = notifiers.begin();
it != notifiers.end(); ++it) {
const message_center::Notifier* notifier = *it;
if (notifier->notifier_id == notifier_id)
return true;
}
return false;
}
// Saves some parameters from the extension installed notification in order
// to verify them in tests.
class InstallObserver : public ExtensionRegistryObserver {
public:
struct InstallParameters {
std::string id;
bool is_update;
bool from_ephemeral;
InstallParameters(
const std::string& id,
bool is_update,
bool from_ephemeral)
: id(id), is_update(is_update), from_ephemeral(from_ephemeral) {}
};
explicit InstallObserver(Profile* profile) : registry_observer_(this) {
registry_observer_.Add(ExtensionRegistry::Get(profile));
}
virtual ~InstallObserver() {}
const InstallParameters& Last() {
CHECK(!install_params_.empty());
return install_params_.back();
}
private:
virtual void OnExtensionWillBeInstalled(
content::BrowserContext* browser_context,
const Extension* extension,
bool is_update,
bool from_ephemeral,
const std::string& old_name) OVERRIDE {
install_params_.push_back(
InstallParameters(extension->id(), is_update, from_ephemeral));
}
std::vector<InstallParameters> install_params_;
ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver>
registry_observer_;
};
} // namespace
// EphemeralAppTestBase:
const char EphemeralAppTestBase::kMessagingReceiverApp[] =
"ephemeral_apps/messaging_receiver";
const char EphemeralAppTestBase::kMessagingReceiverAppV2[] =
"ephemeral_apps/messaging_receiver2";
EphemeralAppTestBase::EphemeralAppTestBase() {}
EphemeralAppTestBase::~EphemeralAppTestBase() {}
void EphemeralAppTestBase::SetUpCommandLine(base::CommandLine* command_line) {
// Skip PlatformAppBrowserTest, which sets different values for the switches
// below.
ExtensionBrowserTest::SetUpCommandLine(command_line);
// Make event pages get suspended immediately.
command_line->AppendSwitchASCII(
extensions::switches::kEventPageIdleTime, "10");
command_line->AppendSwitchASCII(
extensions::switches::kEventPageSuspendingTime, "10");
// Enable ephemeral apps flag.
command_line->AppendSwitch(switches::kEnableEphemeralApps);
}
base::FilePath EphemeralAppTestBase::GetTestPath(const char* test_path) {
return test_data_dir_.AppendASCII("platform_apps").AppendASCII(test_path);
}
const Extension* EphemeralAppTestBase::InstallEphemeralApp(
const char* test_path, Manifest::Location manifest_location) {
const Extension* extension = InstallEphemeralAppWithSourceAndFlags(
GetTestPath(test_path), 1, manifest_location, Extension::NO_FLAGS);
EXPECT_TRUE(extension);
if (extension)
EXPECT_TRUE(extensions::util::IsEphemeralApp(extension->id(), profile()));
return extension;
}
const Extension* EphemeralAppTestBase::InstallEphemeralApp(
const char* test_path) {
return InstallEphemeralApp(test_path, Manifest::INTERNAL);
}
const Extension* EphemeralAppTestBase::InstallAndLaunchEphemeralApp(
const char* test_path) {
ExtensionTestMessageListener launched_listener("launched", false);
const Extension* extension = InstallEphemeralApp(test_path);
EXPECT_TRUE(extension);
if (!extension)
return NULL;
LaunchPlatformApp(extension);
bool wait_result = launched_listener.WaitUntilSatisfied();
EXPECT_TRUE(wait_result);
if (!wait_result)
return NULL;
return extension;
}
const Extension* EphemeralAppTestBase::UpdateEphemeralApp(
const std::string& app_id,
const base::FilePath& test_dir,
const base::FilePath& pem_path) {
// Pack a new version of the app.
base::ScopedTempDir temp_dir;
EXPECT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath crx_path = temp_dir.path().AppendASCII("temp.crx");
if (!base::DeleteFile(crx_path, false)) {
ADD_FAILURE() << "Failed to delete existing crx: " << crx_path.value();
return NULL;
}
base::FilePath app_v2_path = PackExtensionWithOptions(
test_dir, crx_path, pem_path, base::FilePath());
EXPECT_FALSE(app_v2_path.empty());
// Update the ephemeral app and wait for the update to finish.
extensions::CrxInstaller* crx_installer = NULL;
content::WindowedNotificationObserver windowed_observer(
chrome::NOTIFICATION_CRX_INSTALLER_DONE,
content::Source<extensions::CrxInstaller>(crx_installer));
ExtensionService* service =
ExtensionSystem::Get(profile())->extension_service();
EXPECT_TRUE(service->UpdateExtension(app_id, app_v2_path, true,
&crx_installer));
windowed_observer.Wait();
return service->GetExtensionById(app_id, false);
}
void EphemeralAppTestBase::PromoteEphemeralApp(
const extensions::Extension* app) {
ExtensionService* extension_service =
ExtensionSystem::Get(profile())->extension_service();
ASSERT_TRUE(extension_service);
extension_service->PromoteEphemeralApp(app, false);
}
void EphemeralAppTestBase::CloseApp(const std::string& app_id) {
content::WindowedNotificationObserver event_page_destroyed_signal(
chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED,
content::Source<Profile>(profile()));
EXPECT_EQ(1U, GetAppWindowCountForApp(app_id));
apps::AppWindow* app_window = GetFirstAppWindowForApp(app_id);
ASSERT_TRUE(app_window);
CloseAppWindow(app_window);
event_page_destroyed_signal.Wait();
}
void EphemeralAppTestBase::EvictApp(const std::string& app_id) {
// Uninstall the app, which is what happens when ephemeral apps get evicted
// from the cache.
content::WindowedNotificationObserver uninstalled_signal(
chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED,
content::Source<Profile>(profile()));
ExtensionService* service =
ExtensionSystem::Get(profile())->extension_service();
ASSERT_TRUE(service);
service->UninstallExtension(app_id, false, NULL);
uninstalled_signal.Wait();
}
// EphemeralAppBrowserTest:
class EphemeralAppBrowserTest : public EphemeralAppTestBase {
protected:
bool LaunchAppAndRunTest(const Extension* app, const char* test_name) {
ExtensionTestMessageListener launched_listener("launched", true);
LaunchPlatformApp(app);
if (!launched_listener.WaitUntilSatisfied()) {
message_ = "Failed to receive launched message from test";
return false;
}
ResultCatcher catcher;
launched_listener.Reply(test_name);
bool result = catcher.GetNextResult();
message_ = catcher.message();
CloseApp(app->id());
return result;
}
void VerifyAppNotLoaded(const std::string& app_id) {
EXPECT_FALSE(ExtensionSystem::Get(profile())->
process_manager()->GetBackgroundHostForExtension(app_id));
}
void DispatchAlarmEvent(EventRouter* event_router,
const std::string& app_id) {
alarms::Alarm dummy_alarm;
dummy_alarm.name = "test_alarm";
scoped_ptr<base::ListValue> args(new base::ListValue());
args->Append(dummy_alarm.ToValue().release());
scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName,
args.Pass()));
event_router->DispatchEventToExtension(app_id, event.Pass());
}
const Extension* ReplaceEphemeralApp(const std::string& app_id,
const char* test_path) {
return UpdateExtensionWaitForIdle(app_id, GetTestPath(test_path), 0);
}
void VerifyPromotedApp(const std::string& app_id,
ExtensionRegistry::IncludeFlag expected_set) {
const Extension* app = ExtensionRegistry::Get(profile())->GetExtensionById(
app_id, expected_set);
ASSERT_TRUE(app);
// The app should not be ephemeral.
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
ASSERT_TRUE(prefs);
EXPECT_FALSE(prefs->IsEphemeralApp(app_id));
// Check sort ordinals.
extensions::AppSorting* app_sorting = prefs->app_sorting();
EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_id).IsValid());
EXPECT_TRUE(app_sorting->GetPageOrdinal(app_id).IsValid());
}
void InitSyncService() {
ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile());
sync_service->MergeDataAndStartSyncing(
syncer::APPS,
syncer::SyncDataList(),
scoped_ptr<syncer::SyncChangeProcessor>(
new syncer::SyncChangeProcessorWrapperForTest(
&mock_sync_processor_)),
scoped_ptr<syncer::SyncErrorFactory>(
new syncer::SyncErrorFactoryMock()));
}
scoped_ptr<AppSyncData> GetFirstSyncChangeForApp(const std::string& id) {
scoped_ptr<AppSyncData> sync_data;
for (syncer::SyncChangeList::iterator it =
mock_sync_processor_.changes().begin();
it != mock_sync_processor_.changes().end(); ++it) {
sync_data.reset(new AppSyncData(*it));
if (sync_data->id() == id)
return sync_data.Pass();
}
return scoped_ptr<AppSyncData>();
}
void VerifySyncChange(const AppSyncData* sync_change, bool expect_enabled) {
ASSERT_TRUE(sync_change);
EXPECT_TRUE(sync_change->page_ordinal().IsValid());
EXPECT_TRUE(sync_change->app_launch_ordinal().IsValid());
EXPECT_FALSE(sync_change->uninstalled());
EXPECT_EQ(expect_enabled, sync_change->extension_sync_data().enabled());
}
syncer::FakeSyncChangeProcessor mock_sync_processor_;
};
// Verify that ephemeral apps can be launched and receive system events when
// they are running. Once they are inactive they should not receive system
// events.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, EventDispatchWhenLaunched) {
const Extension* extension =
InstallAndLaunchEphemeralApp(kDispatchEventTestApp);
ASSERT_TRUE(extension);
// Send a fake alarm event to the app and verify that a response is
// received.
EventRouter* event_router = EventRouter::Get(profile());
ASSERT_TRUE(event_router);
ExtensionTestMessageListener alarm_received_listener("alarm_received", false);
DispatchAlarmEvent(event_router, extension->id());
ASSERT_TRUE(alarm_received_listener.WaitUntilSatisfied());
CloseApp(extension->id());
// The app needs to be launched once in order to have the onAlarm() event
// registered.
ASSERT_TRUE(event_router->ExtensionHasEventListener(
extension->id(), alarms::OnAlarm::kEventName));
// Dispatch the alarm event again and verify that the event page did not get
// loaded for the app.
DispatchAlarmEvent(event_router, extension->id());
VerifyAppNotLoaded(extension->id());
}
// Verify that ephemeral apps will receive messages while they are running.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, ReceiveMessagesWhenLaunched) {
const Extension* receiver =
InstallAndLaunchEphemeralApp(kMessagingReceiverApp);
ASSERT_TRUE(receiver);
// Verify that messages are received while the app is running.
ExtensionApiTest::ResultCatcher result_catcher;
LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_success",
"Launched");
EXPECT_TRUE(result_catcher.GetNextResult());
CloseApp(receiver->id());
// Verify that messages are not received while the app is inactive.
LoadAndLaunchPlatformApp("ephemeral_apps/messaging_sender_fail", "Launched");
EXPECT_TRUE(result_catcher.GetNextResult());
}
// Verify that an updated ephemeral app will still have its ephemeral flag
// enabled.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, UpdateEphemeralApp) {
const Extension* app_v1 = InstallEphemeralApp(kMessagingReceiverApp);
ASSERT_TRUE(app_v1);
std::string app_id = app_v1->id();
base::Version app_original_version = *app_v1->version();
app_v1 = NULL; // The extension object will be destroyed during update.
// Update to version 2 of the app.
InstallObserver installed_observer(profile());
const Extension* app_v2 = UpdateEphemeralApp(
app_id, GetTestPath(kMessagingReceiverAppV2),
GetTestPath(kMessagingReceiverApp).ReplaceExtension(
FILE_PATH_LITERAL(".pem")));
// Check the notification parameters.
const InstallObserver::InstallParameters& params = installed_observer.Last();
EXPECT_EQ(app_id, params.id);
EXPECT_TRUE(params.is_update);
EXPECT_FALSE(params.from_ephemeral);
// The ephemeral flag should still be enabled.
ASSERT_TRUE(app_v2);
EXPECT_TRUE(app_v2->version()->CompareTo(app_original_version) > 0);
EXPECT_TRUE(extensions::util::IsEphemeralApp(app_v2->id(), profile()));
}
// Verify that if notifications have been disabled for an ephemeral app, it will
// remain disabled even after being evicted from the cache.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, StickyNotificationSettings) {
const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
// Disable notifications for this app.
DesktopNotificationService* notification_service =
DesktopNotificationServiceFactory::GetForProfile(profile());
ASSERT_TRUE(notification_service);
message_center::NotifierId notifier_id(
message_center::NotifierId::APPLICATION, app->id());
EXPECT_TRUE(notification_service->IsNotifierEnabled(notifier_id));
notification_service->SetNotifierEnabled(notifier_id, false);
EXPECT_FALSE(notification_service->IsNotifierEnabled(notifier_id));
// Remove the app.
EvictApp(app->id());
// Reinstall the ephemeral app and verify that notifications remain disabled.
app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
message_center::NotifierId reinstalled_notifier_id(
message_center::NotifierId::APPLICATION, app->id());
EXPECT_FALSE(notification_service->IsNotifierEnabled(
reinstalled_notifier_id));
}
// Verify that only running ephemeral apps will appear in the Notification
// Settings UI. Inactive, cached ephemeral apps should not appear.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
IncludeRunningEphemeralAppsInNotifiers) {
message_center::NotifierSettingsProvider* settings_provider =
message_center::MessageCenter::Get()->GetNotifierSettingsProvider();
// TODO(tmdiep): Remove once notifications settings are supported across
// all platforms. This test will fail for Linux GTK.
if (!settings_provider)
return;
const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
message_center::NotifierId notifier_id(
message_center::NotifierId::APPLICATION, app->id());
// Since the ephemeral app is running, it should be included in the list
// of notifiers to show in the UI.
NotifierList notifiers;
STLElementDeleter<NotifierList> notifier_deleter(¬ifiers);
settings_provider->GetNotifierList(¬ifiers);
EXPECT_TRUE(IsNotifierInList(notifier_id, notifiers));
STLDeleteElements(¬ifiers);
// Close the ephemeral app.
CloseApp(app->id());
// Inactive ephemeral apps should not be included in the list of notifiers to
// show in the UI.
settings_provider->GetNotifierList(¬ifiers);
EXPECT_FALSE(IsNotifierInList(notifier_id, notifiers));
}
// Verify that ephemeral apps will have no ability to retain file entries after
// close. Normal retainEntry behavior for installed apps is tested in
// FileSystemApiTest.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
DisableRetainFileSystemEntries) {
// Create a dummy file that we can just return to the test.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath temp_file;
ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp_file));
using extensions::FileSystemChooseEntryFunction;
FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
&temp_file);
// The temporary file needs to be registered for the tests to pass on
// ChromeOS.
FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
"temp", temp_dir.path());
// The first test opens the file and writes the file handle to local storage.
const Extension* app = InstallEphemeralApp(kFileSystemTestApp,
Manifest::UNPACKED);
ASSERT_TRUE(LaunchAppAndRunTest(app, "OpenAndRetainFile")) << message_;
// Verify that after the app has been closed, all retained entries are
// flushed.
std::vector<apps::SavedFileEntry> file_entries =
apps::SavedFilesService::Get(profile())
->GetAllFileEntries(app->id());
EXPECT_TRUE(file_entries.empty());
// The second test verifies that the file cannot be reopened.
ASSERT_TRUE(LaunchAppAndRunTest(app, "RestoreRetainedFile")) << message_;
}
// Checks the process of installing and then promoting an ephemeral app.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralApp) {
InitSyncService();
const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
// Ephemeral apps should not be synced.
scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id());
EXPECT_FALSE(sync_change.get());
// Promote the app to a regular installed app.
InstallObserver installed_observer(profile());
PromoteEphemeralApp(app);
VerifyPromotedApp(app->id(), ExtensionRegistry::ENABLED);
// Check the notification parameters.
const InstallObserver::InstallParameters& params = installed_observer.Last();
EXPECT_EQ(app->id(), params.id);
EXPECT_TRUE(params.is_update);
EXPECT_TRUE(params.from_ephemeral);
// The installation should now be synced.
sync_change = GetFirstSyncChangeForApp(app->id());
VerifySyncChange(sync_change.get(), true);
}
// Verifies that promoting an ephemeral app will enable it.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralAppAndEnable) {
InitSyncService();
const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
// Disable the ephemeral app due to a permissions increase. This also involves
// setting the DidExtensionEscalatePermissions flag.
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
prefs->SetDidExtensionEscalatePermissions(app, true);
ExtensionService* service =
ExtensionSystem::Get(profile())->extension_service();
service->DisableExtension(app->id(), Extension::DISABLE_PERMISSIONS_INCREASE);
ASSERT_TRUE(ExtensionRegistry::Get(profile())->
GetExtensionById(app->id(), ExtensionRegistry::DISABLED));
// Promote to a regular installed app. It should be enabled.
PromoteEphemeralApp(app);
VerifyPromotedApp(app->id(), ExtensionRegistry::ENABLED);
EXPECT_FALSE(prefs->DidExtensionEscalatePermissions(app->id()));
scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id());
VerifySyncChange(sync_change.get(), true);
}
// Verifies that promoting an ephemeral app that has unsupported requirements
// will not enable it.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
PromoteUnsupportedEphemeralApp) {
InitSyncService();
const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
// Disable the ephemeral app.
ExtensionService* service =
ExtensionSystem::Get(profile())->extension_service();
service->DisableExtension(
app->id(), Extension::DISABLE_UNSUPPORTED_REQUIREMENT);
ASSERT_TRUE(ExtensionRegistry::Get(profile())->
GetExtensionById(app->id(), ExtensionRegistry::DISABLED));
// Promote to a regular installed app. It should remain disabled.
PromoteEphemeralApp(app);
VerifyPromotedApp(app->id(), ExtensionRegistry::DISABLED);
scoped_ptr<AppSyncData> sync_change = GetFirstSyncChangeForApp(app->id());
VerifySyncChange(sync_change.get(), false);
}
// Checks the process of promoting an ephemeral app from sync.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest, PromoteEphemeralAppFromSync) {
InitSyncService();
const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
std::string app_id = app->id();
// Simulate an install from sync.
const syncer::StringOrdinal kAppLaunchOrdinal("x");
const syncer::StringOrdinal kPageOrdinal("y");
AppSyncData app_sync_data(
*app,
true /* enabled */,
false /* incognito enabled */,
false /* remote install */,
kAppLaunchOrdinal,
kPageOrdinal,
extensions::LAUNCH_TYPE_REGULAR);
ExtensionSyncService* sync_service = ExtensionSyncService::Get(profile());
sync_service->ProcessAppSyncData(app_sync_data);
// Verify the installation.
VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED);
// The sort ordinals from sync should not be overridden.
ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
extensions::AppSorting* app_sorting = prefs->app_sorting();
EXPECT_TRUE(app_sorting->GetAppLaunchOrdinal(app_id).Equals(
kAppLaunchOrdinal));
EXPECT_TRUE(app_sorting->GetPageOrdinal(app_id).Equals(kPageOrdinal));
}
// In most cases, ExtensionService::PromoteEphemeralApp() will be called to
// permanently install an ephemeral app. However, there may be cases where an
// install occurs through the usual route of installing from the Web Store (due
// to race conditions). Ensure that the app is still installed correctly.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
ReplaceEphemeralAppWithInstalledApp) {
const Extension* app = InstallEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
std::string app_id = app->id();
app = NULL;
InstallObserver installed_observer(profile());
ReplaceEphemeralApp(app_id, kNotificationsTestApp);
VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED);
// Check the notification parameters.
const InstallObserver::InstallParameters& params = installed_observer.Last();
EXPECT_EQ(app_id, params.id);
EXPECT_TRUE(params.is_update);
EXPECT_TRUE(params.from_ephemeral);
}
// This is similar to ReplaceEphemeralAppWithInstalledApp, but installs will
// be delayed until the app is idle.
IN_PROC_BROWSER_TEST_F(EphemeralAppBrowserTest,
ReplaceEphemeralAppWithDelayedInstalledApp) {
const Extension* app = InstallAndLaunchEphemeralApp(kNotificationsTestApp);
ASSERT_TRUE(app);
std::string app_id = app->id();
app = NULL;
// Initiate install.
ReplaceEphemeralApp(app_id, kNotificationsTestApp);
// The delayed installation will occur when the ephemeral app is closed.
content::WindowedNotificationObserver installed_signal(
chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED,
content::Source<Profile>(profile()));
InstallObserver installed_observer(profile());
CloseApp(app_id);
installed_signal.Wait();
VerifyPromotedApp(app_id, ExtensionRegistry::ENABLED);
// Check the notification parameters.
const InstallObserver::InstallParameters& params = installed_observer.Last();
EXPECT_EQ(app_id, params.id);
EXPECT_TRUE(params.is_update);
EXPECT_TRUE(params.from_ephemeral);
}