1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ui/accessibility/ax_event_generator.h"
6 
7 #include "testing/gmock/include/gmock/gmock-matchers.h"
8 #include "testing/gmock/include/gmock/gmock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "ui/accessibility/ax_enums.mojom.h"
11 #include "ui/accessibility/ax_node.h"
12 #include "ui/accessibility/ax_serializable_tree.h"
13 #include "ui/accessibility/ax_tree_serializer.h"
14 
15 namespace ui {
16 
17 // Required by gmock to print TargetedEvent in a human-readable way.
PrintTo(const AXEventGenerator::TargetedEvent & event,std::ostream * os)18 void PrintTo(const AXEventGenerator::TargetedEvent& event, std::ostream* os) {
19   *os << event.event_params.event << " on " << event.node->id();
20 }
21 
22 namespace {
23 
24 using testing::Matches;
25 using testing::PrintToString;
26 using testing::UnorderedElementsAre;
27 
28 // TODO(gilmanmh): Improve printing of test failure messages when the expected
29 // values are themselves matchers (e.g. Not(3)).
30 MATCHER_P2(HasEventAtNode,
31            expected_event_type,
32            expected_node_id,
33            std::string(negation ? "does not have" : "has") + " " +
34                PrintToString(expected_event_type) + " on " +
35                PrintToString(expected_node_id)) {
36   const auto& event = arg;
37   return Matches(expected_event_type)(event.event_params.event) &&
38          Matches(expected_node_id)(event.node->id());
39 }
40 
41 }  // namespace
42 
TEST(AXEventGeneratorTest,LoadCompleteSameTree)43 TEST(AXEventGeneratorTest, LoadCompleteSameTree) {
44   AXTreeUpdate initial_state;
45   initial_state.root_id = 1;
46   initial_state.nodes.resize(1);
47   initial_state.nodes[0].id = 1;
48   initial_state.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
49   initial_state.has_tree_data = true;
50   AXTree tree(initial_state);
51 
52   AXEventGenerator event_generator(&tree);
53   AXTreeUpdate load_complete_update = initial_state;
54   load_complete_update.tree_data.loaded = true;
55 
56   ASSERT_TRUE(tree.Unserialize(load_complete_update));
57   EXPECT_THAT(event_generator, UnorderedElementsAre(HasEventAtNode(
58                                    AXEventGenerator::Event::LOAD_COMPLETE, 1)));
59 }
60 
TEST(AXEventGeneratorTest,LoadCompleteNewTree)61 TEST(AXEventGeneratorTest, LoadCompleteNewTree) {
62   AXTreeUpdate initial_state;
63   initial_state.root_id = 1;
64   initial_state.nodes.resize(1);
65   initial_state.nodes[0].id = 1;
66   initial_state.has_tree_data = true;
67   initial_state.tree_data.loaded = true;
68   AXTree tree(initial_state);
69 
70   AXEventGenerator event_generator(&tree);
71   AXTreeUpdate load_complete_update;
72   load_complete_update.root_id = 2;
73   load_complete_update.nodes.resize(1);
74   load_complete_update.nodes[0].id = 2;
75   load_complete_update.nodes[0].relative_bounds.bounds =
76       gfx::RectF(0, 0, 800, 600);
77   load_complete_update.has_tree_data = true;
78   load_complete_update.tree_data.loaded = true;
79 
80   ASSERT_TRUE(tree.Unserialize(load_complete_update));
81   EXPECT_THAT(event_generator,
82               UnorderedElementsAre(
83                   HasEventAtNode(AXEventGenerator::Event::LOAD_COMPLETE, 2),
84                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 2)));
85 
86   // Load complete should not be emitted for sizeless roots.
87   load_complete_update.root_id = 3;
88   load_complete_update.nodes.resize(1);
89   load_complete_update.nodes[0].id = 3;
90   load_complete_update.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 0, 0);
91   load_complete_update.has_tree_data = true;
92   load_complete_update.tree_data.loaded = true;
93 
94   ASSERT_TRUE(tree.Unserialize(load_complete_update));
95   EXPECT_THAT(event_generator,
96               UnorderedElementsAre(
97                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 3)));
98 
99   // TODO(accessibility): http://crbug.com/888758
100   // Load complete should not be emitted for chrome-search URLs.
101   load_complete_update.root_id = 4;
102   load_complete_update.nodes.resize(1);
103   load_complete_update.nodes[0].id = 4;
104   load_complete_update.nodes[0].relative_bounds.bounds =
105       gfx::RectF(0, 0, 800, 600);
106   load_complete_update.nodes[0].AddStringAttribute(
107       ax::mojom::StringAttribute::kUrl, "chrome-search://foo");
108   load_complete_update.has_tree_data = true;
109   load_complete_update.tree_data.loaded = true;
110 
111   ASSERT_TRUE(tree.Unserialize(load_complete_update));
112   EXPECT_THAT(event_generator,
113               UnorderedElementsAre(
114                   HasEventAtNode(AXEventGenerator::Event::LOAD_COMPLETE, 4),
115                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 4)));
116 }
117 
TEST(AXEventGeneratorTest,LoadStart)118 TEST(AXEventGeneratorTest, LoadStart) {
119   AXTreeUpdate initial_state;
120   initial_state.root_id = 1;
121   initial_state.nodes.resize(1);
122   initial_state.nodes[0].id = 1;
123   initial_state.nodes[0].relative_bounds.bounds = gfx::RectF(0, 0, 800, 600);
124   initial_state.has_tree_data = true;
125   AXTree tree(initial_state);
126 
127   AXEventGenerator event_generator(&tree);
128   AXTreeUpdate load_start_update;
129   load_start_update.root_id = 2;
130   load_start_update.nodes.resize(1);
131   load_start_update.nodes[0].id = 2;
132   load_start_update.nodes[0].relative_bounds.bounds =
133       gfx::RectF(0, 0, 800, 600);
134   load_start_update.has_tree_data = true;
135   load_start_update.tree_data.loaded = false;
136 
137   ASSERT_TRUE(tree.Unserialize(load_start_update));
138   EXPECT_THAT(event_generator,
139               UnorderedElementsAre(
140                   HasEventAtNode(AXEventGenerator::Event::LOAD_START, 2),
141                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 2)));
142 }
143 
TEST(AXEventGeneratorTest,DocumentSelectionChanged)144 TEST(AXEventGeneratorTest, DocumentSelectionChanged) {
145   AXTreeUpdate initial_state;
146   initial_state.root_id = 1;
147   initial_state.nodes.resize(1);
148   initial_state.nodes[0].id = 1;
149   initial_state.has_tree_data = true;
150   initial_state.tree_data.sel_focus_object_id = 1;
151   initial_state.tree_data.sel_focus_offset = 1;
152   AXTree tree(initial_state);
153 
154   AXEventGenerator event_generator(&tree);
155   AXTreeUpdate update = initial_state;
156   update.tree_data.sel_focus_offset = 2;
157 
158   ASSERT_TRUE(tree.Unserialize(update));
159   EXPECT_THAT(event_generator,
160               UnorderedElementsAre(HasEventAtNode(
161                   AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED, 1)));
162 }
163 
TEST(AXEventGeneratorTest,DocumentTitleChanged)164 TEST(AXEventGeneratorTest, DocumentTitleChanged) {
165   AXTreeUpdate initial_state;
166   initial_state.root_id = 1;
167   initial_state.nodes.resize(1);
168   initial_state.nodes[0].id = 1;
169   initial_state.has_tree_data = true;
170   initial_state.tree_data.title = "Before";
171   AXTree tree(initial_state);
172 
173   AXEventGenerator event_generator(&tree);
174   AXTreeUpdate update = initial_state;
175   update.tree_data.title = "After";
176 
177   ASSERT_TRUE(tree.Unserialize(update));
178   EXPECT_THAT(event_generator,
179               UnorderedElementsAre(HasEventAtNode(
180                   AXEventGenerator::Event::DOCUMENT_TITLE_CHANGED, 1)));
181 }
182 
TEST(AXEventGeneratorTest,ExpandedAndRowCount)183 TEST(AXEventGeneratorTest, ExpandedAndRowCount) {
184   AXTreeUpdate initial_state;
185   initial_state.root_id = 1;
186   initial_state.nodes.resize(4);
187   initial_state.nodes[0].id = 1;
188   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
189   initial_state.nodes[0].child_ids.push_back(2);
190   initial_state.nodes[0].child_ids.push_back(4);
191   initial_state.nodes[1].id = 2;
192   initial_state.nodes[1].role = ax::mojom::Role::kTable;
193   initial_state.nodes[1].child_ids.push_back(3);
194   initial_state.nodes[2].id = 3;
195   initial_state.nodes[2].role = ax::mojom::Role::kRow;
196   initial_state.nodes[3].id = 4;
197   initial_state.nodes[3].role = ax::mojom::Role::kPopUpButton;
198   initial_state.nodes[3].AddState(ax::mojom::State::kExpanded);
199   AXTree tree(initial_state);
200 
201   AXEventGenerator event_generator(&tree);
202   AXTreeUpdate update = initial_state;
203   update.nodes[2].AddState(ax::mojom::State::kExpanded);
204   update.nodes[3].state = 0;
205 
206   ASSERT_TRUE(tree.Unserialize(update));
207   EXPECT_THAT(event_generator,
208               UnorderedElementsAre(
209                   HasEventAtNode(AXEventGenerator::Event::COLLAPSED, 4),
210                   HasEventAtNode(AXEventGenerator::Event::EXPANDED, 3),
211                   HasEventAtNode(AXEventGenerator::Event::ROW_COUNT_CHANGED, 2),
212                   HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 3),
213                   HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 4)));
214 }
215 
TEST(AXEventGeneratorTest,SelectedAndSelectedChildren)216 TEST(AXEventGeneratorTest, SelectedAndSelectedChildren) {
217   AXTreeUpdate initial_state;
218   initial_state.root_id = 1;
219   initial_state.nodes.resize(4);
220   initial_state.nodes[0].id = 1;
221   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
222   initial_state.nodes[0].child_ids.push_back(2);
223   initial_state.nodes[0].child_ids.push_back(4);
224   initial_state.nodes[1].id = 2;
225   initial_state.nodes[1].role = ax::mojom::Role::kMenu;
226   initial_state.nodes[1].child_ids.push_back(3);
227   initial_state.nodes[2].id = 3;
228   initial_state.nodes[2].role = ax::mojom::Role::kMenuItem;
229   initial_state.nodes[3].id = 4;
230   initial_state.nodes[3].role = ax::mojom::Role::kListBoxOption;
231   initial_state.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected,
232                                           true);
233   AXTree tree(initial_state);
234 
235   AXEventGenerator event_generator(&tree);
236   AXTreeUpdate update = initial_state;
237   update.nodes[2].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);
238   update.nodes.pop_back();
239   update.nodes.emplace_back();
240   update.nodes[3].id = 4;
241   update.nodes[3].role = ax::mojom::Role::kListBoxOption;
242   update.nodes[3].AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false);
243 
244   ASSERT_TRUE(tree.Unserialize(update));
245   EXPECT_THAT(
246       event_generator,
247       UnorderedElementsAre(
248           HasEventAtNode(AXEventGenerator::Event::SELECTED_CHILDREN_CHANGED, 2),
249           HasEventAtNode(AXEventGenerator::Event::SELECTED_CHANGED, 3),
250           HasEventAtNode(AXEventGenerator::Event::SELECTED_CHANGED, 4)));
251 }
252 
TEST(AXEventGeneratorTest,StringValueChanged)253 TEST(AXEventGeneratorTest, StringValueChanged) {
254   AXTreeUpdate initial_state;
255   initial_state.root_id = 1;
256   initial_state.nodes.resize(1);
257   initial_state.nodes[0].id = 1;
258   initial_state.nodes[0].role = ax::mojom::Role::kTextField;
259   initial_state.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kValue,
260                                             "Before");
261   AXTree tree(initial_state);
262 
263   AXEventGenerator event_generator(&tree);
264   AXTreeUpdate update = initial_state;
265   update.nodes[0].string_attributes.clear();
266   update.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kValue,
267                                      "After");
268 
269   ASSERT_TRUE(tree.Unserialize(update));
270   EXPECT_THAT(event_generator, UnorderedElementsAre(HasEventAtNode(
271                                    AXEventGenerator::Event::VALUE_CHANGED, 1)));
272 }
273 
TEST(AXEventGeneratorTest,FloatValueChanged)274 TEST(AXEventGeneratorTest, FloatValueChanged) {
275   AXTreeUpdate initial_state;
276   initial_state.root_id = 1;
277   initial_state.nodes.resize(1);
278   initial_state.nodes[0].id = 1;
279   initial_state.nodes[0].role = ax::mojom::Role::kSlider;
280   initial_state.nodes[0].AddFloatAttribute(
281       ax::mojom::FloatAttribute::kValueForRange, 1.0);
282   AXTree tree(initial_state);
283 
284   AXEventGenerator event_generator(&tree);
285   AXTreeUpdate update = initial_state;
286   update.nodes[0].float_attributes.clear();
287   update.nodes[0].AddFloatAttribute(ax::mojom::FloatAttribute::kValueForRange,
288                                     2.0);
289 
290   ASSERT_TRUE(tree.Unserialize(update));
291   EXPECT_THAT(event_generator, UnorderedElementsAre(HasEventAtNode(
292                                    AXEventGenerator::Event::VALUE_CHANGED, 1)));
293 }
294 
TEST(AXEventGeneratorTest,InvalidStatusChanged)295 TEST(AXEventGeneratorTest, InvalidStatusChanged) {
296   AXTreeUpdate initial_state;
297   initial_state.root_id = 1;
298   initial_state.nodes.resize(1);
299   initial_state.nodes[0].id = 1;
300   initial_state.nodes[0].role = ax::mojom::Role::kTextField;
301   initial_state.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kValue,
302                                             "Text");
303   AXTree tree(initial_state);
304 
305   AXEventGenerator event_generator(&tree);
306   AXTreeUpdate update = initial_state;
307   update.nodes[0].SetInvalidState(ax::mojom::InvalidState::kTrue);
308   ASSERT_TRUE(tree.Unserialize(update));
309   EXPECT_THAT(event_generator,
310               UnorderedElementsAre(HasEventAtNode(
311                   AXEventGenerator::Event::INVALID_STATUS_CHANGED, 1)));
312 }
313 
TEST(AXEventGeneratorTest,AddLiveRegionAttribute)314 TEST(AXEventGeneratorTest, AddLiveRegionAttribute) {
315   AXTreeUpdate initial_state;
316   initial_state.root_id = 1;
317   initial_state.nodes.resize(1);
318   initial_state.nodes[0].id = 1;
319   AXTree tree(initial_state);
320 
321   AXEventGenerator event_generator(&tree);
322   AXTreeUpdate update = initial_state;
323   update.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kLiveStatus,
324                                      "polite");
325   ASSERT_TRUE(tree.Unserialize(update));
326   EXPECT_THAT(
327       event_generator,
328       UnorderedElementsAre(
329           HasEventAtNode(AXEventGenerator::Event::LIVE_STATUS_CHANGED, 1),
330           HasEventAtNode(AXEventGenerator::Event::LIVE_REGION_CREATED, 1)));
331 
332   event_generator.ClearEvents();
333   update.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kLiveStatus,
334                                      "assertive");
335   ASSERT_TRUE(tree.Unserialize(update));
336   EXPECT_THAT(event_generator,
337               UnorderedElementsAre(HasEventAtNode(
338                   AXEventGenerator::Event::LIVE_STATUS_CHANGED, 1)));
339 
340   event_generator.ClearEvents();
341   update.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kLiveStatus,
342                                      "off");
343 
344   ASSERT_TRUE(tree.Unserialize(update));
345   EXPECT_THAT(event_generator,
346               UnorderedElementsAre(HasEventAtNode(
347                   AXEventGenerator::Event::LIVE_STATUS_CHANGED, 1)));
348 }
349 
TEST(AXEventGeneratorTest,CheckedStateChanged)350 TEST(AXEventGeneratorTest, CheckedStateChanged) {
351   AXTreeUpdate initial_state;
352   initial_state.root_id = 1;
353   initial_state.nodes.resize(1);
354   initial_state.nodes[0].id = 1;
355   initial_state.nodes[0].role = ax::mojom::Role::kCheckBox;
356   AXTree tree(initial_state);
357 
358   AXEventGenerator event_generator(&tree);
359   AXTreeUpdate update = initial_state;
360   update.nodes[0].SetCheckedState(ax::mojom::CheckedState::kTrue);
361   ASSERT_TRUE(tree.Unserialize(update));
362   EXPECT_THAT(event_generator,
363               UnorderedElementsAre(HasEventAtNode(
364                   AXEventGenerator::Event::CHECKED_STATE_CHANGED, 1)));
365 }
366 
TEST(AXEventGeneratorTest,ActiveDescendantChanged)367 TEST(AXEventGeneratorTest, ActiveDescendantChanged) {
368   AXTreeUpdate initial_state;
369   initial_state.root_id = 1;
370   initial_state.nodes.resize(3);
371   initial_state.nodes[0].id = 1;
372   initial_state.nodes[0].role = ax::mojom::Role::kListBox;
373   initial_state.nodes[0].child_ids.push_back(2);
374   initial_state.nodes[0].child_ids.push_back(3);
375   initial_state.nodes[0].AddIntAttribute(
376       ax::mojom::IntAttribute::kActivedescendantId, 2);
377   initial_state.nodes[1].id = 2;
378   initial_state.nodes[1].role = ax::mojom::Role::kListBoxOption;
379   initial_state.nodes[2].id = 3;
380   initial_state.nodes[2].role = ax::mojom::Role::kListBoxOption;
381   AXTree tree(initial_state);
382 
383   AXEventGenerator event_generator(&tree);
384   AXTreeUpdate update = initial_state;
385   update.nodes[0].int_attributes.clear();
386   update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
387                                   3);
388   ASSERT_TRUE(tree.Unserialize(update));
389   EXPECT_THAT(
390       event_generator,
391       UnorderedElementsAre(
392           HasEventAtNode(AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED, 1),
393           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 1)));
394 }
395 
TEST(AXEventGeneratorTest,CreateAlertAndLiveRegion)396 TEST(AXEventGeneratorTest, CreateAlertAndLiveRegion) {
397   AXTreeUpdate initial_state;
398   initial_state.root_id = 1;
399   initial_state.nodes.resize(1);
400   initial_state.nodes[0].id = 1;
401   AXTree tree(initial_state);
402 
403   AXEventGenerator event_generator(&tree);
404   AXTreeUpdate update = initial_state;
405   update.nodes.resize(4);
406   update.nodes[0].child_ids.push_back(2);
407   update.nodes[0].child_ids.push_back(3);
408   update.nodes[0].child_ids.push_back(4);
409 
410   update.nodes[1].id = 2;
411   update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kLiveStatus,
412                                      "polite");
413 
414   // Blink should automatically add aria-live="assertive" to elements with role
415   // kAlert, but we should fire an alert event regardless.
416   update.nodes[2].id = 3;
417   update.nodes[2].role = ax::mojom::Role::kAlert;
418 
419   // Elements with role kAlertDialog will *not* usually have a live region
420   // status, but again, we should always fire an alert event.
421   update.nodes[3].id = 4;
422   update.nodes[3].role = ax::mojom::Role::kAlertDialog;
423 
424   ASSERT_TRUE(tree.Unserialize(update));
425   EXPECT_THAT(
426       event_generator,
427       UnorderedElementsAre(
428           HasEventAtNode(AXEventGenerator::Event::ALERT, 3),
429           HasEventAtNode(AXEventGenerator::Event::ALERT, 4),
430           HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 1),
431           HasEventAtNode(AXEventGenerator::Event::LIVE_REGION_CREATED, 2),
432           HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 2),
433           HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 3),
434           HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 4)));
435 }
436 
TEST(AXEventGeneratorTest,LiveRegionChanged)437 TEST(AXEventGeneratorTest, LiveRegionChanged) {
438   AXTreeUpdate initial_state;
439   initial_state.root_id = 1;
440   initial_state.nodes.resize(3);
441   initial_state.nodes[0].id = 1;
442   initial_state.nodes[0].AddStringAttribute(
443       ax::mojom::StringAttribute::kLiveStatus, "polite");
444   initial_state.nodes[0].AddStringAttribute(
445       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
446   initial_state.nodes[0].child_ids.push_back(2);
447   initial_state.nodes[0].child_ids.push_back(3);
448   initial_state.nodes[1].id = 2;
449   initial_state.nodes[1].role = ax::mojom::Role::kStaticText;
450   initial_state.nodes[1].AddStringAttribute(
451       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
452   initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
453                                             "Before 1");
454   initial_state.nodes[2].id = 3;
455   initial_state.nodes[2].role = ax::mojom::Role::kStaticText;
456   initial_state.nodes[2].AddStringAttribute(
457       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
458   initial_state.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
459                                             "Before 2");
460   AXTree tree(initial_state);
461 
462   AXEventGenerator event_generator(&tree);
463   AXTreeUpdate update = initial_state;
464   update.nodes[1].string_attributes.clear();
465   update.nodes[1].AddStringAttribute(
466       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
467   update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
468                                      "After 1");
469   update.nodes[2].string_attributes.clear();
470   update.nodes[2].AddStringAttribute(
471       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
472   update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
473                                      "After 2");
474 
475   ASSERT_TRUE(tree.Unserialize(update));
476   EXPECT_THAT(
477       event_generator,
478       UnorderedElementsAre(
479           HasEventAtNode(AXEventGenerator::Event::LIVE_REGION_CHANGED, 1),
480           HasEventAtNode(AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED, 2),
481           HasEventAtNode(AXEventGenerator::Event::LIVE_REGION_NODE_CHANGED, 3),
482           HasEventAtNode(AXEventGenerator::Event::NAME_CHANGED, 2),
483           HasEventAtNode(AXEventGenerator::Event::NAME_CHANGED, 3)));
484 }
485 
TEST(AXEventGeneratorTest,LiveRegionOnlyTextChanges)486 TEST(AXEventGeneratorTest, LiveRegionOnlyTextChanges) {
487   AXTreeUpdate initial_state;
488   initial_state.root_id = 1;
489   initial_state.nodes.resize(3);
490   initial_state.nodes[0].id = 1;
491   initial_state.nodes[0].AddStringAttribute(
492       ax::mojom::StringAttribute::kLiveStatus, "polite");
493   initial_state.nodes[0].AddStringAttribute(
494       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
495   initial_state.nodes[0].child_ids.push_back(2);
496   initial_state.nodes[0].child_ids.push_back(3);
497   initial_state.nodes[1].id = 2;
498   initial_state.nodes[1].role = ax::mojom::Role::kStaticText;
499   initial_state.nodes[1].AddStringAttribute(
500       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
501   initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
502                                             "Before 1");
503   initial_state.nodes[2].id = 3;
504   initial_state.nodes[2].role = ax::mojom::Role::kStaticText;
505   initial_state.nodes[2].AddStringAttribute(
506       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
507   initial_state.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
508                                             "Before 2");
509   AXTree tree(initial_state);
510 
511   AXEventGenerator event_generator(&tree);
512   AXTreeUpdate update = initial_state;
513   update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kDescription,
514                                      "Description 1");
515   update.nodes[2].SetCheckedState(ax::mojom::CheckedState::kTrue);
516 
517   // Note that we do NOT expect a LIVE_REGION_CHANGED event here, because
518   // the name did not change.
519   ASSERT_TRUE(tree.Unserialize(update));
520   EXPECT_THAT(
521       event_generator,
522       UnorderedElementsAre(
523           HasEventAtNode(AXEventGenerator::Event::CHECKED_STATE_CHANGED, 3),
524           HasEventAtNode(AXEventGenerator::Event::DESCRIPTION_CHANGED, 2)));
525 }
526 
TEST(AXEventGeneratorTest,BusyLiveRegionChanged)527 TEST(AXEventGeneratorTest, BusyLiveRegionChanged) {
528   AXTreeUpdate initial_state;
529   initial_state.root_id = 1;
530   initial_state.nodes.resize(3);
531   initial_state.nodes[0].id = 1;
532   initial_state.nodes[0].AddStringAttribute(
533       ax::mojom::StringAttribute::kLiveStatus, "polite");
534   initial_state.nodes[0].AddStringAttribute(
535       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
536   initial_state.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kBusy,
537                                           true);
538   initial_state.nodes[0].child_ids.push_back(2);
539   initial_state.nodes[0].child_ids.push_back(3);
540   initial_state.nodes[1].id = 2;
541   initial_state.nodes[1].role = ax::mojom::Role::kStaticText;
542   initial_state.nodes[1].AddStringAttribute(
543       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
544   initial_state.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
545                                             "Before 1");
546   initial_state.nodes[2].id = 3;
547   initial_state.nodes[2].role = ax::mojom::Role::kStaticText;
548   initial_state.nodes[2].AddStringAttribute(
549       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
550   initial_state.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
551                                             "Before 2");
552   AXTree tree(initial_state);
553 
554   AXEventGenerator event_generator(&tree);
555   AXTreeUpdate update = initial_state;
556   update.nodes[1].string_attributes.clear();
557   update.nodes[1].AddStringAttribute(
558       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
559   update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
560                                      "After 1");
561   update.nodes[2].string_attributes.clear();
562   update.nodes[2].AddStringAttribute(
563       ax::mojom::StringAttribute::kContainerLiveStatus, "polite");
564   update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kName,
565                                      "After 2");
566 
567   ASSERT_TRUE(tree.Unserialize(update));
568   EXPECT_THAT(event_generator,
569               UnorderedElementsAre(
570                   HasEventAtNode(AXEventGenerator::Event::NAME_CHANGED, 2),
571                   HasEventAtNode(AXEventGenerator::Event::NAME_CHANGED, 3)));
572 }
573 
TEST(AXEventGeneratorTest,AddChild)574 TEST(AXEventGeneratorTest, AddChild) {
575   AXTreeUpdate initial_state;
576   initial_state.root_id = 1;
577   initial_state.nodes.resize(2);
578   initial_state.nodes[0].id = 1;
579   initial_state.nodes[0].child_ids.push_back(2);
580   initial_state.nodes[1].id = 2;
581   AXTree tree(initial_state);
582 
583   AXEventGenerator event_generator(&tree);
584   AXTreeUpdate update = initial_state;
585   update.nodes.resize(3);
586   update.nodes[0].child_ids.push_back(3);
587   update.nodes[2].id = 3;
588 
589   ASSERT_TRUE(tree.Unserialize(update));
590   EXPECT_THAT(event_generator,
591               UnorderedElementsAre(
592                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 1),
593                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 3)));
594 }
595 
TEST(AXEventGeneratorTest,RemoveChild)596 TEST(AXEventGeneratorTest, RemoveChild) {
597   AXTreeUpdate initial_state;
598   initial_state.root_id = 1;
599   initial_state.nodes.resize(3);
600   initial_state.nodes[0].id = 1;
601   initial_state.nodes[0].child_ids.push_back(2);
602   initial_state.nodes[0].child_ids.push_back(3);
603   initial_state.nodes[1].id = 2;
604   initial_state.nodes[2].id = 3;
605   AXTree tree(initial_state);
606 
607   AXEventGenerator event_generator(&tree);
608   AXTreeUpdate update = initial_state;
609   update.nodes.resize(2);
610   update.nodes[0].child_ids.clear();
611   update.nodes[0].child_ids.push_back(2);
612 
613   ASSERT_TRUE(tree.Unserialize(update));
614   EXPECT_THAT(event_generator,
615               UnorderedElementsAre(HasEventAtNode(
616                   AXEventGenerator::Event::CHILDREN_CHANGED, 1)));
617 }
618 
TEST(AXEventGeneratorTest,ReorderChildren)619 TEST(AXEventGeneratorTest, ReorderChildren) {
620   AXTreeUpdate initial_state;
621   initial_state.root_id = 1;
622   initial_state.nodes.resize(3);
623   initial_state.nodes[0].id = 1;
624   initial_state.nodes[0].child_ids.push_back(2);
625   initial_state.nodes[0].child_ids.push_back(3);
626   initial_state.nodes[1].id = 2;
627   initial_state.nodes[2].id = 3;
628   AXTree tree(initial_state);
629 
630   AXEventGenerator event_generator(&tree);
631   AXTreeUpdate update = initial_state;
632   update.nodes[0].child_ids.clear();
633   update.nodes[0].child_ids.push_back(3);
634   update.nodes[0].child_ids.push_back(2);
635 
636   ASSERT_TRUE(tree.Unserialize(update));
637   EXPECT_THAT(event_generator,
638               UnorderedElementsAre(HasEventAtNode(
639                   AXEventGenerator::Event::CHILDREN_CHANGED, 1)));
640 }
641 
TEST(AXEventGeneratorTest,ScrollHorizontalPositionChanged)642 TEST(AXEventGeneratorTest, ScrollHorizontalPositionChanged) {
643   AXTreeUpdate initial_state;
644   initial_state.root_id = 1;
645   initial_state.nodes.resize(1);
646   initial_state.nodes[0].id = 1;
647   AXTree tree(initial_state);
648 
649   AXEventGenerator event_generator(&tree);
650   AXTreeUpdate update = initial_state;
651   update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollX, 10);
652   EXPECT_TRUE(tree.Unserialize(update));
653   EXPECT_THAT(
654       event_generator,
655       UnorderedElementsAre(HasEventAtNode(
656           AXEventGenerator::Event::SCROLL_HORIZONTAL_POSITION_CHANGED, 1)));
657 }
658 
TEST(AXEventGeneratorTest,ScrollVerticalPositionChanged)659 TEST(AXEventGeneratorTest, ScrollVerticalPositionChanged) {
660   AXTreeUpdate initial_state;
661   initial_state.root_id = 1;
662   initial_state.nodes.resize(1);
663   initial_state.nodes[0].id = 1;
664   AXTree tree(initial_state);
665 
666   AXEventGenerator event_generator(&tree);
667   AXTreeUpdate update = initial_state;
668   update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kScrollY, 10);
669   ASSERT_TRUE(tree.Unserialize(update));
670   EXPECT_THAT(
671       event_generator,
672       UnorderedElementsAre(HasEventAtNode(
673           AXEventGenerator::Event::SCROLL_VERTICAL_POSITION_CHANGED, 1)));
674 }
675 
TEST(AXEventGeneratorTest,OtherAttributeChanged)676 TEST(AXEventGeneratorTest, OtherAttributeChanged) {
677   AXTreeUpdate initial_state;
678   initial_state.root_id = 1;
679   initial_state.nodes.resize(6);
680   initial_state.nodes[0].id = 1;
681   initial_state.nodes[0].child_ids.push_back(2);
682   initial_state.nodes[0].child_ids.push_back(3);
683   initial_state.nodes[0].child_ids.push_back(4);
684   initial_state.nodes[0].child_ids.push_back(5);
685   initial_state.nodes[0].child_ids.push_back(6);
686   initial_state.nodes[1].id = 2;
687   initial_state.nodes[2].id = 3;
688   initial_state.nodes[3].id = 4;
689   initial_state.nodes[4].id = 5;
690   initial_state.nodes[5].id = 6;
691   AXTree tree(initial_state);
692 
693   AXEventGenerator event_generator(&tree);
694   AXTreeUpdate update = initial_state;
695   update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kLanguage,
696                                      "de");
697   update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kAriaCellColumnIndex,
698                                   7);
699   update.nodes[3].AddFloatAttribute(ax::mojom::FloatAttribute::kFontSize,
700                                     12.0f);
701   update.nodes[4].AddBoolAttribute(ax::mojom::BoolAttribute::kModal, true);
702   std::vector<int> ids = {2};
703   update.nodes[5].AddIntListAttribute(ax::mojom::IntListAttribute::kControlsIds,
704                                       ids);
705   ASSERT_TRUE(tree.Unserialize(update));
706   EXPECT_THAT(
707       event_generator,
708       UnorderedElementsAre(
709           HasEventAtNode(AXEventGenerator::Event::CONTROLS_CHANGED, 6),
710           HasEventAtNode(AXEventGenerator::Event::LANGUAGE_CHANGED, 2),
711           HasEventAtNode(AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED, 3),
712           HasEventAtNode(AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED, 4),
713           HasEventAtNode(AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED, 5),
714           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 6)));
715 }
716 
TEST(AXEventGeneratorTest,NameChanged)717 TEST(AXEventGeneratorTest, NameChanged) {
718   AXTreeUpdate initial_state;
719   initial_state.root_id = 1;
720   initial_state.nodes.resize(2);
721   initial_state.nodes[0].id = 1;
722   initial_state.nodes[0].child_ids.push_back(2);
723   initial_state.nodes[1].id = 2;
724   AXTree tree(initial_state);
725 
726   AXEventGenerator event_generator(&tree);
727   AXTreeUpdate update = initial_state;
728   update.nodes[1].AddStringAttribute(ax::mojom::StringAttribute::kName,
729                                      "Hello");
730   ASSERT_TRUE(tree.Unserialize(update));
731   EXPECT_THAT(event_generator, UnorderedElementsAre(HasEventAtNode(
732                                    AXEventGenerator::Event::NAME_CHANGED, 2)));
733 }
734 
TEST(AXEventGeneratorTest,DescriptionChanged)735 TEST(AXEventGeneratorTest, DescriptionChanged) {
736   AXTreeUpdate initial_state;
737   initial_state.root_id = 1;
738   initial_state.nodes.resize(1);
739   initial_state.nodes[0].id = 1;
740   AXTree tree(initial_state);
741 
742   AXEventGenerator event_generator(&tree);
743   AXTreeUpdate update = initial_state;
744   update.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kDescription,
745                                      "Hello");
746   ASSERT_TRUE(tree.Unserialize(update));
747   EXPECT_THAT(event_generator,
748               UnorderedElementsAre(HasEventAtNode(
749                   AXEventGenerator::Event::DESCRIPTION_CHANGED, 1)));
750 }
751 
TEST(AXEventGeneratorTest,RoleChanged)752 TEST(AXEventGeneratorTest, RoleChanged) {
753   AXTreeUpdate initial_state;
754   initial_state.root_id = 1;
755   initial_state.nodes.resize(1);
756   initial_state.nodes[0].id = 1;
757   AXTree tree(initial_state);
758 
759   AXEventGenerator event_generator(&tree);
760   AXTreeUpdate update = initial_state;
761   update.nodes[0].role = ax::mojom::Role::kCheckBox;
762   ASSERT_TRUE(tree.Unserialize(update));
763   EXPECT_THAT(event_generator, UnorderedElementsAre(HasEventAtNode(
764                                    AXEventGenerator::Event::ROLE_CHANGED, 1)));
765 }
766 
TEST(AXEventGeneratorTest,MenuItemSelected)767 TEST(AXEventGeneratorTest, MenuItemSelected) {
768   AXTreeUpdate initial_state;
769   initial_state.root_id = 1;
770   initial_state.nodes.resize(3);
771   initial_state.nodes[0].id = 1;
772   initial_state.nodes[0].role = ax::mojom::Role::kMenu;
773   initial_state.nodes[0].child_ids.push_back(2);
774   initial_state.nodes[0].child_ids.push_back(3);
775   initial_state.nodes[0].AddIntAttribute(
776       ax::mojom::IntAttribute::kActivedescendantId, 2);
777   initial_state.nodes[1].id = 2;
778   initial_state.nodes[1].role = ax::mojom::Role::kMenuListOption;
779   initial_state.nodes[2].id = 3;
780   initial_state.nodes[2].role = ax::mojom::Role::kMenuListOption;
781   AXTree tree(initial_state);
782 
783   AXEventGenerator event_generator(&tree);
784   AXTreeUpdate update = initial_state;
785   update.nodes[0].int_attributes.clear();
786   update.nodes[0].AddIntAttribute(ax::mojom::IntAttribute::kActivedescendantId,
787                                   3);
788   ASSERT_TRUE(tree.Unserialize(update));
789   EXPECT_THAT(
790       event_generator,
791       UnorderedElementsAre(
792           HasEventAtNode(AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED, 1),
793           HasEventAtNode(AXEventGenerator::Event::MENU_ITEM_SELECTED, 3),
794           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 1)));
795 }
796 
TEST(AXEventGeneratorTest,NodeBecomesIgnored)797 TEST(AXEventGeneratorTest, NodeBecomesIgnored) {
798   AXTreeUpdate initial_state;
799   initial_state.root_id = 1;
800   initial_state.nodes.resize(5);
801   initial_state.nodes[0].id = 1;
802   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
803   initial_state.nodes[0].child_ids.push_back(2);
804   initial_state.nodes[1].id = 2;
805   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
806   initial_state.nodes[1].child_ids.push_back(3);
807   initial_state.nodes[2].id = 3;
808   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
809   initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
810   initial_state.nodes[2].child_ids.push_back(4);
811   initial_state.nodes[3].id = 4;
812   initial_state.nodes[3].role = ax::mojom::Role::kGroup;
813   initial_state.nodes[3].child_ids.push_back(5);
814   initial_state.nodes[4].id = 5;
815   initial_state.nodes[4].role = ax::mojom::Role::kStaticText;
816 
817   AXTree tree(initial_state);
818 
819   AXEventGenerator event_generator(&tree);
820   AXTreeUpdate update = initial_state;
821   update.nodes[3].AddState(ax::mojom::State::kIgnored);
822   ASSERT_TRUE(tree.Unserialize(update));
823   EXPECT_THAT(event_generator,
824               UnorderedElementsAre(
825                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 2),
826                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 4)));
827 }
828 
TEST(AXEventGeneratorTest,NodeBecomesIgnored2)829 TEST(AXEventGeneratorTest, NodeBecomesIgnored2) {
830   AXTreeUpdate initial_state;
831   initial_state.root_id = 1;
832   initial_state.nodes.resize(5);
833   initial_state.nodes[0].id = 1;
834   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
835   initial_state.nodes[0].child_ids.push_back(2);
836   initial_state.nodes[1].id = 2;
837   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
838   initial_state.nodes[1].child_ids.push_back(3);
839   initial_state.nodes[2].id = 3;
840   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
841   initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
842   initial_state.nodes[2].child_ids.push_back(4);
843   initial_state.nodes[3].id = 4;
844   initial_state.nodes[3].role = ax::mojom::Role::kGroup;
845   initial_state.nodes[3].child_ids.push_back(5);
846   initial_state.nodes[4].id = 5;
847   initial_state.nodes[4].role = ax::mojom::Role::kStaticText;
848 
849   AXTree tree(initial_state);
850 
851   AXEventGenerator event_generator(&tree);
852   AXTreeUpdate update = initial_state;
853   // Marking as ignored should fire CHILDREN_CHANGED on 2
854   update.nodes[3].AddState(ax::mojom::State::kIgnored);
855   // Remove node id 5 so it also fires CHILDREN_CHANGED on 4.
856   update.nodes.pop_back();
857   update.nodes[3].child_ids.clear();
858   ASSERT_TRUE(tree.Unserialize(update));
859   EXPECT_THAT(event_generator,
860               UnorderedElementsAre(
861                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 2),
862                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 4)));
863 }
864 
TEST(AXEventGeneratorTest,NodeBecomesUnignored)865 TEST(AXEventGeneratorTest, NodeBecomesUnignored) {
866   AXTreeUpdate initial_state;
867   initial_state.root_id = 1;
868   initial_state.nodes.resize(5);
869   initial_state.nodes[0].id = 1;
870   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
871   initial_state.nodes[0].child_ids.push_back(2);
872   initial_state.nodes[1].id = 2;
873   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
874   initial_state.nodes[1].child_ids.push_back(3);
875   initial_state.nodes[2].id = 3;
876   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
877   initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
878   initial_state.nodes[2].child_ids.push_back(4);
879   initial_state.nodes[3].id = 4;
880   initial_state.nodes[3].role = ax::mojom::Role::kGroup;
881   initial_state.nodes[3].AddState(ax::mojom::State::kIgnored);
882   initial_state.nodes[3].child_ids.push_back(5);
883   initial_state.nodes[4].id = 5;
884   initial_state.nodes[4].role = ax::mojom::Role::kStaticText;
885 
886   AXTree tree(initial_state);
887 
888   AXEventGenerator event_generator(&tree);
889   AXTreeUpdate update = initial_state;
890   update.nodes[3].state = 0;
891   ASSERT_TRUE(tree.Unserialize(update));
892   EXPECT_THAT(event_generator,
893               UnorderedElementsAre(
894                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 2),
895                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 4),
896                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 4)));
897 }
898 
TEST(AXEventGeneratorTest,NodeBecomesUnignored2)899 TEST(AXEventGeneratorTest, NodeBecomesUnignored2) {
900   AXTreeUpdate initial_state;
901   initial_state.root_id = 1;
902   initial_state.nodes.resize(5);
903   initial_state.nodes[0].id = 1;
904   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
905   initial_state.nodes[0].child_ids.push_back(2);
906   initial_state.nodes[1].id = 2;
907   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
908   initial_state.nodes[1].child_ids.push_back(3);
909   initial_state.nodes[2].id = 3;
910   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
911   initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
912   initial_state.nodes[2].child_ids.push_back(4);
913   initial_state.nodes[3].id = 4;
914   initial_state.nodes[3].role = ax::mojom::Role::kGroup;
915   initial_state.nodes[3].AddState(ax::mojom::State::kIgnored);
916   initial_state.nodes[3].child_ids.push_back(5);
917   initial_state.nodes[4].id = 5;
918   initial_state.nodes[4].role = ax::mojom::Role::kStaticText;
919 
920   AXTree tree(initial_state);
921 
922   AXEventGenerator event_generator(&tree);
923   AXTreeUpdate update = initial_state;
924   // Marking as no longer ignored should fire CHILDREN_CHANGED on 2
925   update.nodes[3].state = 0;
926   // Remove node id 5 so it also fires CHILDREN_CHANGED on 4.
927   update.nodes.pop_back();
928   update.nodes[3].child_ids.clear();
929   ASSERT_TRUE(tree.Unserialize(update));
930   EXPECT_THAT(event_generator,
931               UnorderedElementsAre(
932                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 2),
933                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 4),
934                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 4)));
935 }
936 
TEST(AXEventGeneratorTest,SubtreeBecomesUnignored)937 TEST(AXEventGeneratorTest, SubtreeBecomesUnignored) {
938   AXTreeUpdate initial_state;
939   initial_state.root_id = 1;
940   initial_state.nodes.resize(3);
941   initial_state.nodes[0].id = 1;
942   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
943   initial_state.nodes[0].child_ids.push_back(2);
944   initial_state.nodes[1].id = 2;
945   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
946   initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
947   initial_state.nodes[1].child_ids.push_back(3);
948   initial_state.nodes[2].id = 3;
949   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
950   initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
951 
952   AXTree tree(initial_state);
953 
954   AXEventGenerator event_generator(&tree);
955   AXTreeUpdate update = initial_state;
956   update.nodes[1].RemoveState(ax::mojom::State::kIgnored);
957   update.nodes[2].RemoveState(ax::mojom::State::kIgnored);
958   ASSERT_TRUE(tree.Unserialize(update));
959   EXPECT_THAT(event_generator,
960               UnorderedElementsAre(
961                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 1),
962                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 2),
963                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 2),
964                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3)));
965 }
966 
TEST(AXEventGeneratorTest,TwoNodesSwapIgnored)967 TEST(AXEventGeneratorTest, TwoNodesSwapIgnored) {
968   AXTreeUpdate initial_state;
969   initial_state.root_id = 1;
970   initial_state.nodes.resize(3);
971   initial_state.nodes[0].id = 1;
972   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
973   initial_state.nodes[0].child_ids.push_back(2);
974   initial_state.nodes[1].id = 2;
975   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
976   initial_state.nodes[1].child_ids.push_back(3);
977   initial_state.nodes[2].id = 3;
978   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
979   initial_state.nodes[2].AddState(ax::mojom::State::kIgnored);
980 
981   AXTree tree(initial_state);
982 
983   AXEventGenerator event_generator(&tree);
984   AXTreeUpdate update = initial_state;
985   update.nodes[1].AddState(ax::mojom::State::kIgnored);
986   update.nodes[2].RemoveState(ax::mojom::State::kIgnored);
987   ASSERT_TRUE(tree.Unserialize(update));
988   EXPECT_THAT(event_generator,
989               UnorderedElementsAre(
990                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 1),
991                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 2),
992                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3),
993                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 3)));
994 }
995 
TEST(AXEventGeneratorTest,TwoNodesSwapIgnored2)996 TEST(AXEventGeneratorTest, TwoNodesSwapIgnored2) {
997   AXTreeUpdate initial_state;
998   initial_state.root_id = 1;
999   initial_state.nodes.resize(3);
1000   initial_state.nodes[0].id = 1;
1001   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
1002   initial_state.nodes[0].child_ids.push_back(2);
1003   initial_state.nodes[1].id = 2;
1004   initial_state.nodes[1].role = ax::mojom::Role::kArticle;
1005   initial_state.nodes[1].AddState(ax::mojom::State::kIgnored);
1006   initial_state.nodes[1].child_ids.push_back(3);
1007   initial_state.nodes[2].id = 3;
1008   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
1009 
1010   AXTree tree(initial_state);
1011 
1012   AXEventGenerator event_generator(&tree);
1013   AXTreeUpdate update = initial_state;
1014   update.nodes[1].RemoveState(ax::mojom::State::kIgnored);
1015   update.nodes[2].AddState(ax::mojom::State::kIgnored);
1016   ASSERT_TRUE(tree.Unserialize(update));
1017   EXPECT_THAT(event_generator,
1018               UnorderedElementsAre(
1019                   HasEventAtNode(AXEventGenerator::Event::CHILDREN_CHANGED, 1),
1020                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 2),
1021                   HasEventAtNode(AXEventGenerator::Event::IGNORED_CHANGED, 3),
1022                   HasEventAtNode(AXEventGenerator::Event::SUBTREE_CREATED, 2)));
1023 }
1024 
TEST(AXEventGeneratorTest,ActiveDescendantChangeOnDescendant)1025 TEST(AXEventGeneratorTest, ActiveDescendantChangeOnDescendant) {
1026   AXTreeUpdate initial_state;
1027   initial_state.root_id = 1;
1028   initial_state.nodes.resize(5);
1029   initial_state.nodes[0].id = 1;
1030   initial_state.nodes[0].role = ax::mojom::Role::kRootWebArea;
1031   initial_state.nodes[0].child_ids.push_back(2);
1032   initial_state.nodes[1].id = 2;
1033   initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer;
1034   initial_state.nodes[1].child_ids.push_back(3);
1035   initial_state.nodes[2].id = 3;
1036   initial_state.nodes[2].role = ax::mojom::Role::kGroup;
1037   initial_state.nodes[2].AddIntAttribute(
1038       ax::mojom::IntAttribute::kActivedescendantId, 4);
1039   initial_state.nodes[2].child_ids.push_back(4);
1040   initial_state.nodes[2].child_ids.push_back(5);
1041   initial_state.nodes[3].id = 4;
1042   initial_state.nodes[3].role = ax::mojom::Role::kGroup;
1043   initial_state.nodes[4].id = 5;
1044   initial_state.nodes[4].role = ax::mojom::Role::kGroup;
1045 
1046   AXTree tree(initial_state);
1047 
1048   AXEventGenerator event_generator(&tree);
1049   initial_state.nodes[2].RemoveIntAttribute(
1050       ax::mojom::IntAttribute::kActivedescendantId);
1051   initial_state.nodes[2].AddIntAttribute(
1052       ax::mojom::IntAttribute::kActivedescendantId, 5);
1053   AXTreeUpdate update = initial_state;
1054   update.node_id_to_clear = 2;
1055   ASSERT_TRUE(tree.Unserialize(update));
1056   EXPECT_THAT(
1057       event_generator,
1058       UnorderedElementsAre(
1059           HasEventAtNode(AXEventGenerator::Event::ACTIVE_DESCENDANT_CHANGED, 3),
1060           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 3)));
1061 }
1062 
TEST(AXEventGeneratorTest,ImageAnnotationChanged)1063 TEST(AXEventGeneratorTest, ImageAnnotationChanged) {
1064   AXTreeUpdate initial_state;
1065   initial_state.root_id = 1;
1066   initial_state.nodes.resize(1);
1067   initial_state.nodes[0].id = 1;
1068   AXTree tree(initial_state);
1069 
1070   AXEventGenerator event_generator(&tree);
1071   AXTreeUpdate update = initial_state;
1072   update.nodes[0].AddStringAttribute(
1073       ax::mojom::StringAttribute::kImageAnnotation, "Hello");
1074   ASSERT_TRUE(tree.Unserialize(update));
1075   EXPECT_THAT(event_generator,
1076               UnorderedElementsAre(HasEventAtNode(
1077                   AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED, 1)));
1078 }
1079 
TEST(AXEventGeneratorTest,ImageAnnotationStatusChanged)1080 TEST(AXEventGeneratorTest, ImageAnnotationStatusChanged) {
1081   AXTreeUpdate initial_state;
1082   initial_state.root_id = 1;
1083   initial_state.nodes.resize(1);
1084   initial_state.nodes[0].id = 1;
1085   AXTree tree(initial_state);
1086 
1087   AXEventGenerator event_generator(&tree);
1088   AXTreeUpdate update = initial_state;
1089   update.nodes[0].SetImageAnnotationStatus(
1090       ax::mojom::ImageAnnotationStatus::kAnnotationSucceeded);
1091 
1092   ASSERT_TRUE(tree.Unserialize(update));
1093   EXPECT_THAT(event_generator,
1094               UnorderedElementsAre(HasEventAtNode(
1095                   AXEventGenerator::Event::IMAGE_ANNOTATION_CHANGED, 1)));
1096 }
1097 
TEST(AXEventGeneratorTest,StringPropertyChanges)1098 TEST(AXEventGeneratorTest, StringPropertyChanges) {
1099   AXTreeUpdate initial_state;
1100   initial_state.root_id = 1;
1101   initial_state.nodes.resize(1);
1102   initial_state.nodes[0].id = 1;
1103 
1104   struct {
1105     ax::mojom::StringAttribute id;
1106     std::string old_value;
1107     std::string new_value;
1108   } attributes[] = {
1109       {ax::mojom::StringAttribute::kAccessKey, "a", "b"},
1110       {ax::mojom::StringAttribute::kClassName, "a", "b"},
1111       {ax::mojom::StringAttribute::kKeyShortcuts, "a", "b"},
1112       {ax::mojom::StringAttribute::kLanguage, "a", "b"},
1113       {ax::mojom::StringAttribute::kPlaceholder, "a", "b"},
1114   };
1115   for (auto&& attrib : attributes) {
1116     initial_state.nodes.push_back({});
1117     initial_state.nodes.back().id = initial_state.nodes.size();
1118     initial_state.nodes.back().AddStringAttribute(attrib.id, attrib.old_value);
1119     initial_state.nodes[0].child_ids.push_back(initial_state.nodes.size());
1120   }
1121 
1122   AXTree tree(initial_state);
1123 
1124   AXEventGenerator event_generator(&tree);
1125   int index = 1;
1126   for (auto&& attrib : attributes) {
1127     initial_state.nodes[index++].AddStringAttribute(attrib.id,
1128                                                     attrib.new_value);
1129   }
1130 
1131   AXTreeUpdate update = initial_state;
1132   EXPECT_TRUE(tree.Unserialize(update));
1133   EXPECT_THAT(
1134       event_generator,
1135       UnorderedElementsAre(
1136           HasEventAtNode(AXEventGenerator::Event::ACCESS_KEY_CHANGED, 2),
1137           HasEventAtNode(AXEventGenerator::Event::CLASS_NAME_CHANGED, 3),
1138           HasEventAtNode(AXEventGenerator::Event::KEY_SHORTCUTS_CHANGED, 4),
1139           HasEventAtNode(AXEventGenerator::Event::LANGUAGE_CHANGED, 5),
1140           HasEventAtNode(AXEventGenerator::Event::PLACEHOLDER_CHANGED, 6)));
1141 }
1142 
TEST(AXEventGeneratorTest,IntPropertyChanges)1143 TEST(AXEventGeneratorTest, IntPropertyChanges) {
1144   AXTreeUpdate initial_state;
1145   initial_state.root_id = 1;
1146   initial_state.nodes.resize(1);
1147   initial_state.nodes[0].id = 1;
1148 
1149   struct {
1150     ax::mojom::IntAttribute id;
1151     int old_value;
1152     int new_value;
1153   } attributes[] = {
1154       {ax::mojom::IntAttribute::kHierarchicalLevel, 1, 2},
1155       {ax::mojom::IntAttribute::kPosInSet, 1, 2},
1156       {ax::mojom::IntAttribute::kSetSize, 1, 2},
1157   };
1158   for (auto&& attrib : attributes) {
1159     initial_state.nodes.push_back({});
1160     initial_state.nodes.back().id = initial_state.nodes.size();
1161     initial_state.nodes.back().AddIntAttribute(attrib.id, attrib.old_value);
1162     initial_state.nodes[0].child_ids.push_back(initial_state.nodes.size());
1163   }
1164 
1165   AXTree tree(initial_state);
1166 
1167   AXEventGenerator event_generator(&tree);
1168   int index = 1;
1169   for (auto&& attrib : attributes)
1170     initial_state.nodes[index++].AddIntAttribute(attrib.id, attrib.new_value);
1171 
1172   AXTreeUpdate update = initial_state;
1173   EXPECT_TRUE(tree.Unserialize(update));
1174   EXPECT_THAT(
1175       event_generator,
1176       UnorderedElementsAre(
1177           HasEventAtNode(AXEventGenerator::Event::HIERARCHICAL_LEVEL_CHANGED,
1178                          2),
1179           HasEventAtNode(AXEventGenerator::Event::POSITION_IN_SET_CHANGED, 3),
1180           HasEventAtNode(AXEventGenerator::Event::SET_SIZE_CHANGED, 4)));
1181 }
1182 
TEST(AXEventGeneratorTest,IntListPropertyChanges)1183 TEST(AXEventGeneratorTest, IntListPropertyChanges) {
1184   AXTreeUpdate initial_state;
1185   initial_state.root_id = 1;
1186   initial_state.nodes.resize(1);
1187   initial_state.nodes[0].id = 1;
1188 
1189   struct {
1190     ax::mojom::IntListAttribute id;
1191     std::vector<int> old_value;
1192     std::vector<int> new_value;
1193   } attributes[] = {
1194       {ax::mojom::IntListAttribute::kDescribedbyIds, {1}, {2}},
1195       {ax::mojom::IntListAttribute::kFlowtoIds, {1}, {2}},
1196       {ax::mojom::IntListAttribute::kLabelledbyIds, {1}, {2}},
1197   };
1198   for (auto&& attrib : attributes) {
1199     initial_state.nodes.push_back({});
1200     initial_state.nodes.back().id = initial_state.nodes.size();
1201     initial_state.nodes.back().AddIntListAttribute(attrib.id, attrib.old_value);
1202     initial_state.nodes[0].child_ids.push_back(initial_state.nodes.size());
1203   }
1204 
1205   AXTree tree(initial_state);
1206 
1207   AXEventGenerator event_generator(&tree);
1208   int index = 1;
1209   for (auto&& attrib : attributes) {
1210     initial_state.nodes[index++].AddIntListAttribute(attrib.id,
1211                                                      attrib.new_value);
1212   }
1213 
1214   AXTreeUpdate update = initial_state;
1215   EXPECT_TRUE(tree.Unserialize(update));
1216   EXPECT_THAT(
1217       event_generator,
1218       UnorderedElementsAre(
1219           HasEventAtNode(AXEventGenerator::Event::DESCRIBED_BY_CHANGED, 2),
1220           HasEventAtNode(AXEventGenerator::Event::FLOW_FROM_CHANGED, 1),
1221           HasEventAtNode(AXEventGenerator::Event::FLOW_FROM_CHANGED, 2),
1222           HasEventAtNode(AXEventGenerator::Event::FLOW_TO_CHANGED, 3),
1223           HasEventAtNode(AXEventGenerator::Event::LABELED_BY_CHANGED, 4),
1224           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 2),
1225           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 3),
1226           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 4)));
1227 }
1228 
TEST(AXEventGeneratorTest,AriaBusyChanged)1229 TEST(AXEventGeneratorTest, AriaBusyChanged) {
1230   AXTreeUpdate initial_state;
1231   initial_state.root_id = 1;
1232   initial_state.nodes.resize(1);
1233   initial_state.nodes[0].id = 1;
1234   AXTree tree(initial_state);
1235   initial_state.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kBusy,
1236                                           true);
1237 
1238   AXEventGenerator event_generator(&tree);
1239   AXTreeUpdate update = initial_state;
1240   update.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kBusy, false);
1241 
1242   ASSERT_TRUE(tree.Unserialize(update));
1243   EXPECT_THAT(
1244       event_generator,
1245       UnorderedElementsAre(
1246           HasEventAtNode(AXEventGenerator::Event::BUSY_CHANGED, 1),
1247           HasEventAtNode(AXEventGenerator::Event::LAYOUT_INVALIDATED, 1)));
1248 }
1249 
TEST(AXEventGeneratorTest,MultiselectableStateChanged)1250 TEST(AXEventGeneratorTest, MultiselectableStateChanged) {
1251   AXTreeUpdate initial_state;
1252   initial_state.root_id = 1;
1253   initial_state.nodes.resize(1);
1254   initial_state.nodes[0].id = 1;
1255   initial_state.nodes[0].role = ax::mojom::Role::kGrid;
1256 
1257   AXTree tree(initial_state);
1258   AXEventGenerator event_generator(&tree);
1259   AXTreeUpdate update = initial_state;
1260 
1261   update.nodes[0].AddState(ax::mojom::State::kMultiselectable);
1262   EXPECT_TRUE(tree.Unserialize(update));
1263   EXPECT_THAT(
1264       event_generator,
1265       UnorderedElementsAre(
1266           HasEventAtNode(AXEventGenerator::Event::MULTISELECTABLE_STATE_CHANGED,
1267                          1),
1268           HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1)));
1269 }
1270 
TEST(AXEventGeneratorTest,RequiredStateChanged)1271 TEST(AXEventGeneratorTest, RequiredStateChanged) {
1272   AXTreeUpdate initial_state;
1273   initial_state.root_id = 1;
1274   initial_state.nodes.resize(1);
1275   initial_state.nodes[0].id = 1;
1276   initial_state.nodes[0].role = ax::mojom::Role::kTextField;
1277 
1278   AXTree tree(initial_state);
1279   AXEventGenerator event_generator(&tree);
1280   AXTreeUpdate update = initial_state;
1281 
1282   update.nodes[0].AddState(ax::mojom::State::kRequired);
1283   EXPECT_TRUE(tree.Unserialize(update));
1284   EXPECT_THAT(
1285       event_generator,
1286       UnorderedElementsAre(
1287           HasEventAtNode(AXEventGenerator::Event::REQUIRED_STATE_CHANGED, 1),
1288           HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1)));
1289 }
1290 
TEST(AXEventGeneratorTest,FlowToChanged)1291 TEST(AXEventGeneratorTest, FlowToChanged) {
1292   AXTreeUpdate initial_state;
1293   initial_state.root_id = 1;
1294   initial_state.nodes.resize(6);
1295   initial_state.nodes[0].id = 1;
1296   initial_state.nodes[0].role = ax::mojom::Role::kGenericContainer;
1297   initial_state.nodes[0].child_ids.assign({2, 3, 4, 5, 6});
1298   initial_state.nodes[1].id = 2;
1299   initial_state.nodes[1].role = ax::mojom::Role::kGenericContainer;
1300   initial_state.nodes[1].AddIntListAttribute(
1301       ax::mojom::IntListAttribute::kFlowtoIds, {3, 4});
1302   initial_state.nodes[2].id = 3;
1303   initial_state.nodes[2].role = ax::mojom::Role::kGenericContainer;
1304   initial_state.nodes[3].id = 4;
1305   initial_state.nodes[3].role = ax::mojom::Role::kGenericContainer;
1306   initial_state.nodes[4].id = 5;
1307   initial_state.nodes[4].role = ax::mojom::Role::kGenericContainer;
1308   initial_state.nodes[5].id = 6;
1309   initial_state.nodes[5].role = ax::mojom::Role::kGenericContainer;
1310 
1311   AXTree tree(initial_state);
1312 
1313   AXEventGenerator event_generator(&tree);
1314   AXTreeUpdate update = initial_state;
1315   update.nodes[1].AddIntListAttribute(ax::mojom::IntListAttribute::kFlowtoIds,
1316                                       {4, 5, 6});
1317 
1318   EXPECT_TRUE(tree.Unserialize(update));
1319   EXPECT_THAT(
1320       event_generator,
1321       UnorderedElementsAre(
1322           HasEventAtNode(AXEventGenerator::Event::FLOW_FROM_CHANGED, 3),
1323           HasEventAtNode(AXEventGenerator::Event::FLOW_FROM_CHANGED, 5),
1324           HasEventAtNode(AXEventGenerator::Event::FLOW_FROM_CHANGED, 6),
1325           HasEventAtNode(AXEventGenerator::Event::FLOW_TO_CHANGED, 2),
1326           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 2)));
1327 }
1328 
TEST(AXEventGeneratorTest,ControlsChanged)1329 TEST(AXEventGeneratorTest, ControlsChanged) {
1330   AXTreeUpdate initial_state;
1331   initial_state.root_id = 1;
1332   initial_state.nodes.resize(2);
1333   initial_state.nodes[0].id = 1;
1334   initial_state.nodes[0].child_ids.push_back(2);
1335   initial_state.nodes[1].id = 2;
1336 
1337   AXTree tree(initial_state);
1338   AXEventGenerator event_generator(&tree);
1339   AXTreeUpdate update = initial_state;
1340 
1341   std::vector<int> ids = {2};
1342   update.nodes[0].AddIntListAttribute(ax::mojom::IntListAttribute::kControlsIds,
1343                                       ids);
1344   EXPECT_TRUE(tree.Unserialize(update));
1345   EXPECT_THAT(
1346       event_generator,
1347       UnorderedElementsAre(
1348           HasEventAtNode(AXEventGenerator::Event::CONTROLS_CHANGED, 1),
1349           HasEventAtNode(AXEventGenerator::Event::RELATED_NODE_CHANGED, 1)));
1350 }
1351 
TEST(AXEventGeneratorTest,AtomicChanged)1352 TEST(AXEventGeneratorTest, AtomicChanged) {
1353   AXTreeUpdate initial_state;
1354   initial_state.root_id = 1;
1355   initial_state.nodes.resize(1);
1356   initial_state.nodes[0].id = 1;
1357 
1358   AXTree tree(initial_state);
1359   AXEventGenerator event_generator(&tree);
1360   AXTreeUpdate update = initial_state;
1361 
1362   update.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kLiveAtomic, true);
1363   EXPECT_TRUE(tree.Unserialize(update));
1364   EXPECT_THAT(event_generator,
1365               UnorderedElementsAre(
1366                   HasEventAtNode(AXEventGenerator::Event::ATOMIC_CHANGED, 1)));
1367 }
1368 
TEST(AXEventGeneratorTest,DropeffectChanged)1369 TEST(AXEventGeneratorTest, DropeffectChanged) {
1370   AXTreeUpdate initial_state;
1371   initial_state.root_id = 1;
1372   initial_state.nodes.resize(1);
1373   initial_state.nodes[0].id = 1;
1374 
1375   AXTree tree(initial_state);
1376   AXEventGenerator event_generator(&tree);
1377   AXTreeUpdate update = initial_state;
1378 
1379   update.nodes[0].AddDropeffect(ax::mojom::Dropeffect::kCopy);
1380   EXPECT_TRUE(tree.Unserialize(update));
1381   EXPECT_THAT(event_generator,
1382               UnorderedElementsAre(HasEventAtNode(
1383                   AXEventGenerator::Event::DROPEFFECT_CHANGED, 1)));
1384 }
1385 
TEST(AXEventGeneratorTest,GrabbedChanged)1386 TEST(AXEventGeneratorTest, GrabbedChanged) {
1387   AXTreeUpdate initial_state;
1388   initial_state.root_id = 1;
1389   initial_state.nodes.resize(1);
1390   initial_state.nodes[0].id = 1;
1391 
1392   AXTree tree(initial_state);
1393   AXEventGenerator event_generator(&tree);
1394   AXTreeUpdate update = initial_state;
1395 
1396   update.nodes[0].AddBoolAttribute(ax::mojom::BoolAttribute::kGrabbed, true);
1397   EXPECT_TRUE(tree.Unserialize(update));
1398   EXPECT_THAT(event_generator,
1399               UnorderedElementsAre(
1400                   HasEventAtNode(AXEventGenerator::Event::GRABBED_CHANGED, 1)));
1401 }
1402 
TEST(AXEventGeneratorTest,HasPopupChanged)1403 TEST(AXEventGeneratorTest, HasPopupChanged) {
1404   AXTreeUpdate initial_state;
1405   initial_state.root_id = 1;
1406   initial_state.nodes.resize(1);
1407   initial_state.nodes[0].id = 1;
1408 
1409   AXTree tree(initial_state);
1410   AXEventGenerator event_generator(&tree);
1411   AXTreeUpdate update = initial_state;
1412 
1413   update.nodes[0].SetHasPopup(ax::mojom::HasPopup::kTrue);
1414   EXPECT_TRUE(tree.Unserialize(update));
1415   EXPECT_THAT(event_generator,
1416               UnorderedElementsAre(HasEventAtNode(
1417                   AXEventGenerator::Event::HASPOPUP_CHANGED, 1)));
1418 }
1419 
TEST(AXEventGeneratorTest,LiveRelevantChanged)1420 TEST(AXEventGeneratorTest, LiveRelevantChanged) {
1421   AXTreeUpdate initial_state;
1422   initial_state.root_id = 1;
1423   initial_state.nodes.resize(1);
1424   initial_state.nodes[0].id = 1;
1425 
1426   AXTree tree(initial_state);
1427   AXEventGenerator event_generator(&tree);
1428   AXTreeUpdate update = initial_state;
1429 
1430   update.nodes[0].AddStringAttribute(ax::mojom::StringAttribute::kLiveRelevant,
1431                                      "all");
1432   EXPECT_TRUE(tree.Unserialize(update));
1433   EXPECT_THAT(event_generator,
1434               UnorderedElementsAre(HasEventAtNode(
1435                   AXEventGenerator::Event::LIVE_RELEVANT_CHANGED, 1)));
1436 }
1437 
TEST(AXEventGeneratorTest,MultilineStateChanged)1438 TEST(AXEventGeneratorTest, MultilineStateChanged) {
1439   AXTreeUpdate initial_state;
1440   initial_state.root_id = 1;
1441   initial_state.nodes.resize(1);
1442   initial_state.nodes[0].id = 1;
1443 
1444   AXTree tree(initial_state);
1445   AXEventGenerator event_generator(&tree);
1446   AXTreeUpdate update = initial_state;
1447 
1448   update.nodes[0].AddState(ax::mojom::State::kMultiline);
1449   EXPECT_TRUE(tree.Unserialize(update));
1450   EXPECT_THAT(
1451       event_generator,
1452       UnorderedElementsAre(
1453           HasEventAtNode(AXEventGenerator::Event::MULTILINE_STATE_CHANGED, 1),
1454           HasEventAtNode(AXEventGenerator::Event::STATE_CHANGED, 1)));
1455 }
1456 
1457 }  // namespace ui
1458