
This reverts commit59f713c438
. Reason for revert: The broken Tast tests should be fixed now, so this CL can be relanded. Original change's description: > Revert "HTML Mode Removal: Removes HTML accessibility mode from all platforms" > > This reverts commit4a2d1a0575
. > > Reason for revert: Breaks several tast tests on multiple boards and blocking uprev. > > Many chrome builders (https://ci.chromium.org/p/chrome/g/chrome/console), e.g. chrome-octopus-chrome, chromeos-brya-chrome-skylab, etc. started failing several tast tests at the same time. The tests are also failing on the uprev CL https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/5676585. > > I manually verified that this CL causes the tast.filemanager.ZipMount failure, and suspect that all the below failures that started at the same time are also caused by this as they all seem related to the downloads folder in the file manager: > > tast.apps.Sharesheet > tast.arc.RemovableMedia.vm > tast.bruschetta.CopyFilesToGuest > tast.bruschetta.HomeDirectoryShare > tast.bruschetta.ShareDownloads > tast.crostini.CopyFilesToLinuxFiles.bookworm_stable > tast.crostini.HomeDirectoryShare.bookworm_stable > tast.crostini.OpenWithTerminal.bookworm_stable > tast.crostini.ShareFolderZipFile.bookworm_stable > tast.crostini.ShareFolders.bookworm_stable > tast.crostini.ShareMovies.bookworm_stable_gaia > tast.crostini.ShareMovies.bullseye_stable_gaia > tast.dlp.DataLeakPreventionRulesListFilesUSB.ash_blocked > tast.filemanager.ZipMount > > > Original change's description: > > HTML Mode Removal: Removes HTML accessibility mode from all platforms > > > > This patch turns off the HTML accessibility mode from all > > platforms, including ChromeOS. This only > > affects the platform specific accessibility APIs, and does > > not change the behavior of Read Anything (AKA Reader), or > > the Google Assistant feature (see e.g. ax_assistant_structure.*). > > The above changes save up memory by removing the dumping of all > > the DOM attributes in the accessibility tree. This used to > > happen for every HTML node. > > > > One remaining usage should also be removed: > > Read Anything (AKA Reader) should not rely on HTML attributes > > to determine if a node in the accessibility tree has > > the "aria-expanded" attribute set. > > I realize that this is needed for making Gmail Inboxes appear > > correctly in Reader, but we should find an alternative approach. > > > > Finally, Select-To-Speak should not need to synthesize key > > presses for ARIA sliders, since this is handled > > automatically by Blink. > > > > R=aleventhal@chromium.org, dtseng@chromium.org > > > > AX-Relnotes: n/a. > > Bug: 336844380 > > Fixed: 703277 > > Change-Id: Iebd73fb4653f568a449201ac402f2644e08c8ccf > > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5656790 > > Reviewed-by: David Tseng <dtseng@chromium.org> > > Commit-Queue: Nektarios Paisios <nektar@chromium.org> > > Reviewed-by: Joe Mason <joenotcharles@google.com> > > Reviewed-by: Alexander Timin <altimin@chromium.org> > > Reviewed-by: Aaron Leventhal <aleventhal@chromium.org> > > Cr-Commit-Position: refs/heads/main@{#1322926} > > Bug: 336844380 > Change-Id: I41854dda34528209750e89a58d5c8ff02f778f50 > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5678259 > Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> > Owners-Override: Timothy Loh <timloh@chromium.org> > Commit-Queue: Timothy Loh <timloh@chromium.org> > Auto-Submit: Timothy Loh <timloh@chromium.org> > Cr-Commit-Position: refs/heads/main@{#1323295} Bug: 336844380 Change-Id: I52c78f84eb5149dd6e2a406da3b0e31724de995f Cq-Include-Trybots: luci.chrome.try:chromeos-betty-chrome-dchecks Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5773586 Reviewed-by: Aaron Leventhal <aleventhal@chromium.org> Reviewed-by: Ryan Sturm <ryansturm@chromium.org> Reviewed-by: Sophie Chang <sophiechang@chromium.org> Commit-Queue: Tzarial <zork@chromium.org> Cr-Commit-Position: refs/heads/main@{#1365867}
1760 lines
54 KiB
C++
1760 lines
54 KiB
C++
// Copyright 2018 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/accessibility/ax_language_detection.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "base/command_line.h"
|
|
#include "base/test/metrics/histogram_tester.h"
|
|
#include "base/test/scoped_feature_list.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "ui/accessibility/accessibility_features.h"
|
|
#include "ui/accessibility/accessibility_switches.h"
|
|
#include "ui/accessibility/ax_enums.mojom.h"
|
|
#include "ui/accessibility/ax_node.h"
|
|
#include "ui/accessibility/ax_tree.h"
|
|
|
|
namespace ui {
|
|
|
|
const std::string kTextEnglish =
|
|
"This is text created using Google Translate, it is unlikely to be "
|
|
"idiomatic in the given target language. This text is only used to "
|
|
"test language detection";
|
|
|
|
const std::string kTextFrench =
|
|
"Ce texte a été créé avec Google Translate, il est peu probable qu'il "
|
|
"soit idiomatique dans la langue cible indiquée Ce texte est "
|
|
"uniquement utilisé pour tester la détection de la langue.";
|
|
|
|
const std::string kTextGerman =
|
|
"Dies ist ein mit Google Translate erstellter Text. Es ist "
|
|
"unwahrscheinlich, dass er in der angegebenen Zielsprache idiomatisch "
|
|
"ist. Dieser Text wird nur zum Testen der Spracherkennung verwendet.";
|
|
|
|
const std::string kTextSpanish =
|
|
"Este es un texto creado usando Google Translate, es poco probable que sea "
|
|
"idiomático en el idioma de destino dado. Este texto solo se usa para "
|
|
"probar la detección de idioma.";
|
|
|
|
// This test fixture is a friend of classes in ax_language_detection.h in order
|
|
// to enable testing of internals.
|
|
//
|
|
// When used with TEST_F, the test body is a subclass of this fixture, so we
|
|
// need to re-expose any members through this fixture in order for them to
|
|
// be accessible from within the test body.
|
|
class AXLanguageDetectionTestFixture : public testing::Test {
|
|
public:
|
|
AXLanguageDetectionTestFixture() = default;
|
|
~AXLanguageDetectionTestFixture() override = default;
|
|
|
|
AXLanguageDetectionTestFixture(const AXLanguageDetectionTestFixture&) =
|
|
delete;
|
|
AXLanguageDetectionTestFixture& operator=(
|
|
const AXLanguageDetectionTestFixture&) = delete;
|
|
|
|
protected:
|
|
bool IsStaticLanguageDetectionEnabled() {
|
|
return AXLanguageDetectionManager::IsStaticLanguageDetectionEnabled();
|
|
}
|
|
|
|
bool IsDynamicLanguageDetectionEnabled() {
|
|
return AXLanguageDetectionManager::IsDynamicLanguageDetectionEnabled();
|
|
}
|
|
|
|
AXLanguageDetectionObserver* getObserver(AXTree& tree) {
|
|
return tree.language_detection_manager->language_detection_observer_.get();
|
|
}
|
|
|
|
int get_score(AXTree& tree, const std::string& lang) {
|
|
return tree.language_detection_manager->lang_info_stats_.GetScore(lang);
|
|
}
|
|
|
|
// Accessors for testing metric data.
|
|
void disable_metric_clearing(AXTree& tree) {
|
|
tree.language_detection_manager->lang_info_stats_.disable_metric_clearing_ =
|
|
true;
|
|
}
|
|
|
|
int count_detection_attempted(AXTree& tree) const {
|
|
return tree.language_detection_manager->lang_info_stats_
|
|
.count_detection_attempted_;
|
|
}
|
|
|
|
int count_detection_results(AXTree& tree) const {
|
|
return tree.language_detection_manager->lang_info_stats_
|
|
.count_detection_results_;
|
|
}
|
|
|
|
int count_labelled(AXTree& tree) const {
|
|
return tree.language_detection_manager->lang_info_stats_.count_labelled_;
|
|
}
|
|
|
|
int count_labelled_with_top_result(AXTree& tree) const {
|
|
return tree.language_detection_manager->lang_info_stats_
|
|
.count_labelled_with_top_result_;
|
|
}
|
|
|
|
int count_overridden(AXTree& tree) const {
|
|
return tree.language_detection_manager->lang_info_stats_.count_overridden_;
|
|
}
|
|
|
|
const std::unordered_set<std::string>& unique_top_lang_detected(
|
|
AXTree& tree) const {
|
|
return tree.language_detection_manager->lang_info_stats_
|
|
.unique_top_lang_detected_;
|
|
}
|
|
};
|
|
|
|
class AXLanguageDetectionTestStaticContent
|
|
: public AXLanguageDetectionTestFixture {
|
|
public:
|
|
AXLanguageDetectionTestStaticContent() = default;
|
|
~AXLanguageDetectionTestStaticContent() override = default;
|
|
|
|
AXLanguageDetectionTestStaticContent(
|
|
const AXLanguageDetectionTestStaticContent&) = delete;
|
|
AXLanguageDetectionTestStaticContent& operator=(
|
|
const AXLanguageDetectionTestStaticContent&) = delete;
|
|
|
|
void SetUp() override {
|
|
AXLanguageDetectionTestFixture::SetUp();
|
|
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
}
|
|
};
|
|
|
|
class AXLanguageDetectionTestDynamicContent
|
|
: public AXLanguageDetectionTestStaticContent {
|
|
public:
|
|
AXLanguageDetectionTestDynamicContent() = default;
|
|
~AXLanguageDetectionTestDynamicContent() override = default;
|
|
|
|
AXLanguageDetectionTestDynamicContent(
|
|
const AXLanguageDetectionTestDynamicContent&) = delete;
|
|
AXLanguageDetectionTestDynamicContent& operator=(
|
|
const AXLanguageDetectionTestDynamicContent&) = delete;
|
|
|
|
void SetUp() override {
|
|
AXLanguageDetectionTestStaticContent::SetUp();
|
|
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
|
|
}
|
|
};
|
|
|
|
TEST_F(AXLanguageDetectionTestFixture, StaticContentFeatureFlag) {
|
|
// TODO(crbug.com/41417304): Remove this test once this feature is stable
|
|
EXPECT_FALSE(
|
|
::switches::IsExperimentalAccessibilityLanguageDetectionEnabled());
|
|
EXPECT_FALSE(IsStaticLanguageDetectionEnabled());
|
|
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
|
|
EXPECT_TRUE(
|
|
::switches::IsExperimentalAccessibilityLanguageDetectionEnabled());
|
|
EXPECT_TRUE(IsStaticLanguageDetectionEnabled());
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestFixture, DynamicContentFeatureFlag) {
|
|
// TODO(crbug.com/41417304): Remove this test once this feature is stable
|
|
EXPECT_FALSE(
|
|
::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
|
|
EXPECT_FALSE(IsDynamicLanguageDetectionEnabled());
|
|
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
|
|
|
|
EXPECT_TRUE(
|
|
::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
|
|
EXPECT_TRUE(IsDynamicLanguageDetectionEnabled());
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestFixture, FeatureFlag) {
|
|
// TODO(crbug.com/41417304): Remove this test once this feature is stable
|
|
EXPECT_FALSE(IsStaticLanguageDetectionEnabled());
|
|
EXPECT_FALSE(IsDynamicLanguageDetectionEnabled());
|
|
|
|
base::test::ScopedFeatureList scoped_feature_list;
|
|
scoped_feature_list.InitWithFeatures(
|
|
{features::kEnableAccessibilityLanguageDetection}, {});
|
|
|
|
EXPECT_TRUE(IsStaticLanguageDetectionEnabled());
|
|
EXPECT_TRUE(IsDynamicLanguageDetectionEnabled());
|
|
}
|
|
|
|
TEST(AXLanguageDetectionTest, LangAttrInheritanceFeatureFlagOff) {
|
|
// Test lang attribute inheritance when feature flag is off.
|
|
//
|
|
// Lang attribute inheritance is handled by GetLanguage.
|
|
//
|
|
// Tree:
|
|
// 1
|
|
// 2 3
|
|
// 4
|
|
// 5
|
|
//
|
|
// 1 - English lang attribute
|
|
// 2 - French lang attribute
|
|
//
|
|
// Expected:
|
|
// 3 - inherit English from 1
|
|
// 4 - inherit French from 2
|
|
// 5 - inherit Frnech from 4/2
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(5);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "en");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kGenericContainer;
|
|
node2.child_ids.resize(1);
|
|
node2.child_ids[0] = 4;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node4 = initial_state.nodes[3];
|
|
node4.id = 4;
|
|
node4.role = ax::mojom::Role::kStaticText;
|
|
node4.child_ids.resize(1);
|
|
node4.child_ids[0] = 5;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node5 = initial_state.nodes[4];
|
|
node5.id = 5;
|
|
node5.role = ax::mojom::Role::kInlineTextBox;
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
EXPECT_EQ(node1->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node1->GetLanguage(), "en");
|
|
}
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
EXPECT_EQ(node2->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
EXPECT_EQ(node3->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node3->GetLanguage(), "en");
|
|
}
|
|
|
|
{
|
|
AXNode* node4 = tree.GetFromId(4);
|
|
EXPECT_EQ(node4->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node4->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node5 = tree.GetFromId(5);
|
|
EXPECT_EQ(node5->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node5->GetLanguage(), "fr");
|
|
}
|
|
}
|
|
|
|
TEST(AXLanguageDetectionTest, LangAttrInheritanceFeatureFlagOn) {
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
|
|
// Test lang attribute inheritance in the absence of any detected language.
|
|
//
|
|
// Lang attribute inheritance is handled by the Label step.
|
|
//
|
|
// Tree:
|
|
// 1
|
|
// 2 3
|
|
// 4
|
|
// 5
|
|
//
|
|
// 1 - English lang attribute
|
|
// 2 - French lang attribute
|
|
//
|
|
// Expected:
|
|
// 3 - inherit English from 1
|
|
// 4 - inherit French from 2
|
|
// 5 - inherit Frnech from 4/2
|
|
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(5);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "en");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kGenericContainer;
|
|
node2.child_ids.resize(1);
|
|
node2.child_ids[0] = 4;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node4 = initial_state.nodes[3];
|
|
node4.id = 4;
|
|
node4.role = ax::mojom::Role::kStaticText;
|
|
node4.child_ids.resize(1);
|
|
node4.child_ids[0] = 5;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node5 = initial_state.nodes[4];
|
|
node5.id = 5;
|
|
node5.role = ax::mojom::Role::kInlineTextBox;
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
// No detection for non text nodes.
|
|
EXPECT_EQ(node1->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node1->GetLanguage(), "en");
|
|
}
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
EXPECT_EQ(node2->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
// Inherited languages are not stored in lang info.
|
|
EXPECT_EQ(node3->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node3->GetLanguage(), "en");
|
|
}
|
|
|
|
{
|
|
AXNode* node4 = tree.GetFromId(4);
|
|
// Inherited languages are not stored in lang info.
|
|
EXPECT_EQ(node4->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node4->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node5 = tree.GetFromId(5);
|
|
// Inherited languages are not stored in lang info.
|
|
EXPECT_EQ(node5->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node5->GetLanguage(), "fr");
|
|
}
|
|
}
|
|
|
|
// Tests that AXNode::GetLanguage() terminates when there is no lang attribute.
|
|
TEST_F(AXLanguageDetectionTestStaticContent, GetLanguageBoringTree) {
|
|
// This test checks the behaviour of Detect, Label, and GetLanguage on a
|
|
// 'boring' tree.
|
|
//
|
|
// The tree built here contains no lang attributes, nor does it contain any
|
|
// text to perform detection on.
|
|
//
|
|
// Tree:
|
|
// 1
|
|
// 2 3
|
|
// 4
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(4);
|
|
initial_state.nodes[0].id = 1;
|
|
initial_state.nodes[0].child_ids.resize(2);
|
|
initial_state.nodes[0].child_ids[0] = 2;
|
|
initial_state.nodes[0].child_ids[1] = 3;
|
|
initial_state.nodes[1].id = 2;
|
|
initial_state.nodes[1].child_ids.resize(1);
|
|
initial_state.nodes[1].child_ids[0] = 4;
|
|
initial_state.nodes[2].id = 3;
|
|
initial_state.nodes[3].id = 4;
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// Check that tree parenting conforms to expected shape.
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
EXPECT_EQ(node1->parent(), nullptr);
|
|
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_EQ(node2->parent(), node1);
|
|
EXPECT_EQ(node2->parent()->parent(), nullptr);
|
|
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
ASSERT_EQ(node3->parent(), node1);
|
|
EXPECT_EQ(node3->parent()->parent(), nullptr);
|
|
|
|
AXNode* node4 = tree.GetFromId(4);
|
|
ASSERT_EQ(node4->parent(), node2);
|
|
ASSERT_EQ(node4->parent()->parent(), node1);
|
|
EXPECT_EQ(node4->parent()->parent()->parent(), nullptr);
|
|
|
|
EXPECT_EQ(node1->GetLanguage(), "");
|
|
EXPECT_EQ(node2->GetLanguage(), "");
|
|
EXPECT_EQ(node3->GetLanguage(), "");
|
|
EXPECT_EQ(node4->GetLanguage(), "");
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestStaticContent, Basic) {
|
|
// Tree:
|
|
// 1
|
|
// 2 3
|
|
// 4
|
|
// 5
|
|
//
|
|
// 1 - German lang attribute, no text
|
|
// 2 - French lang attribute, no text
|
|
// 3 - no attribute, French text
|
|
// 4 - no attribute, English text
|
|
// 5 - no attribute, no text
|
|
//
|
|
// Expected:
|
|
// 3 - French detected
|
|
// 4 - English detected
|
|
// 5 - inherit English from 4
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(5);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "de");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kGenericContainer;
|
|
node2.child_ids.resize(1);
|
|
node2.child_ids[0] = 4;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node4 = initial_state.nodes[3];
|
|
node4.id = 4;
|
|
node4.child_ids.resize(1);
|
|
node4.child_ids[0] = 5;
|
|
node4.role = ax::mojom::Role::kStaticText;
|
|
node4.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node5 = initial_state.nodes[4];
|
|
node5.id = 5;
|
|
node5.role = ax::mojom::Role::kInlineTextBox;
|
|
node5.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
// node1 is not a text node, so no lang info should be attached.
|
|
EXPECT_EQ(node1->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node1->GetLanguage(), "de");
|
|
}
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
// node2 is not a text node, so no lang info should be attached.
|
|
EXPECT_EQ(node2->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
EXPECT_TRUE(node3->IsText());
|
|
EXPECT_NE(node3->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node3->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node4 = tree.GetFromId(4);
|
|
EXPECT_TRUE(node4->IsText());
|
|
EXPECT_NE(node4->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node4->GetLanguage(), "en");
|
|
}
|
|
|
|
{
|
|
AXNode* node5 = tree.GetFromId(5);
|
|
EXPECT_TRUE(node5->IsText());
|
|
// Inherited languages are not stored in lang info.
|
|
EXPECT_EQ(node5->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node5->GetLanguage(), "en");
|
|
}
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestStaticContent, MetricCollection) {
|
|
// Tree:
|
|
// 1
|
|
// 2 3 4 5 6
|
|
//
|
|
// 1 - German lang attribute, no text
|
|
// 2 - no attribute, German text
|
|
// 3 - no attribute, French text
|
|
// 4 - no attribute, English text
|
|
// 5 - no attribute, Spanish text
|
|
// 6 - no attribute, text too short to get detection results.
|
|
//
|
|
// Expected:
|
|
// 2 - German detected
|
|
// 3 - French detected
|
|
// 4 - English detected
|
|
// 5 - Spanish detected
|
|
// 6 - too short for results
|
|
//
|
|
// only 3 of these languages can be labelled due to heuristics.
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(6);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(5);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.child_ids[2] = 4;
|
|
node1.child_ids[3] = 5;
|
|
node1.child_ids[4] = 6;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "de");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node4 = initial_state.nodes[3];
|
|
node4.id = 4;
|
|
node4.role = ax::mojom::Role::kStaticText;
|
|
node4.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node5 = initial_state.nodes[4];
|
|
node5.id = 5;
|
|
node5.role = ax::mojom::Role::kStaticText;
|
|
node5.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextSpanish);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node6 = initial_state.nodes[5];
|
|
node6.id = 6;
|
|
node6.role = ax::mojom::Role::kStaticText;
|
|
node6.AddStringAttribute(ax::mojom::StringAttribute::kName,
|
|
"too short for detection.");
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
|
|
// Specifically disable clearing of metrics.
|
|
disable_metric_clearing(tree);
|
|
// Our histogram for testing.
|
|
base::HistogramTester histograms;
|
|
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// All 4 of our languages should have been detected for one node each, scoring
|
|
// a maximum 3 points.
|
|
EXPECT_EQ(3, get_score(tree, "de"));
|
|
EXPECT_EQ(3, get_score(tree, "en"));
|
|
EXPECT_EQ(3, get_score(tree, "fr"));
|
|
EXPECT_EQ(3, get_score(tree, "es"));
|
|
|
|
// 5 nodes (2, 3, 4, 5, 6) should have had detection attempted.
|
|
EXPECT_EQ(5, count_detection_attempted(tree));
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.CountDetectionAttempted", 5, 1);
|
|
|
|
// 4 nodes (2, 3, 4, 5) should have had detection results.
|
|
EXPECT_EQ(4, count_detection_results(tree));
|
|
// 5 nodes attempted, 4 got results = 4*100/5 = 80%
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.PercentageLanguageDetected", 80, 1);
|
|
|
|
// 3 nodes (any of 2, 3, 4, 5) should have been labelled.
|
|
EXPECT_EQ(3, count_labelled(tree));
|
|
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.CountLabelled",
|
|
3, 1);
|
|
|
|
// 3 nodes (any of 2, 3, 4, 5) should have been given top label.
|
|
EXPECT_EQ(3, count_labelled_with_top_result(tree));
|
|
// 3 nodes labelled, all of them given top result = 100%.
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.PercentageLabelledWithTop", 100, 1);
|
|
|
|
// 3 nodes (3, 4, 5) should have been labelled to disagree with node1 author
|
|
// provided language.
|
|
EXPECT_EQ(3, count_overridden(tree));
|
|
// 3 nodes labelled, all 3 disagree with node1 = 100%.
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.PercentageOverridden", 100, 1);
|
|
|
|
// There should be 4 unique languages (de, en, fr, es).
|
|
{
|
|
const auto& top_lang = unique_top_lang_detected(tree);
|
|
const std::unordered_set<std::string> expected_top_lang = {"de", "en", "es",
|
|
"fr"};
|
|
EXPECT_EQ(top_lang, expected_top_lang);
|
|
}
|
|
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.LangsPerPage",
|
|
4, 1);
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestStaticContent, DetectOnly) {
|
|
// This tests a Detect step without any matching Label step.
|
|
//
|
|
// Tree:
|
|
// 1
|
|
// 2 3
|
|
// 4
|
|
// 5
|
|
//
|
|
// 1 - German lang attribute, no text
|
|
// 2 - French lang attribute, no text
|
|
// 3 - no attribute, French text
|
|
// 4 - no attribute, English text
|
|
// 5 - no attribute, no text
|
|
//
|
|
// Expected:
|
|
// 3 - French detected, never labelled, so still inherits German from 1
|
|
// 4 - English detected, never labelled, so still inherits French from 2
|
|
// 5 - English inherited from 4, still inherits French from 4
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(5);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "de");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kGenericContainer;
|
|
node2.child_ids.resize(1);
|
|
node2.child_ids[0] = 4;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node4 = initial_state.nodes[3];
|
|
node4.id = 4;
|
|
node4.child_ids.resize(1);
|
|
node4.child_ids[0] = 5;
|
|
node4.role = ax::mojom::Role::kStaticText;
|
|
node4.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node5 = initial_state.nodes[4];
|
|
node5.id = 5;
|
|
node5.role = ax::mojom::Role::kInlineTextBox;
|
|
node5.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
tree.language_detection_manager->DetectLanguages();
|
|
// Purposefully not calling Label so we can test Detect in isolation.
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
// node1 is not a text node, so no lang info should be attached.
|
|
EXPECT_EQ(node1->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node1->GetLanguage(), "de");
|
|
}
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
// node2 is not a text node, so no lang info should be attached.
|
|
EXPECT_EQ(node2->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
EXPECT_TRUE(node3->IsText());
|
|
ASSERT_NE(node3->GetLanguageInfo(), nullptr);
|
|
ASSERT_GT(node3->GetLanguageInfo()->detected_languages.size(), (unsigned)0);
|
|
ASSERT_EQ(node3->GetLanguageInfo()->detected_languages[0], "fr");
|
|
EXPECT_TRUE(node3->GetLanguageInfo()->language.empty());
|
|
EXPECT_EQ(node3->GetLanguage(), "de");
|
|
}
|
|
|
|
{
|
|
AXNode* node4 = tree.GetFromId(4);
|
|
EXPECT_TRUE(node4->IsText());
|
|
ASSERT_NE(node4->GetLanguageInfo(), nullptr);
|
|
ASSERT_GT(node4->GetLanguageInfo()->detected_languages.size(), (unsigned)0);
|
|
ASSERT_EQ(node4->GetLanguageInfo()->detected_languages[0], "en");
|
|
EXPECT_TRUE(node4->GetLanguageInfo()->language.empty());
|
|
EXPECT_EQ(node4->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node5 = tree.GetFromId(5);
|
|
EXPECT_TRUE(node5->IsText());
|
|
// Inherited languages are not stored in lang info.
|
|
ASSERT_EQ(node5->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node5->GetLanguage(), "fr");
|
|
}
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestStaticContent, kLanguageUntouched) {
|
|
// This test is to ensure that the kLanguage string attribute is not updated
|
|
// during language detection and labelling, even when it disagrees with the
|
|
// detected language.
|
|
|
|
// Built tree:
|
|
// 1
|
|
// 2 3
|
|
//
|
|
// 1 - German lang attribute, no text
|
|
// 2 - English lang attribute, French text
|
|
// 3 - French lang attribute, English text
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(3);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "de");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "en");
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_EQ(node1->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node1->GetLanguage(), "de");
|
|
}
|
|
|
|
{
|
|
// French should be detected, original English attr should be untouched.
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node2->GetLanguageInfo()->language, "fr");
|
|
EXPECT_EQ(node2->GetStringAttribute(ax::mojom::StringAttribute::kLanguage),
|
|
"en");
|
|
EXPECT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
// English should be detected, original French attr should be untouched.
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
ASSERT_NE(node3->GetLanguageInfo(), nullptr);
|
|
EXPECT_EQ(node3->GetLanguageInfo()->language, "en");
|
|
EXPECT_EQ(node3->GetStringAttribute(ax::mojom::StringAttribute::kLanguage),
|
|
"fr");
|
|
EXPECT_EQ(node3->GetLanguage(), "en");
|
|
}
|
|
}
|
|
|
|
// Test RegisterLanguageDetectionObserver correctly respects the command line
|
|
// flags.
|
|
TEST_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFlag) {
|
|
// Enable only the flag controlling static language detection.
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
|
|
// Construct empty tree and check initialisation.
|
|
AXTree tree;
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
ASSERT_EQ(getObserver(tree), nullptr);
|
|
|
|
// Try registration without enabling Dynamic feature flag, should be a no-op.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
|
|
ASSERT_EQ(getObserver(tree), nullptr);
|
|
|
|
// Now enable the dynamic feature flag.
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
|
|
|
|
// Try registration again, this should construct and register observer as flag
|
|
// is now enabled.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
|
|
// Check our observer was constructed.
|
|
ASSERT_NE(getObserver(tree), nullptr);
|
|
|
|
// Check our observer was registered in our tree.
|
|
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
|
|
}
|
|
|
|
// Test RegisterLanguageDetectionObserver correctly respects the feature flag.
|
|
TEST_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFeatureFlag) {
|
|
// Construct empty tree and check initialisation.
|
|
AXTree tree;
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
ASSERT_EQ(getObserver(tree), nullptr);
|
|
|
|
// Try registration without enabling Dynamic feature flag, should be a no-op.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
|
|
ASSERT_EQ(getObserver(tree), nullptr);
|
|
|
|
// Enable general feature flag which gates both Static and Dynamic features.
|
|
base::test::ScopedFeatureList scoped_feature_list;
|
|
scoped_feature_list.InitWithFeatures(
|
|
{features::kEnableAccessibilityLanguageDetection}, {});
|
|
|
|
// Try registration again, this should now construct and register an observer.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
|
|
// Check our observer was constructed.
|
|
ASSERT_NE(getObserver(tree), nullptr);
|
|
|
|
// Check our observer was registered in our tree.
|
|
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestDynamicContent, Basic) {
|
|
// Tree:
|
|
// 1
|
|
// 2
|
|
//
|
|
// 1 - kStaticText - English text.
|
|
// 2 - kInlineTextBox - English text.
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(2);
|
|
|
|
// TODO(chrishall): Create more realistic kStaticText with multiple
|
|
// kInlineTextBox(es) children. Look at the real-world behaviour of
|
|
// kStaticText, kInlineText and kLineBreak around empty divs and empty lines
|
|
// within paragraphs of text.
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kStaticText;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
node1.child_ids.resize(1);
|
|
node1.child_ids[0] = 2;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kInlineTextBox;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
|
|
// Manually run initial language detection and labelling.
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// Quickly verify "before" state
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_NE(node1, nullptr);
|
|
ASSERT_NE(node1->GetLanguageInfo(), nullptr);
|
|
ASSERT_EQ(node1->GetLanguage(), "en");
|
|
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
// Inherited language not stored in lang info.
|
|
ASSERT_EQ(node2->GetLanguageInfo(), nullptr);
|
|
// Should still inherit language from parent.
|
|
ASSERT_EQ(node2->GetLanguage(), "en");
|
|
}
|
|
|
|
// Manually register observer.
|
|
AXLanguageDetectionObserver observer(&tree);
|
|
|
|
// Observer constructor is responsible for attaching itself to tree.
|
|
ASSERT_TRUE(tree.HasObserver(&observer));
|
|
|
|
// Dynamic update
|
|
//
|
|
// New tree:
|
|
// 1
|
|
// 2
|
|
//
|
|
// 1 - Text changed to German.
|
|
// 2 - Text changed to German.
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 1;
|
|
update_state.nodes.resize(2);
|
|
|
|
// Change text to German.
|
|
{
|
|
AXNodeData& node1 = update_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kStaticText;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
node1.child_ids.resize(1);
|
|
node1.child_ids[0] = 2;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = update_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kInlineTextBox;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
// Check language detection was re-run on new content.
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_NE(node1, nullptr);
|
|
ASSERT_NE(node1->GetLanguageInfo(), nullptr);
|
|
ASSERT_EQ(node1->GetLanguage(), "de");
|
|
}
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
// Inherited language not stored in lang info.
|
|
ASSERT_EQ(node2->GetLanguageInfo(), nullptr);
|
|
// Should inherit new language from parent.
|
|
ASSERT_EQ(node2->GetLanguage(), "de");
|
|
}
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestDynamicContent, MetricCollection) {
|
|
// Tree:
|
|
// 1
|
|
// 2 3
|
|
//
|
|
// 1 - kGenericContainer, French lang attribute.
|
|
// 2 - kStaticText - English text.
|
|
// 3 - kSTaticText - German text.
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(3);
|
|
|
|
// TODO(chrishall): Create more realistic kStaticText with multiple
|
|
// kInlineTextBox(es) children. Look at the real-world behaviour of
|
|
// kStaticText, kInlineText and kLineBreak around empty divs and empty lines
|
|
// within paragraphs of text.
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = initial_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
|
|
// Manually run initial language detection and labelling.
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// Quickly verify "before" metrics were cleared.
|
|
EXPECT_EQ(0, count_detection_attempted(tree));
|
|
|
|
// Specifically disable clearing of metrics for dynamic only.
|
|
disable_metric_clearing(tree);
|
|
// Our histogram for testing.
|
|
base::HistogramTester histograms;
|
|
|
|
// Manually register observer.
|
|
AXLanguageDetectionObserver observer(&tree);
|
|
|
|
// Observer constructor is responsible for attaching itself to tree.
|
|
ASSERT_TRUE(tree.HasObserver(&observer));
|
|
|
|
// Dynamic update
|
|
//
|
|
// New tree:
|
|
// 1
|
|
// 2 3 4
|
|
//
|
|
// 1 - no change.
|
|
// 2 - Text changed to French.
|
|
// 3 - no change.
|
|
// 4 - new kStaticText node, Spanish text.
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 1;
|
|
update_state.nodes.resize(3);
|
|
|
|
// Change text to German.
|
|
{
|
|
AXNodeData& node1 = update_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(3);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
node1.child_ids[2] = 4;
|
|
node1.AddStringAttribute(ax::mojom::StringAttribute::kLanguage, "fr");
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = update_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
{
|
|
AXNodeData& node4 = update_state.nodes[2];
|
|
node4.id = 4;
|
|
node4.role = ax::mojom::Role::kStaticText;
|
|
node4.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextSpanish);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
// Check "after" metrics.
|
|
// note that the metrics were cleared after static work had finished, so these
|
|
// metrics only reflect the dynamic work.
|
|
|
|
// All 4 of our languages should have been detected for one node each, scoring
|
|
// a maximum 3 points.
|
|
EXPECT_EQ(3, get_score(tree, "de"));
|
|
EXPECT_EQ(3, get_score(tree, "en"));
|
|
EXPECT_EQ(3, get_score(tree, "fr"));
|
|
EXPECT_EQ(3, get_score(tree, "es"));
|
|
|
|
// 2 nodes (2, 4) should have had detection attempted.
|
|
EXPECT_EQ(2, count_detection_attempted(tree));
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.CountDetectionAttempted", 2, 1);
|
|
|
|
// 2 nodes (2, 4) should have had detection results
|
|
EXPECT_EQ(2, count_detection_results(tree));
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.PercentageLanguageDetected", 100, 1);
|
|
|
|
// 2 nodes (2, 4) should have been labelled
|
|
EXPECT_EQ(2, count_labelled(tree));
|
|
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.CountLabelled",
|
|
2, 1);
|
|
|
|
// 2 nodes (2, 4) should have been given top label
|
|
EXPECT_EQ(2, count_labelled_with_top_result(tree));
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.PercentageLabelledWithTop", 100, 1);
|
|
|
|
// 1 nodes (4) should have been labelled to disagree with node1 author
|
|
// provided language.
|
|
EXPECT_EQ(1, count_overridden(tree));
|
|
// 2 nodes were labelled, 1 disagreed with node1 = 50%.
|
|
histograms.ExpectUniqueSample(
|
|
"Accessibility.LanguageDetection.PercentageOverridden", 50, 1);
|
|
|
|
// There should be 2 unique languages (fr, es).
|
|
{
|
|
auto top_lang = unique_top_lang_detected(tree);
|
|
const std::unordered_set<std::string> expected_top_lang = {"es", "fr"};
|
|
EXPECT_EQ(top_lang, expected_top_lang);
|
|
}
|
|
// There should be a single (unique, 1) value for '2' unique languages.
|
|
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.LangsPerPage",
|
|
2, 1);
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestDynamicContent, MultipleUpdates) {
|
|
// This test runs language detection a total of 3 times, once for the initial
|
|
// 'static' content, and then twice for 'dynamic' updates.
|
|
|
|
// Tree:
|
|
// 1
|
|
// 2
|
|
//
|
|
// 1 - GenericContainer
|
|
// 2 - English text
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(2);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(1);
|
|
node1.child_ids[0] = 2;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = initial_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextEnglish);
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
|
|
// Run initial language detection and labelling.
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// Quickly verify "before" state
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_NE(node1, nullptr);
|
|
ASSERT_EQ(node1->GetLanguage(), "");
|
|
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
ASSERT_EQ(node2->GetLanguage(), "en");
|
|
}
|
|
|
|
// Register dynamic content observer.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
ASSERT_NE(getObserver(tree), nullptr);
|
|
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
|
|
|
|
// First update
|
|
{
|
|
// Dynamic update
|
|
//
|
|
// New tree layout will be:
|
|
// 1
|
|
// 2 3
|
|
//
|
|
// 1 - GenericContainer, unchanged
|
|
// 2 - changed to German text
|
|
// 3 - new child, French text
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 1;
|
|
update_state.nodes.resize(3);
|
|
|
|
// Update node1 to include new child node3.
|
|
{
|
|
AXNodeData& node1 = update_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
}
|
|
|
|
// Change node2 text to German
|
|
{
|
|
AXNodeData& node2 = update_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
// Add new node3 with French text.
|
|
{
|
|
AXNodeData& node3 = update_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_NE(node1, nullptr);
|
|
ASSERT_EQ(node1->GetLanguage(), "");
|
|
}
|
|
|
|
{
|
|
// Detection should have been re-run on node2, detecting German.
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
ASSERT_EQ(node2->GetLanguage(), "de");
|
|
}
|
|
|
|
{
|
|
// New node3 should have detected French.
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
ASSERT_NE(node3, nullptr);
|
|
ASSERT_EQ(node3->GetLanguage(), "fr");
|
|
}
|
|
}
|
|
|
|
// Second update
|
|
{
|
|
// Dynamic update
|
|
//
|
|
// New tree layout will be:
|
|
// 1
|
|
// 2 x
|
|
//
|
|
// 1 - GenericContainer, unchanged
|
|
// 2 - changed to French text
|
|
// 3 - deleted
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 1;
|
|
update_state.nodes.resize(2);
|
|
|
|
// Update node1 to delete child node3.
|
|
{
|
|
AXNodeData& node1 = update_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(1);
|
|
node1.child_ids[0] = 2;
|
|
}
|
|
|
|
// Change node2 text to French
|
|
{
|
|
AXNodeData& node2 = update_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_NE(node1, nullptr);
|
|
ASSERT_EQ(node1->GetLanguage(), "");
|
|
}
|
|
|
|
{
|
|
// Detection should have been re-run on node2, detecting French.
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
ASSERT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
// Node3 should be no more.
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
ASSERT_EQ(node3, nullptr);
|
|
}
|
|
}
|
|
|
|
// Third update.
|
|
{
|
|
// Dynamic update
|
|
//
|
|
// New tree layout will be:
|
|
// 1
|
|
// 2 3
|
|
//
|
|
// 1 - GenericContainer, unchanged
|
|
// 2 - French text, unchanged
|
|
// 3 - new node, German text
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 1;
|
|
update_state.nodes.resize(2);
|
|
|
|
// Update node1 to include new child node3.
|
|
{
|
|
AXNodeData& node1 = update_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(2);
|
|
node1.child_ids[0] = 2;
|
|
node1.child_ids[1] = 3;
|
|
}
|
|
|
|
// Add new node3 with German text.
|
|
{
|
|
AXNodeData& node3 = update_state.nodes[1];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
{
|
|
AXNode* node1 = tree.GetFromId(1);
|
|
ASSERT_NE(node1, nullptr);
|
|
ASSERT_EQ(node1->GetLanguage(), "");
|
|
}
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
ASSERT_EQ(node2->GetLanguage(), "fr");
|
|
}
|
|
|
|
{
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
ASSERT_NE(node3, nullptr);
|
|
ASSERT_EQ(node3->GetLanguage(), "de");
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestDynamicContent, NewRoot) {
|
|
// Artificial test change which changes the root node.
|
|
|
|
// Tree:
|
|
// 1
|
|
//
|
|
// 1 - GenericContainer
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(1);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
|
|
// Run initial language detection and labelling.
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// Register dynamic content observer.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
ASSERT_NE(getObserver(tree), nullptr);
|
|
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
|
|
|
|
// New Tree:
|
|
// 2
|
|
// 2 - new root, German text
|
|
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 2;
|
|
update_state.nodes.resize(1);
|
|
|
|
{
|
|
AXNodeData& node2 = update_state.nodes[0];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kStaticText;
|
|
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
{
|
|
AXNode* node2 = tree.GetFromId(2);
|
|
ASSERT_NE(node2, nullptr);
|
|
ASSERT_EQ(node2->GetLanguage(), "de");
|
|
}
|
|
}
|
|
|
|
TEST_F(AXLanguageDetectionTestDynamicContent, ChainOfNewNodes) {
|
|
// Artificial test which adds two new nodes in a 'chain', simultaneously
|
|
// adding a child of the root and a grandchild of the root.
|
|
|
|
// Tree:
|
|
// 1
|
|
//
|
|
// 1 - GenericContainer
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(1);
|
|
|
|
{
|
|
AXNodeData& node1 = initial_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
}
|
|
|
|
AXTree tree(initial_state);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
|
|
// Run initial language detection and labelling.
|
|
tree.language_detection_manager->DetectLanguages();
|
|
tree.language_detection_manager->LabelLanguages();
|
|
|
|
// Register dynamic content observer.
|
|
tree.language_detection_manager->RegisterLanguageDetectionObserver();
|
|
ASSERT_NE(getObserver(tree), nullptr);
|
|
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
|
|
|
|
// New Tree:
|
|
// 1
|
|
// 2
|
|
// 3
|
|
// 1 - generic container
|
|
// 2 - generic container
|
|
// 3 - German text
|
|
|
|
AXTreeUpdate update_state;
|
|
update_state.root_id = 1;
|
|
update_state.nodes.resize(3);
|
|
|
|
{
|
|
AXNodeData& node1 = update_state.nodes[0];
|
|
node1.id = 1;
|
|
node1.role = ax::mojom::Role::kGenericContainer;
|
|
node1.child_ids.resize(1);
|
|
node1.child_ids[0] = 2;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node2 = update_state.nodes[1];
|
|
node2.id = 2;
|
|
node2.role = ax::mojom::Role::kGenericContainer;
|
|
node2.child_ids.resize(1);
|
|
node2.child_ids[0] = 3;
|
|
}
|
|
|
|
{
|
|
AXNodeData& node3 = update_state.nodes[2];
|
|
node3.id = 3;
|
|
node3.role = ax::mojom::Role::kStaticText;
|
|
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
|
|
}
|
|
|
|
// Perform update.
|
|
ASSERT_TRUE(tree.Unserialize(update_state));
|
|
|
|
{
|
|
AXNode* node3 = tree.GetFromId(3);
|
|
ASSERT_NE(node3, nullptr);
|
|
ASSERT_EQ(node3->GetLanguage(), "de");
|
|
}
|
|
}
|
|
|
|
TEST(AXLanguageDetectionTest, AXLanguageInfoStatsBasic) {
|
|
AXLanguageInfoStats stats;
|
|
|
|
{
|
|
std::vector<std::string> detected_languages;
|
|
detected_languages.push_back("en");
|
|
detected_languages.push_back("fr");
|
|
detected_languages.push_back("ja");
|
|
stats.Add(detected_languages);
|
|
}
|
|
|
|
ASSERT_EQ(stats.GetScore("en"), 3);
|
|
ASSERT_EQ(stats.GetScore("fr"), 2);
|
|
ASSERT_EQ(stats.GetScore("ja"), 1);
|
|
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("en"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("fr"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("ja"));
|
|
|
|
{
|
|
std::vector<std::string> detected_languages;
|
|
detected_languages.push_back("en");
|
|
detected_languages.push_back("de");
|
|
detected_languages.push_back("fr");
|
|
stats.Add(detected_languages);
|
|
}
|
|
|
|
ASSERT_EQ(stats.GetScore("en"), 6);
|
|
ASSERT_EQ(stats.GetScore("fr"), 3);
|
|
ASSERT_EQ(stats.GetScore("de"), 2);
|
|
ASSERT_EQ(stats.GetScore("ja"), 1);
|
|
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("en"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("fr"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("de"));
|
|
|
|
EXPECT_FALSE(stats.CheckLanguageWithinTop("ja"));
|
|
|
|
{
|
|
std::vector<std::string> detected_languages;
|
|
detected_languages.push_back("fr");
|
|
stats.Add(detected_languages);
|
|
}
|
|
|
|
ASSERT_EQ(stats.GetScore("en"), 6);
|
|
ASSERT_EQ(stats.GetScore("fr"), 6);
|
|
ASSERT_EQ(stats.GetScore("de"), 2);
|
|
ASSERT_EQ(stats.GetScore("ja"), 1);
|
|
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("en"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("fr"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("de"));
|
|
|
|
EXPECT_FALSE(stats.CheckLanguageWithinTop("ja"));
|
|
|
|
{
|
|
std::vector<std::string> detected_languages;
|
|
detected_languages.push_back("ja");
|
|
detected_languages.push_back("qq");
|
|
detected_languages.push_back("zz");
|
|
stats.Add(detected_languages);
|
|
}
|
|
|
|
ASSERT_EQ(stats.GetScore("en"), 6);
|
|
ASSERT_EQ(stats.GetScore("fr"), 6);
|
|
ASSERT_EQ(stats.GetScore("ja"), 4);
|
|
ASSERT_EQ(stats.GetScore("de"), 2);
|
|
ASSERT_EQ(stats.GetScore("qq"), 2);
|
|
ASSERT_EQ(stats.GetScore("zz"), 1);
|
|
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("en"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("fr"));
|
|
EXPECT_TRUE(stats.CheckLanguageWithinTop("ja"));
|
|
|
|
EXPECT_FALSE(stats.CheckLanguageWithinTop("de"));
|
|
EXPECT_FALSE(stats.CheckLanguageWithinTop("qq"));
|
|
EXPECT_FALSE(stats.CheckLanguageWithinTop("zz"));
|
|
}
|
|
|
|
TEST(AXLanguageDetectionTest, ShortLanguageDetectorLabeledTest) {
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(2);
|
|
initial_state.nodes[0].id = 1;
|
|
initial_state.nodes[0].child_ids = {2};
|
|
initial_state.nodes[1].id = 2;
|
|
initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
|
|
"Hello");
|
|
initial_state.nodes[1].AddStringAttribute(
|
|
ax::mojom::StringAttribute::kLanguage, "en");
|
|
AXTree tree(initial_state);
|
|
|
|
AXNode* item = tree.GetFromId(2);
|
|
std::vector<AXLanguageSpan> annotation;
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
// Empty output.
|
|
annotation =
|
|
tree.language_detection_manager->GetLanguageAnnotationForStringAttribute(
|
|
*item, ax::mojom::StringAttribute::kMathContent);
|
|
ASSERT_EQ(0, (int)annotation.size());
|
|
// Returns single AXLanguageSpan.
|
|
annotation =
|
|
tree.language_detection_manager->GetLanguageAnnotationForStringAttribute(
|
|
*item, ax::mojom::StringAttribute::kName);
|
|
ASSERT_EQ(1, (int)annotation.size());
|
|
AXLanguageSpan* lang_span = &annotation[0];
|
|
ASSERT_EQ("en", lang_span->language);
|
|
std::string name =
|
|
item->GetStringAttribute(ax::mojom::StringAttribute::kName);
|
|
ASSERT_EQ("Hello",
|
|
name.substr(lang_span->start_index,
|
|
lang_span->end_index - lang_span->start_index));
|
|
}
|
|
|
|
TEST(AXLanguageDetectionTest, ShortLanguageDetectorCharacterTest) {
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(2);
|
|
initial_state.nodes[0].id = 1;
|
|
initial_state.nodes[0].child_ids = {2};
|
|
initial_state.nodes[1].id = 2;
|
|
initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
|
|
"δ");
|
|
AXTree tree(initial_state);
|
|
|
|
AXNode* item = tree.GetFromId(2);
|
|
std::vector<AXLanguageSpan> annotation;
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
// Returns single LanguageSpan.
|
|
annotation =
|
|
tree.language_detection_manager->GetLanguageAnnotationForStringAttribute(
|
|
*item, ax::mojom::StringAttribute::kName);
|
|
ASSERT_EQ(1, (int)annotation.size());
|
|
AXLanguageSpan* lang_span = &annotation[0];
|
|
ASSERT_EQ("el", lang_span->language);
|
|
std::string name =
|
|
item->GetStringAttribute(ax::mojom::StringAttribute::kName);
|
|
ASSERT_EQ("δ", name.substr(lang_span->start_index,
|
|
lang_span->end_index - lang_span->start_index));
|
|
}
|
|
|
|
TEST(AXLanguageDetectionTest, ShortLanguageDetectorMultipleLanguagesTest) {
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(2);
|
|
initial_state.nodes[0].id = 1;
|
|
initial_state.nodes[0].child_ids = {2};
|
|
initial_state.nodes[1].id = 2;
|
|
initial_state.nodes[1].AddStringAttribute(
|
|
ax::mojom::StringAttribute::kName,
|
|
"This text should be read in English. 차에 한하여 중임할 수. Followed "
|
|
"by English.");
|
|
AXTree tree(initial_state);
|
|
|
|
AXNode* item = tree.GetFromId(2);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
std::vector<AXLanguageSpan> annotation =
|
|
tree.language_detection_manager->GetLanguageAnnotationForStringAttribute(
|
|
*item, ax::mojom::StringAttribute::kName);
|
|
ASSERT_EQ(3, (int)annotation.size());
|
|
std::string name =
|
|
item->GetStringAttribute(ax::mojom::StringAttribute::kName);
|
|
AXLanguageSpan* lang_span = &annotation[0];
|
|
ASSERT_EQ("This text should be read in English. ",
|
|
name.substr(lang_span->start_index,
|
|
lang_span->end_index - lang_span->start_index));
|
|
lang_span = &annotation[1];
|
|
ASSERT_EQ("차에 한하여 중임할 수. ",
|
|
name.substr(lang_span->start_index,
|
|
lang_span->end_index - lang_span->start_index));
|
|
lang_span = &annotation[2];
|
|
ASSERT_EQ("Followed by English.",
|
|
name.substr(lang_span->start_index,
|
|
lang_span->end_index - lang_span->start_index));
|
|
}
|
|
|
|
// Assert that GetLanguageAnnotationForStringAttribute works for attributes
|
|
// other than kName.
|
|
TEST(AXLanguageDetectionTest, DetectLanguagesForRoleTest) {
|
|
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
|
::switches::kEnableExperimentalAccessibilityLanguageDetection);
|
|
AXTreeUpdate initial_state;
|
|
initial_state.root_id = 1;
|
|
initial_state.nodes.resize(1);
|
|
initial_state.nodes[0].id = 1;
|
|
initial_state.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kValue,
|
|
"どうぞよろしくお願いします.");
|
|
AXTree tree(initial_state);
|
|
|
|
AXNode* item = tree.GetFromId(1);
|
|
ASSERT_NE(tree.language_detection_manager, nullptr);
|
|
std::vector<AXLanguageSpan> annotation =
|
|
tree.language_detection_manager->GetLanguageAnnotationForStringAttribute(
|
|
*item, ax::mojom::StringAttribute::kValue);
|
|
ASSERT_EQ(1, (int)annotation.size());
|
|
std::string value =
|
|
item->GetStringAttribute(ax::mojom::StringAttribute::kValue);
|
|
AXLanguageSpan* lang_span = &annotation[0];
|
|
ASSERT_EQ("どうぞよろしくお願いします.",
|
|
value.substr(lang_span->start_index,
|
|
lang_span->end_index - lang_span->start_index));
|
|
}
|
|
|
|
} // namespace ui
|