Repository: opensistemas-hub/osbrain
Pull request #251 (2018-02-15 10:12:59)

Safer one-way kill in close IPC socket test
Hopefully prevents this from happening again:

https://travis-ci.org/opensistemas-hub/osbrain/jobs/341672571

codecov[bot]: # [Codecov](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=h1) Report
> Merging [#251](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=desc) into [master](https://codecov.io/gh/opensistemas-hub/osbrain/commit/6e3e96a1df83e51b2da51174b31f8a3c93c3a394?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251/graphs/tree.svg?width=650&height=150&src=pr&token=LfvMoNOrOc)](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #251      +/-   ##
==========================================
+ Coverage   99.08%   99.08%   +<.01%     
==========================================
  Files          26       26              
  Lines        3481     3484       +3     
  Branches      250      251       +1     
==========================================
+ Hits         3449     3452       +3     
  Misses         20       20              
  Partials       12       12
```


| [Impacted Files](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [osbrain/helper.py](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251/diff?src=pr&el=tree#diff-b3NicmFpbi9oZWxwZXIucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [osbrain/tests/test\_agent\_ipc\_sockets.py](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251/diff?src=pr&el=tree#diff-b3NicmFpbi90ZXN0cy90ZXN0X2FnZW50X2lwY19zb2NrZXRzLnB5) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=footer). Last update [6e3e96a...3a61e24](https://codecov.io/gh/opensistemas-hub/osbrain/pull/251?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Peque: @ocaballeror Yeah, I bet there are a couple of tests that could benefit from this. I'll implement the "negated wait_condition()" too in that case and ask you for review again. We can then open a ticket to have a look at where we could use this function.
Peque: @ocaballeror Added a `negate` parameter, which should be useful if we integrate the use of this function in other tests in the near future.

Patch:
diff --git a/osbrain/helper.py b/osbrain/helper.py
index 0fa7062..fd052ae 100644
--- a/osbrain/helper.py
+++ b/osbrain/helper.py
@@ -195,21 +195,21 @@ def wait_agent_attr(agent, name='received', length=None, data=None, value=None,
     return False
 
 
-def wait_agent_condition(agent, condition, *args, timeout=3, **kwargs):
+def wait_condition(condition, *args, negate=False, timeout=3, **kwargs):
     """
-    Wait for an agent's condition to be true.
+    Wait for a condition to be true.
 
-    The condition is passed as a function which must take, at least, the
-    actual agent as a parameter, and must evaluate to a boolean result.
+    The condition is passed as a callable and must evaluate to a boolean
+    result.
 
     Parameters
     ----------
-    agent : Proxy
-        A proxy to the agent.
     condition : Callable
         A function that evaluates the desired condition.
     timeout : float, default is 3
         After this number of seconds the function will return `False`.
+    negate : bool, default is False
+        Negate the condition (wait for the condition to be false instead).
 
     Returns
     -------
@@ -218,13 +218,38 @@ def wait_agent_condition(agent, condition, *args, timeout=3, **kwargs):
     """
     t0 = time.time()
     while True:
-        if agent.execute_as_method(condition, *args, **kwargs):
+        if condition(*args, **kwargs) != negate:
             return True
         if time.time() - t0 > timeout:
             return False
         time.sleep(0.01)
 
 
+def wait_agent_condition(agent, condition, *args, timeout=3, **kwargs):
+    """
+    Wait for an agent's condition to be true.
+
+    The condition is passed as a function which must take, at least, the
+    actual agent as a parameter, and must evaluate to a boolean result.
+
+    Parameters
+    ----------
+    agent : Proxy
+        A proxy to the agent.
+    condition : Callable
+        A function that evaluates the desired condition.
+    timeout : float, default is 3
+        After this number of seconds the function will return `False`.
+
+    Returns
+    -------
+    bool
+        Whether the specified condition was True.
+    """
+    return wait_condition(agent.execute_as_method, condition, *args,
+                          timeout=timeout, **kwargs)
+
+
 def last_received_endswith(agent, tail):
     """
     Check if the agent's last received message ends with `tail`.


Test patch:
diff --git a/osbrain/tests/test_agent_ipc_sockets.py b/osbrain/tests/test_agent_ipc_sockets.py
index 68f174b..8e353c2 100644
--- a/osbrain/tests/test_agent_ipc_sockets.py
+++ b/osbrain/tests/test_agent_ipc_sockets.py
@@ -6,6 +6,7 @@ import pytest
 from osbrain import run_agent
 from osbrain import run_nameserver
 from osbrain.helper import agent_dies
+from osbrain.helper import wait_condition
 
 from common import nsproxy  # pragma: no flakes
 
@@ -30,10 +31,10 @@ def test_agent_close_ipc_socket_agent_kill(nsproxy):
     """
     agent = run_agent('name')
     address = agent.bind('PUSH')
-    agent.kill()
+    agent.oneway.kill()
 
     assert agent_dies('name', nsproxy, timeout=3)
-    assert not address.address.exists()
+    assert wait_condition(lambda x: not x.exists(), address.address)
 
 
 def test_agent_close_ipc_socket_agent_blocked_nameserver_shutdown():
diff --git a/osbrain/tests/test_helper.py b/osbrain/tests/test_helper.py
index 339535e..2d704db 100644
--- a/osbrain/tests/test_helper.py
+++ b/osbrain/tests/test_helper.py
@@ -1,6 +1,7 @@
 """
 Test file for functionality implemented in `osbrain/tests/common.py`.
 """
+from threading import Timer
 import time
 
 import pytest
@@ -12,12 +13,27 @@ from osbrain.helper import agent_dies
 from osbrain.helper import attribute_match_all
 from osbrain.helper import last_received_endswith
 from osbrain.helper import wait_agent_attr
+from osbrain.helper import wait_condition
 from osbrain.helper import wait_agent_condition
 
 from common import nsproxy  # pragma: no flakes
 from common import agent_logger  # pragma: no flakes
 
 
+class SugarAddict():
+    def __init__(self):
+        self.glucose = 0
+
+    def feed_candy(self):
+        self.glucose += 1
+
+    def happy(self):
+        return bool(self.glucose)
+
+    def sad(self):
+        return not bool(self.glucose)
+
+
 def test_agent_dies(nsproxy):
     """
     The function `agent_dies` should return `False` if the agent does not die
@@ -145,6 +161,32 @@ def test_wait_agent_attr(nsproxy):
     assert wait_agent_attr(a0, value=42, timeout=2.)
 
 
+@pytest.mark.parametrize('delay,timeout,result', [
+    (1, 0.5, False),
+    (0.5, 1, True),
+])
+def test_wait_condition(delay, timeout, result):
+    """
+    Test the `wait_agent_condition` function.
+    """
+    kid = SugarAddict()
+    Timer(delay, kid.feed_candy).start()
+    assert wait_condition(kid.happy, timeout=timeout) == result
+
+
+@pytest.mark.parametrize('delay,timeout,result', [
+    (1, 0.5, False),
+    (0.5, 1, True),
+])
+def test_wait_condition_negate(delay, timeout, result):
+    """
+    Test the negated `wait_agent_condition` function.
+    """
+    kid = SugarAddict()
+    Timer(delay, kid.feed_candy).start()
+    assert wait_condition(kid.sad, negate=True, timeout=timeout) == result
+
+
 def test_wait_agent_condition(nsproxy):
     """
     Test `wait_agent_condition` function.