#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.";
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);
}
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 absl::flat_hash_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) {
EXPECT_FALSE(
::switches::IsExperimentalAccessibilityLanguageDetectionEnabled());
EXPECT_FALSE(IsStaticLanguageDetectionEnabled());
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetection);
EXPECT_TRUE(
::switches::IsExperimentalAccessibilityLanguageDetectionEnabled());
EXPECT_TRUE(IsStaticLanguageDetectionEnabled());
}
TEST_F(AXLanguageDetectionTestFixture, DynamicContentFeatureFlag) {
EXPECT_FALSE(
::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
EXPECT_FALSE(IsDynamicLanguageDetectionEnabled());
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
EXPECT_TRUE(
::switches::IsExperimentalAccessibilityLanguageDetectionDynamicEnabled());
EXPECT_TRUE(IsDynamicLanguageDetectionEnabled());
}
TEST_F(AXLanguageDetectionTestFixture, FeatureFlag) {
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) {
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);
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_F(AXLanguageDetectionTestStaticContent, GetLanguageBoringTree) {
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();
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) {
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);
EXPECT_EQ(node1->GetLanguageInfo(), nullptr);
EXPECT_EQ(node1->GetLanguage(), "de");
}
{
AXNode* node2 = tree.GetFromId(2);
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());
EXPECT_EQ(node5->GetLanguageInfo(), nullptr);
EXPECT_EQ(node5->GetLanguage(), "en");
}
}
TEST_F(AXLanguageDetectionTestStaticContent, MetricCollection) {
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);
disable_metric_clearing(tree);
base::HistogramTester histograms;
tree.language_detection_manager->DetectLanguages();
tree.language_detection_manager->LabelLanguages();
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"));
EXPECT_EQ(5, count_detection_attempted(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.CountDetectionAttempted", 5, 1);
EXPECT_EQ(4, count_detection_results(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.PercentageLanguageDetected", 80, 1);
EXPECT_EQ(3, count_labelled(tree));
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.CountLabelled",
3, 1);
EXPECT_EQ(3, count_labelled_with_top_result(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.PercentageLabelledWithTop", 100, 1);
EXPECT_EQ(3, count_overridden(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.PercentageOverridden", 100, 1);
{
const auto& top_lang = unique_top_lang_detected(tree);
const absl::flat_hash_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) {
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();
{
AXNode* node1 = tree.GetFromId(1);
EXPECT_EQ(node1->GetLanguageInfo(), nullptr);
EXPECT_EQ(node1->GetLanguage(), "de");
}
{
AXNode* node2 = tree.GetFromId(2);
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());
ASSERT_EQ(node5->GetLanguageInfo(), nullptr);
EXPECT_EQ(node5->GetLanguage(), "fr");
}
}
TEST_F(AXLanguageDetectionTestStaticContent, kLanguageUntouched) {
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");
}
{
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");
}
{
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_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFlag) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetection);
AXTree tree;
ASSERT_NE(tree.language_detection_manager, nullptr);
ASSERT_EQ(getObserver(tree), nullptr);
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_EQ(getObserver(tree), nullptr);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kEnableExperimentalAccessibilityLanguageDetectionDynamic);
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_NE(getObserver(tree), nullptr);
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
}
TEST_F(AXLanguageDetectionTestFixture, ObserverRegistrationObeysFeatureFlag) {
AXTree tree;
ASSERT_NE(tree.language_detection_manager, nullptr);
ASSERT_EQ(getObserver(tree), nullptr);
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_EQ(getObserver(tree), nullptr);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
{features::kEnableAccessibilityLanguageDetection}, {});
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_NE(getObserver(tree), nullptr);
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
}
TEST_F(AXLanguageDetectionTestDynamicContent, Basic) {
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::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);
tree.language_detection_manager->DetectLanguages();
tree.language_detection_manager->LabelLanguages();
{
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);
ASSERT_EQ(node2->GetLanguageInfo(), nullptr);
ASSERT_EQ(node2->GetLanguage(), "en");
}
AXLanguageDetectionObserver observer(&tree);
ASSERT_TRUE(tree.HasObserver(&observer));
AXTreeUpdate update_state;
update_state.root_id = 1;
update_state.nodes.resize(2);
{
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);
}
ASSERT_TRUE(tree.Unserialize(update_state));
{
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);
ASSERT_EQ(node2->GetLanguageInfo(), nullptr);
ASSERT_EQ(node2->GetLanguage(), "de");
}
}
TEST_F(AXLanguageDetectionTestDynamicContent, MetricCollection) {
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, "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);
tree.language_detection_manager->DetectLanguages();
tree.language_detection_manager->LabelLanguages();
EXPECT_EQ(0, count_detection_attempted(tree));
disable_metric_clearing(tree);
base::HistogramTester histograms;
AXLanguageDetectionObserver observer(&tree);
ASSERT_TRUE(tree.HasObserver(&observer));
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(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);
}
ASSERT_TRUE(tree.Unserialize(update_state));
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"));
EXPECT_EQ(2, count_detection_attempted(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.CountDetectionAttempted", 2, 1);
EXPECT_EQ(2, count_detection_results(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.PercentageLanguageDetected", 100, 1);
EXPECT_EQ(2, count_labelled(tree));
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.CountLabelled",
2, 1);
EXPECT_EQ(2, count_labelled_with_top_result(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.PercentageLabelledWithTop", 100, 1);
EXPECT_EQ(1, count_overridden(tree));
histograms.ExpectUniqueSample(
"Accessibility.LanguageDetection.PercentageOverridden", 50, 1);
{
auto top_lang = unique_top_lang_detected(tree);
const absl::flat_hash_set<std::string> expected_top_lang = {"es", "fr"};
EXPECT_EQ(top_lang, expected_top_lang);
}
histograms.ExpectUniqueSample("Accessibility.LanguageDetection.LangsPerPage",
2, 1);
}
TEST_F(AXLanguageDetectionTestDynamicContent, MultipleUpdates) {
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);
tree.language_detection_manager->DetectLanguages();
tree.language_detection_manager->LabelLanguages();
{
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");
}
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_NE(getObserver(tree), nullptr);
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
{
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(2);
node1.child_ids[0] = 2;
node1.child_ids[1] = 3;
}
{
AXNodeData& node2 = update_state.nodes[1];
node2.id = 2;
node2.role = ax::mojom::Role::kStaticText;
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
}
{
AXNodeData& node3 = update_state.nodes[2];
node3.id = 3;
node3.role = ax::mojom::Role::kStaticText;
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
}
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(), "de");
}
{
AXNode* node3 = tree.GetFromId(3);
ASSERT_NE(node3, nullptr);
ASSERT_EQ(node3->GetLanguage(), "fr");
}
}
{
AXTreeUpdate update_state;
update_state.root_id = 1;
update_state.nodes.resize(2);
{
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::kStaticText;
node2.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextFrench);
}
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_EQ(node3, nullptr);
}
}
{
AXTreeUpdate update_state;
update_state.root_id = 1;
update_state.nodes.resize(2);
{
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;
}
{
AXNodeData& node3 = update_state.nodes[1];
node3.id = 3;
node3.role = ax::mojom::Role::kStaticText;
node3.AddStringAttribute(ax::mojom::StringAttribute::kName, kTextGerman);
}
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) {
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);
tree.language_detection_manager->DetectLanguages();
tree.language_detection_manager->LabelLanguages();
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_NE(getObserver(tree), nullptr);
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
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);
}
ASSERT_TRUE(tree.Unserialize(update_state));
{
AXNode* node2 = tree.GetFromId(2);
ASSERT_NE(node2, nullptr);
ASSERT_EQ(node2->GetLanguage(), "de");
}
}
TEST_F(AXLanguageDetectionTestDynamicContent, ChainOfNewNodes) {
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);
tree.language_detection_manager->DetectLanguages();
tree.language_detection_manager->LabelLanguages();
tree.language_detection_manager->RegisterLanguageDetectionObserver();
ASSERT_NE(getObserver(tree), nullptr);
ASSERT_TRUE(tree.HasObserver(getObserver(tree)));
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);
}
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);
annotation =
tree.language_detection_manager->GetLanguageAnnotationForStringAttribute(
*item, ax::mojom::StringAttribute::kMathContent);
ASSERT_EQ(0, (int)annotation.size());
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);
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));
}
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));
}
}