瀏覽代碼

Terminal enhancements

Bastien Sevajol 8 年之前
父節點
當前提交
ff0d901a01
共有 7 個文件被更改,包括 274 次插入80 次删除
  1. 33 22
      sandbox/life_game/run.py
  2. 15 2
      sandbox/life_game/simulation.py
  3. 11 2
      synergine2/core.py
  4. 18 9
      synergine2/cycle.py
  5. 24 1
      synergine2/simulation.py
  6. 79 25
      synergine2/terminals.py
  7. 94 19
      tests/test_terminals.py

+ 33 - 22
sandbox/life_game/run.py 查看文件

1
-import sys
2
 import collections
1
 import collections
3
-from sandbox.life_game.simulation import CellBornBehaviour, CellDieBehaviour, Cell, Empty
2
+from sandbox.life_game.simulation import Cell
3
+from sandbox.life_game.simulation import Empty
4
+from sandbox.life_game.simulation import CellDieEvent
5
+from sandbox.life_game.simulation import CellBornEvent
4
 
6
 
5
 from sandbox.life_game.utils import get_subjects_from_str_representation
7
 from sandbox.life_game.utils import get_subjects_from_str_representation
6
 from synergine2.core import Core
8
 from synergine2.core import Core
7
 from synergine2.cycle import CycleManager
9
 from synergine2.cycle import CycleManager
8
 from synergine2.simulation import Simulation
10
 from synergine2.simulation import Simulation
11
+from synergine2.simulation import Event
9
 from synergine2.terminals import Terminal
12
 from synergine2.terminals import Terminal
10
 from synergine2.terminals import TerminalPackage
13
 from synergine2.terminals import TerminalPackage
11
 from synergine2.terminals import TerminalManager
14
 from synergine2.terminals import TerminalManager
13
 
16
 
14
 
17
 
15
 class SimplePrintTerminal(Terminal):
18
 class SimplePrintTerminal(Terminal):
19
+    subscribed_events = [
20
+        CellDieEvent,
21
+        CellBornEvent,
22
+    ]
23
+
16
     def __init__(self):
24
     def __init__(self):
17
         super().__init__()
25
         super().__init__()
18
-        self.subjects = None
26
+        self._cycle_born_count = 0
27
+        self._cycle_die_count = 0
28
+        self.register_event_handler(CellDieEvent, self.record_die)
29
+        self.register_event_handler(CellBornEvent, self.record_born)
19
 
30
 
20
-    def receive(self, value):
21
-        self.update_with_package(value)
22
-        self.print_str_representation()
31
+    def record_die(self, event: Event):
32
+        self._cycle_die_count += 1
23
 
33
 
24
-    def update_with_package(self, package: TerminalPackage):
25
-        self.subjects = package.subjects if package.subjects else self.subjects
26
-        for subject_id, actions in package.actions.items():
27
-            for action, value in actions.items():
28
-                if action == CellBornBehaviour:
29
-                    # Remove Empty subject
30
-                    self.subjects = [s for s in self.subjects[:] if s.id != subject_id]
31
-                    # Add born subject
32
-                    self.subjects.append(value)
33
-                if action == CellDieBehaviour:
34
-                    # Remove Cell subject
35
-                    self.subjects = [s for s in self.subjects[:] if s.id != subject_id]
36
-                    # Add Empty subject
37
-                    self.subjects.append(value)
34
+    def record_born(self, event: Event):
35
+        self._cycle_born_count += 1
36
+
37
+    def receive(self, package: TerminalPackage):
38
+        self._cycle_born_count = 0
39
+        self._cycle_die_count = 0
40
+        super().receive(package)
41
+        self.print_str_representation()
38
 
42
 
39
     def print_str_representation(self):
43
     def print_str_representation(self):
40
         items_positions = collections.defaultdict(list)
44
         items_positions = collections.defaultdict(list)
41
-        for subject in self.subjects:
45
+        for subject in self.subjects.values():
42
             if type(subject) == Cell:
46
             if type(subject) == Cell:
43
                 items_positions['1'].append(subject.position)
47
                 items_positions['1'].append(subject.position)
44
             if type(subject) == Empty:
48
             if type(subject) == Empty:
46
         print(get_str_representation_from_positions(
50
         print(get_str_representation_from_positions(
47
             items_positions,
51
             items_positions,
48
             separator=' ',
52
             separator=' ',
49
-            #force_items_as=(('0', ' '),),
53
+            force_items_as=(('0', ' '),),
50
         ))
54
         ))
55
+
56
+        # Display current cycle events
57
+        print('This cycle: {0} born, {1} dead'.format(
58
+            self._cycle_born_count,
59
+            self._cycle_die_count,
60
+        ))
61
+
51
         print()
62
         print()
52
 
63
 
53
 
64
 

+ 15 - 2
sandbox/life_game/simulation.py 查看文件

1
 from synergine2.simulation import Subject
1
 from synergine2.simulation import Subject
2
+from synergine2.simulation import Event
2
 from synergine2.simulation import Behaviour
3
 from synergine2.simulation import Behaviour
3
 from synergine2.xyz import ProximityMechanism
4
 from synergine2.xyz import ProximityMechanism
4
 from synergine2.xyz import XYZSubjectMixin
5
 from synergine2.xyz import XYZSubjectMixin
6
 COLLECTION_CELL = 'COLLECTION_CELL'  # Collections of Cell type
7
 COLLECTION_CELL = 'COLLECTION_CELL'  # Collections of Cell type
7
 
8
 
8
 
9
 
10
+class CellDieEvent(Event):
11
+    def __init__(self, subject_id, *args, **kwargs):
12
+        super().__init__(*args, **kwargs)
13
+        self.subject_id = subject_id
14
+
15
+
16
+class CellBornEvent(Event):
17
+    def __init__(self, subject_id, *args, **kwargs):
18
+        super().__init__(*args, **kwargs)
19
+        self.subject_id = subject_id
20
+
21
+
9
 class CellProximityMechanism(ProximityMechanism):
22
 class CellProximityMechanism(ProximityMechanism):
10
     distance = 1.41  # distance when on angle
23
     distance = 1.41  # distance when on angle
11
     feel_collections = [COLLECTION_CELL]
24
     feel_collections = [COLLECTION_CELL]
29
         )
42
         )
30
         self.simulation.subjects.remove(self.subject)
43
         self.simulation.subjects.remove(self.subject)
31
         self.simulation.subjects.append(new_empty)
44
         self.simulation.subjects.append(new_empty)
32
-        return new_empty
45
+        return [CellDieEvent(self.subject.id)]
33
 
46
 
34
 
47
 
35
 class CellBornBehaviour(Behaviour):
48
 class CellBornBehaviour(Behaviour):
48
         )
61
         )
49
         self.simulation.subjects.remove(self.subject)
62
         self.simulation.subjects.remove(self.subject)
50
         self.simulation.subjects.append(new_cell)
63
         self.simulation.subjects.append(new_cell)
51
-        return new_cell
64
+        return [CellBornEvent(new_cell.id)]
52
 
65
 
53
 
66
 
54
 class Cell(XYZSubjectMixin, Subject):
67
 class Cell(XYZSubjectMixin, Subject):

+ 11 - 2
synergine2/core.py 查看文件

25
 
25
 
26
             while True:
26
             while True:
27
                 # TODO: receive from terminals
27
                 # TODO: receive from terminals
28
-                actions = self.cycle_manager.next()
29
-                cycle_package = TerminalPackage(actions=actions)
28
+                events = self.cycle_manager.next()
29
+                cycle_package = TerminalPackage(
30
+                    events=events,
31
+                    add_subjects=self.simulation.subjects.adds,
32
+                    remove_subjects=self.simulation.subjects.removes,
33
+                )
30
                 self.terminal_manager.send(cycle_package)
34
                 self.terminal_manager.send(cycle_package)
35
+
36
+                # Reinitialize these list for next cycle
37
+                self.simulation.subjects.adds = []
38
+                self.simulation.subjects.removes = []
39
+
31
                 import time
40
                 import time
32
                 time.sleep(1)  # TODO: tick control
41
                 time.sleep(1)  # TODO: tick control
33
         except KeyboardInterrupt:
42
         except KeyboardInterrupt:

+ 18 - 9
synergine2/cycle.py 查看文件

1
 import multiprocessing
1
 import multiprocessing
2
-import collections
3
 
2
 
4
 from synergine2.processing import ProcessManager
3
 from synergine2.processing import ProcessManager
5
-from synergine2.simulation import Subject, Behaviour, Mechanism
4
+from synergine2.simulation import Subject
5
+from synergine2.simulation import Behaviour
6
+from synergine2.simulation import Mechanism
7
+from synergine2.simulation import Event
8
+from synergine2.simulation import Subjects
6
 from synergine2.utils import ChunkManager
9
 from synergine2.utils import ChunkManager
7
 
10
 
8
 
11
 
9
 class CycleManager(object):
12
 class CycleManager(object):
10
     def __init__(
13
     def __init__(
11
             self,
14
             self,
12
-            subjects: list,
15
+            subjects: Subjects,
13
             process_manager: ProcessManager=None,
16
             process_manager: ProcessManager=None,
14
     ):
17
     ):
15
         if process_manager is None:
18
         if process_manager is None:
22
         self.subjects = subjects
25
         self.subjects = subjects
23
         self.process_manager = process_manager
26
         self.process_manager = process_manager
24
         self.current_cycle = 0
27
         self.current_cycle = 0
28
+        self.first_cycle = True
29
+
30
+    def next(self) -> [Event]:
31
+        if self.first_cycle:
32
+            # To dispatch subjects add/removes, enable track on them
33
+            self.subjects.track_changes = True
34
+            self.first_cycle = False
25
 
35
 
26
-    def next(self):
27
         results = {}
36
         results = {}
28
         results_by_processes = self.process_manager.execute_jobs(self.subjects)
37
         results_by_processes = self.process_manager.execute_jobs(self.subjects)
29
-        actions = collections.defaultdict(dict)
38
+        events = []
30
         for process_results in results_by_processes:
39
         for process_results in results_by_processes:
31
             results.update(process_results)
40
             results.update(process_results)
32
         for subject in self.subjects[:]:  # Duplicate list to prevent conflicts with behaviours subjects manipulations
41
         for subject in self.subjects[:]:  # Duplicate list to prevent conflicts with behaviours subjects manipulations
33
             for behaviour_class in results[subject.id]:
42
             for behaviour_class in results[subject.id]:
34
                 # TODO: Ajouter une etape de selection des actions a faire (genre neuronnal)
43
                 # TODO: Ajouter une etape de selection des actions a faire (genre neuronnal)
35
-                # TODO: les behaviour_class ont le même uniqueid apres le process ?
36
-                action_result = subject.behaviours[behaviour_class].action(results[subject.id][behaviour_class])
37
-                actions[subject.id][behaviour_class] = action_result
44
+                # (genre se cacher et fuir son pas compatibles)
45
+                actions_events = subject.behaviours[behaviour_class].action(results[subject.id][behaviour_class])
46
+                events.extend(actions_events)
38
 
47
 
39
-        return actions
48
+        return events
40
 
49
 
41
     def computing(self, subjects):
50
     def computing(self, subjects):
42
         # compute mechanisms (prepare check to not compute slienced or not used mechanisms)
51
         # compute mechanisms (prepare check to not compute slienced or not used mechanisms)

+ 24 - 1
synergine2/simulation.py 查看文件

39
 
39
 
40
 
40
 
41
 class Subjects(list):
41
 class Subjects(list):
42
+    """
43
+    TODO: Manage other list methods
44
+    """
42
     def __init__(self, *args, **kwargs):
45
     def __init__(self, *args, **kwargs):
43
         self.simulation = kwargs.pop('simulation')
46
         self.simulation = kwargs.pop('simulation')
47
+        self.removes = []
48
+        self.adds = []
49
+        self.track_changes = False
44
         super().__init__(*args, **kwargs)
50
         super().__init__(*args, **kwargs)
45
 
51
 
46
     def remove(self, value: Subject):
52
     def remove(self, value: Subject):
53
+        # Remove from subjects list
47
         super().remove(value)
54
         super().remove(value)
55
+        # Remove from collections
48
         for collection_name in value.collections:
56
         for collection_name in value.collections:
49
             self.simulation.collections[collection_name].remove(value)
57
             self.simulation.collections[collection_name].remove(value)
58
+        # Add to removed listing
59
+        if self.track_changes:
60
+            self.removes.append(value)
61
+
62
+    def append(self, p_object):
63
+        # Add to subjects list
64
+        super().append(p_object)
65
+        # Add to adds list
66
+        if self.track_changes:
67
+            self.adds.append(p_object)
50
 
68
 
51
 
69
 
52
 class Mechanism(object):
70
 class Mechanism(object):
62
         raise NotImplementedError()
80
         raise NotImplementedError()
63
 
81
 
64
 
82
 
83
+class Event(object):
84
+    def __init__(self, *args, **kwargs):
85
+        pass
86
+
87
+
65
 class Behaviour(object):
88
 class Behaviour(object):
66
     def __init__(
89
     def __init__(
67
             self,
90
             self,
82
         """
105
         """
83
         raise NotImplementedError()
106
         raise NotImplementedError()
84
 
107
 
85
-    def action(self, data) -> object:
108
+    def action(self, data) -> [Event]:
86
         """
109
         """
87
         Method called in main process
110
         Method called in main process
88
         Return value will be give to terminals
111
         Return value will be give to terminals

+ 79 - 25
synergine2/terminals.py 查看文件

1
+import collections
2
+from copy import copy
1
 from multiprocessing import Queue
3
 from multiprocessing import Queue
2
 
4
 
3
 from multiprocessing import Process
5
 from multiprocessing import Process
4
 from queue import Empty
6
 from queue import Empty
5
 
7
 
6
 import time
8
 import time
7
-from synergine2.simulation import Simulation, Subject
9
+from synergine2.simulation import Subject
10
+from synergine2.simulation import Event
8
 
11
 
9
 STOP_SIGNAL = '__STOP_SIGNAL__'
12
 STOP_SIGNAL = '__STOP_SIGNAL__'
10
 
13
 
13
     def __init__(
16
     def __init__(
14
             self,
17
             self,
15
             subjects: [Subject]=None,
18
             subjects: [Subject]=None,
16
-            actions: ['TODO']=None,
19
+            add_subjects: [Subject]=None,
20
+            remove_subjects: [Subject]=None,
21
+            events: [Event]=None,
22
+            *args,
23
+            **kwargs
17
     ):
24
     ):
18
         self.subjects = subjects
25
         self.subjects = subjects
19
-        self.actions = actions or {}
26
+        self.add_subjects = add_subjects or []
27
+        self.remove_subjects = remove_subjects or []
28
+        self.events = events or []
20
 
29
 
21
 
30
 
22
 class Terminal(object):
31
 class Terminal(object):
23
-    """Default behaviour is to do nothing.
24
-    DEFAULT_SLEEP is sleep time between each queue read"""
25
-    DEFAULT_SLEEP = 1
32
+    # Default behaviour is to do nothing.
33
+    # DEFAULT_SLEEP is sleep time between each queue read
34
+    default_sleep = 1
35
+    # List of subscribed Event classes. Terminal will not receive events
36
+    # who are not instance of listed classes
37
+    subscribed_events = [Event]
26
 
38
 
27
     def __init__(self):
39
     def __init__(self):
28
         self._input_queue = None
40
         self._input_queue = None
29
         self._output_queue = None
41
         self._output_queue = None
30
         self._stop_required = False
42
         self._stop_required = False
43
+        self.subjects = {}
44
+        self.cycle_events = []
45
+        self.event_handlers = collections.defaultdict(list)
46
+
47
+    def accept_event(self, event: Event) -> bool:
48
+        for event_class in self.subscribed_events:
49
+            if isinstance(event, event_class):
50
+                return True
51
+        return False
31
 
52
 
32
     def start(self, input_queue: Queue, output_queue: Queue) -> None:
53
     def start(self, input_queue: Queue, output_queue: Queue) -> None:
33
         self._input_queue = input_queue
54
         self._input_queue = input_queue
40
         """
61
         """
41
         try:
62
         try:
42
             while self.read():
63
             while self.read():
43
-                time.sleep(self.DEFAULT_SLEEP)
64
+                time.sleep(self.default_sleep)
44
         except KeyboardInterrupt:
65
         except KeyboardInterrupt:
45
             pass
66
             pass
46
 
67
 
56
             except Empty:
77
             except Empty:
57
                 return True  # Finished to read Queue
78
                 return True  # Finished to read Queue
58
 
79
 
59
-    def receive(self, value):
60
-        raise NotImplementedError()
80
+    def receive(self, package: TerminalPackage):
81
+        self.update_with_package(package)
82
+
83
+    def send(self, package: TerminalPackage):
84
+        self._output_queue.put(package)
85
+
86
+    def register_event_handler(self, event_class, func):
87
+        self.event_handlers[event_class].append(func)
88
+
89
+    def update_with_package(self, package: TerminalPackage):
90
+        if package.subjects:
91
+            self.subjects = {s.id: s for s in package.subjects}
92
+
93
+        for new_subject in package.add_subjects:
94
+            self.subjects[new_subject.id] = new_subject
61
 
95
 
62
-    def send(self, value):
63
-        self._output_queue.put(value)
96
+        for deleted_subject in package.remove_subjects:
97
+            del self.subjects[deleted_subject.id]
98
+
99
+        self.cycle_events = package.events
100
+        self.execute_event_handlers(package.events)
101
+
102
+    def execute_event_handlers(self, events: [Event]):
103
+        for event in events:
104
+            for event_class, handlers in self.event_handlers.items():
105
+                if isinstance(event, event_class):
106
+                    for handler in handlers:
107
+                        handler(event)
64
 
108
 
65
 
109
 
66
 class TerminalManager(object):
110
 class TerminalManager(object):
67
     def __init__(self, terminals: [Terminal]):
111
     def __init__(self, terminals: [Terminal]):
68
-        self._terminals = terminals
69
-        self._outputs_queues = []
70
-        self._inputs_queues = []
112
+        self.terminals = terminals
113
+        self.outputs_queues = {}
114
+        self.inputs_queues = {}
71
 
115
 
72
     def start(self) -> None:
116
     def start(self) -> None:
73
-        for terminal in self._terminals:
117
+        for terminal in self.terminals:
74
             output_queue = Queue()
118
             output_queue = Queue()
75
-            self._outputs_queues.append(output_queue)
119
+            self.outputs_queues[terminal] = output_queue
76
 
120
 
77
             input_queue = Queue()
121
             input_queue = Queue()
78
-            self._inputs_queues.append(input_queue)
122
+            self.inputs_queues[terminal] = input_queue
79
 
123
 
80
             process = Process(target=terminal.start, kwargs=dict(
124
             process = Process(target=terminal.start, kwargs=dict(
81
                 input_queue=output_queue,
125
                 input_queue=output_queue,
84
             process.start()
128
             process.start()
85
 
129
 
86
     def stop(self):
130
     def stop(self):
87
-        for output_queue in self._outputs_queues:
131
+        for output_queue in self.outputs_queues.values():
88
             output_queue.put(STOP_SIGNAL)
132
             output_queue.put(STOP_SIGNAL)
89
 
133
 
90
-    def send(self, value):
91
-        for output_queue in self._outputs_queues:
92
-            output_queue.put(value)
134
+    def send(self, package: TerminalPackage):
135
+        for terminal, output_queue in self.outputs_queues.items():
136
+            # Terminal maybe don't want all events, so reduce list of event
137
+            # Thirst make a copy to personalize this package
138
+            terminal_adapted_package = copy(package)
139
+            # Duplicate events list to personalize it
140
+            terminal_adapted_package.events = terminal_adapted_package.events[:]
141
+
142
+            for package_event in terminal_adapted_package.events[:]:
143
+                if not terminal.accept_event(package_event):
144
+                    terminal_adapted_package.events.remove(package_event)
145
+
146
+            output_queue.put(terminal_adapted_package)
93
 
147
 
94
     def receive(self) -> []:
148
     def receive(self) -> []:
95
-        values = []
96
-        for input_queue in self._inputs_queues:
149
+        packages = []
150
+        for input_queue in self.inputs_queues.values():
97
             try:
151
             try:
98
                 while True:
152
                 while True:
99
-                    values.append(input_queue.get(block=False, timeout=None))
153
+                    packages.append(input_queue.get(block=False, timeout=None))
100
             except Empty:
154
             except Empty:
101
                 pass  # Queue is empty
155
                 pass  # Queue is empty
102
 
156
 
103
-        return values
157
+        return packages

+ 94 - 19
tests/test_terminals.py 查看文件

1
 import time
1
 import time
2
 
2
 
3
+from synergine2.simulation import Event
3
 from synergine2.terminals import Terminal
4
 from synergine2.terminals import Terminal
5
+from synergine2.terminals import TerminalPackage
4
 from synergine2.terminals import TerminalManager
6
 from synergine2.terminals import TerminalManager
5
 from tests import BaseTest
7
 from tests import BaseTest
6
 
8
 
7
 
9
 
10
+class ValueTerminalPackage(TerminalPackage):
11
+    def __init__(self, value, *args, **kwargs):
12
+        super().__init__(*args, **kwargs)
13
+        self.value = value
14
+
15
+
8
 class MultiplyTerminal(Terminal):
16
 class MultiplyTerminal(Terminal):
9
-    def receive(self, value):
10
-        self.send(value * 2)
11
-        self.send(value * 4)
17
+    def receive(self, package: ValueTerminalPackage):
18
+        self.send(ValueTerminalPackage(value=package.value * 2))
19
+        self.send(ValueTerminalPackage(value=package.value * 4))
12
 
20
 
13
 
21
 
14
 class DivideTerminal(Terminal):
22
 class DivideTerminal(Terminal):
15
-    def receive(self, value):
16
-        self.send(value / 2)
17
-        self.send(value / 4)
23
+    def receive(self, package: ValueTerminalPackage):
24
+        self.send(ValueTerminalPackage(value=package.value / 2))
25
+        self.send(ValueTerminalPackage(value=package.value / 4))
26
+
27
+
28
+class AnEvent(Event):
29
+    pass
30
+
31
+
32
+class AnOtherEvent(Event):
33
+    pass
34
+
35
+
36
+class SendBackTerminal(Terminal):
37
+    def receive(self, package: ValueTerminalPackage):
38
+        self.send(package)
18
 
39
 
19
 
40
 
20
 class TestTerminals(BaseTest):
41
 class TestTerminals(BaseTest):
25
             ]
46
             ]
26
         )
47
         )
27
         terminals_manager.start()
48
         terminals_manager.start()
28
-        terminals_manager.send(42)
49
+        terminals_manager.send(ValueTerminalPackage(value=42))
29
 
50
 
30
         # We wait max 2s (see time.sleep) to consider
51
         # We wait max 2s (see time.sleep) to consider
31
         # process have finished. If not, it will fail
52
         # process have finished. If not, it will fail
32
-        values = []
53
+        packages = []
33
         for i in range(200):
54
         for i in range(200):
34
-            values.extend(terminals_manager.receive())
35
-            if len(values) == 2:
55
+            packages.extend(terminals_manager.receive())
56
+            if len(packages) == 2:
36
                 break
57
                 break
37
             time.sleep(0.01)
58
             time.sleep(0.01)
38
 
59
 
39
-        assert 2 == len(values)
40
-        values = [v for v in values]
60
+        assert 2 == len(packages)
61
+        values = [p.value for p in packages]
41
         assert 84 in values
62
         assert 84 in values
42
         assert 168 in values
63
         assert 168 in values
43
 
64
 
51
             ]
72
             ]
52
         )
73
         )
53
         terminals_manager.start()
74
         terminals_manager.start()
54
-        terminals_manager.send(42)
75
+        terminals_manager.send(ValueTerminalPackage(value=42))
55
 
76
 
56
         # We wait max 2s (see time.sleep) to consider
77
         # We wait max 2s (see time.sleep) to consider
57
         # process have finished. If not, it will fail
78
         # process have finished. If not, it will fail
58
-        values = []
79
+        packages = []
59
         for i in range(200):
80
         for i in range(200):
60
-            values.extend(terminals_manager.receive())
61
-            if len(values) == 4:
81
+            packages.extend(terminals_manager.receive())
82
+            if len(packages) == 4:
62
                 break
83
                 break
63
             time.sleep(0.01)
84
             time.sleep(0.01)
64
 
85
 
65
-        assert 4 == len(values)
66
-        values = [v for v in values]
86
+        assert 4 == len(packages)
87
+        values = [p.value for p in packages]
67
         assert 84 in values
88
         assert 84 in values
68
         assert 168 in values
89
         assert 168 in values
69
         assert 21 in values
90
         assert 21 in values
70
         assert 10.5 in values
91
         assert 10.5 in values
71
 
92
 
72
-        terminals_manager.stop()  # pytest must execute this if have fail
93
+        terminals_manager.stop()  # TODO pytest must execute this if have fail
94
+
95
+    def test_event_listen_everything(self):
96
+        class ListenEverythingTerminal(SendBackTerminal):
97
+            pass
98
+
99
+        terminals_manager = TerminalManager(
100
+            terminals=[ListenEverythingTerminal()]
101
+        )
102
+        terminals_manager.start()
103
+        terminals_manager.send(ValueTerminalPackage(value=42))
104
+        an_event = AnEvent(84)
105
+        terminals_manager.send(TerminalPackage(events=[an_event]))
106
+
107
+        # We wait max 2s (see time.sleep) to consider
108
+        # process have finished. If not, it will fail
109
+        packages = []
110
+        for i in range(200):
111
+            packages.extend(terminals_manager.receive())
112
+            if len(packages) == 2:
113
+                break
114
+            time.sleep(0.01)
115
+
116
+        assert 2 == len(packages)
117
+        assert 42 == packages[0].value
118
+        assert AnEvent == type(packages[1].events[0])
119
+
120
+        terminals_manager.stop()  # TODO pytest must execute this if have fail
121
+
122
+    def test_event_listen_specified(self):
123
+        class ListenAnEventTerminal(SendBackTerminal):
124
+            subscribed_events = [AnOtherEvent]
125
+
126
+        terminals_manager = TerminalManager(
127
+            terminals=[ListenAnEventTerminal()]
128
+        )
129
+        terminals_manager.start()
130
+        terminals_manager.send(ValueTerminalPackage(value=42))
131
+        an_event = AnEvent(84)
132
+        an_other_event = AnOtherEvent(168)
133
+        terminals_manager.send(TerminalPackage(events=[an_event, an_other_event]))
134
+
135
+        # We wait max 2s (see time.sleep) to consider
136
+        # process have finished. If not, it will fail
137
+        packages = []
138
+        for i in range(200):
139
+            packages.extend(terminals_manager.receive())
140
+            if len(packages) == 1:
141
+                break
142
+            time.sleep(0.01)
143
+
144
+        assert 2 == len(packages)
145
+        assert AnOtherEvent == type(packages[1].events[0])
146
+
147
+        terminals_manager.stop()  # TODO pytest must execute this if have fail