// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <set>
#include <vector>
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "components/services/app_service/public/cpp/instance.h"
#include "components/services/app_service/public/cpp/instance_registry.h"
#include "components/services/app_service/public/cpp/instance_update.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
class InstanceRegistryTest : public testing::Test,
public apps::InstanceRegistry::Observer {
protected:
void MakeInstanceWithWindow(
const char* app_id,
aura::Window* window,
apps::InstanceState state = apps::InstanceState::kUnknown,
base::Time time = base::Time()) {
MakeInstance(app_id, window, state, time);
}
void MakeInstance(const char* app_id,
aura::Window* window,
apps::InstanceState state = apps::InstanceState::kUnknown,
base::Time time = base::Time()) {
apps::InstanceParams params(app_id, window);
params.state = std::make_pair(state, time);
instance_registry_.CreateOrUpdateInstance(std::move(params));
}
void CallForEachInstance(apps::InstanceRegistry& instance_registry) {
instance_registry.ForEachInstance(
[this](const apps::InstanceUpdate& update) {
OnInstanceUpdate(update);
});
}
std::set<apps::InstanceState> GetStates(const std::string& app_id,
const aura::Window* window) {
std::set<apps::InstanceState> states;
instance_registry_.ForInstancesWithWindow(
window, [&](const apps::InstanceUpdate& update) {
if (update.AppId() == app_id) {
states.insert(update.State());
}
});
return states;
}
// apps::InstanceRegistry::Observer overrides.
void OnInstanceUpdate(const apps::InstanceUpdate& update) override {
EXPECT_NE("", update.AppId());
if (update.StateChanged() &&
update.State() == apps::InstanceState::kRunning) {
num_running_apps_++;
}
updated_ids_.insert(update.AppId());
updated_enclosing_windows_.insert(update.Window());
}
void OnInstanceRegistryWillBeDestroyed(
apps::InstanceRegistry* instance_registry) override {
// The test code explicitly calls both AddObserver and RemoveObserver.
NOTREACHED_IN_MIGRATION();
}
apps::InstanceRegistry& instance_registry() { return instance_registry_; }
int num_running_apps_ = 0;
std::set<std::string> updated_ids_;
std::set<raw_ptr<const aura::Window, SetExperimental>>
updated_enclosing_windows_;
apps::InstanceRegistry instance_registry_;
};
// In the tests below, just "recursive" means that instance_registry.OnInstance
// calls observer.OnInstanceUpdate which calls instance_registry.ForEachInstance
// and instance_registry.ForOneInstance. "Super-recursive" means that
// instance_registry.OnInstance calls observer.OnInstanceUpdate calls
// instance_registry.OnInstance which calls observer.OnInstanceUpdate.
class InstanceRecursiveObserver : public apps::InstanceRegistry::Observer {
public:
explicit InstanceRecursiveObserver(apps::InstanceRegistry* instance_registry)
: instance_registry_(instance_registry) {
instance_registry_observation_.Observe(instance_registry);
}
~InstanceRecursiveObserver() override = default;
void PrepareParamsForOnInstances(
int expected_num_instances,
std::vector<std::unique_ptr<apps::InstanceParams>>*
super_recursive_instance_params = nullptr) {
expected_num_instances_ = expected_num_instances;
num_instances_seen_on_instance_update_ = 0;
if (super_recursive_instance_params) {
super_recursive_instance_params_.swap(*super_recursive_instance_params);
}
}
void PrepareForOnInstances(int expected_num_instances,
std::vector<std::unique_ptr<apps::Instance>>*
super_recursive_instances = nullptr) {
expected_num_instances_ = expected_num_instances;
num_instances_seen_on_instance_update_ = 0;
if (super_recursive_instances) {
super_recursive_instances_.swap(*super_recursive_instances);
}
}
int NumInstancesSeenOnInstanceUpdate() {
return num_instances_seen_on_instance_update_;
}
protected:
// apps::InstanceRegistry::Observer overrides.
void OnInstanceUpdate(const apps::InstanceUpdate& outer) override {
int num_instance = 0;
instance_registry_->ForEachInstance(
[&outer, &num_instance](const apps::InstanceUpdate& inner) {
if (outer.InstanceId() == inner.InstanceId()) {
ExpectEq(outer, inner);
}
num_instance++;
});
EXPECT_TRUE(instance_registry_->ForOneInstance(
outer.InstanceId(), [&outer](const apps::InstanceUpdate& inner) {
ExpectEq(outer, inner);
}));
EXPECT_TRUE(instance_registry_->ForInstancesWithWindow(
outer.Window(), [&outer](const apps::InstanceUpdate& inner) {
if (outer.InstanceId() == inner.InstanceId()) {
ExpectEq(outer, inner);
}
}));
if (expected_num_instances_ >= 0) {
EXPECT_EQ(expected_num_instances_, num_instance);
}
while (!super_recursive_instance_params_.empty()) {
std::unique_ptr<apps::InstanceParams> params =
std::move(super_recursive_instance_params_.back());
if (params.get() == nullptr) {
// This is the placeholder 'punctuation'.
super_recursive_instance_params_.pop_back();
break;
}
instance_registry_->CreateOrUpdateInstance(std::move(*params));
super_recursive_instance_params_.pop_back();
}
while (!super_recursive_instances_.empty()) {
std::unique_ptr<apps::Instance> instance =
std::move(super_recursive_instances_.back());
if (instance.get() == nullptr) {
// This is the placeholder 'punctuation'.
super_recursive_instances_.pop_back();
break;
}
instance_registry_->OnInstance(std::move(instance));
super_recursive_instances_.pop_back();
}
num_instances_seen_on_instance_update_++;
}
void OnInstanceRegistryWillBeDestroyed(
apps::InstanceRegistry* instance_registry) override {
instance_registry_observation_.Reset();
}
static void ExpectEq(const apps::InstanceUpdate& outer,
const apps::InstanceUpdate& inner) {
EXPECT_EQ(outer.AppId(), inner.AppId());
EXPECT_EQ(outer.InstanceId(), inner.InstanceId());
EXPECT_EQ(outer.Window(), inner.Window());
EXPECT_EQ(outer.LaunchId(), inner.LaunchId());
EXPECT_EQ(outer.State(), inner.State());
EXPECT_EQ(outer.LastUpdatedTime(), inner.LastUpdatedTime());
EXPECT_EQ(outer.BrowserContext(), inner.BrowserContext());
}
raw_ptr<apps::InstanceRegistry> instance_registry_;
int expected_num_instances_ = -1;
int num_instances_seen_on_instance_update_ = 0;
// Non-empty when this.OnInstanceUpdate should trigger more
// instance_registry_.OnInstance calls.
//
// During OnInstanceUpdate, this vector (a stack) is popped from the back
// until a nullptr 'punctuation' element (a group terminator) is seen. If that
// group of popped elements (in LIFO order) is non-empty, that group forms the
// vector of App's passed to instance_registry_.OnInstance.
std::vector<std::unique_ptr<apps::InstanceParams>>
super_recursive_instance_params_;
std::vector<std::unique_ptr<apps::Instance>> super_recursive_instances_;
base::ScopedObservation<apps::InstanceRegistry,
apps::InstanceRegistry::Observer>
instance_registry_observation_{this};
};
TEST_F(InstanceRegistryTest, ForEachInstance) {
updated_enclosing_windows_.clear();
updated_ids_.clear();
CallForEachInstance(instance_registry());
EXPECT_EQ(0u, updated_enclosing_windows_.size());
EXPECT_EQ(0u, updated_ids_.size());
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("a", &window1);
MakeInstanceWithWindow("b", &window2);
MakeInstanceWithWindow("c", &window3);
updated_enclosing_windows_.clear();
updated_ids_.clear();
CallForEachInstance(instance_registry());
EXPECT_EQ(3u, updated_enclosing_windows_.size());
EXPECT_EQ(3u, updated_ids_.size());
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window1));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window2));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window3));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
aura::Window window4(nullptr);
window4.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("a", &window1, apps::InstanceState::kRunning);
MakeInstanceWithWindow("c", &window4);
updated_enclosing_windows_.clear();
updated_ids_.clear();
CallForEachInstance(instance_registry());
EXPECT_EQ(4u, updated_enclosing_windows_.size());
EXPECT_EQ(3u, updated_ids_.size());
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window1));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window2));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window3));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window4));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
// Test that ForOneApp succeeds for window4 and fails for window5.
bool found_window4 = false;
EXPECT_TRUE(instance_registry().ForInstancesWithWindow(
&window4, [&found_window4](const apps::InstanceUpdate& update) {
found_window4 = true;
EXPECT_EQ("c", update.AppId());
}));
EXPECT_TRUE(found_window4);
bool found_window5 = false;
aura::Window window5(nullptr);
window5.Init(ui::LAYER_NOT_DRAWN);
EXPECT_FALSE(instance_registry().ForInstancesWithWindow(
&window5, [&found_window5](const apps::InstanceUpdate& update) {
found_window5 = true;
}));
EXPECT_FALSE(found_window5);
}
TEST_F(InstanceRegistryTest, Observer) {
instance_registry().AddObserver(this);
num_running_apps_ = 0;
updated_enclosing_windows_.clear();
updated_ids_.clear();
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("a", &window1);
MakeInstanceWithWindow("c", &window2);
MakeInstanceWithWindow("a", &window3);
EXPECT_EQ(0, num_running_apps_);
EXPECT_EQ(3u, updated_enclosing_windows_.size());
EXPECT_EQ(2u, updated_ids_.size());
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window1));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window2));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window3));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("a"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
num_running_apps_ = 0;
updated_ids_.clear();
aura::Window window4(nullptr);
window4.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("b", &window4);
MakeInstanceWithWindow("c", &window2, apps::InstanceState::kRunning);
EXPECT_EQ(1, num_running_apps_);
EXPECT_EQ(2u, updated_ids_.size());
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window2));
EXPECT_NE(updated_enclosing_windows_.end(),
updated_enclosing_windows_.find(&window4));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("b"));
EXPECT_NE(updated_ids_.end(), updated_ids_.find("c"));
instance_registry().RemoveObserver(this);
num_running_apps_ = 0;
updated_enclosing_windows_.clear();
updated_ids_.clear();
aura::Window window5(nullptr);
window5.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("f", &window5, apps::InstanceState::kRunning);
EXPECT_EQ(0, num_running_apps_);
EXPECT_EQ(0u, updated_enclosing_windows_.size());
EXPECT_EQ(0u, updated_ids_.size());
}
TEST_F(InstanceRegistryTest, WholeProcessForOneWindow) {
InstanceRecursiveObserver observer(&instance_registry());
apps::InstanceState instance_state = apps::InstanceState::kStarted;
aura::Window window(nullptr);
window.Init(ui::LAYER_NOT_DRAWN);
observer.PrepareForOnInstances(1);
MakeInstanceWithWindow("p", &window, instance_state);
// Instance is updated twice, for creation and state modification.
EXPECT_EQ(1, observer.NumInstancesSeenOnInstanceUpdate());
instance_state = static_cast<apps::InstanceState>(
instance_state | apps::InstanceState::kRunning |
apps::InstanceState::kActive | apps::InstanceState::kVisible);
observer.PrepareForOnInstances(1);
MakeInstanceWithWindow("p", &window, instance_state);
EXPECT_EQ(1, observer.NumInstancesSeenOnInstanceUpdate());
apps::InstanceState state1 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState state2 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState state3 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState state4 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning |
apps::InstanceState::kActive | apps::InstanceState::kVisible);
apps::InstanceState state5 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning |
apps::InstanceState::kVisible);
apps::InstanceState state6 = apps::InstanceState::kDestroyed;
observer.PrepareForOnInstances(1);
MakeInstanceWithWindow("p", &window, state1);
MakeInstanceWithWindow("p", &window, state2);
MakeInstanceWithWindow("p", &window, state3);
MakeInstanceWithWindow("p", &window, state4);
MakeInstanceWithWindow("p", &window, state5);
MakeInstanceWithWindow("p", &window, state6);
// OnInstanceUpdate is called for state1, because state1 is different with
// previous instance_state. state2 and state3 is not changed, so
// OnInstanceUpdate is not called. OnInstanceUpdate is called for state4,
// state5, and state6 separately, because they are different. So
// OnInstanceUpdate is called 4 times, for state1, state4, state5, and state6.
EXPECT_EQ(4, observer.NumInstancesSeenOnInstanceUpdate());
EXPECT_FALSE(instance_registry().ContainsAppId("p"));
bool found_window = false;
EXPECT_FALSE(instance_registry().ForInstancesWithWindow(
&window, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_FALSE(found_window);
observer.PrepareForOnInstances(1);
MakeInstanceWithWindow("p", &window, state5);
EXPECT_EQ(1, observer.NumInstancesSeenOnInstanceUpdate());
found_window = false;
EXPECT_TRUE(instance_registry().ForInstancesWithWindow(
&window, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_TRUE(found_window);
}
TEST_F(InstanceRegistryTest, Recursive) {
InstanceRecursiveObserver observer(&instance_registry());
apps::InstanceState instance_state1 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState instance_state2 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
observer.PrepareForOnInstances(-1);
MakeInstanceWithWindow("o", &window1, instance_state1);
MakeInstanceWithWindow("p", &window2, instance_state2);
EXPECT_EQ(2, observer.NumInstancesSeenOnInstanceUpdate());
apps::InstanceState instance_state3 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState instance_state4 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
std::vector<apps::InstanceState> latest_state;
latest_state.push_back(instance_state3);
latest_state.push_back(instance_state3);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
aura::Window window4(nullptr);
window4.Init(ui::LAYER_NOT_DRAWN);
observer.PrepareForOnInstances(-1);
MakeInstanceWithWindow("p", &window2, instance_state3);
MakeInstanceWithWindow("q", &window3, instance_state4);
MakeInstanceWithWindow("p", &window4, instance_state3);
EXPECT_EQ(2, observer.NumInstancesSeenOnInstanceUpdate());
apps::InstanceState instance_state5 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState instance_state6 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning);
apps::InstanceState instance_state7 = static_cast<apps::InstanceState>(
apps::InstanceState::kStarted | apps::InstanceState::kRunning |
apps::InstanceState::kActive);
observer.PrepareForOnInstances(4);
MakeInstanceWithWindow("p", &window2, instance_state5);
MakeInstanceWithWindow("p", &window2, instance_state6);
MakeInstanceWithWindow("p", &window2, instance_state7);
EXPECT_EQ(1, observer.NumInstancesSeenOnInstanceUpdate());
apps::InstanceState instance_state8 =
static_cast<apps::InstanceState>(apps::InstanceState::kDestroyed);
observer.PrepareForOnInstances(-1);
MakeInstanceWithWindow("p", &window2, instance_state8);
MakeInstanceWithWindow("p", &window4, instance_state8);
MakeInstanceWithWindow("q", &window3, instance_state8);
MakeInstanceWithWindow("o", &window1, instance_state8);
EXPECT_EQ(4, observer.NumInstancesSeenOnInstanceUpdate());
EXPECT_FALSE(instance_registry().ContainsAppId("o"));
EXPECT_FALSE(instance_registry().ContainsAppId("p"));
EXPECT_FALSE(instance_registry().ContainsAppId("q"));
bool found_window = false;
EXPECT_FALSE(instance_registry().ForInstancesWithWindow(
&window2, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_FALSE(found_window);
found_window = false;
EXPECT_FALSE(instance_registry().ForInstancesWithWindow(
&window4, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_FALSE(found_window);
found_window = false;
EXPECT_FALSE(instance_registry().ForInstancesWithWindow(
&window3, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_FALSE(found_window);
found_window = false;
EXPECT_FALSE(instance_registry().ForInstancesWithWindow(
&window1, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_FALSE(found_window);
observer.PrepareForOnInstances(1);
MakeInstanceWithWindow("p", &window2, instance_state7);
EXPECT_EQ(1, observer.NumInstancesSeenOnInstanceUpdate());
found_window = false;
EXPECT_TRUE(instance_registry().ForInstancesWithWindow(
&window2, [&found_window](const apps::InstanceUpdate& update) {
found_window = true;
}));
EXPECT_TRUE(found_window);
}
TEST_F(InstanceRegistryTest, SuperRecursive) {
InstanceRecursiveObserver observer(&instance_registry());
// Set up a series of OnInstance to be called during
// observer.OnInstanceUpdate:
// - the 1st update is {'a', &window2, kActive}.
// - the 2nd update is {'b', &window3, kActive}.
// - the 3rd update is {'c', &window4, kActive}.
// - the 4th update is {'b', &window5, kVisible}.
// - the 5th update is {'c', &window4, kVisible}.
// - the 6td update is {'b', &window3, kRunning}.
// - the 7th update is {'a', &window2, kRunning}.
// - the 8th update is {'a', &window2, kDestroyed}.
// - the 9th update is {'a', &window2, kUnknown}.
// - the 10th update is {'a', &window1, kStarted}.
//
// The vector is processed in LIFO order with nullptr punctuation to
// terminate each group. See the comment on the
// RecursiveObserver::super_recursive_apps_ field.
std::vector<std::unique_ptr<apps::InstanceParams>> super_recursive_apps;
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
aura::Window window4(nullptr);
window4.Init(ui::LAYER_NOT_DRAWN);
aura::Window window5(nullptr);
window5.Init(ui::LAYER_NOT_DRAWN);
super_recursive_apps.push_back(nullptr);
std::unique_ptr<apps::InstanceParams> params =
std::make_unique<apps::InstanceParams>("a", &window1);
params->state = std::make_pair(apps::InstanceState::kStarted, base::Time());
super_recursive_apps.push_back(std::move(params));
super_recursive_apps.push_back(nullptr);
super_recursive_apps.push_back(nullptr);
super_recursive_apps.push_back(
std::make_unique<apps::InstanceParams>("a", &window2));
super_recursive_apps.push_back(nullptr);
super_recursive_apps.push_back(
std::make_unique<apps::InstanceParams>("b", &window3));
params = std::make_unique<apps::InstanceParams>("a", &window2);
params->state = std::make_pair(apps::InstanceState::kDestroyed, base::Time());
super_recursive_apps.push_back(std::move(params));
params = std::make_unique<apps::InstanceParams>("a", &window2);
params->state = std::make_pair(apps::InstanceState::kRunning, base::Time());
super_recursive_apps.push_back(std::move(params));
params = std::make_unique<apps::InstanceParams>("b", &window3);
params->state = std::make_pair(apps::InstanceState::kRunning, base::Time());
super_recursive_apps.push_back(std::move(params));
super_recursive_apps.push_back(nullptr);
super_recursive_apps.push_back(nullptr);
params = std::make_unique<apps::InstanceParams>("c", &window4);
params->state = std::make_pair(apps::InstanceState::kVisible, base::Time());
super_recursive_apps.push_back(std::move(params));
params = std::make_unique<apps::InstanceParams>("b", &window5);
params->state = std::make_pair(apps::InstanceState::kVisible, base::Time());
super_recursive_apps.push_back(std::move(params));
params = std::make_unique<apps::InstanceParams>("a", &window2);
params->state = std::make_pair(apps::InstanceState::kActive, base::Time());
MakeInstanceWithWindow("a", &window2, apps::InstanceState::kActive);
params = std::make_unique<apps::InstanceParams>("b", &window3);
params->state = std::make_pair(apps::InstanceState::kActive, base::Time());
MakeInstanceWithWindow("b", &window3, apps::InstanceState::kActive);
observer.PrepareParamsForOnInstances(-1, &super_recursive_apps);
params = std::make_unique<apps::InstanceParams>("c", &window4);
params->state = std::make_pair(apps::InstanceState::kActive, base::Time());
MakeInstanceWithWindow("c", &window4, apps::InstanceState::kActive);
EXPECT_EQ(8, observer.NumInstancesSeenOnInstanceUpdate());
// After all of that, check that for each window, the last delta won.
EXPECT_EQ(apps::InstanceState::kStarted,
instance_registry().GetState(&window1));
EXPECT_EQ(apps::InstanceState::kUnknown,
instance_registry().GetState(&window2));
EXPECT_EQ(apps::InstanceState::kRunning,
instance_registry().GetState(&window3));
EXPECT_EQ(apps::InstanceState::kVisible,
instance_registry().GetState(&window4));
EXPECT_EQ(apps::InstanceState::kVisible,
instance_registry().GetState(&window5));
EXPECT_TRUE(instance_registry().Exists(&window1));
EXPECT_TRUE(instance_registry().Exists(&window2));
EXPECT_TRUE(instance_registry().Exists(&window3));
EXPECT_TRUE(instance_registry().Exists(&window4));
EXPECT_TRUE(instance_registry().Exists(&window5));
}
TEST_F(InstanceRegistryTest, GetInstances) {
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("a", &window1);
MakeInstanceWithWindow("b", &window2);
MakeInstanceWithWindow("a", &window3);
auto instances = instance_registry().GetInstances("a");
EXPECT_EQ(2U, instances.size());
std::set<aura::Window*> windows;
for (const apps::Instance* instance : instances) {
EXPECT_EQ("a", instance->AppId());
windows.insert(instance->Window());
}
EXPECT_TRUE(base::Contains(windows, &window1));
EXPECT_TRUE(base::Contains(windows, &window3));
instances = instance_registry().GetInstances("b");
EXPECT_EQ(1U, instances.size());
auto it = instances.begin();
EXPECT_EQ("b", (*it)->AppId());
EXPECT_EQ(&window2, (*it)->Window());
MakeInstanceWithWindow("a", &window1, apps::InstanceState::kDestroyed);
MakeInstanceWithWindow("b", &window2, apps::InstanceState::kDestroyed);
instances = instance_registry().GetInstances("a");
EXPECT_EQ(1U, instances.size());
it = instances.begin();
EXPECT_EQ("a", (*it)->AppId());
EXPECT_EQ(&window3, (*it)->Window());
instances = instance_registry().GetInstances("b");
EXPECT_TRUE(instances.empty());
}
TEST_F(InstanceRegistryTest, ContainsAppId) {
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
MakeInstanceWithWindow("a", &window1);
MakeInstanceWithWindow("b", &window2);
MakeInstanceWithWindow("a", &window3);
EXPECT_TRUE(instance_registry().ContainsAppId("a"));
EXPECT_TRUE(instance_registry().ContainsAppId("b"));
EXPECT_FALSE(instance_registry().ContainsAppId("c"));
MakeInstanceWithWindow("a", &window1, apps::InstanceState::kDestroyed);
MakeInstanceWithWindow("b", &window2, apps::InstanceState::kDestroyed);
EXPECT_TRUE(instance_registry().ContainsAppId("a"));
EXPECT_FALSE(instance_registry().ContainsAppId("b"));
}
TEST_F(InstanceRegistryTest, WindowIsChanged) {
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
auto instance_id1 = base::UnguessableToken::Create();
auto delta = std::make_unique<apps::Instance>("a", instance_id1, &window1);
instance_registry().OnInstance(std::move(delta));
EXPECT_TRUE(instance_registry().Exists(&window1));
EXPECT_EQ(1U, instance_registry().GetInstances("a").size());
// Modify window for the instance of `a`.
delta = std::make_unique<apps::Instance>("a", instance_id1, &window2);
instance_registry().OnInstance(std::move(delta));
EXPECT_FALSE(instance_registry().Exists(&window1));
EXPECT_TRUE(instance_registry().Exists(&window2));
EXPECT_EQ(1U, instance_registry().GetInstances("a").size());
// Create an instance of `window2` for `b`.
auto instance_id2 = base::UnguessableToken::Create();
delta = std::make_unique<apps::Instance>("b", instance_id2, &window2);
instance_registry().OnInstance(std::move(delta));
EXPECT_TRUE(instance_registry().Exists(&window2));
EXPECT_EQ(1U, instance_registry().GetInstances("b").size());
// Close window for the instance of `a`.
delta = std::make_unique<apps::Instance>("a", instance_id1, &window2);
delta->UpdateState(apps::InstanceState::kDestroyed, base::Time());
instance_registry().OnInstance(std::move(delta));
EXPECT_TRUE(instance_registry().Exists(&window2));
EXPECT_TRUE(instance_registry().GetInstances("a").empty());
// Close window for the instance of `b`.
delta = std::make_unique<apps::Instance>("b", instance_id2, &window2);
delta->UpdateState(apps::InstanceState::kDestroyed, base::Time());
instance_registry().OnInstance(std::move(delta));
EXPECT_FALSE(instance_registry().Exists(&window2));
EXPECT_TRUE(instance_registry().GetInstances("b").empty());
}
TEST_F(InstanceRegistryTest, SuperRecursiveWithWindowChanged) {
InstanceRecursiveObserver observer(&instance_registry());
// Set up a series of OnInstance to be called during
// observer.OnInstanceUpdate:
// - the 1st update is {'a', id1, &window2, kActive}.
// - the 2nd update is {'b', id2, &window3, kActive}.
// - the 3rd update is {'c', id3, &window4, kActive}.
// - the 4th update is {'b', id4, &window3, kVisible}.
// - the 5th update is {'c', id3, &window3, kVisible}.
// - the 6td update is {'b', id2, &window3, kDestroyed}.
// - the 7th update is {'a', id1, &window5, kRunning}.
// - the 8th update is {'a', id1, &window5, kDestroyed}.
// - the 9th update is {'a', id5, &window2, kUnknown}.
// - the 10th update is {'a', id6, &window1, kStarted}.
//
// The vector is processed in LIFO order with nullptr punctuation to
// terminate each group. See the comment on the
// RecursiveObserver::super_recursive_apps_ field.
aura::Window window1(nullptr);
window1.Init(ui::LAYER_NOT_DRAWN);
aura::Window window2(nullptr);
window2.Init(ui::LAYER_NOT_DRAWN);
aura::Window window3(nullptr);
window3.Init(ui::LAYER_NOT_DRAWN);
aura::Window window4(nullptr);
window4.Init(ui::LAYER_NOT_DRAWN);
aura::Window window5(nullptr);
window5.Init(ui::LAYER_NOT_DRAWN);
auto instance_id1 = base::UnguessableToken::Create();
auto instance_id2 = base::UnguessableToken::Create();
auto instance_id3 = base::UnguessableToken::Create();
auto instance_id4 = base::UnguessableToken::Create();
auto instance_id5 = base::UnguessableToken::Create();
auto instance_id6 = base::UnguessableToken::Create();
std::vector<std::unique_ptr<apps::Instance>> super_recursive_apps;
super_recursive_apps.push_back(nullptr);
super_recursive_apps.push_back(nullptr);
// the 10th update is {'a', id6, &window1, kStarted}.
auto delta = std::make_unique<apps::Instance>("a", instance_id6, &window1);
delta->UpdateState(apps::InstanceState::kStarted, base::Time());
super_recursive_apps.push_back(std::move(delta));
delta = std::make_unique<apps::Instance>("a", instance_id6, &window1);
delta->UpdateState(apps::InstanceState::kDestroyed, base::Time());
super_recursive_apps.push_back(std::move(delta));
super_recursive_apps.push_back(nullptr);
// The 9th update is {'a', id5, &window2, kUnknown}.
super_recursive_apps.push_back(
std::make_unique<apps::Instance>("a", instance_id5, &window2));
// The 8th update is {'a', id1, &window5, kDestroyed}.
delta = std::make_unique<apps::Instance>("a", instance_id1, &window5);
delta->UpdateState(apps::InstanceState::kDestroyed, base::Time());
super_recursive_apps.push_back(std::move(delta));
// The 7th update is {'a', id1, &window5, kRunning}.
delta = std::make_unique<apps::Instance>("a", instance_id1, &window5);
delta->UpdateState(apps::InstanceState::kRunning, base::Time());
super_recursive_apps.push_back(std::move(delta));
// The 6td update is {'b', id2, &window3, kDestroyed}.
delta = std::make_unique<apps::Instance>("b", instance_id2, &window3);
delta->UpdateState(apps::InstanceState::kDestroyed, base::Time());
super_recursive_apps.push_back(std::move(delta));
super_recursive_apps.push_back(nullptr);
super_recursive_apps.push_back(nullptr);
// The 5th update is {'c', id3, &window3, kVisible}.
delta = std::make_unique<apps::Instance>("c", instance_id3, &window3);
delta->UpdateState(apps::InstanceState::kVisible, base::Time());
super_recursive_apps.push_back(std::move(delta));
// The 4th update is {'b', id4, &window3, kVisible}.
delta = std::make_unique<apps::Instance>("b", instance_id4, &window3);
delta->UpdateState(apps::InstanceState::kVisible, base::Time());
super_recursive_apps.push_back(std::move(delta));
// The 1st update is {'a', id1, &window2, kActive}
delta = std::make_unique<apps::Instance>("a", instance_id1, &window2);
delta->UpdateState(apps::InstanceState::kActive, base::Time());
instance_registry().OnInstance(std::move(delta));
// The 2nd update is {'b', id2, &window3, kActive}.
delta = std::make_unique<apps::Instance>("b", instance_id2, &window3);
delta->UpdateState(apps::InstanceState::kActive, base::Time());
instance_registry().OnInstance(std::move(delta));
// The 3rd update is {'c', id3, &window4, kActive}.
delta = std::make_unique<apps::Instance>("c", instance_id3, &window4);
delta->UpdateState(apps::InstanceState::kActive, base::Time());
observer.PrepareForOnInstances(-1, &super_recursive_apps);
instance_registry().OnInstance(std::move(delta));
EXPECT_EQ(8, observer.NumInstancesSeenOnInstanceUpdate());
// After all of that, check that for each window, the last delta won.
auto states = GetStates("a", &window1);
EXPECT_EQ(1U, states.size());
EXPECT_EQ(apps::InstanceState::kStarted, *states.begin());
states = GetStates("a", &window2);
EXPECT_EQ(1U, states.size());
EXPECT_EQ(apps::InstanceState::kUnknown, *states.begin());
EXPECT_EQ(2U, instance_registry().GetInstances("a").size());
states = GetStates("b", &window3);
EXPECT_EQ(1U, states.size());
EXPECT_EQ(apps::InstanceState::kVisible, *states.begin());
EXPECT_EQ(1U, instance_registry().GetInstances("b").size());
states = GetStates("c", &window3);
EXPECT_EQ(1U, states.size());
EXPECT_EQ(apps::InstanceState::kVisible, *states.begin());
EXPECT_EQ(1U, instance_registry().GetInstances("c").size());
EXPECT_TRUE(GetStates("c", &window4).empty());
EXPECT_TRUE(GetStates("a", &window5).empty());
EXPECT_TRUE(instance_registry().Exists(&window1));
EXPECT_TRUE(instance_registry().Exists(&window2));
EXPECT_TRUE(instance_registry().Exists(&window3));
EXPECT_FALSE(instance_registry().Exists(&window4));
EXPECT_FALSE(instance_registry().Exists(&window5));
}