blob: 86091b939deef1010358d2610c64452dbfa6d2e1 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/accessibility/tree/widget_view_ax_cache.h"
#include <memory>
#include <utility>
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/view.h"
namespace views::test {
class WidgetViewAXCacheTest : public testing::Test {
protected:
WidgetViewAXCacheTest() = default;
~WidgetViewAXCacheTest() override = default;
WidgetViewAXCache& cache() { return cache_; }
private:
WidgetViewAXCache cache_;
};
TEST_F(WidgetViewAXCacheTest, CacheInsertGetRemove) {
auto v = std::make_unique<View>();
EXPECT_EQ(cache().Get(v->GetViewAccessibility().GetUniqueId()), nullptr);
cache().Insert(&v->GetViewAccessibility());
EXPECT_EQ(cache().Get(v->GetViewAccessibility().GetUniqueId()),
&v->GetViewAccessibility());
cache().Remove(v->GetViewAccessibility().GetUniqueId());
EXPECT_EQ(cache().Get(v->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_FALSE(cache().HasCachedChildren(&v->GetViewAccessibility()));
}
TEST_F(WidgetViewAXCacheTest, ChildSnapshotIsStableUntilCleared) {
auto parent = std::make_unique<View>();
auto* a = parent->AddChildView(std::make_unique<View>());
auto* b = parent->AddChildView(std::make_unique<View>());
// Take snapshot.
cache().CacheChildrenIfNeeded(&parent->GetViewAccessibility());
// Snapshot sees exactly [a, b].
EXPECT_TRUE(cache().HasCachedChildren(&parent->GetViewAccessibility()));
EXPECT_EQ(cache().CachedChildCount(&parent->GetViewAccessibility()), 2u);
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 0),
&a->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 1),
&b->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 2), nullptr);
// Mutate the live view hierarchy after the snapshot, which shouldn't update
// the snapshot.
auto removed_b = parent->RemoveChildViewT(b);
auto* c = parent->AddChildView(std::make_unique<View>());
// Snapshot must not change until cleared.
EXPECT_EQ(cache().CachedChildCount(&parent->GetViewAccessibility()), 2u);
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 0),
&a->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 1),
&removed_b->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 2), nullptr);
// Clear and re-snapshot and validate the current live children are [a, c].
cache().RemoveFromChildCache(&parent->GetViewAccessibility());
EXPECT_FALSE(cache().HasCachedChildren(&parent->GetViewAccessibility()));
cache().CacheChildrenIfNeeded(&parent->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildCount(&parent->GetViewAccessibility()), 2u);
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 0),
&a->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 1),
&c->GetViewAccessibility());
// Clear the cache again and validate that no children are cached.
cache().RemoveFromChildCache(&parent->GetViewAccessibility());
EXPECT_FALSE(cache().HasCachedChildren(&parent->GetViewAccessibility()));
EXPECT_EQ(cache().CachedChildCount(&parent->GetViewAccessibility()), 0u);
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 0), nullptr);
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 1), nullptr);
EXPECT_EQ(cache().CachedChildAt(&parent->GetViewAccessibility(), 2), nullptr);
}
TEST_F(WidgetViewAXCacheTest, Init_RootOnly) {
auto parent = std::make_unique<View>();
auto* a = parent->AddChildView(std::make_unique<View>());
auto* b = parent->AddChildView(std::make_unique<View>());
auto* b1 = b->AddChildView(std::make_unique<View>());
auto* b2 = b->AddChildView(std::make_unique<View>());
auto excluded = std::make_unique<View>();
cache().Init(parent->GetViewAccessibility(), false /* full_tree */);
EXPECT_EQ(cache().Get(parent->GetViewAccessibility().GetUniqueId()),
&parent->GetViewAccessibility());
EXPECT_EQ(cache().Get(a->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(b->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(b1->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(b2->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(excluded->GetViewAccessibility().GetUniqueId()),
nullptr);
}
TEST_F(WidgetViewAXCacheTest, Init_FullTree) {
auto parent = std::make_unique<View>();
auto* a = parent->AddChildView(std::make_unique<View>());
auto* b = parent->AddChildView(std::make_unique<View>());
auto* b1 = b->AddChildView(std::make_unique<View>());
auto* b2 = b->AddChildView(std::make_unique<View>());
auto excluded = std::make_unique<View>();
cache().Init(parent->GetViewAccessibility());
EXPECT_EQ(cache().Get(parent->GetViewAccessibility().GetUniqueId()),
&parent->GetViewAccessibility());
EXPECT_EQ(cache().Get(a->GetViewAccessibility().GetUniqueId()),
&a->GetViewAccessibility());
EXPECT_EQ(cache().Get(b->GetViewAccessibility().GetUniqueId()),
&b->GetViewAccessibility());
EXPECT_EQ(cache().Get(b1->GetViewAccessibility().GetUniqueId()),
&b1->GetViewAccessibility());
EXPECT_EQ(cache().Get(b2->GetViewAccessibility().GetUniqueId()),
&b2->GetViewAccessibility());
EXPECT_EQ(cache().Get(excluded->GetViewAccessibility().GetUniqueId()),
nullptr);
}
TEST_F(WidgetViewAXCacheTest, Init_FirstRootOnlyThenFullTree) {
auto parent = std::make_unique<View>();
auto* a = parent->AddChildView(std::make_unique<View>());
auto* b = parent->AddChildView(std::make_unique<View>());
auto* b1 = b->AddChildView(std::make_unique<View>());
auto* b2 = b->AddChildView(std::make_unique<View>());
auto excluded = std::make_unique<View>();
cache().Init(parent->GetViewAccessibility(), false /* full_tree */);
EXPECT_EQ(cache().Get(parent->GetViewAccessibility().GetUniqueId()),
&parent->GetViewAccessibility());
EXPECT_EQ(cache().Get(a->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(b->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(b1->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(b2->GetViewAccessibility().GetUniqueId()), nullptr);
EXPECT_EQ(cache().Get(excluded->GetViewAccessibility().GetUniqueId()),
nullptr);
cache().Init(parent->GetViewAccessibility());
EXPECT_EQ(cache().Get(parent->GetViewAccessibility().GetUniqueId()),
&parent->GetViewAccessibility());
EXPECT_EQ(cache().Get(a->GetViewAccessibility().GetUniqueId()),
&a->GetViewAccessibility());
EXPECT_EQ(cache().Get(b->GetViewAccessibility().GetUniqueId()),
&b->GetViewAccessibility());
EXPECT_EQ(cache().Get(b1->GetViewAccessibility().GetUniqueId()),
&b1->GetViewAccessibility());
EXPECT_EQ(cache().Get(b2->GetViewAccessibility().GetUniqueId()),
&b2->GetViewAccessibility());
EXPECT_EQ(cache().Get(excluded->GetViewAccessibility().GetUniqueId()),
nullptr);
}
TEST_F(WidgetViewAXCacheTest, CachedChildrenSnapshotIgnoresMutations) {
auto parent = std::make_unique<View>();
auto* a = parent->AddChildView(std::make_unique<View>());
auto* b = parent->AddChildView(std::make_unique<View>());
auto& pax = parent->GetViewAccessibility();
cache().CacheChildrenIfNeeded(&pax);
// Mutate the live hierarchy after the snapshot.
parent->AddChildView(std::make_unique<View>());
EXPECT_TRUE(cache().HasCachedChildren(&pax));
EXPECT_EQ(cache().CachedChildCount(&pax), 2u);
EXPECT_EQ(cache().CachedChildAt(&pax, 0), &a->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&pax, 1), &b->GetViewAccessibility());
EXPECT_EQ(cache().CachedChildAt(&pax, 2), nullptr);
}
TEST_F(WidgetViewAXCacheTest, RemoveFromChildCache_ClearsSnapshot) {
auto parent = std::make_unique<View>();
auto* a = parent->AddChildView(std::make_unique<View>());
auto* b = parent->AddChildView(std::make_unique<View>());
auto& pax = parent->GetViewAccessibility();
cache().Insert(&pax);
cache().Insert(&a->GetViewAccessibility());
cache().Insert(&b->GetViewAccessibility());
cache().CacheChildrenIfNeeded(&pax);
ASSERT_TRUE(cache().HasCachedChildren(&pax));
ASSERT_EQ(cache().CachedChildCount(&pax), 2u);
cache().RemoveFromChildCache(&pax);
EXPECT_FALSE(cache().HasCachedChildren(&pax));
EXPECT_EQ(cache().CachedChildCount(&pax), 0u);
EXPECT_EQ(cache().CachedChildAt(&pax, 0), nullptr);
EXPECT_EQ(cache().Get(a->GetViewAccessibility().GetUniqueId()),
&a->GetViewAccessibility());
EXPECT_EQ(cache().Get(b->GetViewAccessibility().GetUniqueId()),
&b->GetViewAccessibility());
}
} // namespace views::test