android-env 1.2.1__py3-none-any.whl → 1.2.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. android_env/__init__.py +1 -1
  2. android_env/components/__init__.py +1 -1
  3. android_env/components/a11y/__init__.py +15 -0
  4. android_env/components/a11y/a11y_events.py +118 -0
  5. android_env/components/a11y/a11y_events_test.py +173 -0
  6. android_env/components/a11y/a11y_forests.py +128 -0
  7. android_env/components/a11y/a11y_forests_test.py +237 -0
  8. android_env/components/a11y/a11y_servicer.py +199 -0
  9. android_env/components/a11y/a11y_servicer_test.py +224 -0
  10. android_env/components/action_fns.py +132 -0
  11. android_env/components/action_fns_test.py +227 -0
  12. android_env/components/action_type.py +26 -3
  13. android_env/components/adb_call_parser.py +239 -196
  14. android_env/components/adb_call_parser_test.py +179 -209
  15. android_env/components/adb_controller.py +90 -52
  16. android_env/components/adb_controller_test.py +187 -16
  17. android_env/components/adb_log_stream.py +17 -5
  18. android_env/components/adb_log_stream_test.py +17 -3
  19. android_env/components/app_screen_checker.py +17 -15
  20. android_env/components/app_screen_checker_test.py +7 -8
  21. android_env/components/config_classes.py +203 -0
  22. android_env/components/coordinator.py +102 -338
  23. android_env/components/coordinator_test.py +59 -199
  24. android_env/components/device_settings.py +174 -0
  25. android_env/components/device_settings_test.py +228 -0
  26. android_env/components/dumpsys_thread.py +3 -4
  27. android_env/components/dumpsys_thread_test.py +1 -1
  28. android_env/components/errors.py +52 -10
  29. android_env/components/errors_test.py +110 -0
  30. android_env/components/log_stream.py +7 -5
  31. android_env/components/log_stream_test.py +1 -1
  32. android_env/components/logcat_thread.py +9 -8
  33. android_env/components/logcat_thread_test.py +3 -4
  34. android_env/components/{utils.py → pixel_fns.py} +20 -20
  35. android_env/components/{utils_test.py → pixel_fns_test.py} +20 -15
  36. android_env/components/setup_step_interpreter.py +47 -39
  37. android_env/components/setup_step_interpreter_test.py +4 -4
  38. android_env/components/simulators/__init__.py +1 -1
  39. android_env/components/simulators/base_simulator.py +116 -44
  40. android_env/components/simulators/base_simulator_test.py +131 -9
  41. android_env/components/simulators/emulator/__init__.py +1 -1
  42. android_env/components/simulators/emulator/emulator_launcher.py +67 -77
  43. android_env/components/simulators/emulator/emulator_launcher_test.py +153 -49
  44. android_env/components/simulators/emulator/emulator_simulator.py +276 -95
  45. android_env/components/simulators/emulator/emulator_simulator_test.py +314 -89
  46. android_env/components/simulators/fake/__init__.py +1 -1
  47. android_env/components/simulators/fake/fake_simulator.py +17 -25
  48. android_env/components/simulators/fake/fake_simulator_test.py +29 -12
  49. android_env/components/specs.py +18 -28
  50. android_env/components/specs_test.py +1 -44
  51. android_env/components/task_manager.py +48 -48
  52. android_env/components/task_manager_test.py +71 -60
  53. android_env/env_interface.py +37 -23
  54. android_env/environment.py +83 -51
  55. android_env/environment_test.py +68 -29
  56. android_env/loader.py +57 -43
  57. android_env/loader_test.py +115 -35
  58. android_env/proto/__init__.py +1 -1
  59. android_env/proto/a11y/__init__.py +15 -0
  60. android_env/proto/a11y/a11y.proto +75 -0
  61. android_env/proto/a11y/a11y_pb2.py +54 -0
  62. android_env/proto/a11y/a11y_pb2.pyi +49 -0
  63. android_env/proto/a11y/a11y_pb2_grpc.py +202 -0
  64. android_env/proto/a11y/android_accessibility_action.proto +32 -0
  65. android_env/proto/a11y/android_accessibility_action_pb2.py +37 -0
  66. android_env/proto/a11y/android_accessibility_action_pb2.pyi +13 -0
  67. android_env/proto/a11y/android_accessibility_action_pb2_grpc.py +24 -0
  68. android_env/proto/a11y/android_accessibility_forest.proto +29 -0
  69. android_env/proto/a11y/android_accessibility_forest_pb2.py +38 -0
  70. android_env/proto/a11y/android_accessibility_forest_pb2.pyi +13 -0
  71. android_env/proto/a11y/android_accessibility_forest_pb2_grpc.py +24 -0
  72. android_env/proto/a11y/android_accessibility_node_info.proto +122 -0
  73. android_env/proto/a11y/android_accessibility_node_info_clickable_span.proto +49 -0
  74. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.py +39 -0
  75. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2.pyi +28 -0
  76. android_env/proto/a11y/android_accessibility_node_info_clickable_span_pb2_grpc.py +24 -0
  77. android_env/proto/a11y/android_accessibility_node_info_pb2.py +42 -0
  78. android_env/proto/a11y/android_accessibility_node_info_pb2.pyi +75 -0
  79. android_env/proto/a11y/android_accessibility_node_info_pb2_grpc.py +24 -0
  80. android_env/proto/a11y/android_accessibility_tree.proto +29 -0
  81. android_env/proto/a11y/android_accessibility_tree_pb2.py +38 -0
  82. android_env/proto/a11y/android_accessibility_tree_pb2.pyi +13 -0
  83. android_env/proto/a11y/android_accessibility_tree_pb2_grpc.py +24 -0
  84. android_env/proto/a11y/android_accessibility_window_info.proto +84 -0
  85. android_env/proto/a11y/android_accessibility_window_info_pb2.py +41 -0
  86. android_env/proto/a11y/android_accessibility_window_info_pb2.pyi +48 -0
  87. android_env/proto/a11y/android_accessibility_window_info_pb2_grpc.py +24 -0
  88. android_env/proto/a11y/rect.proto +30 -0
  89. android_env/proto/a11y/rect_pb2.py +37 -0
  90. android_env/proto/a11y/rect_pb2.pyi +17 -0
  91. android_env/proto/a11y/rect_pb2_grpc.py +24 -0
  92. android_env/proto/adb.proto +17 -6
  93. android_env/proto/adb_pb2.py +120 -107
  94. android_env/proto/adb_pb2.pyi +396 -0
  95. android_env/proto/adb_pb2_grpc.py +20 -0
  96. android_env/proto/emulator_controller.proto +68 -63
  97. android_env/proto/emulator_controller_pb2.py +142 -131
  98. android_env/proto/emulator_controller_pb2.pyi +672 -0
  99. android_env/proto/emulator_controller_pb2_grpc.py +505 -142
  100. android_env/proto/snapshot.proto +169 -0
  101. android_env/proto/snapshot_pb2.py +47 -0
  102. android_env/proto/snapshot_pb2.pyi +117 -0
  103. android_env/proto/snapshot_pb2_grpc.py +24 -0
  104. android_env/proto/snapshot_service.proto +289 -0
  105. android_env/proto/snapshot_service_pb2.py +54 -0
  106. android_env/proto/snapshot_service_pb2.pyi +86 -0
  107. android_env/proto/snapshot_service_pb2_grpc.py +487 -0
  108. android_env/proto/state.proto +63 -0
  109. android_env/proto/state_pb2.py +63 -0
  110. android_env/proto/state_pb2.pyi +85 -0
  111. android_env/proto/state_pb2_grpc.py +24 -0
  112. android_env/proto/task.proto +5 -1
  113. android_env/proto/task_pb2.py +42 -31
  114. android_env/proto/task_pb2.pyi +160 -0
  115. android_env/proto/task_pb2_grpc.py +20 -0
  116. android_env/wrappers/__init__.py +1 -1
  117. android_env/wrappers/a11y_grpc_wrapper.py +500 -0
  118. android_env/wrappers/a11y_grpc_wrapper_test.py +849 -0
  119. android_env/wrappers/base_wrapper.py +34 -13
  120. android_env/wrappers/base_wrapper_test.py +22 -16
  121. android_env/wrappers/discrete_action_wrapper.py +18 -17
  122. android_env/wrappers/discrete_action_wrapper_test.py +4 -4
  123. android_env/wrappers/flat_interface_wrapper.py +5 -5
  124. android_env/wrappers/flat_interface_wrapper_test.py +7 -11
  125. android_env/wrappers/float_pixels_wrapper.py +9 -10
  126. android_env/wrappers/float_pixels_wrapper_test.py +3 -3
  127. android_env/wrappers/gym_wrapper.py +19 -13
  128. android_env/wrappers/gym_wrapper_test.py +3 -5
  129. android_env/wrappers/image_rescale_wrapper.py +18 -21
  130. android_env/wrappers/image_rescale_wrapper_test.py +25 -37
  131. android_env/wrappers/last_action_wrapper.py +16 -13
  132. android_env/wrappers/last_action_wrapper_test.py +44 -51
  133. android_env/wrappers/rate_limit_wrapper.py +6 -3
  134. android_env/wrappers/rate_limit_wrapper_test.py +22 -1
  135. android_env/wrappers/tap_action_wrapper.py +16 -17
  136. android_env/wrappers/tap_action_wrapper_test.py +51 -16
  137. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/METADATA +14 -18
  138. android_env-1.2.3.dist-info/RECORD +141 -0
  139. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/WHEEL +1 -1
  140. android_env/proto/raw_observation.proto +0 -39
  141. android_env/proto/raw_observation_pb2.py +0 -27
  142. android_env/proto/raw_observation_pb2_grpc.py +0 -4
  143. android_env-1.2.1.dist-info/RECORD +0 -81
  144. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info/licenses}/LICENSE +0 -0
  145. {android_env-1.2.1.dist-info → android_env-1.2.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,849 @@
1
+ # coding=utf-8
2
+ # Copyright 2024 DeepMind Technologies Limited.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ """Tests for a11y_grpc_wrapper."""
17
+
18
+ import time
19
+ from unittest import mock
20
+
21
+ from absl.testing import absltest
22
+ from absl.testing import parameterized
23
+ from android_env import env_interface
24
+ from android_env.proto import adb_pb2
25
+ from android_env.proto.a11y import a11y_pb2
26
+ from android_env.proto.a11y import a11y_pb2_grpc
27
+ from android_env.proto.a11y import android_accessibility_forest_pb2
28
+ from android_env.wrappers import a11y_grpc_wrapper
29
+ import dm_env
30
+ import grpc
31
+ import numpy as np
32
+
33
+
34
+ def empty_forest() -> (
35
+ android_accessibility_forest_pb2.AndroidAccessibilityForest
36
+ ):
37
+ return android_accessibility_forest_pb2.AndroidAccessibilityForest()
38
+
39
+
40
+ def one_empty_window_forest() -> (
41
+ android_accessibility_forest_pb2.AndroidAccessibilityForest
42
+ ):
43
+ forest = android_accessibility_forest_pb2.AndroidAccessibilityForest()
44
+ _ = forest.windows.add()
45
+ return forest
46
+
47
+
48
+ def one_window_one_node_forest() -> (
49
+ android_accessibility_forest_pb2.AndroidAccessibilityForest
50
+ ):
51
+ forest = android_accessibility_forest_pb2.AndroidAccessibilityForest()
52
+ window = forest.windows.add()
53
+ node = window.tree.nodes.add()
54
+ node.class_name = 'foo'
55
+ node.is_clickable = True
56
+ node.hint_text = 'Foo hint'
57
+ return forest
58
+
59
+
60
+ def one_window_two_nodes_forest() -> (
61
+ android_accessibility_forest_pb2.AndroidAccessibilityForest
62
+ ):
63
+ forest = android_accessibility_forest_pb2.AndroidAccessibilityForest()
64
+ window = forest.windows.add()
65
+ node = window.tree.nodes.add()
66
+ node.class_name = 'bar'
67
+ node.is_clickable = True
68
+ node.hint_text = 'Bar hint'
69
+ node = window.tree.nodes.add()
70
+ node.class_name = 'bar'
71
+ node.is_clickable = False
72
+ node.hint_text = 'Bar hint 2'
73
+ return forest
74
+
75
+
76
+ def three_windows_forest() -> (
77
+ android_accessibility_forest_pb2.AndroidAccessibilityForest
78
+ ):
79
+ forest = android_accessibility_forest_pb2.AndroidAccessibilityForest()
80
+ _ = forest.windows.add()
81
+ window = forest.windows.add()
82
+ node = window.tree.nodes.add()
83
+ node.class_name = 'foo'
84
+ node.is_clickable = True
85
+ node.hint_text = 'hint'
86
+ window = forest.windows.add()
87
+ node = window.tree.nodes.add()
88
+ node.class_name = 'baz'
89
+ node.is_clickable = True
90
+ node.hint_text = 'hint'
91
+ node = window.tree.nodes.add()
92
+ node.class_name = 'foobar'
93
+ node.is_clickable = False
94
+ node.hint_text = 'hint'
95
+ return forest
96
+
97
+
98
+ def empty_dict() -> dict[str, str]:
99
+ return {}
100
+
101
+
102
+ def single_item_dict() -> dict[str, str]:
103
+ return {'foo': 'bar'}
104
+
105
+
106
+ def several_long_items_dict() -> dict[str, str]:
107
+ return {
108
+ 'first_key': 'Lorem ipsum ' * 100,
109
+ 'second_key': 'the beginning is the end is' * 100,
110
+ }
111
+
112
+
113
+ def single_item_dict_with_special_chars() -> dict[str, str]:
114
+ return {'foo': 'bar\r\t\nbaz'}
115
+
116
+
117
+ def _ok_response():
118
+ return adb_pb2.AdbResponse(status=adb_pb2.AdbResponse.Status.OK)
119
+
120
+
121
+ class A11yGrpcWrapperTest(parameterized.TestCase):
122
+
123
+ def test_server(self):
124
+ base_env = mock.create_autospec(
125
+ env_interface.AndroidEnvInterface, instance=True
126
+ )
127
+ base_env.task_extras.return_value = {}
128
+ base_env.stats.return_value = {'relaunch_count': 0}
129
+ base_env.execute_adb_call.return_value = _ok_response()
130
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
131
+ wrapped_env.reset()
132
+ channel_creds = grpc.local_channel_credentials()
133
+ with grpc.secure_channel(
134
+ f'[::]:{wrapped_env.get_port()}', channel_creds
135
+ ) as channel:
136
+ grpc.channel_ready_future(channel).result()
137
+ stub = a11y_pb2_grpc.A11yServiceStub(channel)
138
+ stub.SendForest(one_window_one_node_forest())
139
+ stub.SendForest(one_window_two_nodes_forest())
140
+ wrapped_env.step({})
141
+ extras = wrapped_env.task_extras(latest_only=False)
142
+ self.assertIn('accessibility_tree', extras)
143
+ self.assertEqual(extras['accessibility_tree'].shape[0], 2)
144
+
145
+ # tests of fetch_task_extras:
146
+ # exception occurs (ensure attempt to enable networking) and recovers
147
+ # exception occurs and enable networking doesn't help
148
+ # exception occurs twice but with a forest sent between
149
+
150
+ @parameterized.named_parameters(
151
+ ('no_events_or_forests', [], []),
152
+ (
153
+ 'no_events',
154
+ [],
155
+ [one_window_one_node_forest(), one_window_two_nodes_forest()],
156
+ ),
157
+ ('no_forests', [empty_dict(), single_item_dict()], []),
158
+ (
159
+ 'events_and_forests',
160
+ [empty_dict(), single_item_dict()],
161
+ [one_window_one_node_forest(), one_window_two_nodes_forest()],
162
+ ),
163
+ )
164
+ @mock.patch.object(time, 'sleep', autospec=True)
165
+ @mock.patch.object(
166
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
167
+ )
168
+ @mock.patch.object(grpc, 'server', autospec=True)
169
+ def test_fetch_task_extras(
170
+ self,
171
+ received_events,
172
+ received_forests,
173
+ mock_server,
174
+ mock_add_servicer,
175
+ mock_sleep,
176
+ ):
177
+ del mock_server, mock_add_servicer, mock_sleep
178
+ mock_context = mock.create_autospec(grpc.ServicerContext, instance=True)
179
+ base_env = mock.create_autospec(
180
+ env_interface.AndroidEnvInterface, instance=True
181
+ )
182
+ base_env.task_extras.return_value = {
183
+ 'foo': np.array(['bar', 'baz'], dtype='U'),
184
+ 'some_key': np.array(['some_value'], dtype='U'),
185
+ }
186
+ base_env.stats.return_value = {'relaunch_count': 0}
187
+ base_env.execute_adb_call.return_value = _ok_response()
188
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
189
+ wrapped_env.reset()
190
+ for forest in received_forests:
191
+ wrapped_env._servicer.SendForest(forest, mock_context)
192
+ for event in received_events:
193
+ wrapped_env._servicer.SendEvent(
194
+ a11y_pb2.EventRequest(event=event), mock_context
195
+ )
196
+ with mock.patch.object(
197
+ wrapped_env, 'attempt_enable_networking'
198
+ ) as mock_attempt_enable_networking:
199
+ extras = wrapped_env._fetch_task_extras()
200
+ mock_attempt_enable_networking.assert_not_called()
201
+ self.assertIn('foo', extras)
202
+ np.testing.assert_array_equal(extras['foo'], ['bar', 'baz'])
203
+ self.assertIn('some_key', extras)
204
+ np.testing.assert_array_equal(extras['some_key'], ['some_value'])
205
+ if received_events:
206
+ self.assertIn('full_event', extras)
207
+ self.assertLen(extras['full_event'], len(received_events))
208
+ for i, event in enumerate(received_events):
209
+ event = a11y_pb2.EventRequest(event=event)
210
+ np.testing.assert_array_equal(extras['full_event'][i], event)
211
+ else:
212
+ self.assertNotIn('full_event', extras)
213
+ if received_forests:
214
+ self.assertIn('accessibility_tree', extras)
215
+ self.assertLen(extras['accessibility_tree'], len(received_forests))
216
+ for i, forest in enumerate(received_forests):
217
+ np.testing.assert_array_equal(extras['accessibility_tree'][i], forest)
218
+ else:
219
+ self.assertNotIn('accessibility_tree', extras)
220
+
221
+ @mock.patch.object(time, 'sleep', autospec=True)
222
+ @mock.patch.object(
223
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
224
+ )
225
+ @mock.patch.object(grpc, 'server', autospec=True)
226
+ def test_fetch_task_extras_enable_networking(
227
+ self,
228
+ mock_server,
229
+ mock_add_servicer,
230
+ mock_sleep,
231
+ ):
232
+ del mock_server, mock_add_servicer, mock_sleep
233
+ base_env = mock.create_autospec(
234
+ env_interface.AndroidEnvInterface, instance=True
235
+ )
236
+ base_env.task_extras.return_value = {
237
+ 'foo': np.array(['bar'], dtype='U'),
238
+ 'some_key': np.array(['some_value'], dtype='U'),
239
+ 'exception': np.array(['fake exception'], dtype='U'),
240
+ }
241
+ base_env.stats.return_value = {'relaunch_count': 0}
242
+ base_env.execute_adb_call.return_value = _ok_response()
243
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
244
+ with mock.patch.object(
245
+ wrapped_env, 'attempt_enable_networking'
246
+ ) as mock_attempt_enable_networking:
247
+ extras = wrapped_env._fetch_task_extras()
248
+ self.assertNotIn('accessibility_tree', extras)
249
+ self.assertNotIn('full_event', extras)
250
+ future = wrapped_env._enabling_networking_future
251
+ if future is not None:
252
+ future.result()
253
+ mock_attempt_enable_networking.assert_called_once()
254
+
255
+ @mock.patch.object(time, 'sleep', autospec=True)
256
+ @mock.patch.object(
257
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
258
+ )
259
+ @mock.patch.object(grpc, 'server', autospec=True)
260
+ def test_fetch_task_extras_enable_networking_twice(
261
+ self,
262
+ mock_server,
263
+ mock_add_servicer,
264
+ mock_sleep,
265
+ ):
266
+ del mock_server, mock_add_servicer, mock_sleep
267
+ mock_context = mock.create_autospec(grpc.ServicerContext, instance=True)
268
+ base_env = mock.create_autospec(
269
+ env_interface.AndroidEnvInterface, instance=True
270
+ )
271
+ base_env.task_extras.return_value = {
272
+ 'foo': np.array(['bar'], dtype='U'),
273
+ 'some_key': np.array(['some_value'], dtype='U'),
274
+ }
275
+
276
+ base_env.stats.return_value = {'relaunch_count': 0}
277
+ base_env.execute_adb_call.return_value = _ok_response()
278
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
279
+ wrapped_env.reset()
280
+
281
+ base_env.task_extras.return_value = {
282
+ 'foo': np.array(['bar'], dtype='U'),
283
+ 'some_key': np.array(['some_value'], dtype='U'),
284
+ 'exception': np.array(['fake exception'], dtype='U'),
285
+ }
286
+ with mock.patch.object(
287
+ wrapped_env, 'attempt_enable_networking'
288
+ ) as mock_attempt_enable_networking:
289
+ extras = wrapped_env._fetch_task_extras()
290
+ self.assertNotIn('accessibility_tree', extras)
291
+ self.assertNotIn('full_event', extras)
292
+ future = wrapped_env._enabling_networking_future
293
+ if future is not None:
294
+ future.result()
295
+ mock_attempt_enable_networking.assert_called_once()
296
+ # Fixed networking; send a forest so the wrapper knows it worked.
297
+ wrapped_env._servicer.SendForest(one_window_one_node_forest(), mock_context)
298
+ base_env.task_extras.return_value = {
299
+ 'foo': np.array(['bar'], dtype='U'),
300
+ 'some_key': np.array(['some_value'], dtype='U'),
301
+ }
302
+ extras = wrapped_env._fetch_task_extras()
303
+ self.assertIn('accessibility_tree', extras)
304
+ self.assertEqual(extras['accessibility_tree'].shape[0], 1)
305
+ self.assertEqual(
306
+ extras['accessibility_tree'][0], one_window_one_node_forest()
307
+ )
308
+
309
+ base_env.task_extras.return_value = {
310
+ 'foo': np.array(['bar'], dtype='U'),
311
+ 'some_key': np.array(['some_value'], dtype='U'),
312
+ 'exception': np.array(['fake exception'], dtype='U'),
313
+ }
314
+ with mock.patch.object(
315
+ wrapped_env, 'attempt_enable_networking'
316
+ ) as mock_attempt_enable_networking:
317
+ extras = wrapped_env._fetch_task_extras()
318
+ self.assertNotIn('accessibility_tree', extras)
319
+ self.assertNotIn('full_event', extras)
320
+ future = wrapped_env._enabling_networking_future
321
+ if future is not None:
322
+ future.result()
323
+ mock_attempt_enable_networking.assert_called_once()
324
+
325
+ @mock.patch.object(time, 'sleep', autospec=True)
326
+ @mock.patch.object(
327
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
328
+ )
329
+ @mock.patch.object(grpc, 'server', autospec=True)
330
+ def test_task_extras_raises_a11y_info_exception(
331
+ self, mock_sleep, mock_add_servicer, mock_server
332
+ ):
333
+ del mock_server, mock_add_servicer, mock_sleep
334
+ base_env = mock.create_autospec(
335
+ env_interface.AndroidEnvInterface, instance=True
336
+ )
337
+ base_env.task_extras.return_value = {
338
+ 'foo': np.array(['bar'], dtype='U'),
339
+ 'some_key': np.array(['some_value'], dtype='U'),
340
+ }
341
+
342
+ base_env.stats.return_value = {'relaunch_count': 0}
343
+ base_env.execute_adb_call.return_value = _ok_response()
344
+ base_env.reset.return_value = dm_env.restart(observation={'dummy': 42})
345
+ base_env.step.return_value = dm_env.transition(
346
+ observation={'dummy': 42}, reward=0.0
347
+ )
348
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(
349
+ base_env,
350
+ add_latest_a11y_info_to_obs=True,
351
+ max_enable_networking_attempts=1,
352
+ )
353
+ wrapped_env.reset()
354
+
355
+ base_env.task_extras.return_value = {
356
+ 'foo': np.array(['bar'], dtype='U'),
357
+ 'some_key': np.array(['some_value'], dtype='U'),
358
+ 'exception': np.array(['fake exception'], dtype='U'),
359
+ }
360
+ with mock.patch.object(
361
+ wrapped_env, 'attempt_enable_networking'
362
+ ) as mock_attempt_enable_networking:
363
+ extras = wrapped_env._fetch_task_extras()
364
+ self.assertNotIn('accessibility_tree', extras)
365
+ self.assertNotIn('full_event', extras)
366
+ # Wait for the the attempt to finish.
367
+ future = wrapped_env._enabling_networking_future
368
+ if future is not None:
369
+ future.result()
370
+ mock_attempt_enable_networking.assert_called_once()
371
+ # The _fetch_task_extras() call inside the next step will force a restart
372
+ self.assertRaises(
373
+ a11y_grpc_wrapper.EnableNetworkingError, wrapped_env.step, {}
374
+ )
375
+
376
+ @mock.patch.object(
377
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
378
+ )
379
+ @mock.patch.object(grpc, 'server', autospec=True)
380
+ def test_configure_grpc(
381
+ self,
382
+ mock_server,
383
+ mock_add_servicer,
384
+ ):
385
+ del mock_server, mock_add_servicer
386
+ base_env = mock.create_autospec(
387
+ env_interface.AndroidEnvInterface, instance=True
388
+ )
389
+ base_env.task_extras.return_value = {
390
+ 'foo': np.array(['bar'], dtype='U'),
391
+ 'some_key': np.array(['some_value'], dtype='U'),
392
+ }
393
+
394
+ base_env.stats.return_value = {'relaunch_count': 1}
395
+ base_env.execute_adb_call.return_value = _ok_response()
396
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
397
+ with mock.patch.object(
398
+ wrapped_env, '_configure_grpc'
399
+ ) as mock_configure_grpc:
400
+ wrapped_env.reset()
401
+ mock_configure_grpc.assert_called_once()
402
+
403
+ @mock.patch.object(
404
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
405
+ )
406
+ @mock.patch.object(grpc, 'server', autospec=True)
407
+ def test_task_extras_raises_before_reset(
408
+ self, unused_mock_server, unused_mock_add_servicer
409
+ ):
410
+ base_env = mock.create_autospec(
411
+ env_interface.AndroidEnvInterface, instance=True
412
+ )
413
+ base_env.stats.return_value = {'relaunch_count': 0}
414
+ base_env.execute_adb_call.return_value = _ok_response()
415
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
416
+ with self.assertRaisesRegex(
417
+ RuntimeError,
418
+ r'You must call \.reset\(\) before calling \.task_extras\(\)',
419
+ ):
420
+ wrapped_env.task_extras(latest_only=False)
421
+
422
+ @mock.patch.object(
423
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
424
+ )
425
+ @mock.patch.object(grpc, 'server', autospec=True)
426
+ def test_extras_accumulate_between_steps(
427
+ self, mock_server, mock_add_servicer
428
+ ):
429
+ del mock_server, mock_add_servicer
430
+ base_env = mock.create_autospec(
431
+ env_interface.AndroidEnvInterface, instance=True
432
+ )
433
+ base_env.stats.return_value = {'relaunch_count': 0}
434
+ base_env.execute_adb_call.return_value = _ok_response()
435
+ base_env.reset.return_value = dm_env.restart(observation={'dummy': 42})
436
+ base_env.step.return_value = dm_env.transition(
437
+ observation={'dummy': 42}, reward=0.0
438
+ )
439
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(
440
+ base_env, add_latest_a11y_info_to_obs=True
441
+ )
442
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
443
+ wrapped_env._fetch_task_extras.return_value = {
444
+ 'full_event': np.array(single_item_dict(), ndmin=1, dtype=object),
445
+ 'accessibility_tree': np.array(empty_forest(), ndmin=1, dtype=object),
446
+ }
447
+ timestep = wrapped_env.reset()
448
+ self.assertIn('a11y_forest', timestep.observation)
449
+ self.assertEqual(timestep.observation['a11y_forest'], empty_forest())
450
+ wrapped_env._fetch_task_extras.return_value = {
451
+ 'full_event': np.array(empty_dict(), ndmin=1, dtype=object),
452
+ 'accessibility_tree': np.array(
453
+ one_window_two_nodes_forest(), ndmin=1, dtype=object
454
+ ),
455
+ }
456
+ timestep = wrapped_env.step({})
457
+ self.assertIn('a11y_forest', timestep.observation)
458
+ self.assertEqual(
459
+ timestep.observation['a11y_forest'], one_window_two_nodes_forest()
460
+ )
461
+ timestep = wrapped_env.step({})
462
+ self.assertIn('a11y_forest', timestep.observation)
463
+ self.assertEqual(
464
+ timestep.observation['a11y_forest'], one_window_two_nodes_forest()
465
+ )
466
+ wrapped_env._fetch_task_extras.return_value = {
467
+ 'full_event': np.array(single_item_dict(), ndmin=1, dtype=object),
468
+ }
469
+ timestep = wrapped_env.step({})
470
+ self.assertIn('a11y_forest', timestep.observation)
471
+ self.assertEqual(
472
+ timestep.observation['a11y_forest'], one_window_two_nodes_forest()
473
+ )
474
+ expected_task_extras = {
475
+ 'full_event': np.array(
476
+ [
477
+ single_item_dict(),
478
+ empty_dict(),
479
+ empty_dict(),
480
+ single_item_dict(),
481
+ ],
482
+ dtype=object,
483
+ ),
484
+ 'accessibility_tree': np.array(
485
+ [
486
+ empty_forest(),
487
+ one_window_two_nodes_forest(),
488
+ one_window_two_nodes_forest(),
489
+ ],
490
+ dtype=object,
491
+ ),
492
+ }
493
+ expected_task_extras_latest = {
494
+ 'full_event': np.array([single_item_dict()], dtype=object),
495
+ 'accessibility_tree': np.array(
496
+ [one_window_two_nodes_forest()], dtype=object
497
+ ),
498
+ }
499
+ task_extras = wrapped_env.task_extras(latest_only=False)
500
+ np.testing.assert_equal(
501
+ task_extras['full_event'], expected_task_extras['full_event']
502
+ )
503
+ np.testing.assert_equal(
504
+ task_extras['accessibility_tree'],
505
+ expected_task_extras['accessibility_tree'],
506
+ )
507
+
508
+ task_extras = wrapped_env.task_extras(latest_only=True)
509
+ np.testing.assert_equal(
510
+ task_extras['full_event'], expected_task_extras_latest['full_event']
511
+ )
512
+ np.testing.assert_equal(
513
+ task_extras['accessibility_tree'],
514
+ expected_task_extras_latest['accessibility_tree'],
515
+ )
516
+
517
+ @mock.patch.object(
518
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
519
+ )
520
+ @mock.patch.object(grpc, 'server', autospec=True)
521
+ def test_a11y_info_disabled(
522
+ self,
523
+ unused_mock_server,
524
+ unused_mock_add_servicer,
525
+ ):
526
+ base_env = mock.create_autospec(
527
+ env_interface.AndroidEnvInterface, instance=True
528
+ )
529
+ base_env.action_spec.return_value = {
530
+ 'action_type': dm_env.specs.Array(shape=(), dtype=np.int32)
531
+ }
532
+ base_env.stats.return_value = {'relaunch_count': 0}
533
+ base_env.execute_adb_call.return_value = _ok_response()
534
+ base_env.reset.return_value = dm_env.restart(observation={'dummy': 42})
535
+ base_env.step.return_value = dm_env.transition(
536
+ observation={'dummy': 42}, reward=0.0
537
+ )
538
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(
539
+ base_env, add_latest_a11y_info_to_obs=False, a11y_info_timeout=1.0
540
+ )
541
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
542
+ wrapped_env._fetch_task_extras.return_value = {
543
+ 'accessibility_tree': np.array(empty_forest(), ndmin=1, dtype=object),
544
+ }
545
+ timestep = wrapped_env.reset()
546
+ self.assertNotIn('a11y_forest', timestep.observation)
547
+ timestep = wrapped_env.step({})
548
+ self.assertNotIn('a11y_forest', timestep.observation)
549
+
550
+ @mock.patch.object(
551
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
552
+ )
553
+ @mock.patch.object(grpc, 'server', autospec=True)
554
+ def test_a11y_info_with_timer_info_present(
555
+ self,
556
+ unused_mock_server,
557
+ unused_mock_add_servicer,
558
+ ):
559
+ base_env = mock.create_autospec(
560
+ env_interface.AndroidEnvInterface, instance=True
561
+ )
562
+ base_env.action_spec.return_value = {
563
+ 'action_type': dm_env.specs.Array(shape=(), dtype=np.int32)
564
+ }
565
+ base_env.stats.return_value = {'relaunch_count': 0}
566
+ base_env.execute_adb_call.return_value = _ok_response()
567
+ base_env.reset.return_value = dm_env.restart(observation={'dummy': 42})
568
+ base_env.step.return_value = dm_env.transition(
569
+ observation={'dummy': 42}, reward=0.0
570
+ )
571
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(
572
+ base_env, add_latest_a11y_info_to_obs=True, a11y_info_timeout=1.0
573
+ )
574
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
575
+ wrapped_env._fetch_task_extras.side_effect = [{
576
+ 'accessibility_tree': np.array(empty_forest(), ndmin=1, dtype=object),
577
+ }]
578
+ timestep = wrapped_env.reset()
579
+ self.assertIn('a11y_forest', timestep.observation)
580
+ self.assertEqual(timestep.observation['a11y_forest'], empty_forest())
581
+
582
+ @mock.patch.object(
583
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
584
+ )
585
+ @mock.patch.object(grpc, 'server', autospec=True)
586
+ @mock.patch.object(time, 'sleep', autospec=True)
587
+ def test_a11y_info_with_timer_task_extra_returned(
588
+ self, unused_mock_server, unused_mock_add_servicer, unused_mock_sleep
589
+ ):
590
+ base_env = mock.create_autospec(
591
+ env_interface.AndroidEnvInterface, instance=True
592
+ )
593
+ base_env.action_spec.return_value = {
594
+ 'action_type': dm_env.specs.Array(shape=(), dtype=np.int32)
595
+ }
596
+ base_env.stats.return_value = {'relaunch_count': 0}
597
+ base_env.execute_adb_call.return_value = _ok_response()
598
+ base_env.reset.return_value = dm_env.restart(observation={'dummy': 42})
599
+ base_env.step.return_value = dm_env.transition(
600
+ observation={'dummy': 42}, reward=0.0
601
+ )
602
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(
603
+ base_env, add_latest_a11y_info_to_obs=True, a11y_info_timeout=1.0
604
+ )
605
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
606
+ wrapped_env._fetch_task_extras.side_effect = [
607
+ {
608
+ 'accessibility_tree': np.array(
609
+ empty_forest(), ndmin=1, dtype=object
610
+ ),
611
+ },
612
+ ]
613
+ timestep = wrapped_env.reset()
614
+ self.assertIn('a11y_forest', timestep.observation)
615
+ self.assertEqual(timestep.observation['a11y_forest'], empty_forest())
616
+
617
+ @mock.patch.object(
618
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
619
+ )
620
+ @mock.patch.object(grpc, 'server', autospec=True)
621
+ @mock.patch.object(time, 'sleep', autospec=True)
622
+ def test_a11y_info_with_timer_from_action(
623
+ self, unused_mock_server, unused_mock_add_servicer, mock_sleep
624
+ ):
625
+ base_env = mock.create_autospec(
626
+ env_interface.AndroidEnvInterface, instance=True
627
+ )
628
+ base_env.action_spec.return_value = {
629
+ 'action_type': dm_env.specs.Array(shape=(), dtype=np.int32)
630
+ }
631
+ base_env.stats.return_value = {'relaunch_count': 0}
632
+ base_env.execute_adb_call.return_value = _ok_response()
633
+ base_env.reset.return_value = dm_env.restart(observation={'dummy': 42})
634
+ base_env.step.return_value = dm_env.transition(
635
+ observation={'dummy': 42}, reward=0.0
636
+ )
637
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(
638
+ base_env, add_latest_a11y_info_to_obs=True, a11y_info_timeout=0.0
639
+ )
640
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
641
+ wrapped_env._fetch_task_extras.side_effect = [
642
+ {
643
+ 'accessibility_tree': np.array(
644
+ empty_forest(), ndmin=1, dtype=object
645
+ ),
646
+ },
647
+ ]
648
+ timestep = wrapped_env.step(action={'wait_time': 1.0})
649
+ self.assertIn('a11y_forest', timestep.observation)
650
+ mock_sleep.assert_called_once()
651
+ self.assertEqual(timestep.observation['a11y_forest'], empty_forest())
652
+
653
+ @mock.patch.object(
654
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
655
+ )
656
+ @mock.patch.object(grpc, 'server', autospec=True)
657
+ def test_task_extras_same_between_calls(self, mock_server, mock_add_servicer):
658
+ del mock_server, mock_add_servicer
659
+ base_env = mock.create_autospec(
660
+ env_interface.AndroidEnvInterface, instance=True
661
+ )
662
+ base_env.stats.return_value = {'relaunch_count': 0}
663
+ base_env.execute_adb_call.return_value = _ok_response()
664
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
665
+ expected_task_extras = {
666
+ 'full_event': np.array(single_item_dict(), ndmin=1, dtype=object),
667
+ 'accessibility_tree': np.array(empty_forest(), ndmin=1, dtype=object),
668
+ }
669
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
670
+ wrapped_env._fetch_task_extras.return_value = expected_task_extras
671
+ wrapped_env.reset()
672
+ task_extras = wrapped_env.task_extras(latest_only=False)
673
+ np.testing.assert_equal(
674
+ task_extras['full_event'], expected_task_extras['full_event']
675
+ )
676
+ np.testing.assert_equal(
677
+ task_extras['accessibility_tree'],
678
+ expected_task_extras['accessibility_tree'],
679
+ )
680
+
681
+ task_extras = wrapped_env.task_extras(latest_only=False)
682
+ np.testing.assert_equal(
683
+ task_extras['full_event'], expected_task_extras['full_event']
684
+ )
685
+ np.testing.assert_equal(
686
+ task_extras['accessibility_tree'],
687
+ expected_task_extras['accessibility_tree'],
688
+ )
689
+
690
+ expected_task_extras = {
691
+ 'full_event': np.array(empty_dict(), ndmin=1, dtype=object),
692
+ 'accessibility_tree': np.array(
693
+ one_window_two_nodes_forest(), ndmin=1, dtype=object
694
+ ),
695
+ }
696
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
697
+ wrapped_env._fetch_task_extras.return_value = expected_task_extras
698
+ wrapped_env.step({})
699
+ task_extras = wrapped_env.task_extras(latest_only=False)
700
+ np.testing.assert_equal(
701
+ task_extras['full_event'], expected_task_extras['full_event']
702
+ )
703
+ np.testing.assert_equal(
704
+ task_extras['accessibility_tree'],
705
+ expected_task_extras['accessibility_tree'],
706
+ )
707
+
708
+ task_extras = wrapped_env.task_extras(latest_only=False)
709
+ np.testing.assert_equal(
710
+ task_extras['full_event'], expected_task_extras['full_event']
711
+ )
712
+ np.testing.assert_equal(
713
+ task_extras['accessibility_tree'],
714
+ expected_task_extras['accessibility_tree'],
715
+ )
716
+
717
+ @mock.patch.object(
718
+ a11y_pb2_grpc, 'add_A11yServiceServicer_to_server', autospec=True
719
+ )
720
+ @mock.patch.object(grpc, 'server', autospec=True)
721
+ def test_task_extras_clear_if_called_between_step(
722
+ self, mock_server, mock_add_servicer
723
+ ):
724
+ del mock_server, mock_add_servicer
725
+ base_env = mock.create_autospec(
726
+ env_interface.AndroidEnvInterface, instance=True
727
+ )
728
+ base_env.stats.return_value = {'relaunch_count': 0}
729
+ base_env.execute_adb_call.return_value = _ok_response()
730
+ wrapped_env = a11y_grpc_wrapper.A11yGrpcWrapper(base_env)
731
+ with mock.patch.object(wrapped_env, '_fetch_task_extras'):
732
+ expected_task_extras = {
733
+ 'full_event': np.array(empty_dict(), ndmin=1, dtype=object),
734
+ 'accessibility_tree': np.array(empty_forest(), ndmin=1, dtype=object),
735
+ }
736
+ wrapped_env._fetch_task_extras.return_value = expected_task_extras
737
+ wrapped_env.reset()
738
+ task_extras = wrapped_env.task_extras(latest_only=False)
739
+ np.testing.assert_equal(
740
+ task_extras['full_event'], expected_task_extras['full_event']
741
+ )
742
+ np.testing.assert_equal(
743
+ task_extras['accessibility_tree'],
744
+ expected_task_extras['accessibility_tree'],
745
+ )
746
+
747
+ expected_task_extras = {
748
+ 'full_event': np.array(single_item_dict(), ndmin=1, dtype=object),
749
+ 'accessibility_tree': np.array(empty_forest(), ndmin=1, dtype=object),
750
+ }
751
+ wrapped_env._fetch_task_extras.return_value = expected_task_extras
752
+ wrapped_env.step({})
753
+ task_extras = wrapped_env.task_extras(latest_only=False)
754
+ np.testing.assert_equal(
755
+ task_extras['full_event'], expected_task_extras['full_event']
756
+ )
757
+ np.testing.assert_equal(
758
+ task_extras['accessibility_tree'],
759
+ expected_task_extras['accessibility_tree'],
760
+ )
761
+ expected_task_extras = {
762
+ 'full_event': np.array(empty_dict(), ndmin=1, dtype=object),
763
+ 'accessibility_tree': np.array(
764
+ one_window_two_nodes_forest(), ndmin=1, dtype=object
765
+ ),
766
+ }
767
+ wrapped_env._fetch_task_extras.return_value = expected_task_extras
768
+ wrapped_env.step({})
769
+ task_extras = wrapped_env.task_extras(latest_only=False)
770
+ np.testing.assert_equal(
771
+ task_extras['full_event'], expected_task_extras['full_event']
772
+ )
773
+ np.testing.assert_equal(
774
+ task_extras['accessibility_tree'],
775
+ expected_task_extras['accessibility_tree'],
776
+ )
777
+
778
+ @parameterized.named_parameters(
779
+ ('none_true', False, False, False, 0),
780
+ ('only_install', True, False, False, 1),
781
+ ('only_start', False, True, False, 1),
782
+ ('only_enable_a11y_tree', False, False, True, 1),
783
+ ('install_and_start_no_a11y_tree', True, True, False, 2),
784
+ ('install_and_a11y_tree', True, False, True, 2),
785
+ ('start_and_a11y_tree', False, True, True, 2),
786
+ ('all_true', True, True, True, 3),
787
+ )
788
+ @mock.patch.object(time, 'sleep', autospec=True)
789
+ def test_apk_install_and_start(
790
+ self,
791
+ install_a11y_forwarding: bool,
792
+ start_a11y_service: bool,
793
+ enable_a11y_tree_logs: bool,
794
+ expected_adb_calls: int,
795
+ unused_mock_sleep,
796
+ ):
797
+ base_env = mock.create_autospec(
798
+ env_interface.AndroidEnvInterface, instance=True
799
+ )
800
+
801
+ side_effects = []
802
+ if install_a11y_forwarding:
803
+ side_effects.append(_ok_response()) # install response
804
+ if start_a11y_service:
805
+ side_effects.append(_ok_response()) # start service response
806
+ if enable_a11y_tree_logs:
807
+ side_effects.append(_ok_response()) # enable_tree_request
808
+
809
+ base_env.execute_adb_call.side_effect = side_effects
810
+
811
+ _ = a11y_grpc_wrapper.A11yGrpcWrapper(
812
+ base_env,
813
+ install_a11y_forwarding=install_a11y_forwarding,
814
+ start_a11y_service=start_a11y_service,
815
+ enable_a11y_tree_info=enable_a11y_tree_logs,
816
+ )
817
+ self.assertEqual(base_env.execute_adb_call.call_count, expected_adb_calls)
818
+
819
+ @mock.patch.object(time, 'sleep', autospec=True)
820
+ def test_component_and_start(self, unused_mock_sleep):
821
+ base_env = mock.create_autospec(
822
+ env_interface.AndroidEnvInterface, instance=True
823
+ )
824
+
825
+ side_effects = []
826
+ side_effects.append(_ok_response()) # install response
827
+ side_effects.append(_ok_response()) # start service response
828
+ side_effects.append(_ok_response()) # enable_tree_request
829
+
830
+ base_env.execute_adb_call.side_effect = side_effects
831
+
832
+ _ = a11y_grpc_wrapper.A11yGrpcWrapper(
833
+ base_env,
834
+ install_a11y_forwarding=True,
835
+ start_a11y_service=True,
836
+ enable_a11y_tree_info=True,
837
+ )
838
+
839
+ # call_args returns a tuple of which the first member is a tuple containing
840
+ # the most recent args the mock was called with, and execute_adb_call only
841
+ # has one arg (so [0][0] to access the AdbRequest).
842
+ self.assertEqual(
843
+ base_env.execute_adb_call.call_args[0][0].send_broadcast.component,
844
+ 'com.google.androidenv.accessibilityforwarder/com.google.androidenv.accessibilityforwarder.FlagsBroadcastReceiver',
845
+ )
846
+
847
+
848
+ if __name__ == '__main__':
849
+ absltest.main()