xref: /trunk/test/aoo-test/tests/harness/test_client.py (revision 2cb6d6f119bbe2f78742f1c5c8538cb0d4e72ede)
1*2cb6d6f1SJim Jagielski################################################################
2*2cb6d6f1SJim Jagielski#
3*2cb6d6f1SJim Jagielski#  Licensed to the Apache Software Foundation (ASF) under one
4*2cb6d6f1SJim Jagielski#  or more contributor license agreements.  See the NOTICE file
5*2cb6d6f1SJim Jagielski#  distributed with this work for additional information
6*2cb6d6f1SJim Jagielski#  regarding copyright ownership.  The ASF licenses this file
7*2cb6d6f1SJim Jagielski#  to you under the Apache License, Version 2.0 (the
8*2cb6d6f1SJim Jagielski#  "License"); you may not use this file except in compliance
9*2cb6d6f1SJim Jagielski#  with the License.  You may obtain a copy of the License at
10*2cb6d6f1SJim Jagielski#
11*2cb6d6f1SJim Jagielski#    http://www.apache.org/licenses/LICENSE-2.0
12*2cb6d6f1SJim Jagielski#
13*2cb6d6f1SJim Jagielski#  Unless required by applicable law or agreed to in writing,
14*2cb6d6f1SJim Jagielski#  software distributed under the License is distributed on an
15*2cb6d6f1SJim Jagielski#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16*2cb6d6f1SJim Jagielski#  KIND, either express or implied.  See the License for the
17*2cb6d6f1SJim Jagielski#  specific language governing permissions and limitations
18*2cb6d6f1SJim Jagielski#  under the License.
19*2cb6d6f1SJim Jagielski#
20*2cb6d6f1SJim Jagielski################################################################
21*2cb6d6f1SJim Jagielski
22*2cb6d6f1SJim Jagielski"""client.py against the fake worker: normal replies, error replies, and every way a worker can go wrong."""
23*2cb6d6f1SJim Jagielskiimport os
24*2cb6d6f1SJim Jagielskiimport time
25*2cb6d6f1SJim Jagielski
26*2cb6d6f1SJim Jagielskiimport pytest
27*2cb6d6f1SJim Jagielski
28*2cb6d6f1SJim Jagielskifrom aootest import client
29*2cb6d6f1SJim Jagielskifrom aootest.client import ProtocolError, WorkerCrash, WorkerError, WorkerHang
30*2cb6d6f1SJim Jagielski
31*2cb6d6f1SJim Jagielskipytestmark = pytest.mark.selftest
32*2cb6d6f1SJim JagielskiPOSIX = os.name == "posix"
33*2cb6d6f1SJim Jagielski
34*2cb6d6f1SJim Jagielski
35*2cb6d6f1SJim Jagielskidef gone(pid, wait=5.0):
36*2cb6d6f1SJim Jagielski    end = time.time() + wait
37*2cb6d6f1SJim Jagielski    while time.time() < end:
38*2cb6d6f1SJim Jagielski        try:
39*2cb6d6f1SJim Jagielski            os.kill(pid, 0)
40*2cb6d6f1SJim Jagielski        except ProcessLookupError:
41*2cb6d6f1SJim Jagielski            return True
42*2cb6d6f1SJim Jagielski        time.sleep(0.05)
43*2cb6d6f1SJim Jagielski    return False
44*2cb6d6f1SJim Jagielski
45*2cb6d6f1SJim Jagielski
46*2cb6d6f1SJim Jagielskidef test_round_trip(start_worker):
47*2cb6d6f1SJim Jagielski    w = start_worker()
48*2cb6d6f1SJim Jagielski    assert w.hello["protocol"] == 1 and w.hello["pid"] == w.pid
49*2cb6d6f1SJim Jagielski    assert w.call("echo", {"x": 1, "y": [1, 2]}) == {"x": 1, "y": [1, 2]}
50*2cb6d6f1SJim Jagielski    assert w.call("add", {"a": 2, "b": 3}) == {"sum": 5}
51*2cb6d6f1SJim Jagielski    assert w.call("ping") == {"pong": True}
52*2cb6d6f1SJim Jagielski
53*2cb6d6f1SJim Jagielski
54*2cb6d6f1SJim Jagielskidef test_printing_in_the_worker_does_not_corrupt_the_protocol(start_worker):
55*2cb6d6f1SJim Jagielski    w = start_worker()
56*2cb6d6f1SJim Jagielski    assert w.call("chatter") == {"printed": True}
57*2cb6d6f1SJim Jagielski    assert w.call("echo", {"after": True}) == {"after": True}
58*2cb6d6f1SJim Jagielski
59*2cb6d6f1SJim Jagielski
60*2cb6d6f1SJim Jagielskidef test_error_replies_keep_the_worker_usable(start_worker):
61*2cb6d6f1SJim Jagielski    w = start_worker()
62*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
63*2cb6d6f1SJim Jagielski        w.call("fail", {"kind": "uno", "message": "the document would not load"})
64*2cb6d6f1SJim Jagielski    assert (e.value.kind, e.value.message) == ("uno", "the document would not load")
65*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
66*2cb6d6f1SJim Jagielski        w.call("boom")
67*2cb6d6f1SJim Jagielski    assert e.value.kind == "python" and e.value.exc == "ZeroDivisionError" and "ZeroDivisionError" in e.value.traceback
68*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
69*2cb6d6f1SJim Jagielski        w.call("no-such-op")
70*2cb6d6f1SJim Jagielski    assert e.value.kind == "bad_args"
71*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerError) as e:
72*2cb6d6f1SJim Jagielski        w.call("add", {"a": 1})
73*2cb6d6f1SJim Jagielski    assert e.value.kind == "bad_args" and "missing" in e.value.message
74*2cb6d6f1SJim Jagielski    assert w.call("add", {"a": 1, "b": 1}) == {"sum": 2}
75*2cb6d6f1SJim Jagielski
76*2cb6d6f1SJim Jagielski
77*2cb6d6f1SJim Jagielskidef test_close_ends_the_worker(start_worker):
78*2cb6d6f1SJim Jagielski    w = start_worker()
79*2cb6d6f1SJim Jagielski    p = w._proc
80*2cb6d6f1SJim Jagielski    w.close()
81*2cb6d6f1SJim Jagielski    assert p.poll() is not None
82*2cb6d6f1SJim Jagielski
83*2cb6d6f1SJim Jagielski
84*2cb6d6f1SJim Jagielski# ---- HANG ----------------------------------------------------------------------------------------------------------
85*2cb6d6f1SJim Jagielski
86*2cb6d6f1SJim Jagielskidef test_hang_is_reported_and_the_worker_killed(start_worker):
87*2cb6d6f1SJim Jagielski    w = start_worker("hang")
88*2cb6d6f1SJim Jagielski    t0 = time.time()
89*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerHang, match="no answer within 1s"):
90*2cb6d6f1SJim Jagielski        w.call("echo", timeout=1)
91*2cb6d6f1SJim Jagielski    assert time.time() - t0 < 6
92*2cb6d6f1SJim Jagielski    assert w._proc.poll() is not None
93*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerHang):                                   # a finished client stays finished
94*2cb6d6f1SJim Jagielski        w.call("echo")
95*2cb6d6f1SJim Jagielski
96*2cb6d6f1SJim Jagielski
97*2cb6d6f1SJim Jagielski@pytest.mark.skipif(not POSIX, reason="child process ids are only checked on POSIX")
98*2cb6d6f1SJim Jagielskidef test_hang_kills_the_workers_children_too(start_worker):
99*2cb6d6f1SJim Jagielski    w = start_worker()
100*2cb6d6f1SJim Jagielski    child = w.call("spawn_child")["pid"]
101*2cb6d6f1SJim Jagielski    os.kill(child, 0)
102*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerHang):
103*2cb6d6f1SJim Jagielski        w.call("sleep", {"seconds": 60}, timeout=1)
104*2cb6d6f1SJim Jagielski    assert gone(child)
105*2cb6d6f1SJim Jagielski
106*2cb6d6f1SJim Jagielski
107*2cb6d6f1SJim Jagielskidef test_worker_that_never_says_hello(start_worker):
108*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerHang, match="hello"):
109*2cb6d6f1SJim Jagielski        start_worker("silent", hello_timeout=1)
110*2cb6d6f1SJim Jagielski
111*2cb6d6f1SJim Jagielski
112*2cb6d6f1SJim Jagielski# ---- CRASH ---------------------------------------------------------------------------------------------------------
113*2cb6d6f1SJim Jagielski
114*2cb6d6f1SJim Jagielskidef test_exit_139(start_worker):
115*2cb6d6f1SJim Jagielski    w = start_worker("crash139")
116*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerCrash) as e:
117*2cb6d6f1SJim Jagielski        w.call("echo")
118*2cb6d6f1SJim Jagielski    assert e.value.returncode == 139 and "128 + SIGSEGV" in str(e.value)
119*2cb6d6f1SJim Jagielski
120*2cb6d6f1SJim Jagielski
121*2cb6d6f1SJim Jagielski@pytest.mark.skipif(not POSIX, reason="signals")
122*2cb6d6f1SJim Jagielskidef test_killed_by_a_signal(start_worker):
123*2cb6d6f1SJim Jagielski    w = start_worker("sigkill")
124*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerCrash) as e:
125*2cb6d6f1SJim Jagielski        w.call("echo")
126*2cb6d6f1SJim Jagielski    assert e.value.returncode == -9 and "killed by SIGKILL" in str(e.value)
127*2cb6d6f1SJim Jagielski
128*2cb6d6f1SJim Jagielski
129*2cb6d6f1SJim Jagielskidef test_stderr_tail_is_kept(start_worker):
130*2cb6d6f1SJim Jagielski    w = start_worker("stderr-then-crash")
131*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerCrash) as e:
132*2cb6d6f1SJim Jagielski        w.call("echo")
133*2cb6d6f1SJim Jagielski    assert e.value.returncode == 7 and "the office bridge fell over" in str(e.value)
134*2cb6d6f1SJim Jagielski
135*2cb6d6f1SJim Jagielski
136*2cb6d6f1SJim Jagielskidef test_office_dead_reply_is_a_crash_and_the_worker_is_finished(start_worker):
137*2cb6d6f1SJim Jagielski    w = start_worker()
138*2cb6d6f1SJim Jagielski    with pytest.raises(client.OfficeDead, match="ServiceManager") as e:
139*2cb6d6f1SJim Jagielski        w.call("office-gone")
140*2cb6d6f1SJim Jagielski    assert isinstance(e.value, WorkerCrash)
141*2cb6d6f1SJim Jagielski    with pytest.raises(client.OfficeDead):
142*2cb6d6f1SJim Jagielski        w.call("echo")
143*2cb6d6f1SJim Jagielski    assert w._proc.poll() is not None
144*2cb6d6f1SJim Jagielski
145*2cb6d6f1SJim Jagielski
146*2cb6d6f1SJim Jagielskidef test_death_mid_call_in_the_good_worker(start_worker):
147*2cb6d6f1SJim Jagielski    w = start_worker()
148*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerCrash) as e:
149*2cb6d6f1SJim Jagielski        w.call("die139")
150*2cb6d6f1SJim Jagielski    assert e.value.returncode == 139
151*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerCrash):
152*2cb6d6f1SJim Jagielski        w.call("echo")
153*2cb6d6f1SJim Jagielski
154*2cb6d6f1SJim Jagielski
155*2cb6d6f1SJim Jagielskidef test_exit_before_hello(start_worker):
156*2cb6d6f1SJim Jagielski    with pytest.raises(WorkerCrash) as e:
157*2cb6d6f1SJim Jagielski        start_worker("exit-before-hello")
158*2cb6d6f1SJim Jagielski    assert e.value.returncode == 3
159*2cb6d6f1SJim Jagielski
160*2cb6d6f1SJim Jagielski
161*2cb6d6f1SJim Jagielski# ---- garbage -------------------------------------------------------------------------------------------------------
162*2cb6d6f1SJim Jagielski
163*2cb6d6f1SJim Jagielski@pytest.mark.parametrize("mode", ["garbage-reply", "not-an-object", "wrong-id", "binary-reply"])
164*2cb6d6f1SJim Jagielskidef test_bad_replies_are_protocol_errors_and_the_worker_is_killed(start_worker, mode):
165*2cb6d6f1SJim Jagielski    w = start_worker(mode)
166*2cb6d6f1SJim Jagielski    with pytest.raises(ProtocolError):
167*2cb6d6f1SJim Jagielski        w.call("echo", timeout=5)
168*2cb6d6f1SJim Jagielski    assert w._proc.poll() is not None
169*2cb6d6f1SJim Jagielski
170*2cb6d6f1SJim Jagielski
171*2cb6d6f1SJim Jagielski@pytest.mark.parametrize("mode", ["garbage-hello", "wrong-protocol"])
172*2cb6d6f1SJim Jagielskidef test_bad_hello(start_worker, mode):
173*2cb6d6f1SJim Jagielski    with pytest.raises(ProtocolError):
174*2cb6d6f1SJim Jagielski        start_worker(mode, hello_timeout=5)
175*2cb6d6f1SJim Jagielski
176*2cb6d6f1SJim Jagielski
177*2cb6d6f1SJim Jagielski@pytest.mark.parametrize("rc, text", [(0, "exit 0"), (1, "exit 1"), (7, "exit 7"), (139, "exit 139, 128 + SIGSEGV"), (-9, "killed by SIGKILL"),
178*2cb6d6f1SJim Jagielski                                      (-11, "killed by SIGSEGV"), (None, "no exit status")])
179*2cb6d6f1SJim Jagielskidef test_describe_exit(rc, text):
180*2cb6d6f1SJim Jagielski    assert client.describe_exit(rc) == text
181