langfun 0.1.2.dev202509210803__py3-none-any.whl → 0.1.2.dev202509230805__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.
Potentially problematic release.
This version of langfun might be problematic. Click here for more details.
- langfun/env/__init__.py +0 -1
- langfun/env/base_environment.py +126 -66
- langfun/env/base_feature.py +79 -40
- langfun/env/base_sandbox.py +630 -124
- langfun/env/base_test.py +1283 -474
- langfun/env/interface.py +462 -334
- langfun/env/load_balancers.py +4 -4
- langfun/env/load_balancers_test.py +32 -57
- {langfun-0.1.2.dev202509210803.dist-info → langfun-0.1.2.dev202509230805.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202509210803.dist-info → langfun-0.1.2.dev202509230805.dist-info}/RECORD +13 -13
- {langfun-0.1.2.dev202509210803.dist-info → langfun-0.1.2.dev202509230805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202509210803.dist-info → langfun-0.1.2.dev202509230805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202509210803.dist-info → langfun-0.1.2.dev202509230805.dist-info}/top_level.txt +0 -0
langfun/env/base_test.py
CHANGED
|
@@ -11,21 +11,23 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
import concurrent.futures
|
|
15
14
|
import contextlib
|
|
16
15
|
import time
|
|
17
|
-
from typing import Iterator
|
|
16
|
+
from typing import Any, Iterator, Type
|
|
18
17
|
import unittest
|
|
19
18
|
|
|
20
19
|
from langfun.env import base_environment
|
|
21
20
|
from langfun.env import base_feature
|
|
22
21
|
from langfun.env import base_sandbox
|
|
23
22
|
from langfun.env import interface
|
|
23
|
+
import pyglove as pg
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class TestingEnvironment(base_environment.BaseEnvironment):
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
simulate_start_error: Type[BaseException] | None = None
|
|
29
|
+
simulate_shutdown_error: Type[BaseException] | None = None
|
|
30
|
+
simulate_ping_error: Type[BaseException] | None = None
|
|
29
31
|
keepalive_interval: float | None = 60.0
|
|
30
32
|
offline: bool = False
|
|
31
33
|
|
|
@@ -33,21 +35,16 @@ class TestingEnvironment(base_environment.BaseEnvironment):
|
|
|
33
35
|
def id(self) -> interface.EnvironmentId:
|
|
34
36
|
return interface.EnvironmentId('testing-env')
|
|
35
37
|
|
|
36
|
-
def
|
|
37
|
-
self.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for sandbox in self._sandbox_pool:
|
|
41
|
-
sandbox.rebind(
|
|
42
|
-
ping_simulate_error=offline,
|
|
43
|
-
skip_notification=True,
|
|
44
|
-
raise_on_no_change=False
|
|
45
|
-
)
|
|
38
|
+
def wait_for_next_maintenance(self):
|
|
39
|
+
maintenance_count = self._maintenance_count
|
|
40
|
+
while self._maintenance_count == maintenance_count:
|
|
41
|
+
time.sleep(0.1)
|
|
46
42
|
|
|
47
43
|
def _create_sandbox(
|
|
48
44
|
self,
|
|
49
45
|
sandbox_id: str,
|
|
50
|
-
reusable: bool
|
|
46
|
+
reusable: bool,
|
|
47
|
+
proactive_session_setup: bool,
|
|
51
48
|
) -> interface.Sandbox:
|
|
52
49
|
if self.offline:
|
|
53
50
|
raise interface.EnvironmentError(
|
|
@@ -61,44 +58,91 @@ class TestingEnvironment(base_environment.BaseEnvironment):
|
|
|
61
58
|
sandbox_id=sandbox_id
|
|
62
59
|
),
|
|
63
60
|
reusable=reusable,
|
|
64
|
-
|
|
61
|
+
proactive_session_setup=proactive_session_setup,
|
|
62
|
+
simulate_start_error=self.simulate_start_error,
|
|
63
|
+
simulate_shutdown_error=self.simulate_shutdown_error,
|
|
64
|
+
simulate_ping_error=self.simulate_ping_error,
|
|
65
65
|
keepalive_interval=self.keepalive_interval,
|
|
66
66
|
)
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
class TestingSandbox(base_sandbox.BaseSandbox):
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
simulate_start_error: Type[BaseException] | None = None
|
|
72
|
+
simulate_shutdown_error: Type[BaseException] | None = None
|
|
73
|
+
simulate_ping_error: Type[BaseException] | None = None
|
|
72
74
|
|
|
73
75
|
def _on_bound(self) -> None:
|
|
74
76
|
super()._on_bound()
|
|
75
77
|
self._shell_history = []
|
|
76
78
|
self._ping_history = []
|
|
77
79
|
|
|
80
|
+
def _raise_error(self, message, error_type: Type[BaseException], **kwargs):
|
|
81
|
+
if (error_type is interface.SandboxStateError or
|
|
82
|
+
issubclass(error_type, interface.SandboxStateError)):
|
|
83
|
+
kwargs['sandbox'] = self
|
|
84
|
+
raise error_type(message, **kwargs)
|
|
85
|
+
else:
|
|
86
|
+
raise error_type(message)
|
|
87
|
+
|
|
88
|
+
def wait_until(
|
|
89
|
+
self,
|
|
90
|
+
status: interface.Sandbox.Status | tuple[interface.Sandbox.Status, ...]
|
|
91
|
+
) -> None:
|
|
92
|
+
if not isinstance(status, tuple):
|
|
93
|
+
status = (status,)
|
|
94
|
+
while self.status not in status:
|
|
95
|
+
time.sleep(0.1)
|
|
96
|
+
|
|
97
|
+
def wait_until_not(
|
|
98
|
+
self,
|
|
99
|
+
status: interface.Sandbox.Status | tuple[interface.Sandbox.Status, ...]
|
|
100
|
+
) -> None:
|
|
101
|
+
if not isinstance(status, tuple):
|
|
102
|
+
status = (status,)
|
|
103
|
+
while self.status in status:
|
|
104
|
+
time.sleep(0.1)
|
|
105
|
+
|
|
106
|
+
def wait_until_next_housekeep(self) -> None:
|
|
107
|
+
housekeep_count = self._housekeep_count
|
|
108
|
+
while self._housekeep_count == housekeep_count:
|
|
109
|
+
time.sleep(0.1)
|
|
110
|
+
|
|
111
|
+
def _start(self) -> None:
|
|
112
|
+
if self.simulate_start_error:
|
|
113
|
+
self._raise_error('Sandbox start error', self.simulate_start_error)
|
|
114
|
+
super()._start()
|
|
115
|
+
|
|
116
|
+
def _shutdown(self) -> None:
|
|
117
|
+
if self.simulate_shutdown_error:
|
|
118
|
+
self._raise_error('Sandbox shutdown error', self.simulate_shutdown_error)
|
|
119
|
+
super()._shutdown()
|
|
120
|
+
|
|
78
121
|
@base_sandbox.sandbox_service(critical_errors=(RuntimeError,))
|
|
79
122
|
def shell(
|
|
80
123
|
self,
|
|
81
124
|
code: str,
|
|
82
|
-
|
|
125
|
+
raise_error: Type[BaseException] | None = None,
|
|
83
126
|
) -> str:
|
|
84
127
|
self._shell_history.append(code)
|
|
85
|
-
if
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
message = f'shell {len(self._shell_history)} failed'
|
|
89
|
-
if must_succeed:
|
|
90
|
-
raise RuntimeError(message)
|
|
91
|
-
raise ValueError(message)
|
|
128
|
+
if raise_error is not None:
|
|
129
|
+
self._raise_error(f'shell "{code}" failed', raise_error)
|
|
130
|
+
return f'shell "{code}" succeeded'
|
|
92
131
|
|
|
93
132
|
def _ping(self) -> None:
|
|
94
|
-
self._ping_history.append(not self.
|
|
95
|
-
if self.
|
|
96
|
-
|
|
133
|
+
self._ping_history.append(not self.simulate_ping_error)
|
|
134
|
+
if self.simulate_ping_error:
|
|
135
|
+
self._raise_error('Ping error', self.simulate_ping_error, code='ping')
|
|
97
136
|
|
|
98
137
|
|
|
99
138
|
class TestingFeature(base_feature.BaseFeature):
|
|
100
139
|
housekeep_interval = 0
|
|
101
|
-
|
|
140
|
+
setup_session_delay: float = 0.0
|
|
141
|
+
simulate_housekeep_error: Type[BaseException] | None = None
|
|
142
|
+
simulate_setup_error: Type[BaseException] | None = None
|
|
143
|
+
simulate_teardown_error: Type[BaseException] | None = None
|
|
144
|
+
simulate_setup_session_error: Type[BaseException] | None = None
|
|
145
|
+
simulate_teardown_session_error: Type[BaseException] | None = None
|
|
102
146
|
|
|
103
147
|
class Service:
|
|
104
148
|
"""Sandbox."""
|
|
@@ -106,14 +150,40 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
106
150
|
def __init__(self, sandbox: interface.Sandbox):
|
|
107
151
|
self._sandbox = sandbox
|
|
108
152
|
|
|
109
|
-
def do(self, code: str):
|
|
110
|
-
self._sandbox.shell(code)
|
|
153
|
+
def do(self, code: str, raise_error: Type[BaseException] | None = None):
|
|
154
|
+
self._sandbox.shell(code, raise_error=raise_error)
|
|
155
|
+
|
|
156
|
+
def _raise_error(self, message, error_type: Type[BaseException], **kwargs):
|
|
157
|
+
self._sandbox._raise_error(message, error_type, **kwargs)
|
|
111
158
|
|
|
112
159
|
def _setup(self) -> None:
|
|
113
|
-
|
|
160
|
+
if self.simulate_setup_error:
|
|
161
|
+
self._raise_error(f'{self.name} setup error', self.simulate_setup_error)
|
|
162
|
+
self.sandbox.shell(f'"{self.name}" setup')
|
|
114
163
|
|
|
115
164
|
def _teardown(self) -> None:
|
|
116
|
-
|
|
165
|
+
if self.simulate_teardown_error:
|
|
166
|
+
self._raise_error(
|
|
167
|
+
f'{self.name} teardown error', self.simulate_teardown_error
|
|
168
|
+
)
|
|
169
|
+
self.sandbox.shell(f'"{self.name}" teardown')
|
|
170
|
+
|
|
171
|
+
def _setup_session(self) -> None:
|
|
172
|
+
if self.setup_session_delay > 0:
|
|
173
|
+
time.sleep(self.setup_session_delay)
|
|
174
|
+
|
|
175
|
+
if self.simulate_setup_session_error:
|
|
176
|
+
self._raise_error(
|
|
177
|
+
'Feature session setup error', self.simulate_setup_session_error
|
|
178
|
+
)
|
|
179
|
+
self.sandbox.shell(f'"{self.name}" setup session')
|
|
180
|
+
|
|
181
|
+
def _teardown_session(self) -> None:
|
|
182
|
+
if self.simulate_teardown_session_error:
|
|
183
|
+
self._raise_error(
|
|
184
|
+
'Feature session teardown error', self.simulate_teardown_session_error
|
|
185
|
+
)
|
|
186
|
+
self.sandbox.shell(f'"{self.name}" teardown session')
|
|
117
187
|
|
|
118
188
|
@base_sandbox.sandbox_service()
|
|
119
189
|
def num_shell_calls(self) -> None:
|
|
@@ -121,12 +191,17 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
121
191
|
|
|
122
192
|
@base_sandbox.sandbox_service()
|
|
123
193
|
def bad_shell_call(self) -> None:
|
|
124
|
-
self.sandbox.shell('bad command')
|
|
194
|
+
self.sandbox.shell('bad command', raise_error=RuntimeError)
|
|
125
195
|
|
|
126
196
|
@base_sandbox.sandbox_service()
|
|
127
197
|
def show_session_id(self):
|
|
128
198
|
return self.session_id
|
|
129
199
|
|
|
200
|
+
@base_sandbox.sandbox_service()
|
|
201
|
+
def call_with_varargs(self, code: str, *args, **kwargs):
|
|
202
|
+
del code, args, kwargs
|
|
203
|
+
return 0
|
|
204
|
+
|
|
130
205
|
def _on_bound(self) -> None:
|
|
131
206
|
super()._on_bound()
|
|
132
207
|
self._service = None
|
|
@@ -147,21 +222,31 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
147
222
|
)
|
|
148
223
|
|
|
149
224
|
|
|
150
|
-
class TestingEnvironmentEventHandler(
|
|
225
|
+
class TestingEnvironmentEventHandler(
|
|
226
|
+
pg.Object, interface.EnvironmentEventHandler
|
|
227
|
+
):
|
|
228
|
+
log_sandbox_status: bool = False
|
|
229
|
+
log_feature_setup: bool = True
|
|
230
|
+
log_session_setup: bool = False
|
|
151
231
|
|
|
152
|
-
def
|
|
153
|
-
|
|
232
|
+
def _on_bound(self) -> None:
|
|
233
|
+
super()._on_bound()
|
|
234
|
+
self._logs = []
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def logs(self) -> list[str]:
|
|
238
|
+
return self._logs
|
|
154
239
|
|
|
155
240
|
def _add_message(self, message: str, error: Exception | None) -> None:
|
|
156
241
|
"""Adds a message to the history."""
|
|
157
242
|
if error is None:
|
|
158
|
-
self.
|
|
243
|
+
self._logs.append(message)
|
|
159
244
|
else:
|
|
160
|
-
self.
|
|
245
|
+
self._logs.append(f'{message} with {error.__class__.__name__}')
|
|
161
246
|
|
|
162
247
|
def on_environment_start(
|
|
163
248
|
self,
|
|
164
|
-
environment:
|
|
249
|
+
environment: interface.Environment,
|
|
165
250
|
error: Exception | None
|
|
166
251
|
) -> None:
|
|
167
252
|
"""Called when the environment is started."""
|
|
@@ -169,7 +254,7 @@ class TestingEnvironmentEventHandler(interface.EnvironmentEventHandler):
|
|
|
169
254
|
|
|
170
255
|
def on_environment_shutdown(
|
|
171
256
|
self,
|
|
172
|
-
environment:
|
|
257
|
+
environment: interface.Environment,
|
|
173
258
|
error: Exception | None
|
|
174
259
|
) -> None:
|
|
175
260
|
"""Called when the environment is shutdown."""
|
|
@@ -184,6 +269,19 @@ class TestingEnvironmentEventHandler(interface.EnvironmentEventHandler):
|
|
|
184
269
|
del environment
|
|
185
270
|
self._add_message(f'[{sandbox.id}] sandbox started', error)
|
|
186
271
|
|
|
272
|
+
def on_sandbox_status_change(
|
|
273
|
+
self,
|
|
274
|
+
environment: interface.Environment,
|
|
275
|
+
sandbox: interface.Sandbox,
|
|
276
|
+
old_status: interface.Sandbox.Status,
|
|
277
|
+
new_status: interface.Sandbox.Status,
|
|
278
|
+
) -> None:
|
|
279
|
+
if self.log_sandbox_status:
|
|
280
|
+
self._add_message(
|
|
281
|
+
f'[{sandbox.id}] {old_status.value} -> {new_status.value}',
|
|
282
|
+
None,
|
|
283
|
+
)
|
|
284
|
+
|
|
187
285
|
def on_sandbox_shutdown(
|
|
188
286
|
self,
|
|
189
287
|
environment: interface.Environment,
|
|
@@ -192,7 +290,7 @@ class TestingEnvironmentEventHandler(interface.EnvironmentEventHandler):
|
|
|
192
290
|
) -> None:
|
|
193
291
|
self._add_message(f'[{sandbox.id}] sandbox shutdown', error)
|
|
194
292
|
|
|
195
|
-
def
|
|
293
|
+
def on_feature_setup(
|
|
196
294
|
self,
|
|
197
295
|
environment: interface.Environment,
|
|
198
296
|
sandbox: interface.Sandbox,
|
|
@@ -200,26 +298,56 @@ class TestingEnvironmentEventHandler(interface.EnvironmentEventHandler):
|
|
|
200
298
|
error: Exception | None
|
|
201
299
|
) -> None:
|
|
202
300
|
"""Called when a sandbox feature is setup."""
|
|
203
|
-
self.
|
|
204
|
-
|
|
205
|
-
|
|
301
|
+
if self.log_feature_setup:
|
|
302
|
+
self._add_message(
|
|
303
|
+
f'[{sandbox.id}/{feature.name}] feature setup', error
|
|
304
|
+
)
|
|
206
305
|
|
|
207
|
-
def
|
|
306
|
+
def on_feature_teardown(
|
|
208
307
|
self,
|
|
209
|
-
environment:
|
|
210
|
-
sandbox:
|
|
211
|
-
feature:
|
|
308
|
+
environment: interface.Environment,
|
|
309
|
+
sandbox: interface.Sandbox,
|
|
310
|
+
feature: interface.Feature,
|
|
212
311
|
error: Exception | None
|
|
213
312
|
) -> None:
|
|
214
313
|
"""Called when a sandbox feature is teardown."""
|
|
215
|
-
self.
|
|
216
|
-
|
|
217
|
-
|
|
314
|
+
if self.log_feature_setup:
|
|
315
|
+
self._add_message(
|
|
316
|
+
f'[{sandbox.id}/{feature.name}] feature teardown', error
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def on_feature_setup_session(
|
|
320
|
+
self,
|
|
321
|
+
environment: interface.Environment,
|
|
322
|
+
sandbox: interface.Sandbox,
|
|
323
|
+
feature: interface.Feature,
|
|
324
|
+
session_id: str | None,
|
|
325
|
+
error: Exception | None
|
|
326
|
+
) -> None:
|
|
327
|
+
"""Called when a sandbox feature is setup."""
|
|
328
|
+
if self.log_session_setup:
|
|
329
|
+
self._add_message(
|
|
330
|
+
f'[{sandbox.id}/{feature.name}] feature setup session', error
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
def on_feature_teardown_session(
|
|
334
|
+
self,
|
|
335
|
+
environment: interface.Environment,
|
|
336
|
+
sandbox: interface.Sandbox,
|
|
337
|
+
feature: interface.Feature,
|
|
338
|
+
session_id: str,
|
|
339
|
+
error: Exception | None
|
|
340
|
+
) -> None:
|
|
341
|
+
"""Called when a sandbox feature is teardown."""
|
|
342
|
+
if self.log_session_setup:
|
|
343
|
+
self._add_message(
|
|
344
|
+
f'[{sandbox.id}/{feature.name}] feature teardown session', error
|
|
345
|
+
)
|
|
218
346
|
|
|
219
|
-
def
|
|
347
|
+
def on_session_start(
|
|
220
348
|
self,
|
|
221
|
-
environment:
|
|
222
|
-
sandbox:
|
|
349
|
+
environment: interface.Environment,
|
|
350
|
+
sandbox: interface.Sandbox,
|
|
223
351
|
session_id: str,
|
|
224
352
|
error: Exception | None
|
|
225
353
|
) -> None:
|
|
@@ -228,10 +356,10 @@ class TestingEnvironmentEventHandler(interface.EnvironmentEventHandler):
|
|
|
228
356
|
f'[{sandbox.id}] session {session_id!r} started', error
|
|
229
357
|
)
|
|
230
358
|
|
|
231
|
-
def
|
|
359
|
+
def on_session_end(
|
|
232
360
|
self,
|
|
233
|
-
environment:
|
|
234
|
-
sandbox:
|
|
361
|
+
environment: interface.Environment,
|
|
362
|
+
sandbox: interface.Sandbox,
|
|
235
363
|
session_id: str,
|
|
236
364
|
error: Exception | None
|
|
237
365
|
) -> None:
|
|
@@ -240,19 +368,42 @@ class TestingEnvironmentEventHandler(interface.EnvironmentEventHandler):
|
|
|
240
368
|
f'[{sandbox.id}] session {session_id!r} ended', error
|
|
241
369
|
)
|
|
242
370
|
|
|
371
|
+
def on_session_activity(
|
|
372
|
+
self,
|
|
373
|
+
session_id: str,
|
|
374
|
+
name: str,
|
|
375
|
+
environment: interface.Environment,
|
|
376
|
+
sandbox: interface.Sandbox,
|
|
377
|
+
feature: interface.Feature | None,
|
|
378
|
+
error: Exception | None,
|
|
379
|
+
*,
|
|
380
|
+
code: str | None = None,
|
|
381
|
+
**kwargs
|
|
382
|
+
) -> None:
|
|
383
|
+
"""Called when a sandbox activity is performed."""
|
|
384
|
+
del environment, kwargs
|
|
385
|
+
self._add_message(
|
|
386
|
+
f'[{sandbox.id}/{session_id}] {name}: {code}', error
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
#
|
|
390
|
+
# Tests
|
|
391
|
+
#
|
|
243
392
|
|
|
244
|
-
class EnvironmentTest(unittest.TestCase):
|
|
245
393
|
|
|
246
|
-
|
|
247
|
-
|
|
394
|
+
class EnvironmentTests(unittest.TestCase):
|
|
395
|
+
|
|
396
|
+
def test_basics(self):
|
|
248
397
|
env = TestingEnvironment(
|
|
398
|
+
root_dir='/tmp',
|
|
249
399
|
pool_size=0,
|
|
250
400
|
features={'test_feature': TestingFeature()},
|
|
251
401
|
outage_grace_period=1,
|
|
252
|
-
event_handler=event_handler,
|
|
253
402
|
outage_retry_interval=0,
|
|
254
403
|
)
|
|
255
|
-
self.
|
|
404
|
+
self.assertIsNone(interface.Environment.current())
|
|
405
|
+
self.assertEqual(env.status, interface.Environment.Status.CREATED)
|
|
406
|
+
self.assertFalse(env.is_online)
|
|
256
407
|
self.assertEqual(env.min_pool_size, 0)
|
|
257
408
|
self.assertEqual(env.max_pool_size, 0)
|
|
258
409
|
self.assertEqual(env.sandbox_pool, [])
|
|
@@ -264,311 +415,843 @@ class EnvironmentTest(unittest.TestCase):
|
|
|
264
415
|
self.assertIsNone(env.start_time)
|
|
265
416
|
|
|
266
417
|
with env:
|
|
267
|
-
self.assertEqual(
|
|
268
|
-
event_handler.history,
|
|
269
|
-
[
|
|
270
|
-
'[testing-env] environment started',
|
|
271
|
-
]
|
|
272
|
-
)
|
|
418
|
+
self.assertEqual(env.status, interface.Environment.Status.ONLINE)
|
|
273
419
|
self.assertIs(interface.Environment.current(), env)
|
|
274
|
-
self.assertTrue(env.
|
|
420
|
+
self.assertTrue(env.is_online)
|
|
275
421
|
self.assertIsNotNone(env.start_time)
|
|
276
422
|
self.assertEqual(env.offline_duration, 0.0)
|
|
277
423
|
self.assertEqual(env.sandbox_pool, [])
|
|
278
|
-
self.
|
|
424
|
+
self.assertEqual(env.working_dir, '/tmp/testing-env')
|
|
279
425
|
|
|
280
426
|
with env.sandbox('session1') as sb:
|
|
281
|
-
self.assertEqual(env.sandbox_pool, [])
|
|
282
|
-
self.assertTrue(sb.is_alive)
|
|
283
|
-
self.assertTrue(sb.is_busy)
|
|
284
|
-
self.assertEqual(sb.session_id, 'session1')
|
|
285
|
-
self.assertIsNone(sb.working_dir)
|
|
286
427
|
self.assertEqual(
|
|
287
428
|
sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
|
|
288
429
|
)
|
|
289
|
-
sb.
|
|
290
|
-
self.
|
|
291
|
-
self.
|
|
430
|
+
self.assertEqual(sb.session_id, 'session1')
|
|
431
|
+
self.assertEqual(sb.working_dir, '/tmp/testing-env/0')
|
|
432
|
+
self.assertTrue(sb.is_online)
|
|
433
|
+
self.assertIs(sb.test_feature, sb.features['test_feature'])
|
|
292
434
|
with self.assertRaises(AttributeError):
|
|
293
435
|
_ = sb.test_feature2
|
|
294
|
-
|
|
295
|
-
with sb.test_feature.my_service() as service:
|
|
296
|
-
self.assertIs(sb.test_feature._service, service)
|
|
297
|
-
self.assertIsNone(sb.test_feature._service)
|
|
298
|
-
self.assertEqual(sb.session_id, 'session1')
|
|
436
|
+
self.assertFalse(sb.is_online)
|
|
299
437
|
|
|
300
|
-
|
|
301
|
-
self.assertEqual(env.sandbox_pool, [])
|
|
302
|
-
self.assertTrue(sb.is_alive)
|
|
303
|
-
self.assertTrue(sb.is_busy)
|
|
304
|
-
self.assertEqual(sb.session_id, 'session2')
|
|
305
|
-
self.assertEqual(
|
|
306
|
-
sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='1')
|
|
307
|
-
)
|
|
308
|
-
sb.shell('echo "bar"')
|
|
309
|
-
|
|
310
|
-
# Access the feature from the environment without obtaining a sandbox.
|
|
311
|
-
self.assertEqual(
|
|
312
|
-
env.test_feature.num_shell_calls(session_id='num_shell_session'), 1
|
|
313
|
-
)
|
|
438
|
+
self.assertIsInstance(env.test_feature, TestingFeature)
|
|
314
439
|
with self.assertRaises(AttributeError):
|
|
315
440
|
_ = env.test_feature2
|
|
316
|
-
# Access the feature from the environment without obtaining a sandbox.
|
|
317
|
-
with env.test_feature.my_service(
|
|
318
|
-
session_id='my_service_session'
|
|
319
|
-
) as service:
|
|
320
|
-
service.do('echo "baz"')
|
|
321
441
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
'[testing-env/0] session \'session1\' started',
|
|
331
|
-
'[testing-env/0] session \'session1\' ended',
|
|
332
|
-
'[testing-env/0/test_feature] feature teardown',
|
|
333
|
-
'[testing-env/0] sandbox shutdown',
|
|
334
|
-
'[testing-env/1/test_feature] feature setup',
|
|
335
|
-
'[testing-env/1] sandbox started',
|
|
336
|
-
'[testing-env/1] session \'session2\' started',
|
|
337
|
-
'[testing-env/1] session \'session2\' ended',
|
|
338
|
-
'[testing-env/1/test_feature] feature teardown',
|
|
339
|
-
'[testing-env/1] sandbox shutdown',
|
|
340
|
-
'[testing-env/2/test_feature] feature setup',
|
|
341
|
-
'[testing-env/2] sandbox started',
|
|
342
|
-
'[testing-env/2] session \'num_shell_session\' started',
|
|
343
|
-
'[testing-env/2] session \'num_shell_session\' ended',
|
|
344
|
-
'[testing-env/2/test_feature] feature teardown',
|
|
345
|
-
'[testing-env/2] sandbox shutdown',
|
|
346
|
-
'[testing-env/3/test_feature] feature setup',
|
|
347
|
-
'[testing-env/3] sandbox started',
|
|
348
|
-
'[testing-env/3] session \'my_service_session\' started',
|
|
349
|
-
'[testing-env/3] session \'my_service_session\' ended',
|
|
350
|
-
'[testing-env/3/test_feature] feature teardown',
|
|
351
|
-
'[testing-env/3] sandbox shutdown',
|
|
352
|
-
'[testing-env] environment shutdown',
|
|
353
|
-
]
|
|
442
|
+
def test_acquire_env_offline(self):
|
|
443
|
+
env = TestingEnvironment(
|
|
444
|
+
features={'test_feature': TestingFeature()},
|
|
445
|
+
pool_size=0,
|
|
446
|
+
outage_grace_period=1,
|
|
447
|
+
outage_retry_interval=0,
|
|
448
|
+
keepalive_interval=0,
|
|
449
|
+
stats_report_interval=1,
|
|
354
450
|
)
|
|
451
|
+
with self.assertRaises(interface.EnvironmentOutageError):
|
|
452
|
+
env.acquire()
|
|
355
453
|
|
|
356
|
-
def
|
|
357
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
454
|
+
def test_acquire_no_pooling(self):
|
|
358
455
|
env = TestingEnvironment(
|
|
359
|
-
root_dir='/tmp',
|
|
360
|
-
pool_size=0,
|
|
361
456
|
features={'test_feature': TestingFeature()},
|
|
457
|
+
pool_size=0,
|
|
362
458
|
outage_grace_period=1,
|
|
363
459
|
outage_retry_interval=0,
|
|
364
|
-
|
|
460
|
+
keepalive_interval=0,
|
|
461
|
+
stats_report_interval=1,
|
|
365
462
|
)
|
|
366
|
-
self.assertEqual(env.working_dir, '/tmp/testing-env')
|
|
367
463
|
with env:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
self.assertTrue(sb.is_alive)
|
|
371
|
-
self.assertTrue(sb.is_busy)
|
|
372
|
-
self.assertFalse(sb.is_pending)
|
|
373
|
-
self.assertEqual(sb.session_id, 'session1')
|
|
374
|
-
self.assertEqual(sb.working_dir, '/tmp/testing-env/0')
|
|
375
|
-
self.assertEqual(
|
|
376
|
-
sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
|
|
377
|
-
)
|
|
378
|
-
# Non-critical error.
|
|
379
|
-
with self.assertRaises(ValueError):
|
|
380
|
-
sb.shell('bad command', must_succeed=False)
|
|
464
|
+
sb = env.acquire()
|
|
465
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
|
|
381
466
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
467
|
+
def test_acquire_no_pooling_with_error(self):
|
|
468
|
+
env = TestingEnvironment(
|
|
469
|
+
features={
|
|
470
|
+
'test_feature': TestingFeature(
|
|
471
|
+
simulate_setup_error=interface.SandboxStateError
|
|
472
|
+
)
|
|
473
|
+
},
|
|
474
|
+
pool_size=0,
|
|
475
|
+
outage_grace_period=1,
|
|
476
|
+
outage_retry_interval=0,
|
|
477
|
+
keepalive_interval=0,
|
|
478
|
+
stats_report_interval=1,
|
|
479
|
+
)
|
|
480
|
+
with env:
|
|
481
|
+
with self.assertRaises(interface.EnvironmentOutageError):
|
|
482
|
+
env.acquire()
|
|
387
483
|
|
|
388
|
-
|
|
389
|
-
|
|
484
|
+
def test_acquire_with_pooling(self):
|
|
485
|
+
env = TestingEnvironment(
|
|
486
|
+
features={'test_feature': TestingFeature()},
|
|
487
|
+
pool_size=1,
|
|
488
|
+
outage_grace_period=1,
|
|
489
|
+
outage_retry_interval=0,
|
|
490
|
+
keepalive_interval=0,
|
|
491
|
+
stats_report_interval=1,
|
|
492
|
+
)
|
|
493
|
+
with env:
|
|
494
|
+
sb = env.acquire()
|
|
495
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
|
|
390
496
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
497
|
+
def test_acquire_with_pooling_overload(self):
|
|
498
|
+
env = TestingEnvironment(
|
|
499
|
+
features={'test_feature': TestingFeature()},
|
|
500
|
+
pool_size=1,
|
|
501
|
+
outage_grace_period=1,
|
|
502
|
+
outage_retry_interval=0,
|
|
503
|
+
keepalive_interval=0,
|
|
504
|
+
stats_report_interval=1,
|
|
505
|
+
)
|
|
506
|
+
with env:
|
|
507
|
+
sb = env.acquire()
|
|
508
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
|
|
509
|
+
with self.assertRaises(interface.EnvironmentOverloadError):
|
|
510
|
+
env.acquire()
|
|
397
511
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
'[testing-env/0] session \'session1\' started',
|
|
407
|
-
# Sandbox shutdown is triggered by the SandboxStateError before
|
|
408
|
-
# the session end event is triggered.
|
|
409
|
-
'[testing-env/0/test_feature] feature teardown',
|
|
410
|
-
'[testing-env/0] sandbox shutdown',
|
|
411
|
-
'[testing-env/0] session \'session1\' ended',
|
|
412
|
-
'[testing-env/1/test_feature] feature setup',
|
|
413
|
-
'[testing-env/1] sandbox started',
|
|
414
|
-
"[testing-env/1] session 'bad_shell_session' started",
|
|
415
|
-
'[testing-env/1/test_feature] feature teardown',
|
|
416
|
-
'[testing-env/1] sandbox shutdown',
|
|
417
|
-
"[testing-env/1] session 'bad_shell_session' ended",
|
|
418
|
-
'[testing-env/2/test_feature] feature setup',
|
|
419
|
-
'[testing-env/2] sandbox started',
|
|
420
|
-
"[testing-env/2] session 'my_service_session' started",
|
|
421
|
-
'[testing-env/2/test_feature] feature teardown',
|
|
422
|
-
'[testing-env/2] sandbox shutdown',
|
|
423
|
-
"[testing-env/2] session 'my_service_session' ended",
|
|
424
|
-
'[testing-env] environment shutdown',
|
|
425
|
-
]
|
|
512
|
+
def test_acquire_with_growing_pool(self):
|
|
513
|
+
env = TestingEnvironment(
|
|
514
|
+
features={'test_feature': TestingFeature()},
|
|
515
|
+
pool_size=(1, 3),
|
|
516
|
+
outage_grace_period=1,
|
|
517
|
+
outage_retry_interval=0,
|
|
518
|
+
keepalive_interval=0,
|
|
519
|
+
stats_report_interval=1,
|
|
426
520
|
)
|
|
521
|
+
with env:
|
|
522
|
+
self.assertEqual(len(env.sandbox_pool), 1)
|
|
523
|
+
sb = env.acquire()
|
|
524
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
|
|
525
|
+
self.assertEqual(len(env.sandbox_pool), 1)
|
|
526
|
+
sb2 = env.acquire()
|
|
527
|
+
self.assertEqual(sb2.status, interface.Sandbox.Status.ACQUIRED)
|
|
528
|
+
self.assertEqual(len(env.sandbox_pool), 2)
|
|
427
529
|
|
|
428
|
-
def
|
|
429
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
530
|
+
def test_acquire_with_growing_pool_failure(self):
|
|
430
531
|
env = TestingEnvironment(
|
|
431
|
-
pool_size=(1, 2),
|
|
432
532
|
features={'test_feature': TestingFeature()},
|
|
433
|
-
|
|
434
|
-
# To make pool operation deterministic in the test.
|
|
435
|
-
pool_operation_max_parallelism=1,
|
|
533
|
+
pool_size=(1, 3),
|
|
436
534
|
outage_grace_period=1,
|
|
437
535
|
outage_retry_interval=0,
|
|
536
|
+
keepalive_interval=0,
|
|
537
|
+
stats_report_interval=1,
|
|
438
538
|
)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
self.assertEqual(env.id, interface.EnvironmentId('testing-env'))
|
|
444
|
-
self.assertEqual(env.outage_grace_period, 1)
|
|
445
|
-
self.assertEqual(env.stats_report_interval, 60)
|
|
446
|
-
self.assertEqual(env.features['test_feature'].name, 'test_feature')
|
|
539
|
+
with env:
|
|
540
|
+
self.assertEqual(len(env.sandbox_pool), 1)
|
|
541
|
+
sb = env.acquire()
|
|
542
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
|
|
447
543
|
|
|
448
|
-
|
|
544
|
+
# Make future sandbox setup to fail.
|
|
545
|
+
env.features.test_feature.rebind(
|
|
546
|
+
simulate_setup_error=interface.SandboxStateError,
|
|
547
|
+
skip_notification=True
|
|
548
|
+
)
|
|
549
|
+
with self.assertRaises(interface.EnvironmentOutageError):
|
|
550
|
+
env.acquire()
|
|
551
|
+
|
|
552
|
+
def test_maintenance_error(self):
|
|
553
|
+
env = TestingEnvironment(
|
|
554
|
+
features={'test_feature': TestingFeature()},
|
|
555
|
+
pool_size=1,
|
|
556
|
+
proactive_session_setup=True,
|
|
557
|
+
outage_grace_period=1,
|
|
558
|
+
outage_retry_interval=0,
|
|
559
|
+
keepalive_interval=0,
|
|
560
|
+
stats_report_interval=1,
|
|
561
|
+
)
|
|
562
|
+
with env:
|
|
563
|
+
self.assertEqual(len(env.sandbox_pool), 1)
|
|
564
|
+
self.assertEqual(
|
|
565
|
+
env.sandbox_pool[0].status, interface.Sandbox.Status.READY
|
|
566
|
+
)
|
|
567
|
+
# Make future sandbox setup to fail.
|
|
568
|
+
env.features.test_feature.rebind(
|
|
569
|
+
simulate_setup_error=interface.SandboxStateError,
|
|
570
|
+
skip_notification=True
|
|
571
|
+
)
|
|
572
|
+
with env.sandbox() as sb:
|
|
573
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
574
|
+
sb.shell('bad command', raise_error=interface.SandboxStateError)
|
|
575
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
576
|
+
env.wait_for_next_maintenance()
|
|
577
|
+
self.assertFalse(env.is_online)
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class SandboxStatusTests(unittest.TestCase):
|
|
449
581
|
|
|
582
|
+
def setUp(self):
|
|
583
|
+
super().setUp()
|
|
584
|
+
self.event_handler = TestingEnvironmentEventHandler(
|
|
585
|
+
log_sandbox_status=True,
|
|
586
|
+
log_feature_setup=True,
|
|
587
|
+
log_session_setup=True,
|
|
588
|
+
)
|
|
589
|
+
self.maxDiff = None
|
|
590
|
+
|
|
591
|
+
def _create_env(
|
|
592
|
+
self,
|
|
593
|
+
features,
|
|
594
|
+
*,
|
|
595
|
+
pool_size=0,
|
|
596
|
+
**kwargs
|
|
597
|
+
) -> TestingEnvironment:
|
|
598
|
+
return TestingEnvironment(
|
|
599
|
+
pool_size=pool_size,
|
|
600
|
+
features=features,
|
|
601
|
+
outage_grace_period=0,
|
|
602
|
+
event_handlers=[self.event_handler],
|
|
603
|
+
outage_retry_interval=0,
|
|
604
|
+
**kwargs
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
def test_passive_session_setup(self):
|
|
608
|
+
env = self._create_env(
|
|
609
|
+
features={
|
|
610
|
+
'feature1': TestingFeature(),
|
|
611
|
+
'feature2': TestingFeature(),
|
|
612
|
+
},
|
|
613
|
+
)
|
|
450
614
|
with env:
|
|
615
|
+
with env.sandbox('session1') as sb:
|
|
616
|
+
sb.shell('echo "hello"')
|
|
451
617
|
self.assertEqual(
|
|
452
|
-
event_handler.
|
|
618
|
+
self.event_handler.logs,
|
|
453
619
|
[
|
|
454
|
-
'[testing-env/0/test_feature] feature setup',
|
|
455
|
-
'[testing-env/0] sandbox started',
|
|
456
620
|
'[testing-env] environment started',
|
|
621
|
+
'[testing-env/0/feature1] feature setup',
|
|
622
|
+
'[testing-env/0/feature2] feature setup',
|
|
623
|
+
'[testing-env/0] created -> ready',
|
|
624
|
+
'[testing-env/0] sandbox started',
|
|
625
|
+
'[testing-env/0] ready -> acquired',
|
|
626
|
+
'[testing-env/0] acquired -> setting_up',
|
|
627
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
628
|
+
'[testing-env/0/feature1] feature setup session',
|
|
629
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
630
|
+
'[testing-env/0/feature2] feature setup session',
|
|
631
|
+
'[testing-env/0] setting_up -> in_session',
|
|
632
|
+
"[testing-env/0] session 'session1' started",
|
|
633
|
+
'[testing-env/0/session1] shell: echo "hello"',
|
|
634
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
635
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
636
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
637
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
638
|
+
"[testing-env/0] session 'session1' ended",
|
|
639
|
+
'[testing-env/0] in_session -> acquired',
|
|
640
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
641
|
+
'[testing-env/0/feature1] feature teardown',
|
|
642
|
+
'[testing-env/0/feature2] feature teardown',
|
|
643
|
+
'[testing-env/0] shutting_down -> offline',
|
|
644
|
+
'[testing-env/0] sandbox shutdown'
|
|
457
645
|
]
|
|
458
646
|
)
|
|
459
|
-
self.assertIs(interface.Environment.current(), env)
|
|
460
|
-
self.assertEqual(len(env.sandbox_pool), 1)
|
|
461
|
-
self.assertTrue(env.sandbox_pool[0].is_alive)
|
|
462
|
-
self.assertFalse(env.sandbox_pool[0].is_busy)
|
|
463
|
-
self.assertFalse(env.sandbox_pool[0].is_pending)
|
|
464
647
|
|
|
465
|
-
|
|
648
|
+
def test_proactive_session_setup(self):
|
|
649
|
+
env = self._create_env(
|
|
650
|
+
features={
|
|
651
|
+
'feature1': TestingFeature(setup_session_delay=0.1),
|
|
652
|
+
'feature2': TestingFeature(),
|
|
653
|
+
},
|
|
654
|
+
pool_size=1,
|
|
655
|
+
proactive_session_setup=True,
|
|
656
|
+
)
|
|
657
|
+
with env:
|
|
466
658
|
with env.sandbox('session1') as sb:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
self.assertEqual(
|
|
473
|
-
sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
|
|
474
|
-
)
|
|
475
|
-
sb.shell('echo "foo"')
|
|
476
|
-
|
|
477
|
-
self.assertEqual(len(env.sandbox_pool), 1)
|
|
478
|
-
|
|
479
|
-
# Reuse the pooled server.
|
|
480
|
-
with env.sandbox('session2') as sb2:
|
|
481
|
-
self.assertEqual(len(env.sandbox_pool), 1)
|
|
482
|
-
self.assertTrue(sb2.is_alive)
|
|
483
|
-
self.assertTrue(sb2.is_busy)
|
|
484
|
-
self.assertEqual(sb2.session_id, 'session2')
|
|
485
|
-
self.assertEqual(
|
|
486
|
-
sb2.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
|
|
487
|
-
)
|
|
488
|
-
sb2.shell('echo "bar"')
|
|
489
|
-
|
|
490
|
-
# Dynamically bring up a new server in the pool.
|
|
491
|
-
with env.sandbox('session3') as sb3:
|
|
492
|
-
self.assertEqual(len(env.sandbox_pool), 2)
|
|
493
|
-
self.assertTrue(sb3.is_alive)
|
|
494
|
-
self.assertFalse(sb3.is_pending)
|
|
495
|
-
self.assertTrue(sb3.is_busy)
|
|
496
|
-
self.assertEqual(sb3.session_id, 'session3')
|
|
497
|
-
self.assertEqual(
|
|
498
|
-
sb3.id, interface.SandboxId(environment_id=env.id, sandbox_id='1')
|
|
499
|
-
)
|
|
500
|
-
sb3.shell('echo "baz"')
|
|
501
|
-
|
|
502
|
-
self.assertEqual(
|
|
503
|
-
env.stats(),
|
|
504
|
-
dict(
|
|
505
|
-
sandbox=dict(
|
|
506
|
-
num_total=2,
|
|
507
|
-
num_busy=2,
|
|
508
|
-
num_free=0,
|
|
509
|
-
num_dead=0,
|
|
510
|
-
)
|
|
511
|
-
)
|
|
659
|
+
sb.shell('echo "hello"')
|
|
660
|
+
sb.wait_until_not(
|
|
661
|
+
(
|
|
662
|
+
interface.Sandbox.Status.IN_SESSION,
|
|
663
|
+
interface.Sandbox.Status.SETTING_UP
|
|
512
664
|
)
|
|
665
|
+
)
|
|
666
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.READY)
|
|
667
|
+
self.assertEqual(
|
|
668
|
+
self.event_handler.logs,
|
|
669
|
+
[
|
|
670
|
+
'[testing-env/0/feature1] feature setup',
|
|
671
|
+
'[testing-env/0/feature2] feature setup',
|
|
672
|
+
'[testing-env/0/feature1] feature setup session',
|
|
673
|
+
'[testing-env/0/feature2] feature setup session',
|
|
674
|
+
'[testing-env/0] created -> ready',
|
|
675
|
+
'[testing-env/0] sandbox started',
|
|
676
|
+
'[testing-env] environment started',
|
|
677
|
+
'[testing-env/0] ready -> acquired',
|
|
678
|
+
'[testing-env/0] acquired -> setting_up',
|
|
679
|
+
'[testing-env/0] setting_up -> in_session',
|
|
680
|
+
"[testing-env/0] session 'session1' started",
|
|
681
|
+
'[testing-env/0/session1] shell: echo "hello"',
|
|
682
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
683
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
684
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
685
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
686
|
+
"[testing-env/0] session 'session1' ended",
|
|
687
|
+
'[testing-env/0] in_session -> setting_up',
|
|
688
|
+
'[testing-env/0/feature1] feature setup session',
|
|
689
|
+
'[testing-env/0/feature2] feature setup session',
|
|
690
|
+
'[testing-env/0] setting_up -> ready'
|
|
691
|
+
]
|
|
692
|
+
)
|
|
513
693
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
694
|
+
def test_practive_session_setup_with_setup_session_error(self):
|
|
695
|
+
env = self._create_env(
|
|
696
|
+
features={'test_feature': TestingFeature(setup_session_delay=0.5)},
|
|
697
|
+
pool_size=1,
|
|
698
|
+
)
|
|
699
|
+
event_handler = TestingEnvironmentEventHandler(
|
|
700
|
+
log_sandbox_status=True,
|
|
701
|
+
log_feature_setup=True,
|
|
702
|
+
log_session_setup=True,
|
|
703
|
+
)
|
|
704
|
+
with env:
|
|
705
|
+
with env.sandbox('session1') as sb:
|
|
706
|
+
sb.add_event_handler(event_handler)
|
|
707
|
+
sb.test_feature.rebind(
|
|
708
|
+
simulate_setup_session_error=interface.SandboxStateError,
|
|
709
|
+
skip_notification=True
|
|
530
710
|
)
|
|
711
|
+
sb.wait_until_not(
|
|
712
|
+
(
|
|
713
|
+
interface.Sandbox.Status.SETTING_UP,
|
|
714
|
+
interface.Sandbox.Status.SHUTTING_DOWN
|
|
715
|
+
)
|
|
716
|
+
)
|
|
717
|
+
self.assertEqual(len(sb.state_errors), 1)
|
|
718
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
719
|
+
self.assertEqual(
|
|
720
|
+
event_handler.logs,
|
|
721
|
+
[
|
|
722
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
723
|
+
'[testing-env/0/test_feature] feature teardown session',
|
|
724
|
+
"[testing-env/0] session 'session1' ended",
|
|
725
|
+
'[testing-env/0] in_session -> setting_up',
|
|
726
|
+
'[testing-env/0/test_feature] feature setup session with SandboxStateError', # pylint: disable=line-too-long
|
|
727
|
+
'[testing-env/0] setting_up -> shutting_down',
|
|
728
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
729
|
+
'[testing-env/0] shutting_down -> offline',
|
|
730
|
+
'[testing-env/0] sandbox shutdown'
|
|
731
|
+
]
|
|
732
|
+
)
|
|
531
733
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
734
|
+
def test_sandbox_start_non_state_error(self):
|
|
735
|
+
env = self._create_env(
|
|
736
|
+
features={
|
|
737
|
+
'feature1': TestingFeature(),
|
|
738
|
+
'feature2': TestingFeature(),
|
|
739
|
+
},
|
|
740
|
+
simulate_start_error=ValueError,
|
|
741
|
+
)
|
|
742
|
+
with env:
|
|
743
|
+
with self.assertRaises(ValueError):
|
|
744
|
+
with env.sandbox('session1'):
|
|
745
|
+
pass
|
|
746
|
+
self.assertTrue(env.is_online)
|
|
747
|
+
self.assertEqual(
|
|
748
|
+
self.event_handler.logs,
|
|
749
|
+
[
|
|
750
|
+
'[testing-env] environment started',
|
|
751
|
+
'[testing-env/0] sandbox started with ValueError',
|
|
752
|
+
'[testing-env/0] created -> shutting_down',
|
|
753
|
+
'[testing-env/0] shutting_down -> offline',
|
|
754
|
+
'[testing-env/0] sandbox shutdown'
|
|
755
|
+
]
|
|
756
|
+
)
|
|
536
757
|
|
|
537
|
-
|
|
538
|
-
self.
|
|
539
|
-
|
|
758
|
+
def test_sandbox_start_state_error(self):
|
|
759
|
+
env = self._create_env(
|
|
760
|
+
features={
|
|
761
|
+
'feature1': TestingFeature(),
|
|
762
|
+
'feature2': TestingFeature(),
|
|
763
|
+
},
|
|
764
|
+
pool_size=1,
|
|
765
|
+
simulate_start_error=interface.SandboxStateError,
|
|
766
|
+
)
|
|
767
|
+
with self.assertRaises(interface.EnvironmentOutageError):
|
|
768
|
+
with env:
|
|
769
|
+
pass
|
|
540
770
|
self.assertEqual(
|
|
541
|
-
event_handler.
|
|
771
|
+
self.event_handler.logs,
|
|
542
772
|
[
|
|
543
|
-
'[testing-env/0
|
|
773
|
+
'[testing-env/0] sandbox started with SandboxStateError',
|
|
774
|
+
'[testing-env/0] created -> shutting_down',
|
|
775
|
+
'[testing-env/0] shutting_down -> offline',
|
|
776
|
+
'[testing-env/0] sandbox shutdown',
|
|
777
|
+
'[testing-env] environment started with EnvironmentOutageError',
|
|
778
|
+
'[testing-env] environment shutdown'
|
|
779
|
+
]
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
def test_sandbox_shutdown_non_state_error(self):
|
|
783
|
+
env = self._create_env(
|
|
784
|
+
features={
|
|
785
|
+
'feature1': TestingFeature(),
|
|
786
|
+
'feature2': TestingFeature(),
|
|
787
|
+
},
|
|
788
|
+
simulate_shutdown_error=ValueError,
|
|
789
|
+
)
|
|
790
|
+
with env:
|
|
791
|
+
with self.assertRaises(ValueError):
|
|
792
|
+
with env.sandbox('session1') as sb:
|
|
793
|
+
sb.shell('echo "hello"')
|
|
794
|
+
self.assertEqual(len(sb.state_errors), 0)
|
|
795
|
+
|
|
796
|
+
self.assertEqual(
|
|
797
|
+
self.event_handler.logs,
|
|
798
|
+
[
|
|
799
|
+
'[testing-env] environment started',
|
|
800
|
+
'[testing-env/0/feature1] feature setup',
|
|
801
|
+
'[testing-env/0/feature2] feature setup',
|
|
802
|
+
'[testing-env/0] created -> ready',
|
|
803
|
+
'[testing-env/0] sandbox started',
|
|
804
|
+
'[testing-env/0] ready -> acquired',
|
|
805
|
+
'[testing-env/0] acquired -> setting_up',
|
|
806
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
807
|
+
'[testing-env/0/feature1] feature setup session',
|
|
808
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
809
|
+
'[testing-env/0/feature2] feature setup session',
|
|
810
|
+
'[testing-env/0] setting_up -> in_session',
|
|
811
|
+
"[testing-env/0] session 'session1' started",
|
|
812
|
+
'[testing-env/0/session1] shell: echo "hello"',
|
|
813
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
814
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
815
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
816
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
817
|
+
"[testing-env/0] session 'session1' ended",
|
|
818
|
+
'[testing-env/0] in_session -> acquired',
|
|
819
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
820
|
+
'[testing-env/0/feature1] feature teardown',
|
|
821
|
+
'[testing-env/0/feature2] feature teardown',
|
|
822
|
+
'[testing-env/0] shutting_down -> offline',
|
|
823
|
+
'[testing-env/0] sandbox shutdown with ValueError',
|
|
824
|
+
'[testing-env] environment shutdown'
|
|
825
|
+
]
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
def test_env_shutdown_non_state_error(self):
|
|
829
|
+
env = self._create_env(
|
|
830
|
+
pool_size=1,
|
|
831
|
+
features={
|
|
832
|
+
'feature1': TestingFeature(),
|
|
833
|
+
'feature2': TestingFeature(),
|
|
834
|
+
},
|
|
835
|
+
simulate_shutdown_error=ValueError,
|
|
836
|
+
)
|
|
837
|
+
with self.assertRaises(ValueError):
|
|
838
|
+
with env:
|
|
839
|
+
pass
|
|
840
|
+
|
|
841
|
+
self.assertEqual(
|
|
842
|
+
self.event_handler.logs,
|
|
843
|
+
[
|
|
844
|
+
'[testing-env/0/feature1] feature setup',
|
|
845
|
+
'[testing-env/0/feature2] feature setup',
|
|
846
|
+
'[testing-env/0/feature1] feature setup session',
|
|
847
|
+
'[testing-env/0/feature2] feature setup session',
|
|
848
|
+
'[testing-env/0] created -> ready',
|
|
544
849
|
'[testing-env/0] sandbox started',
|
|
545
|
-
# Environment ready is after the first sandbox is started.
|
|
546
850
|
'[testing-env] environment started',
|
|
547
|
-
'[testing-env/0]
|
|
548
|
-
'[testing-env/0]
|
|
549
|
-
'[testing-env/0]
|
|
550
|
-
'[testing-env/
|
|
551
|
-
'[testing-env/
|
|
552
|
-
'[testing-env
|
|
553
|
-
'[testing-env/1] session \'session3\' ended',
|
|
554
|
-
'[testing-env/1] session \'num_shell_session\' started',
|
|
555
|
-
'[testing-env/1] session \'num_shell_session\' ended',
|
|
556
|
-
'[testing-env/0] session \'session2\' ended',
|
|
557
|
-
'[testing-env/0/test_feature] feature teardown',
|
|
558
|
-
'[testing-env/0] sandbox shutdown',
|
|
559
|
-
'[testing-env/1/test_feature] feature teardown',
|
|
560
|
-
'[testing-env/1] sandbox shutdown',
|
|
561
|
-
'[testing-env] environment shutdown',
|
|
851
|
+
'[testing-env/0] ready -> shutting_down',
|
|
852
|
+
'[testing-env/0/feature1] feature teardown',
|
|
853
|
+
'[testing-env/0/feature2] feature teardown',
|
|
854
|
+
'[testing-env/0] shutting_down -> offline',
|
|
855
|
+
'[testing-env/0] sandbox shutdown with ValueError',
|
|
856
|
+
'[testing-env] environment shutdown with ValueError'
|
|
562
857
|
]
|
|
563
858
|
)
|
|
564
859
|
|
|
860
|
+
def test_sandbox_shutdown_state_error(self):
|
|
861
|
+
env = self._create_env(
|
|
862
|
+
features={
|
|
863
|
+
'feature1': TestingFeature(),
|
|
864
|
+
'feature2': TestingFeature(),
|
|
865
|
+
},
|
|
866
|
+
simulate_shutdown_error=interface.SandboxStateError,
|
|
867
|
+
)
|
|
868
|
+
with env:
|
|
869
|
+
with env.sandbox('session1') as sb:
|
|
870
|
+
sb.shell('echo "hello"')
|
|
871
|
+
self.assertEqual(len(sb.state_errors), 1)
|
|
872
|
+
self.assertEqual(
|
|
873
|
+
self.event_handler.logs,
|
|
874
|
+
[
|
|
875
|
+
'[testing-env] environment started',
|
|
876
|
+
'[testing-env/0/feature1] feature setup',
|
|
877
|
+
'[testing-env/0/feature2] feature setup',
|
|
878
|
+
'[testing-env/0] created -> ready',
|
|
879
|
+
'[testing-env/0] sandbox started',
|
|
880
|
+
'[testing-env/0] ready -> acquired',
|
|
881
|
+
'[testing-env/0] acquired -> setting_up',
|
|
882
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
883
|
+
'[testing-env/0/feature1] feature setup session',
|
|
884
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
885
|
+
'[testing-env/0/feature2] feature setup session',
|
|
886
|
+
'[testing-env/0] setting_up -> in_session',
|
|
887
|
+
"[testing-env/0] session 'session1' started",
|
|
888
|
+
'[testing-env/0/session1] shell: echo "hello"',
|
|
889
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
890
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
891
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
892
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
893
|
+
"[testing-env/0] session 'session1' ended",
|
|
894
|
+
'[testing-env/0] in_session -> acquired',
|
|
895
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
896
|
+
'[testing-env/0/feature1] feature teardown',
|
|
897
|
+
'[testing-env/0/feature2] feature teardown',
|
|
898
|
+
'[testing-env/0] shutting_down -> offline',
|
|
899
|
+
'[testing-env/0] sandbox shutdown with SandboxStateError',
|
|
900
|
+
'[testing-env] environment shutdown'
|
|
901
|
+
]
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
def test_feature_setup_non_state_error(self):
|
|
905
|
+
env = self._create_env(
|
|
906
|
+
features={
|
|
907
|
+
'feature1': TestingFeature(),
|
|
908
|
+
'feature2': TestingFeature(
|
|
909
|
+
simulate_setup_error=ValueError
|
|
910
|
+
),
|
|
911
|
+
},
|
|
912
|
+
)
|
|
913
|
+
with env:
|
|
914
|
+
with self.assertRaises(ValueError):
|
|
915
|
+
with env.sandbox('session1'):
|
|
916
|
+
pass
|
|
917
|
+
self.assertEqual(
|
|
918
|
+
self.event_handler.logs,
|
|
919
|
+
[
|
|
920
|
+
'[testing-env] environment started',
|
|
921
|
+
'[testing-env/0/feature1] feature setup',
|
|
922
|
+
'[testing-env/0/feature2] feature setup with ValueError',
|
|
923
|
+
'[testing-env/0] sandbox started with ValueError',
|
|
924
|
+
'[testing-env/0] created -> shutting_down',
|
|
925
|
+
'[testing-env/0/feature1] feature teardown',
|
|
926
|
+
'[testing-env/0/feature2] feature teardown',
|
|
927
|
+
'[testing-env/0] shutting_down -> offline',
|
|
928
|
+
'[testing-env/0] sandbox shutdown'
|
|
929
|
+
]
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
def test_feature_setup_state_error(self):
|
|
933
|
+
env = self._create_env(
|
|
934
|
+
features={
|
|
935
|
+
'feature1': TestingFeature(
|
|
936
|
+
simulate_setup_error=interface.SandboxStateError
|
|
937
|
+
),
|
|
938
|
+
'feature2': TestingFeature(),
|
|
939
|
+
},
|
|
940
|
+
)
|
|
941
|
+
with env:
|
|
942
|
+
with self.assertRaises(interface.EnvironmentOutageError):
|
|
943
|
+
with env.sandbox('session1'):
|
|
944
|
+
pass
|
|
945
|
+
self.assertEqual(
|
|
946
|
+
self.event_handler.logs,
|
|
947
|
+
[
|
|
948
|
+
'[testing-env] environment started',
|
|
949
|
+
'[testing-env/0/feature1] feature setup with SandboxStateError',
|
|
950
|
+
'[testing-env/0] sandbox started with SandboxStateError',
|
|
951
|
+
'[testing-env/0] created -> shutting_down',
|
|
952
|
+
'[testing-env/0/feature1] feature teardown',
|
|
953
|
+
'[testing-env/0] shutting_down -> offline',
|
|
954
|
+
'[testing-env/0] sandbox shutdown',
|
|
955
|
+
'[testing-env] environment shutdown'
|
|
956
|
+
]
|
|
957
|
+
)
|
|
958
|
+
|
|
959
|
+
def test_feature_teardown_non_state_error(self):
|
|
960
|
+
env = self._create_env(
|
|
961
|
+
features={
|
|
962
|
+
'feature1': TestingFeature(),
|
|
963
|
+
'feature2': TestingFeature(
|
|
964
|
+
simulate_teardown_error=ValueError
|
|
965
|
+
),
|
|
966
|
+
},
|
|
967
|
+
)
|
|
968
|
+
with env:
|
|
969
|
+
with self.assertRaises(interface.FeatureTeardownError):
|
|
970
|
+
with env.sandbox('session1'):
|
|
971
|
+
pass
|
|
972
|
+
self.assertEqual(
|
|
973
|
+
self.event_handler.logs,
|
|
974
|
+
[
|
|
975
|
+
'[testing-env] environment started',
|
|
976
|
+
'[testing-env/0/feature1] feature setup',
|
|
977
|
+
'[testing-env/0/feature2] feature setup',
|
|
978
|
+
'[testing-env/0] created -> ready',
|
|
979
|
+
'[testing-env/0] sandbox started',
|
|
980
|
+
'[testing-env/0] ready -> acquired',
|
|
981
|
+
'[testing-env/0] acquired -> setting_up',
|
|
982
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
983
|
+
'[testing-env/0/feature1] feature setup session',
|
|
984
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
985
|
+
'[testing-env/0/feature2] feature setup session',
|
|
986
|
+
'[testing-env/0] setting_up -> in_session',
|
|
987
|
+
"[testing-env/0] session 'session1' started",
|
|
988
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
989
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
990
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
991
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
992
|
+
"[testing-env/0] session 'session1' ended",
|
|
993
|
+
'[testing-env/0] in_session -> acquired',
|
|
994
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
995
|
+
'[testing-env/0/feature1] feature teardown',
|
|
996
|
+
'[testing-env/0/feature2] feature teardown with ValueError',
|
|
997
|
+
'[testing-env/0] shutting_down -> offline',
|
|
998
|
+
'[testing-env/0] sandbox shutdown with FeatureTeardownError',
|
|
999
|
+
]
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
def test_feature_teardown_state_error(self):
|
|
1003
|
+
env = self._create_env(
|
|
1004
|
+
features={
|
|
1005
|
+
'feature1': TestingFeature(
|
|
1006
|
+
simulate_teardown_error=interface.SandboxStateError
|
|
1007
|
+
),
|
|
1008
|
+
'feature2': TestingFeature(
|
|
1009
|
+
),
|
|
1010
|
+
},
|
|
1011
|
+
)
|
|
1012
|
+
with env:
|
|
1013
|
+
with env.sandbox('session1') as sb:
|
|
1014
|
+
pass
|
|
1015
|
+
self.assertEqual(len(sb.state_errors), 1)
|
|
1016
|
+
self.assertEqual(
|
|
1017
|
+
self.event_handler.logs,
|
|
1018
|
+
[
|
|
1019
|
+
'[testing-env] environment started',
|
|
1020
|
+
'[testing-env/0/feature1] feature setup',
|
|
1021
|
+
'[testing-env/0/feature2] feature setup',
|
|
1022
|
+
'[testing-env/0] created -> ready',
|
|
1023
|
+
'[testing-env/0] sandbox started',
|
|
1024
|
+
'[testing-env/0] ready -> acquired',
|
|
1025
|
+
'[testing-env/0] acquired -> setting_up',
|
|
1026
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
1027
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1028
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
1029
|
+
'[testing-env/0/feature2] feature setup session',
|
|
1030
|
+
'[testing-env/0] setting_up -> in_session',
|
|
1031
|
+
"[testing-env/0] session 'session1' started",
|
|
1032
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
1033
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
1034
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
1035
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
1036
|
+
"[testing-env/0] session 'session1' ended",
|
|
1037
|
+
'[testing-env/0] in_session -> acquired',
|
|
1038
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
1039
|
+
'[testing-env/0/feature1] feature teardown with SandboxStateError', # pylint: disable=line-too-long
|
|
1040
|
+
'[testing-env/0/feature2] feature teardown',
|
|
1041
|
+
'[testing-env/0] shutting_down -> offline',
|
|
1042
|
+
'[testing-env/0] sandbox shutdown with FeatureTeardownError'
|
|
1043
|
+
]
|
|
1044
|
+
)
|
|
1045
|
+
|
|
1046
|
+
def test_feature_setup_session_non_state_error(self):
|
|
1047
|
+
env = self._create_env(
|
|
1048
|
+
features={
|
|
1049
|
+
'feature1': TestingFeature(),
|
|
1050
|
+
'feature2': TestingFeature(
|
|
1051
|
+
simulate_setup_session_error=ValueError
|
|
1052
|
+
),
|
|
1053
|
+
},
|
|
1054
|
+
)
|
|
1055
|
+
with env:
|
|
1056
|
+
with self.assertRaises(ValueError):
|
|
1057
|
+
with env.sandbox('session1') as sb:
|
|
1058
|
+
sb.shell('echo "hello"')
|
|
1059
|
+
self.assertEqual(
|
|
1060
|
+
self.event_handler.logs,
|
|
1061
|
+
[
|
|
1062
|
+
'[testing-env] environment started',
|
|
1063
|
+
'[testing-env/0/feature1] feature setup',
|
|
1064
|
+
'[testing-env/0/feature2] feature setup',
|
|
1065
|
+
'[testing-env/0] created -> ready',
|
|
1066
|
+
'[testing-env/0] sandbox started',
|
|
1067
|
+
'[testing-env/0] ready -> acquired',
|
|
1068
|
+
'[testing-env/0] acquired -> setting_up',
|
|
1069
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
1070
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1071
|
+
'[testing-env/0/feature2] feature setup session with ValueError',
|
|
1072
|
+
"[testing-env/0] session 'session1' started with ValueError",
|
|
1073
|
+
'[testing-env/0] setting_up -> shutting_down',
|
|
1074
|
+
'[testing-env/0/session1] shell: "feature1" teardown',
|
|
1075
|
+
'[testing-env/0/feature1] feature teardown',
|
|
1076
|
+
'[testing-env/0/session1] shell: "feature2" teardown',
|
|
1077
|
+
'[testing-env/0/feature2] feature teardown',
|
|
1078
|
+
'[testing-env/0] shutting_down -> offline',
|
|
1079
|
+
'[testing-env/0] sandbox shutdown'
|
|
1080
|
+
]
|
|
1081
|
+
)
|
|
1082
|
+
|
|
1083
|
+
def test_feature_teardown_session_non_state_error(self):
|
|
1084
|
+
env = self._create_env(
|
|
1085
|
+
features={
|
|
1086
|
+
'feature1': TestingFeature(
|
|
1087
|
+
simulate_teardown_session_error=ValueError
|
|
1088
|
+
),
|
|
1089
|
+
'feature2': TestingFeature(),
|
|
1090
|
+
},
|
|
1091
|
+
)
|
|
1092
|
+
with env:
|
|
1093
|
+
with self.assertRaises(interface.SessionTeardownError):
|
|
1094
|
+
with env.sandbox('session1') as sb:
|
|
1095
|
+
sb.shell('echo "hello"')
|
|
1096
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
1097
|
+
self.assertEqual(
|
|
1098
|
+
self.event_handler.logs,
|
|
1099
|
+
[
|
|
1100
|
+
'[testing-env] environment started',
|
|
1101
|
+
'[testing-env/0/feature1] feature setup',
|
|
1102
|
+
'[testing-env/0/feature2] feature setup',
|
|
1103
|
+
'[testing-env/0] created -> ready',
|
|
1104
|
+
'[testing-env/0] sandbox started',
|
|
1105
|
+
'[testing-env/0] ready -> acquired',
|
|
1106
|
+
'[testing-env/0] acquired -> setting_up',
|
|
1107
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
1108
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1109
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
1110
|
+
'[testing-env/0/feature2] feature setup session',
|
|
1111
|
+
'[testing-env/0] setting_up -> in_session',
|
|
1112
|
+
"[testing-env/0] session 'session1' started",
|
|
1113
|
+
'[testing-env/0/session1] shell: echo "hello"',
|
|
1114
|
+
'[testing-env/0/feature1] feature teardown session with ValueError', # pylint: disable=line-too-long
|
|
1115
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
1116
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
1117
|
+
"[testing-env/0] session 'session1' ended",
|
|
1118
|
+
'[testing-env/0] in_session -> acquired',
|
|
1119
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
1120
|
+
'[testing-env/0/feature1] feature teardown',
|
|
1121
|
+
'[testing-env/0/feature2] feature teardown',
|
|
1122
|
+
'[testing-env/0] shutting_down -> offline',
|
|
1123
|
+
'[testing-env/0] sandbox shutdown'
|
|
1124
|
+
]
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
def test_feature_teardown_session_state_error(self):
|
|
1128
|
+
env = self._create_env(
|
|
1129
|
+
features={
|
|
1130
|
+
'feature1': TestingFeature(
|
|
1131
|
+
simulate_teardown_session_error=interface.SandboxStateError
|
|
1132
|
+
),
|
|
1133
|
+
'feature2': TestingFeature(),
|
|
1134
|
+
},
|
|
1135
|
+
)
|
|
1136
|
+
with env:
|
|
1137
|
+
with env.sandbox('session1') as sb:
|
|
1138
|
+
sb.shell('echo "hello"')
|
|
1139
|
+
self.assertEqual(len(sb.state_errors), 1)
|
|
1140
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
1141
|
+
self.assertEqual(
|
|
1142
|
+
self.event_handler.logs,
|
|
1143
|
+
[
|
|
1144
|
+
'[testing-env] environment started',
|
|
1145
|
+
'[testing-env/0/feature1] feature setup',
|
|
1146
|
+
'[testing-env/0/feature2] feature setup',
|
|
1147
|
+
'[testing-env/0] created -> ready',
|
|
1148
|
+
'[testing-env/0] sandbox started',
|
|
1149
|
+
'[testing-env/0] ready -> acquired',
|
|
1150
|
+
'[testing-env/0] acquired -> setting_up',
|
|
1151
|
+
'[testing-env/0/session1] shell: "feature1" setup session',
|
|
1152
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1153
|
+
'[testing-env/0/session1] shell: "feature2" setup session',
|
|
1154
|
+
'[testing-env/0/feature2] feature setup session',
|
|
1155
|
+
'[testing-env/0] setting_up -> in_session',
|
|
1156
|
+
"[testing-env/0] session 'session1' started",
|
|
1157
|
+
'[testing-env/0/session1] shell: echo "hello"',
|
|
1158
|
+
'[testing-env/0/feature1] feature teardown session with SandboxStateError', # pylint: disable=line-too-long
|
|
1159
|
+
'[testing-env/0/session1] shell: "feature2" teardown session',
|
|
1160
|
+
'[testing-env/0/feature2] feature teardown session',
|
|
1161
|
+
"[testing-env/0] session 'session1' ended with SandboxStateError",
|
|
1162
|
+
'[testing-env/0] in_session -> acquired',
|
|
1163
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
1164
|
+
'[testing-env/0/feature1] feature teardown',
|
|
1165
|
+
'[testing-env/0/feature2] feature teardown',
|
|
1166
|
+
'[testing-env/0] shutting_down -> offline',
|
|
1167
|
+
'[testing-env/0] sandbox shutdown'
|
|
1168
|
+
]
|
|
1169
|
+
)
|
|
1170
|
+
|
|
1171
|
+
def test_session_activity_non_state_error(self):
|
|
1172
|
+
env = self._create_env(
|
|
1173
|
+
pool_size=1,
|
|
1174
|
+
features={
|
|
1175
|
+
'feature1': TestingFeature(),
|
|
1176
|
+
},
|
|
1177
|
+
)
|
|
1178
|
+
with env:
|
|
1179
|
+
with env.sandbox('session1') as sb:
|
|
1180
|
+
with self.assertRaises(ValueError):
|
|
1181
|
+
sb.shell('echo foo', raise_error=ValueError)
|
|
1182
|
+
self.assertEqual(len(sb.state_errors), 0)
|
|
1183
|
+
sb.shell('echo bar')
|
|
1184
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.IN_SESSION)
|
|
1185
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.READY)
|
|
1186
|
+
self.assertEqual(
|
|
1187
|
+
self.event_handler.logs,
|
|
1188
|
+
[
|
|
1189
|
+
'[testing-env/0/feature1] feature setup',
|
|
1190
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1191
|
+
'[testing-env/0] created -> ready',
|
|
1192
|
+
'[testing-env/0] sandbox started',
|
|
1193
|
+
'[testing-env] environment started',
|
|
1194
|
+
'[testing-env/0] ready -> acquired',
|
|
1195
|
+
'[testing-env/0] acquired -> setting_up',
|
|
1196
|
+
'[testing-env/0] setting_up -> in_session',
|
|
1197
|
+
"[testing-env/0] session 'session1' started",
|
|
1198
|
+
'[testing-env/0/session1] shell: echo foo with ValueError',
|
|
1199
|
+
'[testing-env/0/session1] shell: echo bar',
|
|
1200
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
1201
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
1202
|
+
"[testing-env/0] session 'session1' ended",
|
|
1203
|
+
'[testing-env/0] in_session -> setting_up',
|
|
1204
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1205
|
+
'[testing-env/0] setting_up -> ready',
|
|
1206
|
+
]
|
|
1207
|
+
)
|
|
1208
|
+
|
|
1209
|
+
def test_session_activity_state_error(self):
|
|
1210
|
+
env = self._create_env(
|
|
1211
|
+
pool_size=1,
|
|
1212
|
+
features={
|
|
1213
|
+
'feature1': TestingFeature(),
|
|
1214
|
+
},
|
|
1215
|
+
)
|
|
1216
|
+
with env:
|
|
1217
|
+
with env.sandbox('session1') as sb:
|
|
1218
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
1219
|
+
sb.shell('echo foo', raise_error=RuntimeError)
|
|
1220
|
+
self.assertEqual(len(sb.state_errors), 1)
|
|
1221
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
1222
|
+
sb.shell('echo bar')
|
|
1223
|
+
self.assertEqual(
|
|
1224
|
+
self.event_handler.logs,
|
|
1225
|
+
[
|
|
1226
|
+
'[testing-env/0/feature1] feature setup',
|
|
1227
|
+
'[testing-env/0/feature1] feature setup session',
|
|
1228
|
+
'[testing-env/0] created -> ready',
|
|
1229
|
+
'[testing-env/0] sandbox started',
|
|
1230
|
+
'[testing-env] environment started',
|
|
1231
|
+
'[testing-env/0] ready -> acquired',
|
|
1232
|
+
'[testing-env/0] acquired -> setting_up',
|
|
1233
|
+
'[testing-env/0] setting_up -> in_session',
|
|
1234
|
+
"[testing-env/0] session 'session1' started",
|
|
1235
|
+
'[testing-env/0/session1] shell: echo foo with RuntimeError',
|
|
1236
|
+
'[testing-env/0/session1] shell: "feature1" teardown session',
|
|
1237
|
+
'[testing-env/0/feature1] feature teardown session',
|
|
1238
|
+
"[testing-env/0] session 'session1' ended with SandboxStateError",
|
|
1239
|
+
'[testing-env/0] in_session -> acquired',
|
|
1240
|
+
'[testing-env/0] acquired -> shutting_down',
|
|
1241
|
+
'[testing-env/0/feature1] feature teardown',
|
|
1242
|
+
'[testing-env/0] shutting_down -> offline',
|
|
1243
|
+
'[testing-env/0] sandbox shutdown'
|
|
1244
|
+
]
|
|
1245
|
+
)
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
class SandboxActivityTests(unittest.TestCase):
|
|
1249
|
+
|
|
565
1250
|
def test_session_id(self):
|
|
566
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
567
1251
|
env = TestingEnvironment(
|
|
568
1252
|
features={'test_feature': TestingFeature()},
|
|
569
|
-
|
|
1253
|
+
pool_size=0
|
|
570
1254
|
)
|
|
571
|
-
|
|
572
1255
|
with env:
|
|
573
1256
|
with env.sandbox() as sb:
|
|
574
1257
|
self.assertRegex(sb.session_id, r'session-[0-9a-f]{7}')
|
|
@@ -587,208 +1270,334 @@ class EnvironmentTest(unittest.TestCase):
|
|
|
587
1270
|
def foo(session_id: str):
|
|
588
1271
|
del session_id
|
|
589
1272
|
|
|
590
|
-
def
|
|
591
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
1273
|
+
def test_ping_error(self):
|
|
592
1274
|
env = TestingEnvironment(
|
|
593
|
-
|
|
594
|
-
pool_size=
|
|
595
|
-
|
|
596
|
-
outage_retry_interval=0,
|
|
597
|
-
event_handler=event_handler,
|
|
1275
|
+
features={'test_feature': TestingFeature(housekeep_interval=0)},
|
|
1276
|
+
pool_size=1,
|
|
1277
|
+
keepalive_interval=0,
|
|
598
1278
|
)
|
|
599
|
-
self.assertEqual(env.working_dir, '/tmp/testing-env')
|
|
600
1279
|
with env:
|
|
601
1280
|
with env.sandbox('session1') as sb:
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
self.assertEqual(sb.session_id, 'session1')
|
|
606
|
-
self.assertEqual(
|
|
607
|
-
sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
|
|
1281
|
+
sb.rebind(
|
|
1282
|
+
simulate_ping_error=interface.SandboxStateError,
|
|
1283
|
+
skip_notification=True
|
|
608
1284
|
)
|
|
1285
|
+
sb.wait_until_next_housekeep()
|
|
1286
|
+
self.assertEqual(sb.status, sb.Status.OFFLINE)
|
|
609
1287
|
|
|
610
|
-
|
|
611
|
-
with self.assertRaises(ValueError):
|
|
612
|
-
sb.shell('bad command', must_succeed=False)
|
|
613
|
-
|
|
614
|
-
# Critical error.
|
|
615
|
-
with self.assertRaises(interface.SandboxStateError):
|
|
616
|
-
sb.shell('bad command')
|
|
617
|
-
maintenance_count = env._maintenance_count
|
|
618
|
-
self.assertFalse(sb.is_alive)
|
|
619
|
-
|
|
620
|
-
self.assertTrue(env.is_alive)
|
|
621
|
-
|
|
622
|
-
# Wait dead sandbox to be replaced.
|
|
623
|
-
while env._maintenance_count == maintenance_count:
|
|
624
|
-
time.sleep(0.5)
|
|
625
|
-
|
|
626
|
-
self.assertTrue(env.sandbox_pool[0].is_alive)
|
|
627
|
-
self.assertEqual(
|
|
628
|
-
env.stats(),
|
|
629
|
-
dict(
|
|
630
|
-
sandbox=dict(
|
|
631
|
-
num_total=1,
|
|
632
|
-
num_busy=0,
|
|
633
|
-
num_free=1,
|
|
634
|
-
num_dead=0,
|
|
635
|
-
)
|
|
636
|
-
)
|
|
637
|
-
)
|
|
638
|
-
|
|
639
|
-
self.assertFalse(env.is_alive)
|
|
640
|
-
self.assertIsNone(interface.Environment.current())
|
|
641
|
-
self.assertEqual(
|
|
642
|
-
event_handler.history,
|
|
643
|
-
[
|
|
644
|
-
'[testing-env/0] sandbox started',
|
|
645
|
-
'[testing-env] environment started',
|
|
646
|
-
'[testing-env/0] session \'session1\' started',
|
|
647
|
-
# Sandbox shutdown is triggered by the SandboxStateError before
|
|
648
|
-
# the session end event is triggered.
|
|
649
|
-
'[testing-env/0] sandbox shutdown',
|
|
650
|
-
'[testing-env/0] session \'session1\' ended',
|
|
651
|
-
# The maintenance loop will replace the dead sandbox and start a new
|
|
652
|
-
# sandbox.
|
|
653
|
-
'[testing-env/0] sandbox started',
|
|
654
|
-
'[testing-env/0] sandbox shutdown',
|
|
655
|
-
'[testing-env] environment shutdown',
|
|
656
|
-
]
|
|
657
|
-
)
|
|
658
|
-
self.assertEqual(
|
|
659
|
-
env.stats(),
|
|
660
|
-
dict(
|
|
661
|
-
sandbox=dict(
|
|
662
|
-
num_total=0,
|
|
663
|
-
num_busy=0,
|
|
664
|
-
num_free=0,
|
|
665
|
-
num_dead=0,
|
|
666
|
-
)
|
|
667
|
-
)
|
|
668
|
-
)
|
|
669
|
-
|
|
670
|
-
def test_environment_outage_with_pooling(self):
|
|
671
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
1288
|
+
def test_housekeep_error(self):
|
|
672
1289
|
env = TestingEnvironment(
|
|
673
|
-
features={'test_feature': TestingFeature()},
|
|
674
|
-
pool_size=
|
|
1290
|
+
features={'test_feature': TestingFeature(housekeep_interval=0)},
|
|
1291
|
+
pool_size=1,
|
|
675
1292
|
outage_grace_period=0,
|
|
676
1293
|
outage_retry_interval=0,
|
|
677
1294
|
keepalive_interval=0,
|
|
678
|
-
event_handler=event_handler,
|
|
679
|
-
stats_report_interval=1,
|
|
680
1295
|
)
|
|
681
1296
|
with env:
|
|
682
1297
|
with env.sandbox('session1') as sb:
|
|
683
1298
|
self.assertEqual(len(env.sandbox_pool), 1)
|
|
684
|
-
self.
|
|
685
|
-
self.assertTrue(sb.is_busy)
|
|
1299
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.IN_SESSION)
|
|
686
1300
|
self.assertEqual(sb.session_id, 'session1')
|
|
687
|
-
env.set_offline(True)
|
|
688
1301
|
housekeep_count = sb._housekeep_count
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
while
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
with env.sandbox('session2'):
|
|
699
|
-
pass
|
|
700
|
-
|
|
701
|
-
with self.assertRaises(interface.EnvironmentOutageError):
|
|
702
|
-
with env.test_feature.my_service():
|
|
703
|
-
pass
|
|
704
|
-
self.assertGreater(env.offline_duration, 0)
|
|
1302
|
+
sb.test_feature.rebind(
|
|
1303
|
+
simulate_housekeep_error=interface.SandboxStateError,
|
|
1304
|
+
skip_notification=True
|
|
1305
|
+
)
|
|
1306
|
+
while sb._housekeep_count == housekeep_count or (
|
|
1307
|
+
sb.status == interface.Sandbox.Status.IN_SESSION
|
|
1308
|
+
):
|
|
1309
|
+
time.sleep(0.1)
|
|
1310
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
705
1311
|
|
|
706
|
-
def
|
|
707
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
1312
|
+
def test_remove_event_handler(self):
|
|
708
1313
|
env = TestingEnvironment(
|
|
709
|
-
features={'test_feature': TestingFeature()},
|
|
710
|
-
pool_size=
|
|
1314
|
+
features={'test_feature': TestingFeature(housekeep_interval=0)},
|
|
1315
|
+
pool_size=1,
|
|
711
1316
|
outage_grace_period=0,
|
|
712
1317
|
outage_retry_interval=0,
|
|
713
1318
|
keepalive_interval=0,
|
|
714
|
-
event_handler=event_handler,
|
|
715
1319
|
stats_report_interval=1,
|
|
716
1320
|
)
|
|
1321
|
+
event_handler = TestingEnvironmentEventHandler()
|
|
1322
|
+
with env:
|
|
1323
|
+
with env.sandbox('session1') as sb:
|
|
1324
|
+
sb.add_event_handler(event_handler)
|
|
1325
|
+
sb.shell('test_feature')
|
|
1326
|
+
sb.remove_event_handler(event_handler)
|
|
1327
|
+
events = list(event_handler.logs)
|
|
1328
|
+
self.assertGreater(len(events), 0)
|
|
1329
|
+
with env.sandbox('session2') as sb:
|
|
1330
|
+
sb.shell('test_feature')
|
|
1331
|
+
self.assertEqual(len(events), len(event_handler.logs))
|
|
717
1332
|
|
|
718
|
-
def _thread_func(i) -> bool:
|
|
719
|
-
time.sleep(0.8 * i)
|
|
720
|
-
with env.sandbox(f'session{i}') as sb:
|
|
721
|
-
return sb.shell('echo "foo"')
|
|
722
1333
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
vs.append(f.result())
|
|
734
|
-
except interface.EnvironmentError as e:
|
|
735
|
-
vs.append(e)
|
|
736
|
-
|
|
737
|
-
def test_environment_outage_during_acquire_pool_not_full(self):
|
|
738
|
-
event_handler = TestingEnvironmentEventHandler()
|
|
739
|
-
env = TestingEnvironment(
|
|
740
|
-
features={
|
|
741
|
-
'test_feature': TestingFeature(simulate_housekeep_error=True)
|
|
742
|
-
},
|
|
743
|
-
pool_size=(1, 3),
|
|
744
|
-
outage_grace_period=1,
|
|
1334
|
+
class SandboxServiceTests(unittest.TestCase):
|
|
1335
|
+
|
|
1336
|
+
def setUp(self):
|
|
1337
|
+
super().setUp()
|
|
1338
|
+
self.maxDiff = None
|
|
1339
|
+
self.event_handler = TestingEnvironmentEventHandler()
|
|
1340
|
+
self.env = TestingEnvironment(
|
|
1341
|
+
features={'test_feature': TestingFeature()},
|
|
1342
|
+
pool_size=0,
|
|
1343
|
+
outage_grace_period=0,
|
|
745
1344
|
outage_retry_interval=0,
|
|
746
1345
|
keepalive_interval=0,
|
|
747
|
-
|
|
1346
|
+
event_handlers=[self.event_handler],
|
|
748
1347
|
stats_report_interval=1,
|
|
1348
|
+
random_seed=1,
|
|
749
1349
|
)
|
|
750
1350
|
|
|
751
|
-
|
|
752
|
-
with env.sandbox('session1') as sb:
|
|
753
|
-
return sb.shell('echo "foo"')
|
|
1351
|
+
def test_service_call_activity_log(self):
|
|
754
1352
|
|
|
755
|
-
|
|
756
|
-
self.assertEqual(len(env.sandbox_pool), 1)
|
|
757
|
-
self.assertFalse(env.sandbox_pool[0].is_alive)
|
|
758
|
-
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
|
|
759
|
-
env.set_offline(True)
|
|
760
|
-
f = executor.submit(_thread_func)
|
|
761
|
-
with self.assertRaises(interface.EnvironmentOutageError):
|
|
762
|
-
f.result()
|
|
1353
|
+
class CustomEventHandler(interface.EnvironmentEventHandler):
|
|
763
1354
|
|
|
764
|
-
|
|
765
|
-
|
|
1355
|
+
def __init__(self):
|
|
1356
|
+
self.calls = []
|
|
1357
|
+
|
|
1358
|
+
def on_session_activity(
|
|
1359
|
+
self,
|
|
1360
|
+
session_id: str,
|
|
1361
|
+
name: str,
|
|
1362
|
+
environment: interface.Environment,
|
|
1363
|
+
sandbox: interface.Sandbox,
|
|
1364
|
+
feature: interface.Feature | None,
|
|
1365
|
+
error: BaseException | None,
|
|
1366
|
+
**kwargs: Any):
|
|
1367
|
+
self.calls.append((session_id, name, kwargs))
|
|
1368
|
+
|
|
1369
|
+
event_handler = CustomEventHandler()
|
|
766
1370
|
env = TestingEnvironment(
|
|
767
|
-
features={
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
)
|
|
771
|
-
},
|
|
772
|
-
pool_size=1,
|
|
773
|
-
outage_grace_period=0,
|
|
774
|
-
outage_retry_interval=0,
|
|
775
|
-
keepalive_interval=0,
|
|
776
|
-
event_handler=event_handler,
|
|
777
|
-
stats_report_interval=1,
|
|
1371
|
+
features={'test_feature': TestingFeature()},
|
|
1372
|
+
pool_size=0,
|
|
1373
|
+
event_handlers=[event_handler],
|
|
778
1374
|
)
|
|
779
1375
|
with env:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
1376
|
+
env.test_feature.call_with_varargs(
|
|
1377
|
+
'sum', 1, 2, debug=True, session_id='session1'
|
|
1378
|
+
)
|
|
1379
|
+
self.assertEqual(
|
|
1380
|
+
event_handler.calls,
|
|
1381
|
+
[
|
|
1382
|
+
('session1', 'shell', {'code': '"test_feature" setup session'}),
|
|
1383
|
+
('session1', 'call_with_varargs', {'args': (1, 2), 'code': 'sum', 'debug': True}), # pylint: disable=line-too-long
|
|
1384
|
+
('session1', 'shell', {'code': '"test_feature" teardown session'}),
|
|
1385
|
+
]
|
|
1386
|
+
)
|
|
1387
|
+
|
|
1388
|
+
def test_service_call_from_feature(self):
|
|
1389
|
+
with self.env:
|
|
1390
|
+
with self.env.sandbox('session1') as sb:
|
|
1391
|
+
self.assertEqual(sb.test_feature.num_shell_calls(), 2)
|
|
1392
|
+
self.assertEqual(sb.test_feature.num_shell_calls(), 2)
|
|
1393
|
+
self.assertEqual(
|
|
1394
|
+
self.event_handler.logs,
|
|
1395
|
+
[
|
|
1396
|
+
# pylint: disable=line-too-long
|
|
1397
|
+
'[testing-env] environment started',
|
|
1398
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1399
|
+
'[testing-env/0] sandbox started',
|
|
1400
|
+
'[testing-env/0/session1] shell: "test_feature" setup session',
|
|
1401
|
+
"[testing-env/0] session 'session1' started",
|
|
1402
|
+
'[testing-env/0/session1] num_shell_calls: None',
|
|
1403
|
+
'[testing-env/0/session1] num_shell_calls: None',
|
|
1404
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
1405
|
+
"[testing-env/0] session 'session1' ended",
|
|
1406
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1407
|
+
'[testing-env/0] sandbox shutdown',
|
|
1408
|
+
'[testing-env] environment shutdown'
|
|
1409
|
+
# pylint: enable=line-too-long
|
|
1410
|
+
]
|
|
1411
|
+
)
|
|
1412
|
+
|
|
1413
|
+
def test_service_call_from_feature_with_error(self):
|
|
1414
|
+
with self.env:
|
|
1415
|
+
with self.env.sandbox('session1') as sb:
|
|
1416
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
1417
|
+
sb.test_feature.bad_shell_call()
|
|
1418
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
1419
|
+
|
|
1420
|
+
self.assertEqual(
|
|
1421
|
+
self.event_handler.logs,
|
|
1422
|
+
[
|
|
1423
|
+
# pylint: disable=line-too-long
|
|
1424
|
+
'[testing-env] environment started',
|
|
1425
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1426
|
+
'[testing-env/0] sandbox started',
|
|
1427
|
+
'[testing-env/0/session1] shell: "test_feature" setup session',
|
|
1428
|
+
"[testing-env/0] session 'session1' started",
|
|
1429
|
+
'[testing-env/0/session1] shell: bad command with RuntimeError',
|
|
1430
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
1431
|
+
"[testing-env/0] session 'session1' ended with SandboxStateError",
|
|
1432
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1433
|
+
'[testing-env/0] sandbox shutdown',
|
|
1434
|
+
'[testing-env/0/session1] bad_shell_call: None with SandboxStateError',
|
|
1435
|
+
'[testing-env] environment shutdown'
|
|
1436
|
+
# pylint: enable=line-too-long
|
|
1437
|
+
]
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
def test_service_call_from_environment(self):
|
|
1441
|
+
with self.env:
|
|
1442
|
+
self.assertEqual(self.env.test_feature.num_shell_calls(), 2)
|
|
1443
|
+
self.assertEqual(
|
|
1444
|
+
self.event_handler.logs,
|
|
1445
|
+
[
|
|
1446
|
+
# pylint: disable=line-too-long
|
|
1447
|
+
'[testing-env] environment started',
|
|
1448
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1449
|
+
'[testing-env/0] sandbox started',
|
|
1450
|
+
'[testing-env/0/session-2291d8c] shell: "test_feature" setup session',
|
|
1451
|
+
"[testing-env/0] session 'session-2291d8c' started",
|
|
1452
|
+
'[testing-env/0/session-2291d8c] num_shell_calls: None',
|
|
1453
|
+
'[testing-env/0/session-2291d8c] shell: "test_feature" teardown session',
|
|
1454
|
+
"[testing-env/0] session 'session-2291d8c' ended",
|
|
1455
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1456
|
+
'[testing-env/0] sandbox shutdown',
|
|
1457
|
+
'[testing-env] environment shutdown'
|
|
1458
|
+
# pylint: enable=line-too-long
|
|
1459
|
+
]
|
|
1460
|
+
)
|
|
1461
|
+
|
|
1462
|
+
def test_service_call_from_environment_with_error(self):
|
|
1463
|
+
with self.env:
|
|
1464
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
1465
|
+
self.env.test_feature.bad_shell_call(session_id='session1')
|
|
1466
|
+
self.assertEqual(
|
|
1467
|
+
self.event_handler.logs,
|
|
1468
|
+
[
|
|
1469
|
+
# pylint: disable=line-too-long
|
|
1470
|
+
'[testing-env] environment started',
|
|
1471
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1472
|
+
'[testing-env/0] sandbox started',
|
|
1473
|
+
'[testing-env/0/session1] shell: "test_feature" setup session',
|
|
1474
|
+
"[testing-env/0] session 'session1' started",
|
|
1475
|
+
'[testing-env/0/session1] shell: bad command with RuntimeError',
|
|
1476
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
1477
|
+
"[testing-env/0] session 'session1' ended with SandboxStateError",
|
|
1478
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1479
|
+
'[testing-env/0] sandbox shutdown',
|
|
1480
|
+
'[testing-env/0/session1] bad_shell_call: None with SandboxStateError',
|
|
1481
|
+
'[testing-env] environment shutdown'
|
|
1482
|
+
# pylint: enable=line-too-long
|
|
1483
|
+
]
|
|
1484
|
+
)
|
|
1485
|
+
|
|
1486
|
+
def test_service_context_manager_from_feature(self):
|
|
1487
|
+
with self.env:
|
|
1488
|
+
with self.env.sandbox('session1') as sb:
|
|
1489
|
+
with sb.test_feature.my_service() as service:
|
|
1490
|
+
service.do('hello')
|
|
1491
|
+
sb.shell('foo')
|
|
1492
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.IN_SESSION)
|
|
1493
|
+
self.assertEqual(
|
|
1494
|
+
self.event_handler.logs,
|
|
1495
|
+
[
|
|
1496
|
+
# pylint: disable=line-too-long
|
|
1497
|
+
'[testing-env] environment started',
|
|
1498
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1499
|
+
'[testing-env/0] sandbox started',
|
|
1500
|
+
'[testing-env/0/session1] shell: "test_feature" setup session',
|
|
1501
|
+
"[testing-env/0] session 'session1' started",
|
|
1502
|
+
'[testing-env/0/session1] my_service: None',
|
|
1503
|
+
'[testing-env/0/session1] shell: hello',
|
|
1504
|
+
'[testing-env/0/session1] shell: foo',
|
|
1505
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
1506
|
+
"[testing-env/0] session 'session1' ended",
|
|
1507
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1508
|
+
'[testing-env/0] sandbox shutdown',
|
|
1509
|
+
'[testing-env] environment shutdown'
|
|
1510
|
+
# pylint: enable=line-too-long
|
|
1511
|
+
]
|
|
1512
|
+
)
|
|
1513
|
+
|
|
1514
|
+
def test_service_context_manager_from_feature_with_error(self):
|
|
1515
|
+
with self.env:
|
|
1516
|
+
with self.env.sandbox('session1') as sb:
|
|
1517
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
1518
|
+
with sb.test_feature.my_service() as service:
|
|
1519
|
+
service.do('hello', raise_error=interface.SandboxStateError)
|
|
1520
|
+
self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
|
|
1521
|
+
self.assertEqual(
|
|
1522
|
+
self.event_handler.logs,
|
|
1523
|
+
[
|
|
1524
|
+
# pylint: disable=line-too-long
|
|
1525
|
+
'[testing-env] environment started',
|
|
1526
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1527
|
+
'[testing-env/0] sandbox started',
|
|
1528
|
+
'[testing-env/0/session1] shell: "test_feature" setup session',
|
|
1529
|
+
"[testing-env/0] session 'session1' started",
|
|
1530
|
+
'[testing-env/0/session1] my_service: None',
|
|
1531
|
+
'[testing-env/0/session1] shell: hello with SandboxStateError',
|
|
1532
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
1533
|
+
"[testing-env/0] session 'session1' ended with SandboxStateError",
|
|
1534
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1535
|
+
'[testing-env/0] sandbox shutdown',
|
|
1536
|
+
'[testing-env] environment shutdown'
|
|
1537
|
+
# pylint: enable=line-too-long
|
|
1538
|
+
]
|
|
1539
|
+
)
|
|
1540
|
+
|
|
1541
|
+
def test_service_context_manager_from_environment(self):
|
|
1542
|
+
with self.env:
|
|
1543
|
+
with self.env.test_feature.my_service(session_id='session1') as service:
|
|
1544
|
+
service.do('foo')
|
|
1545
|
+
with self.env.test_feature.my_service() as service:
|
|
1546
|
+
service.do('bar')
|
|
1547
|
+
self.assertEqual(
|
|
1548
|
+
self.event_handler.logs,
|
|
1549
|
+
[
|
|
1550
|
+
# pylint: disable=line-too-long
|
|
1551
|
+
'[testing-env] environment started',
|
|
1552
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1553
|
+
'[testing-env/0] sandbox started',
|
|
1554
|
+
'[testing-env/0/session1] shell: "test_feature" setup session',
|
|
1555
|
+
"[testing-env/0] session 'session1' started",
|
|
1556
|
+
'[testing-env/0/session1] my_service: None',
|
|
1557
|
+
'[testing-env/0/session1] shell: foo',
|
|
1558
|
+
'[testing-env/0/session1] shell: "test_feature" teardown session',
|
|
1559
|
+
"[testing-env/0] session 'session1' ended",
|
|
1560
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1561
|
+
'[testing-env/0] sandbox shutdown',
|
|
1562
|
+
'[testing-env/1/test_feature] feature setup',
|
|
1563
|
+
'[testing-env/1] sandbox started',
|
|
1564
|
+
'[testing-env/1/session-2291d8c] shell: "test_feature" setup session',
|
|
1565
|
+
"[testing-env/1] session 'session-2291d8c' started",
|
|
1566
|
+
'[testing-env/1/session-2291d8c] my_service: None',
|
|
1567
|
+
'[testing-env/1/session-2291d8c] shell: bar',
|
|
1568
|
+
'[testing-env/1/session-2291d8c] shell: "test_feature" teardown session',
|
|
1569
|
+
"[testing-env/1] session 'session-2291d8c' ended",
|
|
1570
|
+
'[testing-env/1/test_feature] feature teardown',
|
|
1571
|
+
'[testing-env/1] sandbox shutdown',
|
|
1572
|
+
'[testing-env] environment shutdown'
|
|
1573
|
+
# pylint: enable=line-too-long
|
|
1574
|
+
]
|
|
1575
|
+
)
|
|
1576
|
+
|
|
1577
|
+
def test_service_context_manager_from_environment_with_error(self):
|
|
1578
|
+
with self.env:
|
|
1579
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
1580
|
+
with self.env.test_feature.my_service() as service:
|
|
1581
|
+
service.do('hello', raise_error=interface.SandboxStateError)
|
|
1582
|
+
self.assertEqual(
|
|
1583
|
+
self.event_handler.logs,
|
|
1584
|
+
[
|
|
1585
|
+
# pylint: disable=line-too-long
|
|
1586
|
+
'[testing-env] environment started',
|
|
1587
|
+
'[testing-env/0/test_feature] feature setup',
|
|
1588
|
+
'[testing-env/0] sandbox started',
|
|
1589
|
+
'[testing-env/0/session-2291d8c] shell: "test_feature" setup session',
|
|
1590
|
+
"[testing-env/0] session 'session-2291d8c' started",
|
|
1591
|
+
'[testing-env/0/session-2291d8c] my_service: None',
|
|
1592
|
+
'[testing-env/0/session-2291d8c] shell: hello with SandboxStateError',
|
|
1593
|
+
'[testing-env/0/session-2291d8c] shell: "test_feature" teardown session',
|
|
1594
|
+
"[testing-env/0] session 'session-2291d8c' ended with SandboxStateError",
|
|
1595
|
+
'[testing-env/0/test_feature] feature teardown',
|
|
1596
|
+
'[testing-env/0] sandbox shutdown',
|
|
1597
|
+
'[testing-env] environment shutdown'
|
|
1598
|
+
# pylint: enable=line-too-long
|
|
1599
|
+
]
|
|
1600
|
+
)
|
|
792
1601
|
|
|
793
1602
|
|
|
794
1603
|
if __name__ == '__main__':
|