Repository: opensistemas-hub/osbrain
Pull request #174 (2017-07-21 09:55:36)

Wait agent condition
See #170 first.

Implement a new helper function `wait_agent_condition` which should be useful for tests.

Improve coverage a bit.


Patch:
diff --git a/osbrain/agent.py b/osbrain/agent.py
index aca2288..7fa321f 100644
--- a/osbrain/agent.py
+++ b/osbrain/agent.py
@@ -1004,11 +1004,18 @@ class Agent():
             self.log_info('SET self.%s() = %s' % (name, function))
         return name
 
-    def execute_function(self, function, *args, **kwargs):
+    def execute_as_function(self, function, *args, **kwargs):
         """
         Execute a function passed as parameter.
         """
-        return function(args, kwargs)
+        return function(*args, **kwargs)
+
+    def execute_as_method(self, function, *args, **kwargs):
+        """
+        Execute a function as a method, without adding it to the set of
+        agent methods.
+        """
+        return function(self, *args, **kwargs)
 
     def loop(self):
         """
diff --git a/osbrain/helper.py b/osbrain/helper.py
index 256f740..7827f3f 100644
--- a/osbrain/helper.py
+++ b/osbrain/helper.py
@@ -193,3 +193,33 @@ def wait_agent_attr(agent, name='received', length=None, data=None, value=None,
             break
         time.sleep(0.01)
     return False
+
+
+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.
+    """
+    t0 = time.time()
+    while True:
+        if agent.execute_as_method(condition, *args, **kwargs):
+            return True
+        if time.time() - t0 > timeout:
+            return False
+        time.sleep(0.01)


Test patch:
diff --git a/osbrain/tests/test_agent.py b/osbrain/tests/test_agent.py
index 602766d..9811338 100644
--- a/osbrain/tests/test_agent.py
+++ b/osbrain/tests/test_agent.py
@@ -1,6 +1,7 @@
 """
 Test file for agents.
 """
+import os
 import multiprocessing
 import random
 import time
@@ -598,3 +599,44 @@ def test_agent_spawn_process(nsproxy):
 
     agent = run_agent('a0', base=Spawner)
     assert agent.spawn_process()
+
+
+def test_agent_execute_as_function(nsproxy):
+    """
+    Test `execute_as_function` method, which should execute a given function
+    in the remote agent.
+    """
+    class EnvironmentAgent(Agent):
+        def configure(self, value):
+            os.environ['__OSBRAIN_TEST'] = value
+
+    def name(prefix, suffix='suffix'):
+        return prefix + os.environ.get('__OSBRAIN_TEST', '') + suffix
+
+    agent0 = run_agent('a0', base=EnvironmentAgent)
+    agent1 = run_agent('a1', base=EnvironmentAgent)
+    agent0.configure('0')
+    agent1.configure('1')
+
+    assert agent0.execute_as_function(name, 'p') == 'p0suffix'
+    assert agent0.execute_as_function(name, 'p', suffix='s') == 'p0s'
+    assert agent1.execute_as_function(name, 'p') == 'p1suffix'
+    assert agent1.execute_as_function(name, 'p', suffix='s') == 'p1s'
+
+
+def test_agent_execute_as_method(nsproxy):
+    """
+    Test `execute_as_method` method, which should execute a given function
+    in the remote agent as a method (i.e.: passing the agent as first
+    parameter of the function).
+    """
+    def name(agent, prefix, suffix='suffix'):
+        return prefix + agent.name + suffix
+
+    agent0 = run_agent('a0')
+    agent1 = run_agent('a1')
+
+    assert agent0.execute_as_method(name, 'p') == 'pa0suffix'
+    assert agent0.execute_as_method(name, 'p', suffix='s') == 'pa0s'
+    assert agent1.execute_as_method(name, 'p') == 'pa1suffix'
+    assert agent1.execute_as_method(name, 'p', suffix='s') == 'pa1s'
diff --git a/osbrain/tests/test_bugs.py b/osbrain/tests/test_bugs.py
index 6bb0ea0..74614a7 100644
--- a/osbrain/tests/test_bugs.py
+++ b/osbrain/tests/test_bugs.py
@@ -2,9 +2,9 @@
 Test file for bugs found in osbrain.
 """
 import sys
-import time
 
 from osbrain import run_agent
+from osbrain.helper import wait_agent_condition
 
 from common import nsproxy  # pragma: no flakes
 
@@ -21,10 +21,7 @@ def test_timer_recursion(nsproxy):
     agent = run_agent('a0')
     agent.set_attr(count=0)
     agent.each(0.0, inc)
-    while True:
-        count = agent.get_attr('count')
-        if count > sys.getrecursionlimit():
-            return
-        time.sleep(0.1)
-        if count == agent.get_attr('count'):
-            raise RuntimeError('Probable recursion level exceeded!')
+
+    limit = sys.getrecursionlimit()
+    assert wait_agent_condition(agent, lambda agent: agent.count > limit,
+                                timeout=10)
diff --git a/osbrain/tests/test_helper.py b/osbrain/tests/test_helper.py
index 6565672..53be718 100644
--- a/osbrain/tests/test_helper.py
+++ b/osbrain/tests/test_helper.py
@@ -1,6 +1,8 @@
 """
 Test file for functionality implemented in `osbrain/tests/common.py`.
 """
+import time
+
 import pytest
 
 from osbrain import run_agent
@@ -9,6 +11,7 @@ from osbrain.helper import logger_received
 from osbrain.helper import agent_dies
 from osbrain.helper import attribute_match_all
 from osbrain.helper import wait_agent_attr
+from osbrain.helper import wait_agent_condition
 
 from common import nsproxy  # pragma: no flakes
 from common import agent_logger  # pragma: no flakes
@@ -139,3 +142,28 @@ def test_wait_agent_attr(nsproxy):
     a0.after(1, 'set_received_method', 42)
     assert not wait_agent_attr(a0, value=42, timeout=0.)
     assert wait_agent_attr(a0, value=42, timeout=2.)
+
+
+def test_wait_agent_condition(nsproxy):
+    """
+    Test `wait_agent_condition` function.
+    """
+    class Counter(Agent):
+        def on_init(self):
+            self.count = 0
+            self.log = []
+
+        def increment(self):
+            self.log.append(self.count)
+            self.count += 1
+            time.sleep(0.1)
+
+    def log_length(agent, size):
+        return len(agent.log) > size
+
+    a0 = run_agent('a0', base=Counter)
+    a0.each(0., 'increment')
+
+    assert not wait_agent_condition(a0, log_length, size=4, timeout=0.2)
+    assert wait_agent_condition(a0, log_length, size=10, timeout=1.)
+    assert wait_agent_condition(a0, lambda agent: agent.count > 8, timeout=0.)