Passed
Pull Request — master (#114)
by Aldo
03:58
created

TestTraceManager.create_basic_switches()   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 16

Duplication

Lines 21
Ratio 100 %

Code Coverage

Tests 16
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 16
nop 2
dl 21
loc 21
ccs 16
cts 16
cp 1
crap 1
rs 9.6
c 0
b 0
f 0
1
"""
2
    Test tracing.trace_manager
3
"""
4
5 1
from unittest.mock import patch, MagicMock, AsyncMock
6 1
import asyncio
7 1
import pytest
8
9 1
from napps.amlight.sdntrace import settings
10 1
from napps.amlight.sdntrace.tracing.trace_manager import TraceManager
11 1
from napps.amlight.sdntrace.tracing.trace_entries import TraceEntries
12 1
from napps.amlight.sdntrace.shared.switches import Switches
13
14 1
from kytos.lib.helpers import (
15
    get_interface_mock,
16
    get_link_mock,
17
    get_switch_mock,
18
    get_controller_mock,
19
)
20
21
22
# pylint: disable=protected-access
23 1
class TestTraceManager:
24
    """Unit tests for tracing.trace_manager.TraceManager"""
25
26 1
    @pytest.fixture(autouse=True)
27 1
    def commomn_patches(self, request):
28
        """This function handles setup and cleanup for patches"""
29
        # This fixture sets up the common patches,
30
        # and a finalizer is added using addfinalizer to stop
31
        # the common patches after each test. This ensures that the cleanup
32
        # is performed after each test, and additional patch decorators
33
        # can be used within individual test functions.
34
35 1
        patcher = patch("kytos.core.helpers.run_on_thread", lambda x: x)
36 1
        mock_patch = patcher.start()
37
38 1
        _ = request.function.__name__
39
40 1
        def cleanup():
41 1
            patcher.stop()
42
43 1
        request.addfinalizer(cleanup)
44 1
        return mock_patch
45
46 1
    def setup_method(self):
47
        """Set up before each test method"""
48 1
        self.create_basic_switches(get_controller_mock())
49 1
        TraceManager.run_traces = MagicMock()
50 1
        self.trace_manager = TraceManager(controller=get_controller_mock())
51
52 1
    def teardown_method(self) -> None:
53
        """Clean up after each test method"""
54 1
        self.trace_manager.stop_traces()
55
56 1
    @pytest.fixture
57 1
    async def run_traces(self):
58
        """Run task to discover traces."""
59 1
        _async_loop = asyncio.get_running_loop()
60 1
        self.trace_manager._async_loop = _async_loop
61 1
        task = _async_loop.create_task(self.trace_manager._run_traces(0.5))
62 1
        self.trace_manager.tasks.append(task)
63 1
        yield
64
65
        # Clean tasks
66 1
        for _task in self.trace_manager.tasks:
67 1
            if not _task.done():
68 1
                _task.cancel()
69
70 1 View Code Duplication
    @classmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
71 1
    def create_basic_switches(cls, controller):
72
        """Create basic mock switches for Kytos controller."""
73 1
        dpid_a = "00:00:00:00:00:00:00:01"
74 1
        dpid_b = "00:00:00:00:00:00:00:02"
75
76 1
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
77 1
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
78 1
        mock_interface_a = get_interface_mock("s1-eth1", 1, mock_switch_a)
79 1
        mock_interface_b = get_interface_mock("s2-eth1", 1, mock_switch_b)
80
81 1
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
82 1
        mock_link.id = "cf0f4071be4"
83 1
        mock_switch_a.id = dpid_a
84 1
        mock_switch_a.as_dict.return_value = {"metadata": {}}
85 1
        mock_switch_b.id = dpid_b
86 1
        mock_switch_b.as_dict.return_value = {"metadata": {}}
87
88 1
        controller.switches = {dpid_a: mock_switch_a, dpid_b: mock_switch_b}
89
90 1
        Switches(MagicMock())._switches = controller.switches
91
92 1
    def test_is_entry_invalid(self):
93
        """Test if the entry request does not have a valid switch."""
94 1
        eth = {"dl_vlan": 100}
95 1
        dpid = {"dpid": "a", "in_port": 1}
96 1
        switch = {"switch": dpid, "eth": eth}
97 1
        entries = {"trace": switch}
98 1
        entry = self.trace_manager.is_entry_valid(entries)
99
100 1
        assert entry == "Unknown Switch"
101
102 1
    def test_is_entry_empty_dpid(self):
103
        """Test if the entry request does not have a valid switch."""
104 1
        eth = {"dl_vlan": 100}
105 1
        dpid = {"dpid": "", "in_port": 1}
106 1
        switch = {"switch": dpid, "eth": eth}
107 1
        entries = {"trace": switch}
108 1
        entry = self.trace_manager.is_entry_valid(entries)
109
110 1
        assert entry == "Error: dpid allows [a-f], int, and :. Lengths: 1-16 and 23"
111
112 1
    def test_is_entry_missing_dpid(self):
113
        """Test if the entry request with missing dpid."""
114 1
        eth = {"dl_vlan": 100}
115 1
        dpid = {}
116 1
        switch = {"switch": dpid, "eth": eth}
117 1
        entries = {"trace": switch}
118 1
        entry = self.trace_manager.is_entry_valid(entries)
119
120 1
        assert entry == "Error: dpid not provided"
121
122 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
123 1
    def test_is_entry_invalid_not_colored(self, mock_colors):
124
        """Test if the entry request does not have a valid color."""
125 1
        mock_colors.return_value = {}
126 1
        eth = {"dl_vlan": 100}
127 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
128 1
        switch = {"switch": dpid, "eth": eth}
129 1
        entries = {"trace": switch}
130 1
        entry = self.trace_manager.is_entry_valid(entries)
131 1
        assert entry == "Switch not Colored"
132
133 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
134 1
    def test_is_entry_valid(self, mock_colors):
135
        """Test if the entry request is valid."""
136 1
        mock_colors.return_value = {
137
            "color_field": "dl_src",
138
            "color_value": "ee:ee:ee:ee:ee:01",
139
        }
140
141 1
        eth = {"dl_vlan": 100}
142 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
143 1
        switch = {"switch": dpid, "eth": eth}
144 1
        entries = {"trace": switch}
145 1
        entry = self.trace_manager.is_entry_valid(entries)
146 1
        assert entry.dpid == "00:00:00:00:00:00:00:01"
147
148 1 View Code Duplication
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
149 1
    def test_new_trace(self, mock_colors):
150
        """Test trace manager new trace creation."""
151 1
        mock_colors.return_value = {
152
            "color_field": "dl_src",
153
            "color_value": "ee:ee:ee:ee:ee:01",
154
        }
155
156 1
        eth = {"dl_vlan": 100}
157 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
158 1
        switch = {"switch": dpid, "eth": eth}
159 1
        entries = {"trace": switch}
160
161 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
162 1
        assert isinstance(trace_entries, TraceEntries)
163
164 1
        trace_id = self.trace_manager.new_trace(trace_entries)
165 1
        assert trace_id == 30001
166
167
        # new_trace does not check duplicated request.
168 1
        trace_id = self.trace_manager.new_trace(trace_entries)
169 1
        assert trace_id == 30002
170
171 1
    def test_get_id(self):
172
        """Test trace manager ID control."""
173 1
        trace_id = self.trace_manager.get_id()
174 1
        assert trace_id == 30001
175
176 1
        trace_id = self.trace_manager.get_id()
177 1
        assert trace_id == 30002
178
179 1
        trace_id = self.trace_manager.get_id()
180 1
        assert trace_id == 30003
181
182 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
183 1
    @patch("napps.amlight.sdntrace.tracing.tracer.TracePath.send_trace_probe")
184 1
    def test_trace_pending(self, mock_send_probe, mock_colors):
185
        """Test trace manager tracing request."""
186 1
        mock_colors.return_value = {
187
            "color_field": "dl_src",
188
            "color_value": "ee:ee:ee:ee:ee:01",
189
        }
190 1
        mock_send_probe.return_value = {
191
            "dpid": "00:00:00:00:00:00:00:01",
192
            "port": 1,
193
        }, ""
194
195 1
        eth = {"dl_vlan": 100}
196 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
197 1
        switch = {"switch": dpid, "eth": eth}
198 1
        entries = {"trace": switch}
199
200 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
201 1
        assert isinstance(trace_entries, TraceEntries)
202
203 1
        trace_id = self.trace_manager.new_trace(trace_entries)
204 1
        assert trace_id == 30001
205
206 1
        pending = self.trace_manager.number_pending_requests()
207 1
        assert pending == 1
208
209 1
        result = self.trace_manager.get_result(trace_id)
210 1
        assert result == {"msg": "trace pending"}
211
212 1
    def test_request_invalid_trace_id(self):
213
        """Test trace manager tracing request."""
214 1
        result = self.trace_manager.get_result("1234")
215 1
        assert result == {"msg": "unknown trace id"}
216
217 1
    def test_trace_in_process(self):
218
        """Test trace manager in process."""
219 1
        self.trace_manager._spawn_trace = AsyncMock()
220 1
        trace_id = 30001
221 1
        self.trace_manager._running_traces[trace_id] = {}
222 1
        result = self.trace_manager.get_result(trace_id)
223 1
        assert result == {"msg": "trace in process"}
224
225
    # pylint: disable=unused-argument, too-many-locals
226 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
227 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.aget_switch_color")
228 1
    @patch("napps.amlight.sdntrace.tracing.tracer.TracePath.send_trace_probe")
229 1
    @patch("napps.amlight.sdntrace.tracing.tracer.TracePath.tracepath_loop")
230 1
    async def test_get_result(
231
        self, mock_trace_loop, mock_send_probe, mock_colors, mock_acolors, run_traces
232
    ):
233
        """Test tracemanager tracing request and resultS."""
234 1
        colors = {
235
            "color_field": "dl_src",
236
            "color_value": "ee:ee:ee:ee:ee:01",
237
        }
238 1
        mock_colors.return_value = colors
239 1
        mock_acolors.return_value = colors
240 1
        mock_send_probe.return_value = {
241
            "dpid": "00:00:00:00:00:00:00:01",
242
            "port": 1,
243
        }, ""
244 1
        mock_trace_loop.return_value = True
245
246 1
        eth = {"dl_vlan": 100}
247 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
248 1
        switch = {"switch": dpid, "eth": eth, "timeout": 0.1}
249 1
        entries = {"trace": switch}
250
251 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
252 1
        trace_id = self.trace_manager.new_trace(trace_entries)
253 1
        pending = self.trace_manager.number_pending_requests()
254
255
        # Waiting thread to start processing the request
256 1
        count = 0
257 1
        while pending == 1:
258 1
            result = self.trace_manager.get_result(trace_id)
259 1
            pending = self.trace_manager.number_pending_requests()
260 1
            await asyncio.sleep(0.1)
261 1
            count += 1
262 1
            if count > 30:
263
                pytest.fail("Timeout waiting to start processing trace")
264
                break
265
266 1
        count = 0
267 1
        result = self.trace_manager.get_result(trace_id)
268
        # Waiting thread to process the request
269 1
        while "msg" in result and result["msg"] == "trace in process":
270
            result = self.trace_manager.get_result(trace_id)
271
            await asyncio.sleep(0.1)
272
            count += 1
273
            if count > 30:
274
                pytest.fail("Timeout waiting to process trace")
275
                break
276
277 1
        assert result["request_id"] == 30001
278 1
        assert result["result"][0]["type"] == "starting"
279 1
        assert result["result"][0]["dpid"] == "00:00:00:00:00:00:00:01"
280 1
        assert result["result"][0]["port"] == 1
281 1
        assert result["result"][0]["time"] is not None
282 1
        assert result["start_time"] is not None
283 1
        assert result["total_time"] is not None
284 1
        assert result["request"]["trace"]["switch"]["dpid"] == "00:00:00:00:00:00:00:01"
285 1
        assert result["request"]["trace"]["switch"]["in_port"] == 1
286
287 1 View Code Duplication
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
288 1
    def test_duplicated_request(self, mock_colors):
289
        """Test trace manager new trace creation."""
290 1
        mock_colors.return_value = {
291
            "color_field": "dl_src",
292
            "color_value": "ee:ee:ee:ee:ee:01",
293
        }
294
295 1
        eth = {"dl_vlan": 100}
296 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
297 1
        switch = {"switch": dpid, "eth": eth}
298 1
        entries = {"trace": switch}
299
300 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
301 1
        assert isinstance(trace_entries, TraceEntries)
302
303 1
        trace_id = self.trace_manager.new_trace(trace_entries)
304 1
        assert trace_id == 30001
305
306 1
        duplicated = self.trace_manager.avoid_duplicated_request(trace_entries)
307 1
        assert duplicated is True
308
309 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
310 1
    def test_avoid_duplicated_request(self, mock_colors):
311
        """Test trace manager new trace creation."""
312 1
        mock_colors.return_value = {
313
            "color_field": "dl_src",
314
            "color_value": "ee:ee:ee:ee:ee:01",
315
        }
316
317 1
        eth = {"dl_vlan": 100}
318 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
319 1
        switch = {"switch": dpid, "eth": eth}
320 1
        entries = {"trace": switch}
321
322 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
323 1
        assert isinstance(trace_entries, TraceEntries)
324
325 1
        trace_id = self.trace_manager.new_trace(trace_entries)
326 1
        assert trace_id == 30001
327
328 1
        entries["trace"]["switch"]["dpid"] = "00:00:00:00:00:00:00:02"
329 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
330 1
        assert isinstance(trace_entries, TraceEntries)
331
332 1
        duplicated = self.trace_manager.avoid_duplicated_request(trace_entries)
333 1
        assert duplicated is False
334
335 1
    def test_limit_traces_reached(self):
336
        """Test trace manager limit for thread processing."""
337
        # filling the running traces array
338 1
        for i in range(settings.PARALLEL_TRACES - 1):
339 1
            self.trace_manager._running_traces[i] = i
340 1
            is_limit = self.trace_manager.limit_traces_reached()
341 1
            assert not is_limit
342
343 1
        self.trace_manager._running_traces[settings.PARALLEL_TRACES] = 9999
344 1
        is_limit = self.trace_manager.limit_traces_reached()
345 1
        assert is_limit
346
347 1
    @patch("napps.amlight.sdntrace.tracing.tracer.TracePath.tracepath")
348 1
    async def test_spawn_trace(self, mock_tracepath):
349
        """Test spawn trace."""
350
        # mock_tracepath
351 1
        trace_id = 0
352 1
        trace_entries = MagicMock()
353
354 1
        self.trace_manager._running_traces[0] = 9999
355
356 1
        await self.trace_manager._spawn_trace(trace_id, trace_entries)
357
358 1
        mock_tracepath.assert_called_once()
359 1
        assert len(self.trace_manager._running_traces) == 0
360
361
362 1
class TestTraceManagerTheadTest:
363
    """Now, load all entries at once"""
364
365 1
    def setup_method(self):
366
        """Set up before each test method"""
367 1
        TraceManager.run_traces = MagicMock()
368 1
        self.trace_manager = TraceManager(controller=get_controller_mock())
369 1
        self.trace_manager.stop_traces()
370
371 1
        self.count_running_traces = 0
372
373 1
    def run_trace_once(self):
374
        """function to replace the <flag> in "while <flag>()" code
375
        to run the loop just once."""
376 1
        if self.count_running_traces == 0:
377 1
            self.count_running_traces = self.count_running_traces + 1
378 1
            return True
379 1
        return False
380
381 1
    @patch("napps.amlight.sdntrace.shared.colors.Colors.get_switch_color")
382 1
    @patch("napps.amlight.sdntrace.tracing.tracer.TracePath.send_trace_probe")
383 1
    @patch("napps.amlight.sdntrace.tracing.tracer.TracePath.tracepath_loop")
384 1
    async def test_run_traces(self, mock_trace_loop, mock_send_probe, mock_colors):
385
        """Test tracemanager tracing request and results."""
386 1
        self.trace_manager._async_loop = asyncio.get_running_loop()
387 1
        mock_colors.return_value = {
388
            "color_field": "dl_src",
389
            "color_value": "ee:ee:ee:ee:ee:01",
390
        }
391 1
        mock_send_probe.return_value = {
392
            "dpid": "00:00:00:00:00:00:00:01",
393
            "port": 1,
394
        }, ""
395 1
        mock_trace_loop.return_value = True
396
397 1
        eth = {"dl_vlan": 100}
398 1
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
399 1
        switch = {"switch": dpid, "eth": eth}
400 1
        entries = {"trace": switch}
401
402 1
        trace_entries = self.trace_manager.is_entry_valid(entries)
403
404 1
        self.trace_manager._request_queue[1] = trace_entries
405
406 1
        with patch.object(
407
            TraceManager, "is_tracing_running", side_effect=self.run_trace_once
408
        ):
409 1
            await self.trace_manager._run_traces(0.5)
410
411 1
        assert len(self.trace_manager._request_queue) == 0
412
        assert self.trace_manager.number_pending_requests() == 0
413