Passed
Push — master ( 55f662...e18d69 )
by Humberto
02:50 queued 11s
created

TestMain.test_is_multipart_reply_ours()   A

Complexity

Conditions 1

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 15
rs 9.75
c 0
b 0
f 0
cc 1
nop 1
1
"""Test Main methods."""
2
from unittest import TestCase
3
from unittest.mock import MagicMock, create_autospec, patch, PropertyMock
4
5
from pyof.foundation.network_types import Ethernet
6
from pyof.v0x01.controller2switch.common import StatsType
7
from pyof.v0x04.controller2switch.common import MultipartType
8
9
from kytos.core.connection import ConnectionState
10
from kytos.lib.helpers import (get_switch_mock, get_kytos_event_mock,
11
                               get_connection_mock)
12
from napps.kytos.of_core.utils import NegotiationException
13
from tests.helpers import get_controller_mock
14
15
16
# pylint: disable=protected-access, too-many-public-methods
17
class TestMain(TestCase):
18
    """Test the Main class."""
19
20
    def setUp(self):
21
        """Execute steps before each tests.
22
        Set the server_name_url from kytos/of_core
23
        """
24
        self.switch_v0x01 = get_switch_mock("00:00:00:00:00:00:00:01", 0x01)
25
        self.switch_v0x04 = get_switch_mock("00:00:00:00:00:00:00:02", 0x04)
26
        self.switch_v0x01.connection = get_connection_mock(
27
            0x01, get_switch_mock("00:00:00:00:00:00:00:03"))
28
        self.switch_v0x04.connection = get_connection_mock(
29
            0x04, get_switch_mock("00:00:00:00:00:00:00:04"))
30
31
        patch('kytos.core.helpers.run_on_thread', lambda x: x).start()
32
        # pylint: disable=bad-option-value
33
        from napps.kytos.of_core.main import Main
34
        self.addCleanup(patch.stopall)
35
        self.napp = Main(get_controller_mock())
36
37
    @patch('napps.kytos.of_core.v0x01.utils.send_echo')
38
    @patch('napps.kytos.of_core.v0x04.utils.send_echo')
39
    def test_execute(self, *args):
40
        """Test execute."""
41
        (mock_of_core_v0x04_utils, mock_of_core_v0x01_utils) = args
42
        self.switch_v0x01.is_connected.return_value = True
43
        self.switch_v0x04.is_connected.return_value = True
44
        self.napp.controller.switches = {"00:00:00:00:00:00:00:01":
45
                                         self.switch_v0x01}
46
        self.napp.execute()
47
        mock_of_core_v0x01_utils.assert_called()
48
49
        self.napp.controller.switches = {"00:00:00:00:00:00:00:01":
50
                                         self.switch_v0x04}
51
        self.napp.execute()
52
        mock_of_core_v0x04_utils.assert_called()
53
54
    @patch('napps.kytos.of_core.v0x04.utils.update_flow_list')
55
    @patch('napps.kytos.of_core.v0x01.utils.update_flow_list')
56
    def test_request_flow_list(self, *args):
57
        """Test request flow list."""
58
        (mock_update_flow_list_v0x01, mock_update_flow_list_v0x04) = args
59
        mock_update_flow_list_v0x04.return_value = "ABC"
60
        self.napp._request_flow_list(self.switch_v0x01)
61
        mock_update_flow_list_v0x01.assert_called_with(self.napp.controller,
62
                                                       self.switch_v0x01)
63
        self.napp._request_flow_list(self.switch_v0x04)
64
        mock_update_flow_list_v0x04.assert_called_with(self.napp.controller,
65
                                                       self.switch_v0x04)
66
67
    @patch('napps.kytos.of_core.v0x01.flow.Flow.from_of_flow_stats')
68
    def test_handle_stats_reply(self, mock_from_of_flow_stats_v0x01):
69
        """Test handle stats reply."""
70
        mock_from_of_flow_stats_v0x01.return_value = "ABC"
71
72
        flow_msg = MagicMock()
73
        flow_msg.body = "A"
74
        flow_msg.body_type = StatsType.OFPST_FLOW
75
76
        name = 'kytos/of_core.v0x01.messages.in.ofpt_stats_reply'
77
        content = {"source": self.switch_v0x01.connection,
78
                   "message": flow_msg}
79
        event = get_kytos_event_mock(name=name, content=content)
80
        self.napp.handle_stats_reply(event)
81
        mock_from_of_flow_stats_v0x01.assert_called_with(
82
            flow_msg.body, self.switch_v0x01.connection.switch)
83
84
        desc_msg = MagicMock()
85
        desc_msg.body = "A"
86
        desc_msg.body_type = StatsType.OFPST_DESC
87
        content = {"source": self.switch_v0x01.connection,
88
                   "message": desc_msg}
89
        event = get_kytos_event_mock(name=name, content=content)
90
        switch_update = self.switch_v0x01.connection.switch.update_description
91
        self.napp.handle_stats_reply(event)
92
        self.assertEqual(switch_update.call_count, 1)
93
94
    @patch('napps.kytos.of_core.main.Main._handle_multipart_flow_stats')
95
    @patch('napps.kytos.of_core.v0x04.utils.handle_port_desc')
96
    def test_handle_multipart_reply(self, *args):
97
        """Test handle multipart reply."""
98
        (mock_of_core_v0x04_utils, mock_from_of_flow_stats_v0x04) = args
99
100
        flow_msg = MagicMock()
101
        flow_msg.multipart_type = MultipartType.OFPMP_FLOW
102
        name = 'kytos/of_core.v0x04.messages.in.ofpt_multipart_reply'
103
        content = {"source": self.switch_v0x04.connection,
104
                   "message": flow_msg}
105
        event = get_kytos_event_mock(name=name, content=content)
106
107
        self.napp.handle_multipart_reply(event)
108
        mock_from_of_flow_stats_v0x04.assert_called_with(
109
            flow_msg, self.switch_v0x04.connection.switch)
110
111
        ofpmp_port_desc = MagicMock()
112
        ofpmp_port_desc.body = "A"
113
        ofpmp_port_desc.multipart_type = MultipartType.OFPMP_PORT_DESC
114
        content = {"source": self.switch_v0x04.connection,
115
                   "message": ofpmp_port_desc}
116
        event = get_kytos_event_mock(name=name, content=content)
117
        self.napp.handle_multipart_reply(event)
118
        mock_of_core_v0x04_utils.assert_called_with(
119
            self.napp.controller, self.switch_v0x04.connection.switch,
120
            ofpmp_port_desc.body)
121
122
        ofpmp_desc = MagicMock()
123
        ofpmp_desc.body = "A"
124
        ofpmp_desc.multipart_type = MultipartType.OFPMP_DESC
125
        content = {"source": self.switch_v0x04.connection,
126
                   "message": ofpmp_desc}
127
        event = get_kytos_event_mock(name=name, content=content)
128
        switch_update = self.switch_v0x04.connection.switch.update_description
129
        self.napp.handle_multipart_reply(event)
130
        self.assertEqual(switch_update.call_count, 1)
131
132
    @patch('kytos.core.buffers.KytosEventBuffer.put')
133
    @patch('napps.kytos.of_core.v0x04.utils.send_set_config')
134
    @patch('napps.kytos.of_core.v0x01.utils.send_set_config')
135
    @patch('napps.kytos.of_core.v0x04.utils.send_desc_request')
136
    @patch('napps.kytos.of_core.v0x01.utils.send_desc_request')
137
    @patch('napps.kytos.of_core.v0x04.utils.handle_features_reply')
138
    @patch('napps.kytos.of_core.v0x01.utils.handle_features_reply')
139
    def test_handle_features_reply(self, *args):
140
        """Test handle features reply."""
141
        (mock_freply_v0x01, mock_freply_v0x04, mock_send_desc_request_v0x01,
142
         mock_send_desc_request_v0x04, mock_send_set_config_v0x01,
143
         mock_send_set_config_v0x04, mock_buffers_put) = args
144
        mock_freply_v0x01.return_value = self.switch_v0x01.connection.switch
145
        mock_freply_v0x04.return_value = self.switch_v0x04.connection.switch
146
147
        self.switch_v0x01.connection.state = ConnectionState.SETUP
148
        self.switch_v0x01.connection.protocol.state = 'waiting_features_reply'
149
        name = 'kytos/of_core.v0x0[14].messages.in.ofpt_features_reply'
150
        content = {"source": self.switch_v0x01.connection}
151
        event = get_kytos_event_mock(name=name, content=content)
152
        self.napp.handle_features_reply(event)
153
        mock_freply_v0x01.assert_called_with(self.napp.controller, event)
154
        mock_send_desc_request_v0x01.assert_called_with(
155
            self.napp.controller, self.switch_v0x01.connection.switch)
156
        mock_send_set_config_v0x01.assert_called_with(
157
            self.napp.controller, self.switch_v0x01.connection.switch)
158
159
        self.switch_v0x04.connection.state = ConnectionState.SETUP
160
        self.switch_v0x04.connection.protocol.state = 'waiting_features_reply'
161
        content = {"source": self.switch_v0x04.connection}
162
        event = get_kytos_event_mock(name=name, content=content)
163
        self.napp.handle_features_reply(event)
164
        mock_freply_v0x04.assert_called_with(self.napp.controller, event)
165
        mock_send_desc_request_v0x04.assert_called_with(
166
            self.napp.controller, self.switch_v0x04.connection.switch)
167
        mock_send_set_config_v0x04.assert_called_with(
168
            self.napp.controller, self.switch_v0x04.connection.switch)
169
170
        mock_buffers_put.assert_called()
171
172
    @patch('napps.kytos.of_core.main.Main._update_switch_flows')
173
    @patch('napps.kytos.of_core.v0x04.flow.Flow.from_of_flow_stats')
174
    @patch('napps.kytos.of_core.main.Main._is_multipart_reply_ours')
175
    def test_handle_multipart_flow_stats(self, *args):
176
        """Test handle multipart flow stats."""
177
        (mock_is_multipart_reply_ours, mock_from_of_flow_stats_v0x01,
178
         mock_update_switch_flows) = args
179
        mock_is_multipart_reply_ours.return_value = True
180
        mock_from_of_flow_stats_v0x01.return_value = "ABC"
181
182
        flow_msg = MagicMock()
183
        flow_msg.body = "A"
184
        flow_msg.flags.value = 2
185
        flow_msg.body_type = StatsType.OFPST_FLOW
186
187
        self.napp._handle_multipart_flow_stats(flow_msg, self.switch_v0x04)
188
189
        mock_is_multipart_reply_ours.assert_called_with(flow_msg,
190
                                                        self.switch_v0x04)
191
        mock_from_of_flow_stats_v0x01.assert_called_with(flow_msg.body,
192
                                                         self.switch_v0x04)
193
        mock_update_switch_flows.assert_called_with(self.switch_v0x04)
194
195
    def test_update_switch_flows(self):
196
        """Test update_switch_flows."""
197
        dpid = '00:00:00:00:00:00:00:01'
198
        mock_switch = get_switch_mock(dpid)
199
        mock_switch.id = dpid
200
        self.napp._multipart_replies_flows = {dpid: mock_switch}
201
        self.napp._multipart_replies_xids = {dpid: mock_switch}
202
        self.napp._update_switch_flows(mock_switch)
203
        self.assertEqual(self.napp._multipart_replies_xids, {})
204
        self.assertEqual(self.napp._multipart_replies_flows, {})
205
206
    def test_is_multipart_reply_ours(self):
207
        """Test _is_multipart_reply_ours."""
208
        dpid_a = '00:00:00:00:00:00:00:01'
209
        dpid_b = '00:00:00:00:00:00:00:02'
210
        mock_switch = get_switch_mock(dpid_a)
211
        mock_reply = MagicMock()
212
        mock_reply.header.xid = mock_switch
213
        type(mock_switch).id = PropertyMock(side_effect=[dpid_a,
214
                                                         dpid_a, dpid_b])
215
        self.napp._multipart_replies_xids = {dpid_a: mock_switch}
216
        response = self.napp._is_multipart_reply_ours(mock_reply, mock_switch)
217
        self.assertEqual(response, True)
218
219
        response = self.napp._is_multipart_reply_ours(mock_reply, mock_switch)
220
        self.assertEqual(response, False)
221
222
    @patch('napps.kytos.of_core.main.of_slicer')
223
    @patch('napps.kytos.of_core.main.Main._negotiate')
224
    @patch('napps.kytos.of_core.main.Main.emit_message_in')
225
    def test_handle_raw_in(self, *args):
226
        """Test handle_raw_in."""
227
        (mock_emit_message_in, mock_negotiate, mock_of_slicer) = args
228
229
        mock_packets = MagicMock()
230
        mock_data = MagicMock()
231
        mock_connection = MagicMock()
232
        mock_connection.is_new.side_effect = [True, False, True, False]
233
        mock_connection.is_during_setup.return_value = False
234
        mock_of_slicer.return_value = [[mock_packets, mock_packets], b'']
235
        name = 'kytos/core.openflow.raw.in'
236
        content = {'source': mock_connection, 'new_data': mock_data}
237
        mock_event = get_kytos_event_mock(name=name, content=content)
238
239
        self.napp.handle_raw_in(mock_event)
240
        mock_negotiate.assert_called()
241
        mock_emit_message_in.assert_called()
242
243
        # Test Fail
244
        mock_negotiate.side_effect = NegotiationException('Foo')
245
        self.napp.handle_raw_in(mock_event)
246
        self.assertEqual(mock_connection.close.call_count, 1)
247
248
        mock_connection.close.call_count = 0
249
        mock_connection.protocol.unpack.side_effect = AttributeError()
250
        self.napp.handle_raw_in(mock_event)
251
        self.assertEqual(mock_connection.close.call_count, 1)
252
253
    @patch('napps.kytos.of_core.main.Main.update_port_status')
254
    @patch('napps.kytos.of_core.main.Main.update_links')
255
    def test_emit_message_in(self, *args):
256
        """Test emit_message_in."""
257
        (mock_update_links, mock_update_port_status) = args
258
259
        mock_port_connection = MagicMock()
260
        msg_port_mock = MagicMock()
261
        msg_port_mock.header.message_type.name = 'ofpt_port_status'
262
        mock_port_connection.side_effect = True
263
        self.napp.emit_message_in(mock_port_connection,
264
                                  msg_port_mock)
265
        mock_update_port_status.assert_called_with(msg_port_mock,
266
                                                   mock_port_connection)
267
268
        mock_packet_in_connection = MagicMock()
269
        msg_packet_in_mock = MagicMock()
270
        mock_packet_in_connection.side_effect = True
271
        msg_packet_in_mock.header.message_type.name = 'ofpt_packet_in'
272
        self.napp.emit_message_in(mock_packet_in_connection,
273
                                  msg_packet_in_mock)
274
        mock_update_links.assert_called_with(msg_packet_in_mock,
275
                                             mock_packet_in_connection)
276
277
    @patch('napps.kytos.of_core.main.emit_message_out')
278
    def test_emit_message_out(self, mock_emit_message_out):
279
        """Test emit message_out."""
280
        mock_connection = MagicMock()
281
        mock_message = MagicMock()
282
        mock_connection.is_alive.return_value = True
283
        self.napp.emit_message_out(mock_connection, mock_message)
284
        mock_emit_message_out.assert_called()
285
286
    @patch('pyof.utils.v0x04.symmetric.echo_reply.EchoReply')
287
    @patch('napps.kytos.of_core.main.Main.emit_message_out')
288
    def test_handle_echo_request(self, *args):
289
        """Test handle echo request messages."""
290
        (mock_emit_message_out, mock_echo_reply) = args
291
        mock_event = MagicMock()
292
        mock_echo_request = MagicMock()
293
        mock_echo_reply.return_value = "A"
294
        mock_echo_request.header.xid = "A"
295
        mock_echo_request.data = "A"
296
        mock_event.source.protocol.version = 4
297
        mock_event.message = mock_echo_request
298
        self.napp.handle_echo_request(mock_event)
299
        mock_echo_reply.assert_called_with(xid=mock_echo_request.header.xid,
300
                                           data=mock_echo_request.data)
301
        mock_emit_message_out.assert_called_with(mock_event.source, "A")
302
303
    @patch('napps.kytos.of_core.main.Main.send_features_request')
304
    @patch('napps.kytos.of_core.v0x04.utils.say_hello')
305
    @patch('napps.kytos.of_core.main._get_version_from_bitmask')
306
    @patch('napps.kytos.of_core.main._get_version_from_header')
307
    def test_negotiate(self, *args):
308
        """Test negotiate."""
309
        (mock_version_header, mock_version_bitmask, mock_say_hello,
310
         mock_features_request) = args
311
        mock_version_header.return_value = 4
312
        mock_version_bitmask.side_effect = [4, None]
313
        mock_connection = MagicMock()
314
        mock_message = MagicMock()
315
        type(mock_message).versions = PropertyMock(side_effect=[4, 4, 4,
316
                                                                False])
317
318
        self.napp._negotiate(mock_connection, mock_message)
319
        mock_version_bitmask.assert_called_with(mock_message.versions)
320
        mock_say_hello.assert_called_with(self.napp.controller,
321
                                          mock_connection)
322
        mock_features_request.assert_called_with(mock_connection)
323
324
        self.napp._negotiate(mock_connection, mock_message)
325
        mock_say_hello.assert_called_with(self.napp.controller,
326
                                          mock_connection)
327
        mock_features_request.assert_called_with(mock_connection)
328
329
        # Test Fail
330
        with self.assertRaises(NegotiationException):
331
            type(mock_message).versions = PropertyMock(return_value=[4])
332
            self.napp._negotiate(mock_connection, mock_message)
333
334
    @patch('pyof.utils.v0x04.asynchronous.error_msg.ErrorMsg')
335
    @patch('napps.kytos.of_core.main.Main.emit_message_out')
336
    @patch('kytos.core.buffers.KytosEventBuffer.put')
337
    def tests_fail_negotiation(self, *args):
338
        """Test fail_negotiation."""
339
        (mock_event_buffer, mock_emit_message_out,
340
         mock_error_msg) = args
341
        mock_connection = MagicMock()
342
        mock_message = MagicMock()
343
        mock_connection.id = "A"
344
        mock_message.side_effect = 4
345
        self.napp.fail_negotiation(mock_connection, mock_message)
346
        mock_event_buffer.assert_called()
347
        mock_emit_message_out.assert_called_with(mock_connection,
348
                                                 mock_error_msg.return_value)
349
350
    @patch('napps.kytos.of_core.settings.SEND_FEATURES_REQUEST_ON_ECHO')
351
    @patch('napps.kytos.of_core.main.Main.send_features_request')
352
    def test_handle_queued_openflow_echo_reply(self, *args):
353
        """Test handle queued OpenFlow echo reply messages."""
354
        (mock_send_features_request, mock_settings) = args
355
        mock_settings.return_value = True
356
        mock_event = MagicMock()
357
        self.napp.handle_queued_openflow_echo_reply(mock_event)
358
        mock_send_features_request.assert_called_with(mock_event.destination)
359
360
    @patch('pyof.utils.v0x04.controller2switch.'
361
           'features_request.FeaturesRequest')
362
    @patch('napps.kytos.of_core.main.Main.emit_message_out')
363
    def test_send_features_request(self, *args):
364
        """Test send send_features_request."""
365
        (mock_emit_message_out, mock_features_request) = args
366
        mock_destination = MagicMock()
367
        mock_destination.protocol.version = 4
368
        mock_features_request.return_value = "A"
369
        self.napp.send_features_request(mock_destination)
370
        mock_features_request.assert_called()
371
        mock_emit_message_out.assert_called_with(mock_destination, "A")
372
373
    def test_handle_features_request_sent(self):
374
        """Test tests_handle_features_request_sent."""
375
        mock_protocol = MagicMock()
376
        mock_protocol.protocol.state = 'sending_features'
377
        expected = 'waiting_features_reply'
378
        name = 'kytos/of_core.v0x0[14].messages.out.ofpt_features_request'
379
        content = {'destination': mock_protocol}
380
        mock_event = get_kytos_event_mock(name=name, content=content)
381
        self.napp.handle_features_request_sent(mock_event)
382
        self.assertEqual(mock_event.destination.protocol.state, expected)
383
384
    def test_handle_openflow_in_hello_failed(self):
385
        """Test handle_openflow_in_hello_failed."""
386
        mock_destination = MagicMock()
387
        content = {'destination': mock_destination}
388
        mock_event = get_kytos_event_mock(name='kytos/of_core',
389
                                          content=content)
390
        self.napp.handle_openflow_in_hello_failed(mock_event)
391
        self.assertEqual(mock_event.destination.close.call_count, 1)
392
393
    @patch('napps.kytos.of_core.main.log')
394
    def test_shutdown(self, mock_log):
395
        """Test shutdown."""
396
        self.napp.shutdown()
397
        self.assertEqual(mock_log.debug.call_count, 1)
398
399
    @patch('kytos.core.buffers.KytosEventBuffer.put')
400
    @patch('napps.kytos.of_core.main.Ethernet')
401
    def test_update_links(self, *args):
402
        """Test update_links."""
403
        (mock_ethernet, mock_buffer_put) = args
404
        ethernet = create_autospec(Ethernet)
405
        ethernet.ether_type = "A"
406
        mock_ethernet.side_effect = ethernet
407
        mock_message = MagicMock()
408
        mock_s = MagicMock()
409
        mock_s.switch.get_interface_by_port_no.side_effect = [AttributeError(),
410
                                                              True]
411
        self.napp.update_links(mock_message, mock_s)
412
        mock_ethernet.assert_called()
413
        mock_buffer_put.assert_called()
414
415
    @patch('kytos.core.buffers.KytosEventBuffer.put')
416
    def test_send_specific_port_mod(self, mock_buffer_put):
417
        """Test send specific port."""
418
        mock_port = MagicMock()
419
        mock_interface = MagicMock()
420
        type(mock_port.state).value = PropertyMock(side_effect=[0, 1, 2])
421
        current_state = 0
422
        self.napp._send_specific_port_mod(mock_port,
423
                                          mock_interface, current_state)
424
        mock_buffer_put.assert_called()
425
426
        current_state = 1
427
        self.napp._send_specific_port_mod(mock_port,
428
                                          mock_interface, current_state)
429
        mock_buffer_put.assert_called()
430
431
        current_state = 2
432
        self.napp._send_specific_port_mod(mock_port,
433
                                          mock_interface, current_state)
434
        mock_buffer_put.assert_called()
435
436
    @patch('kytos.core.buffers.KytosEventBuffer.put')
437
    @patch('napps.kytos.of_core.main.Interface')
438
    @patch('napps.kytos.of_core.main.Main._send_specific_port_mod')
439
    def test_update_port_status(self, *args):
440
        """Test update_port_status."""
441
        (mock_port_mod, mock_interface, mock_buffer_put) = args
442
        mock_port_status = MagicMock()
443
        mock_source = MagicMock()
444
445
        mock_port_status.reason.value.side_effect = [0, 1, 2]
446
        mock_port_status.reason.enum_ref(0).name = 'OFPPR_ADD'
447
        self.napp.update_port_status(mock_port_status, mock_source)
448
        mock_interface.assert_called()
449
450
        # check OFPRR_MODIFY
451
        mock_port_status.reason.enum_ref(1).name = 'OFPPR_MODIFY'
452
        mock_source.switch.get_interface_by_port_no.return_value = False
453
        self.napp.update_port_status(mock_port_status, mock_source)
454
        mock_port_mod.assert_called()
455
        mock_buffer_put.assert_called()
456
457
        mock_source.switch.get_interface_by_port_no.return_value = MagicMock()
458
        self.napp.update_port_status(mock_port_status, mock_source)
459
        mock_port_mod.assert_called()
460
        mock_buffer_put.assert_called()
461
462
        # check OFPRR_DELETE
463
        mock_port_status.reason.enum_ref(2).name = 'OFPPR_DELETE'
464
        self.napp.update_port_status(mock_port_status, mock_source)
465
        mock_port_mod.assert_called()
466
        mock_buffer_put.assert_called()
467