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
@@ -1,5 +1,5 @@
1
1
  # coding=utf-8
2
- # Copyright 2022 DeepMind Technologies Limited.
2
+ # Copyright 2024 DeepMind Technologies Limited.
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -23,13 +23,17 @@ from unittest import mock
23
23
  from absl.testing import absltest
24
24
  from android_env.components import adb_call_parser
25
25
  from android_env.components import adb_controller
26
+ from android_env.components import config_classes
26
27
  from android_env.components.simulators.emulator import emulator_launcher
27
28
  from android_env.components.simulators.emulator import emulator_simulator
29
+ from android_env.proto import state_pb2
28
30
  import grpc
29
31
  from PIL import Image
32
+ import portpicker
30
33
 
31
34
  from android_env.proto import emulator_controller_pb2
32
35
  from android_env.proto import emulator_controller_pb2_grpc
36
+ from android_env.proto import snapshot_service_pb2
33
37
 
34
38
 
35
39
  class EmulatorSimulatorTest(absltest.TestCase):
@@ -47,21 +51,16 @@ class EmulatorSimulatorTest(absltest.TestCase):
47
51
 
48
52
  self._grpc_channel = mock.create_autospec(grpc.Channel)
49
53
  mock.patch.object(
50
- grpc.aio, 'secure_channel',
51
- return_value=self._grpc_channel).start()
54
+ grpc.aio, 'secure_channel', return_value=self._grpc_channel).start()
52
55
  mock.patch.object(
53
- grpc, 'secure_channel',
54
- return_value=self._grpc_channel).start()
56
+ grpc, 'secure_channel', return_value=self._grpc_channel).start()
55
57
  mock.patch.object(
56
58
  grpc, 'local_channel_credentials',
57
59
  return_value=self._grpc_channel).start()
58
60
  self._mock_future = mock.create_autospec(grpc.Future)
59
61
  mock.patch.object(
60
- grpc, 'channel_ready_future',
61
- return_value=self._mock_future).start()
62
- mock.patch.object(
63
- time, 'time',
64
- return_value=12345).start()
62
+ grpc, 'channel_ready_future', return_value=self._mock_future).start()
63
+ mock.patch.object(time, 'time', return_value=12345).start()
65
64
 
66
65
  mock.patch.object(
67
66
  adb_controller, 'AdbController',
@@ -76,67 +75,110 @@ class EmulatorSimulatorTest(absltest.TestCase):
76
75
  return_value=self._launcher).start()
77
76
 
78
77
  def test_adb_device_name_not_empty(self):
79
- tmp_dir = absltest.get_default_test_tmpdir()
80
- simulator = emulator_simulator.EmulatorSimulator(
81
- tmp_dir=tmp_dir,
82
- emulator_launcher_args={'grpc_port': 1234},
83
- adb_controller_args={
84
- 'adb_path': '/my/adb',
85
- 'adb_server_port': 5037,
86
- })
78
+ config = config_classes.EmulatorConfig(
79
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
80
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
81
+ ),
82
+ adb_controller=config_classes.AdbControllerConfig(
83
+ adb_path='/my/adb',
84
+ adb_server_port=5037,
85
+ ),
86
+ )
87
+ simulator = emulator_simulator.EmulatorSimulator(config)
87
88
  self.assertNotEmpty(simulator.adb_device_name())
88
89
 
89
90
  @mock.patch.object(os.path, 'exists', autospec=True, return_value=True)
90
91
  @mock.patch.object(builtins, 'open', autospec=True)
91
92
  def test_logfile_path(self, mock_open, unused_mock_exists):
92
- tmp_dir = absltest.get_default_test_tmpdir()
93
- simulator = emulator_simulator.EmulatorSimulator(
94
- tmp_dir=tmp_dir,
93
+ config = config_classes.EmulatorConfig(
95
94
  logfile_path='fake/logfile/path',
96
- emulator_launcher_args={'grpc_port': 1234},
97
- adb_controller_args={
98
- 'adb_path': '/my/adb',
99
- 'adb_server_port': 5037,
100
- })
95
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
96
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
97
+ ),
98
+ adb_controller=config_classes.AdbControllerConfig(
99
+ adb_path='/my/adb',
100
+ adb_server_port=5037,
101
+ ),
102
+ )
103
+ simulator = emulator_simulator.EmulatorSimulator(config)
101
104
  mock_open.return_value.__enter__.return_value.read.return_value = (
102
105
  'fake_logs'.encode('utf-8'))
103
106
  logs = simulator.get_logs()
104
107
  mock_open.assert_called_once_with('fake/logfile/path', 'rb')
105
108
  self.assertEqual(logs, 'fake_logs')
106
109
 
107
- def test_launch(self):
108
-
109
- # Make sure that adb_controller is started before Emulator is launched.
110
+ @mock.patch.object(portpicker, 'is_port_free', return_value=True)
111
+ def test_grpc_port(self, unused_mock_portpicker):
112
+
113
+ launcher_config = config_classes.EmulatorLauncherConfig(
114
+ tmp_dir=self.create_tempdir().full_path
115
+ )
116
+ config = config_classes.EmulatorConfig(
117
+ emulator_launcher=launcher_config,
118
+ adb_controller=config_classes.AdbControllerConfig(
119
+ adb_path='/my/adb',
120
+ adb_server_port=5037,
121
+ ),
122
+ )
123
+ simulator = emulator_simulator.EmulatorSimulator(config)
124
+ self.assertEqual(launcher_config.grpc_port, 8554)
125
+
126
+ @mock.patch.object(portpicker, 'is_port_free', return_value=False)
127
+ def test_grpc_port_unavailable(self, unused_mock_portpicker):
128
+
129
+ launcher_config = config_classes.EmulatorLauncherConfig(
130
+ tmp_dir=self.create_tempdir().full_path
131
+ )
132
+ config = config_classes.EmulatorConfig(
133
+ emulator_launcher=launcher_config,
134
+ adb_controller=config_classes.AdbControllerConfig(
135
+ adb_path='/my/adb',
136
+ adb_server_port=5037,
137
+ ),
138
+ )
139
+ simulator = emulator_simulator.EmulatorSimulator(config)
140
+ self.assertNotEqual(launcher_config.grpc_port, 8554)
141
+
142
+ def test_launch_operation_order(self):
143
+ """Makes sure that adb_controller is started before Emulator is launched."""
144
+
145
+ # Arrange.
110
146
  call_order = []
111
- self._adb_controller.init_server.side_effect = (
112
- lambda *a, **kw: call_order.append('init_server'))
147
+ self._adb_controller.init_server.side_effect = lambda: call_order.append(
148
+ 'init_server'
149
+ )
113
150
  self._launcher.launch_emulator_process.side_effect = (
114
- lambda *a, **kw: call_order.append('launch_emulator_process'))
115
-
116
- tmp_dir = absltest.get_default_test_tmpdir()
117
- simulator = emulator_simulator.EmulatorSimulator(
118
- tmp_dir=tmp_dir,
119
- emulator_launcher_args={'grpc_port': 1234},
120
- adb_controller_args={
121
- 'adb_path': '/my/adb',
122
- 'adb_server_port': 5037,
123
- 'prompt_regex': 'awesome>',
124
- })
125
-
126
- # The simulator should launch and not crash.
127
- simulator.launch()
128
-
151
+ lambda: call_order.append('launch_emulator_process')
152
+ )
153
+ config = config_classes.EmulatorConfig(
154
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
155
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
156
+ ),
157
+ adb_controller=config_classes.AdbControllerConfig(
158
+ adb_path='/my/adb',
159
+ adb_server_port=5037,
160
+ ),
161
+ )
162
+ simulator = emulator_simulator.EmulatorSimulator(config)
163
+
164
+ # Act.
165
+ simulator.launch() # The simulator should launch and not crash.
166
+
167
+ # Assert.
168
+ # The adb server should be initialized before launching the emulator.
129
169
  self.assertEqual(call_order, ['init_server', 'launch_emulator_process'])
130
170
 
131
171
  def test_close(self):
132
- tmp_dir = absltest.get_default_test_tmpdir()
133
- simulator = emulator_simulator.EmulatorSimulator(
134
- tmp_dir=tmp_dir,
135
- emulator_launcher_args={'grpc_port': 1234},
136
- adb_controller_args={
137
- 'adb_path': '/my/adb',
138
- 'adb_server_port': 5037,
139
- })
172
+ config = config_classes.EmulatorConfig(
173
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
174
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
175
+ ),
176
+ adb_controller=config_classes.AdbControllerConfig(
177
+ adb_path='/my/adb',
178
+ adb_server_port=5037,
179
+ ),
180
+ )
181
+ simulator = emulator_simulator.EmulatorSimulator(config)
140
182
 
141
183
  # The simulator should launch and not crash.
142
184
  simulator.launch()
@@ -146,15 +188,36 @@ class EmulatorSimulatorTest(absltest.TestCase):
146
188
  # resources.
147
189
  simulator.close()
148
190
 
149
- def test_restart(self):
150
- tmp_dir = absltest.get_default_test_tmpdir()
151
- simulator = emulator_simulator.EmulatorSimulator(
152
- tmp_dir=tmp_dir,
153
- emulator_launcher_args={'grpc_port': 1234},
154
- adb_controller_args={
155
- 'adb_path': '/my/adb',
156
- 'adb_server_port': 5037,
157
- })
191
+ def test_value_error_if_launch_attempt_params_incorrect(self):
192
+ self.assertRaises(
193
+ ValueError,
194
+ emulator_simulator.EmulatorSimulator,
195
+ config=config_classes.EmulatorConfig(
196
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
197
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
198
+ ),
199
+ adb_controller=config_classes.AdbControllerConfig(
200
+ adb_path='/my/adb',
201
+ adb_server_port=5037,
202
+ ),
203
+ launch_n_times_without_reboot=2,
204
+ launch_n_times_without_reinstall=1,
205
+ ),
206
+ )
207
+
208
+ def test_launch_attempt_reboot(self):
209
+ config = config_classes.EmulatorConfig(
210
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
211
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
212
+ ),
213
+ adb_controller=config_classes.AdbControllerConfig(
214
+ adb_path='/my/adb',
215
+ adb_server_port=5037,
216
+ ),
217
+ launch_n_times_without_reboot=1,
218
+ launch_n_times_without_reinstall=2,
219
+ )
220
+ simulator = emulator_simulator.EmulatorSimulator(config)
158
221
 
159
222
  # The simulator should launch and not crash.
160
223
  simulator.launch()
@@ -162,20 +225,82 @@ class EmulatorSimulatorTest(absltest.TestCase):
162
225
  self._launcher.launch_emulator_process.assert_called_once()
163
226
  self._launcher.reset_mock()
164
227
 
165
- # For whatever reason clients may want to restart the EmulatorSimulator.
166
- simulator.restart()
228
+ # Launch attempt 2.
229
+ simulator.launch()
167
230
  self._launcher.confirm_shutdown.assert_called_once()
231
+ self._launcher.close.assert_not_called()
232
+ self._launcher.launch_emulator_process.assert_called_once()
233
+
234
+ def test_launch_attempt_reinstall_after_zero_attempts(self):
235
+ config = config_classes.EmulatorConfig(
236
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
237
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
238
+ ),
239
+ adb_controller=config_classes.AdbControllerConfig(
240
+ adb_path='/my/adb',
241
+ adb_server_port=5037,
242
+ ),
243
+ launch_n_times_without_reboot=0,
244
+ launch_n_times_without_reinstall=0,
245
+ )
246
+ simulator = emulator_simulator.EmulatorSimulator(config)
247
+
248
+ # The simulator should not reboot or reinstall on its very first launch.
249
+ simulator.launch()
250
+ self._launcher.launch_emulator_process.assert_called_once()
251
+ self._launcher.confirm_shutdown.assert_not_called()
252
+ self._launcher.close.assert_not_called()
253
+
254
+ # Every subsequent attempt should reboot and reinstall.
255
+ self._launcher.reset_mock()
256
+ simulator.launch()
257
+ self._launcher.confirm_shutdown.assert_called_once()
258
+ self._launcher.close.assert_called_once() # Now this should `close()`.
259
+ self._launcher.launch_emulator_process.assert_called_once()
260
+
261
+ def test_launch_attempt_reinstall(self):
262
+ config = config_classes.EmulatorConfig(
263
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
264
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
265
+ ),
266
+ adb_controller=config_classes.AdbControllerConfig(
267
+ adb_path='/my/adb',
268
+ adb_server_port=5037,
269
+ ),
270
+ launch_n_times_without_reboot=1,
271
+ launch_n_times_without_reinstall=2,
272
+ )
273
+ simulator = emulator_simulator.EmulatorSimulator(config)
274
+
275
+ # The simulator should launch and not crash.
276
+ simulator.launch()
277
+ self._launcher.launch_emulator_process.assert_called_once()
278
+
279
+ # Launch attempt 2.
280
+ self._launcher.reset_mock()
281
+ simulator.launch()
282
+ self._launcher.confirm_shutdown.assert_called_once()
283
+ self._launcher.close.assert_not_called() # Reboots don't `close()`.
284
+ self._launcher.launch_emulator_process.assert_called_once()
285
+
286
+ # Launch attempt 3.
287
+ self._launcher.reset_mock()
288
+ simulator.launch()
289
+ self._launcher.confirm_shutdown.assert_called_once()
290
+ self._launcher.close.assert_called_once() # Now this should `close()`.
168
291
  self._launcher.launch_emulator_process.assert_called_once()
169
292
 
170
293
  def test_get_screenshot(self):
171
- tmp_dir = absltest.get_default_test_tmpdir()
172
- simulator = emulator_simulator.EmulatorSimulator(
173
- tmp_dir=tmp_dir,
174
- emulator_launcher_args={'grpc_port': 1234},
175
- adb_controller_args={
176
- 'adb_path': '/my/adb',
177
- 'adb_server_port': 5037,
178
- })
294
+ config = config_classes.EmulatorConfig(
295
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
296
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
297
+ ),
298
+ adb_controller=config_classes.AdbControllerConfig(
299
+ adb_path='/my/adb',
300
+ adb_server_port=5037,
301
+ ),
302
+ )
303
+ simulator = emulator_simulator.EmulatorSimulator(config)
179
304
 
180
305
  # The simulator should launch and not crash.
181
306
  simulator.launch()
@@ -191,15 +316,113 @@ class EmulatorSimulatorTest(absltest.TestCase):
191
316
  # and it should have 3 channels (RGB).
192
317
  self.assertEqual(screenshot.shape, (1234, 5678, 3))
193
318
 
319
+ def test_load_state(self):
320
+ config = config_classes.EmulatorConfig(
321
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
322
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
323
+ ),
324
+ adb_controller=config_classes.AdbControllerConfig(
325
+ adb_path='/my/adb',
326
+ adb_server_port=5037,
327
+ ),
328
+ )
329
+ simulator = emulator_simulator.EmulatorSimulator(config)
330
+
331
+ # The simulator should launch and not crash.
332
+ simulator.launch()
333
+
334
+ with mock.patch.object(
335
+ simulator, '_snapshot_stub', create_autospec=True
336
+ ) as mock_snapshot_stub:
337
+ snapshot_list = snapshot_service_pb2.SnapshotList()
338
+ snapshot_list.snapshots.add(snapshot_id='snapshot_name_foo')
339
+ snapshot_list.snapshots.add(snapshot_id='snapshot_name_bar')
340
+ mock_snapshot_stub.ListSnapshots.return_value = snapshot_list
341
+ mock_snapshot_stub.LoadSnapshot.return_value = (
342
+ snapshot_service_pb2.SnapshotPackage(success=True)
343
+ )
344
+ load_response = simulator.load_state(
345
+ request=state_pb2.LoadStateRequest(
346
+ args={'snapshot_name': 'snapshot_name_foo'}
347
+ )
348
+ )
349
+ self.assertEqual(
350
+ load_response.status, state_pb2.LoadStateResponse.Status.OK
351
+ )
352
+ load_response = simulator.load_state(
353
+ request=state_pb2.LoadStateRequest(
354
+ args={'snapshot_name': 'snapshot_name_baz'}
355
+ )
356
+ )
357
+ self.assertEqual(
358
+ load_response.status, state_pb2.LoadStateResponse.Status.NOT_FOUND
359
+ )
360
+ mock_snapshot_stub.LoadSnapshot.return_value = (
361
+ snapshot_service_pb2.SnapshotPackage(success=False, err=b'error')
362
+ )
363
+ load_response = simulator.load_state(
364
+ request=state_pb2.LoadStateRequest(
365
+ args={'snapshot_name': 'snapshot_name_bar'}
366
+ )
367
+ )
368
+ self.assertEqual(
369
+ load_response.status, state_pb2.LoadStateResponse.Status.ERROR
370
+ )
371
+ self.assertEqual(load_response.error_message, 'error')
372
+
373
+ def test_save_state(self):
374
+ config = config_classes.EmulatorConfig(
375
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
376
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
377
+ ),
378
+ adb_controller=config_classes.AdbControllerConfig(
379
+ adb_path='/my/adb',
380
+ adb_server_port=5037,
381
+ ),
382
+ )
383
+ simulator = emulator_simulator.EmulatorSimulator(config)
384
+
385
+ # The simulator should launch and not crash.
386
+ simulator.launch()
387
+
388
+ with mock.patch.object(
389
+ simulator, '_snapshot_stub', create_autospec=True
390
+ ) as mock_snapshot_stub:
391
+ mock_snapshot_stub.SaveSnapshot.return_value = (
392
+ snapshot_service_pb2.SnapshotPackage(success=True)
393
+ )
394
+ save_response = simulator.save_state(
395
+ request=state_pb2.SaveStateRequest(
396
+ args={'snapshot_name': 'snapshot_name_foo'}
397
+ )
398
+ )
399
+ self.assertEqual(
400
+ save_response.status, state_pb2.SaveStateResponse.Status.OK
401
+ )
402
+ mock_snapshot_stub.SaveSnapshot.return_value = (
403
+ snapshot_service_pb2.SnapshotPackage(success=False, err=b'error')
404
+ )
405
+ save_response = simulator.save_state(
406
+ request=state_pb2.SaveStateRequest(
407
+ args={'snapshot_name': 'snapshot_name_bar'}
408
+ )
409
+ )
410
+ self.assertEqual(
411
+ save_response.status, state_pb2.SaveStateResponse.Status.ERROR
412
+ )
413
+ self.assertEqual(save_response.error_message, 'error')
414
+
194
415
  def test_send_touch(self):
195
- tmp_dir = absltest.get_default_test_tmpdir()
196
- simulator = emulator_simulator.EmulatorSimulator(
197
- tmp_dir=tmp_dir,
198
- emulator_launcher_args={'grpc_port': 1234},
199
- adb_controller_args={
200
- 'adb_path': '/my/adb',
201
- 'adb_server_port': 5037,
202
- })
416
+ config = config_classes.EmulatorConfig(
417
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
418
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
419
+ ),
420
+ adb_controller=config_classes.AdbControllerConfig(
421
+ adb_path='/my/adb',
422
+ adb_server_port=5037,
423
+ ),
424
+ )
425
+ simulator = emulator_simulator.EmulatorSimulator(config)
203
426
 
204
427
  # The simulator should launch and not crash.
205
428
  simulator.launch()
@@ -247,14 +470,16 @@ class EmulatorSimulatorTest(absltest.TestCase):
247
470
  ])
248
471
 
249
472
  def test_send_key(self):
250
- tmp_dir = absltest.get_default_test_tmpdir()
251
- simulator = emulator_simulator.EmulatorSimulator(
252
- tmp_dir=tmp_dir,
253
- emulator_launcher_args={'grpc_port': 1234},
254
- adb_controller_args={
255
- 'adb_path': '/my/adb',
256
- 'adb_server_port': 5037,
257
- })
473
+ config = config_classes.EmulatorConfig(
474
+ emulator_launcher=config_classes.EmulatorLauncherConfig(
475
+ grpc_port=1234, tmp_dir=self.create_tempdir().full_path
476
+ ),
477
+ adb_controller=config_classes.AdbControllerConfig(
478
+ adb_path='/my/adb',
479
+ adb_server_port=5037,
480
+ ),
481
+ )
482
+ simulator = emulator_simulator.EmulatorSimulator(config)
258
483
 
259
484
  # The simulator should launch and not crash.
260
485
  simulator.launch()
@@ -1,5 +1,5 @@
1
1
  # coding=utf-8
2
- # Copyright 2022 DeepMind Technologies Limited.
2
+ # Copyright 2024 DeepMind Technologies Limited.
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  # coding=utf-8
2
- # Copyright 2022 DeepMind Technologies Limited.
2
+ # Copyright 2024 DeepMind Technologies Limited.
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -18,16 +18,16 @@
18
18
  import random
19
19
  import threading
20
20
  import time
21
- from typing import List, Optional, Tuple
22
21
 
23
22
  from absl import logging
24
23
  from android_env.components import adb_controller
24
+ from android_env.components import config_classes
25
25
  from android_env.components import log_stream
26
26
  from android_env.components.simulators import base_simulator
27
27
  import numpy as np
28
28
 
29
29
 
30
- class FakeStream():
30
+ class FakeStream:
31
31
  """This class simulates the logs coming from ADB."""
32
32
 
33
33
  def __init__(self):
@@ -77,12 +77,15 @@ class FakeLogStream(log_stream.LogStream):
77
77
  class FakeAdbController(adb_controller.AdbController):
78
78
  """Fake adb controller for FakeSimulator."""
79
79
 
80
- def execute_command(self,
81
- args: List[str],
82
- timeout: Optional[float] = None) -> bytes:
80
+ def execute_command(
81
+ self,
82
+ args: list[str],
83
+ timeout: float | None = None,
84
+ device_specific: bool = True,
85
+ ) -> bytes:
83
86
  """Returns fake output for adb commands."""
84
87
 
85
- del timeout
88
+ del timeout, device_specific
86
89
 
87
90
  # Fake "service is ready" output.
88
91
  if args[:3] == ['shell', 'service', 'check']:
@@ -103,18 +106,10 @@ class FakeAdbController(adb_controller.AdbController):
103
106
  class FakeSimulator(base_simulator.BaseSimulator):
104
107
  """FakeSimulator class."""
105
108
 
106
- def __init__(self,
107
- screen_dimensions: Tuple[int, int] = (480, 320),
108
- **kwargs):
109
- """FakeSimulator class that can replace EmulatorSimulator in AndroidEnv.
110
-
111
- Args:
112
- screen_dimensions: desired screen dimensions in pixels. This determines
113
- the shape of the screenshots returned by get_screenshot().
114
- **kwargs: other keyword arguments for the base class.
115
- """
116
- super().__init__(**kwargs)
117
- self._screen_dimensions = np.array(screen_dimensions)
109
+ def __init__(self, config: config_classes.FakeSimulatorConfig):
110
+ """FakeSimulator class that can replace EmulatorSimulator in AndroidEnv."""
111
+ super().__init__(config)
112
+ self._screen_dimensions = np.array(config.screen_dimensions)
118
113
  logging.info('Created FakeSimulator.')
119
114
 
120
115
  def get_logs(self) -> str:
@@ -124,23 +119,20 @@ class FakeSimulator(base_simulator.BaseSimulator):
124
119
  return 'fake_simulator'
125
120
 
126
121
  def create_adb_controller(self):
127
- return FakeAdbController()
122
+ return FakeAdbController(config_classes.AdbControllerConfig())
128
123
 
129
124
  def create_log_stream(self) -> log_stream.LogStream:
130
125
  return FakeLogStream()
131
126
 
132
- def _restart_impl(self) -> None:
133
- pass
134
-
135
127
  def _launch_impl(self) -> None:
136
128
  pass
137
129
 
138
- def send_touch(self, touches: List[Tuple[int, int, bool, int]]) -> None:
130
+ def send_touch(self, touches: list[tuple[int, int, bool, int]]) -> None:
139
131
  del touches
140
132
 
141
133
  def send_key(self, keycode: np.int32, event_type: str) -> None:
142
134
  del keycode, event_type
143
135
 
144
- def get_screenshot(self) -> np.ndarray:
136
+ def _get_screenshot_impl(self) -> np.ndarray:
145
137
  return np.random.randint(
146
138
  low=0, high=255, size=(*self._screen_dimensions, 3), dtype=np.uint8)