Passed
Pull Request — master (#81)
by
unknown
02:56
created

build.tests.unit.test_main   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 693
Duplicated Lines 14 %

Importance

Changes 0
Metric Value
eloc 449
dl 97
loc 693
rs 10
c 0
b 0
f 0
wmc 21

17 Methods

Rating   Name   Duplication   Size   Complexity  
A TestMain.test_trace_step() 0 51 1
A TestMain.test_has_loop__fail() 0 30 1
A TestMain.get_napp_urls() 0 24 4
A TestMain.test_tracepath() 0 33 1
A TestMain.test_verify_api_urls() 0 17 1
A TestMain.test_trace_step__no_flow() 0 24 1
B TestMain.test_trace() 0 49 1
A TestMain.test_has_loop() 0 48 1
A TestMain.test_update_circuits() 0 11 1
A TestMain.test_update_circuits__no_settings() 0 12 1
A TestMain.setUp() 0 26 2
A TestMain.test_trace_step__no_endpoint() 0 36 1
B TestMain.test_traces() 0 93 1
A TestMain.test_trace_missing_parameter() 0 41 1
A TestMain.test_get_traces() 49 49 1
B TestMain.test_traces_no_action() 0 63 1
A TestMain.test_traces_with_loop() 48 48 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
"""Module to test the main napp file."""
2
import json
3
from unittest import TestCase
4
from unittest.mock import patch, MagicMock
5
6
from kytos.core.interface import Interface
7
from kytos.lib.helpers import (
8
    get_interface_mock,
9
    get_switch_mock,
10
    get_controller_mock,
11
    get_link_mock,
12
    get_test_client,
13
)
14
15
16
# pylint: disable=too-many-public-methods, too-many-lines
17
class TestMain(TestCase):
18
    """Test the Main class."""
19
20
    def setUp(self):
21
        """Execute steps before each tests.
22
23
        Set the server_name_url_url from amlight/sdntrace_cp
24
        """
25
        self.server_name_url = "http://localhost:8181/api/amlight/sdntrace_cp"
26
27
        # The decorator run_on_thread is patched, so methods that listen
28
        # for events do not run on threads while tested.
29
        # Decorators have to be patched before the methods that are
30
        # decorated with them are imported.
31
        patch("kytos.core.helpers.run_on_thread", lambda x: x).start()
32
        # pylint: disable=import-outside-toplevel
33
        from napps.amlight.sdntrace_cp.main import Main
34
35
        self.napp = Main(get_controller_mock())
36
37
        sw1 = get_switch_mock("00:00:00:00:00:00:00:01", 0x04)
38
        intf_sw1 = get_interface_mock("eth1", 1, sw1)
39
        intf_sw1.link = None
40
        sw1.get_interface_by_port_no.return_value = intf_sw1
41
        sw2 = get_switch_mock("00:00:00:00:00:00:00:02", 0x04)
42
        intf_sw2 = get_interface_mock("eth1", 1, sw2)
43
        intf_sw2.link = None
44
        sw2.get_interface_by_port_no.return_value = intf_sw2
45
        self.napp.controller.switches = {sw1.dpid: sw1, sw2.dpid: sw2}
46
47
    @staticmethod
48
    def get_napp_urls(napp):
49
        """Return the amlight/sdntrace_cp urls.
50
51
        The urls will be like:
52
53
        urls = [
54
            (options, methods, url)
55
        ]
56
57
        """
58
        controller = napp.controller
59
        controller.api_server.register_napp_endpoints(napp)
60
61
        urls = []
62
        for rule in controller.api_server.app.url_map.iter_rules():
63
            options = {}
64
            for arg in rule.arguments:
65
                options[arg] = f"[{0}]".format(arg)
66
67
            if f"{napp.username}/{napp.name}" in str(rule):
68
                urls.append((options, rule.methods, f"{str(rule)}"))
69
70
        return urls
71
72
    def test_verify_api_urls(self):
73
        """Verify all APIs registered."""
74
75
        expected_urls = [
76
            (
77
                {},
78
                {"OPTIONS", "HEAD", "PUT"},
79
                "/api/amlight/sdntrace_cp/trace/ ",
80
            ),
81
            (
82
                {},
83
                {"OPTIONS", "HEAD", "PUT"},
84
                "/api/amlight/sdntrace_cp/traces/ ",
85
            ),
86
        ]
87
        urls = self.get_napp_urls(self.napp)
88
        self.assertEqual(len(expected_urls), len(urls))
89
90
    @patch("napps.amlight.sdntrace_cp.main.Main.match_and_apply")
91
    def test_trace_step(self, mock_flow_match):
92
        """Test trace_step success result."""
93
        mock_flow_match.return_value = ["1"], ["entries"], 1
94
        switch = get_switch_mock("00:00:00:00:00:00:00:01", 0x04)
95
96
        mock_interface = Interface("interface A", 1, MagicMock())
97
        mock_interface.address = "00:00:00:00:00:00:00:01"
98
99
        iface1 = get_interface_mock(
100
            "", 1, get_switch_mock("00:00:00:00:00:00:00:01")
101
        )
102
        iface2 = get_interface_mock(
103
            "", 2, get_switch_mock("00:00:00:00:00:00:00:02")
104
        )
105
        mock_interface.link = get_link_mock(iface1, iface2)
106
        mock_interface.link.endpoint_a.port_number = 1
107
        mock_interface.link.endpoint_a.port_number = 2
108
109
        # Patch for utils.find_endpoint
110
        switch.get_interface_by_port_no.return_value = mock_interface
111
112
        entries = MagicMock()
113
114
        stored_flows = {
115
            "flow": {
116
                "table_id": 0,
117
                "cookie": 84114964,
118
                "hard_timeout": 0,
119
                "idle_timeout": 0,
120
                "priority": 10,
121
            },
122
            "flow_id": 1,
123
            "state": "installed",
124
            "switch": "00:00:00:00:00:00:00:01",
125
        }
126
127
        stored_flows_arg = {
128
            "00:00:00:00:00:00:00:01": [stored_flows]
129
        }
130
131
        result = self.napp.trace_step(switch, entries, stored_flows_arg)
132
133
        mock_flow_match.assert_called_once()
134
        self.assertEqual(
135
            result,
136
            {
137
                "dpid": "00:00:00:00:00:00:00:01",
138
                "in_port": 2,
139
                "out_port": 1,
140
                "entries": ["entries"],
141
            },
142
        )
143
144
    @patch("napps.amlight.sdntrace_cp.main.Main.match_and_apply")
145
    def test_trace_step__no_endpoint(self, mock_flow_match):
146
        """Test trace_step without endpoints available for switch/port."""
147
        mock_flow_match.return_value = ["1"], ["entries"], 1
148
        switch = get_switch_mock("00:00:00:00:00:00:00:01", 0x04)
149
150
        mock_interface = Interface("interface A", 1, MagicMock())
151
        mock_interface.address = "00:00:00:00:00:00:00:01"
152
        mock_interface.link = None
153
154
        # Patch for utils.find_endpoint
155
        switch.get_interface_by_port_no.return_value = mock_interface
156
157
        entries = MagicMock()
158
159
        stored_flows = {
160
            "flow": {
161
                "table_id": 0,
162
                "cookie": 84114964,
163
                "hard_timeout": 0,
164
                "idle_timeout": 0,
165
                "priority": 10,
166
            },
167
            "flow_id": 1,
168
            "state": "installed",
169
            "switch": "00:00:00:00:00:00:00:01",
170
        }
171
172
        stored_flows_arg = {
173
            "00:00:00:00:00:00:00:01": [stored_flows]
174
        }
175
176
        result = self.napp.trace_step(switch, entries, stored_flows_arg)
177
178
        mock_flow_match.assert_called_once()
179
        self.assertEqual(result, {"entries": ["entries"], "out_port": 1})
180
181
    def test_trace_step__no_flow(self):
182
        """Test trace_step without flows for the switch."""
183
        switch = get_switch_mock("00:00:00:00:00:00:00:01")
184
        entries = MagicMock()
185
186
        stored_flows = {
187
            "flow": {
188
                "table_id": 0,
189
                "cookie": 84114964,
190
                "hard_timeout": 0,
191
                "idle_timeout": 0,
192
                "priority": 10,
193
            },
194
            "flow_id": 1,
195
            "state": "installed",
196
            "switch": "00:00:00:00:00:00:00:01",
197
        }
198
199
        stored_flows_arg = {
200
            "00:00:00:00:00:00:00:01": [stored_flows]
201
        }
202
203
        result = self.napp.trace_step(switch, entries, stored_flows_arg)
204
        assert result is None
205
206
    @patch("napps.amlight.sdntrace_cp.main.Main.trace_step")
207
    def test_tracepath(self, mock_trace_step):
208
        """Test tracepath with success result."""
209
        eth = {"dl_vlan": 100}
210
        dpid = {"dpid": "00:00:00:00:00:00:00:01", "in_port": 1}
211
        switch = {"switch": dpid, "eth": eth}
212
        entries = {"trace": switch}
213
        mock_trace_step.return_value = {
214
            "dpid": "00:00:00:00:00:00:00:02",
215
            "in_port": 2,
216
            "out_port": 3,
217
            "entries": entries,
218
        }
219
220
        stored_flows_arg = {
221
            "00:00:00:00:00:00:00:01": [],
222
            "00:00:00:00:00:00:00:02": [],
223
        }
224
225
        result = self.napp.tracepath(
226
                                        entries["trace"]["switch"],
227
                                        stored_flows_arg
228
                                    )
229
230
        assert result[0]["in"]["dpid"] == "00:00:00:00:00:00:00:01"
231
        assert result[0]["in"]["port"] == 1
232
        assert result[0]["in"]["type"] == "starting"
233
        assert result[0]["out"]["port"] == 3
234
235
        assert result[1]["in"]["dpid"] == "00:00:00:00:00:00:00:02"
236
        assert result[1]["in"]["port"] == 2
237
        assert result[1]["in"]["type"] == "intermediary"
238
        assert result[1]["out"]["port"] == 3
239
240
    def test_has_loop(self):
241
        """Test has_loop to detect a tracepath with loop."""
242
        trace_result = [
243
            {
244
                "in": {
245
                    "dpid": "00:00:00:00:00:00:00:01",
246
                    "port": 2,
247
                },
248
                "out": {
249
                    "port": 1,
250
                },
251
            },
252
            {
253
                "in": {
254
                    "dpid": "00:00:00:00:00:00:00:03",
255
                    "port": 2,
256
                },
257
                "out": {
258
                    "port": 1,
259
                },
260
            },
261
            {
262
                "in": {
263
                    "dpid": "00:00:00:00:00:00:00:03",
264
                    "port": 3,
265
                },
266
                "out": {
267
                    "port": 1,
268
                },
269
            },
270
            {
271
                "in": {
272
                    "dpid": "00:00:00:00:00:00:00:03",
273
                    "port": 3,
274
                },
275
                "out": {
276
                    "port": 1,
277
                },
278
            },
279
        ]
280
        trace_step = {
281
            "dpid": "00:00:00:00:00:00:00:03",
282
            "port": 3,
283
        }
284
285
        result = self.napp.has_loop(trace_step, trace_result)
286
287
        self.assertTrue(result)
288
289
    def test_has_loop__fail(self):
290
        """Test has_loop to detect a tracepath with loop."""
291
        trace_result = [
292
            {
293
                "in": {
294
                    "dpid": "00:00:00:00:00:00:00:01",
295
                    "port": 2,
296
                },
297
                "out": {
298
                    "port": 1,
299
                },
300
            },
301
            {
302
                "in": {
303
                    "dpid": "00:00:00:00:00:00:00:02",
304
                    "port": 2,
305
                },
306
                "out": {
307
                    "port": 1,
308
                },
309
            },
310
        ]
311
        trace_step = {
312
            "dpid": "00:00:00:00:00:00:00:03",
313
            "port": 2,
314
        }
315
316
        result = self.napp.has_loop(trace_step, trace_result)
317
318
        self.assertFalse(result)
319
320
    @patch("napps.amlight.sdntrace_cp.main.settings")
321
    def test_update_circuits(self, mock_settings):
322
        """Test update_circuits event listener with success."""
323
        mock_settings.FIND_CIRCUITS_IN_FLOWS = True
324
325
        self.napp.automate = MagicMock()
326
        self.napp.automate.find_circuits = MagicMock()
327
328
        self.napp.update_circuits()
329
330
        self.napp.automate.find_circuits.assert_called_once()
331
332
    @patch("napps.amlight.sdntrace_cp.main.settings")
333
    def test_update_circuits__no_settings(self, mock_settings):
334
        """Test update_circuits event listener without
335
        settings option enabled."""
336
        mock_settings.FIND_CIRCUITS_IN_FLOWS = False
337
338
        self.napp.automate = MagicMock()
339
        self.napp.automate.find_circuits = MagicMock()
340
341
        self.napp.update_circuits()
342
343
        self.napp.automate.find_circuits.assert_not_called()
344
345
    @patch("napps.amlight.sdntrace_cp.main.get_stored_flows")
346
    def test_trace(self, mock_stored_flows):
347
        """Test trace rest call."""
348
        api = get_test_client(get_controller_mock(), self.napp)
349
        url = f"{self.server_name_url}/trace/"
350
351
        payload = {
352
            "trace": {
353
                "switch": {
354
                    "dpid": "00:00:00:00:00:00:00:01",
355
                    "in_port": 1
356
                    },
357
                "eth": {"dl_vlan": 100},
358
            }
359
        }
360
        stored_flows = {
361
                "flow": {
362
                    "table_id": 0,
363
                    "cookie": 84114964,
364
                    "hard_timeout": 0,
365
                    "idle_timeout": 0,
366
                    "priority": 10,
367
                    "match": {"dl_vlan": 100, "in_port": 1},
368
                    "actions": [
369
                        {"action_type": "push_vlan"},
370
                        {"action_type": "set_vlan", "vlan_id": 200},
371
                        {"action_type": "output", "port": 2}
372
                    ],
373
                },
374
                "flow_id": 1,
375
                "state": "installed",
376
                "switch": "00:00:00:00:00:00:00:01",
377
        }
378
        mock_stored_flows.return_value = {
379
            "00:00:00:00:00:00:00:01": [stored_flows]
380
        }
381
382
        response = api.put(
383
            url, data=json.dumps(payload), content_type="application/json"
384
        )
385
        current_data = json.loads(response.data)
386
        result = current_data["result"]
387
388
        assert len(result) == 1
389
        assert result[0]["dpid"] == "00:00:00:00:00:00:00:01"
390
        assert result[0]["port"] == 1
391
        assert result[0]["type"] == "last"
392
        assert result[0]["vlan"] == 100
393
        assert result[0]["out"] == {"port": 2, "vlan": 200}
394
395
    @patch("napps.amlight.sdntrace_cp.main.get_stored_flows")
396
    def test_trace_missing_parameter(self, mock_stored_flows):
397
        """Test trace rest call with a missing parameter."""
398
        api = get_test_client(get_controller_mock(), self.napp)
399
        url = f"{self.server_name_url}/trace/"
400
401
        payload = {
402
            "trace": {
403
                "switch": {
404
                    "in_port": 1
405
                    },
406
                "eth": {"dl_vlan": 100},
407
            }
408
        }
409
        stored_flows = {
410
                "flow": {
411
                    "table_id": 0,
412
                    "cookie": 84114964,
413
                    "hard_timeout": 0,
414
                    "idle_timeout": 0,
415
                    "priority": 10,
416
                    "match": {"dl_vlan": 100, "in_port": 1},
417
                    "actions": [
418
                        {"action_type": "push_vlan"},
419
                        {"action_type": "set_vlan", "vlan_id": 200},
420
                        {"action_type": "output", "port": 2}
421
                    ],
422
                },
423
                "flow_id": 1,
424
                "state": "installed",
425
                "switch": "00:00:00:00:00:00:00:01",
426
        }
427
        mock_stored_flows.return_value = {
428
            "00:00:00:00:00:00:00:01": [stored_flows]
429
        }
430
431
        response = api.put(
432
            url, data=json.dumps(payload), content_type="application/json"
433
        )
434
        current_data = json.loads(response.data)
435
        assert current_data["result"] == []
436
437 View Code Duplication
    @patch("napps.amlight.sdntrace_cp.main.get_stored_flows")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
438
    def test_get_traces(self, mock_stored_flows):
439
        """Test traces rest call."""
440
        api = get_test_client(get_controller_mock(), self.napp)
441
        url = f"{self.server_name_url}/traces/"
442
443
        payload = [{
444
            "trace": {
445
                "switch": {
446
                    "dpid": "00:00:00:00:00:00:00:01",
447
                    "in_port": 1
448
                    },
449
                "eth": {"dl_vlan": 100},
450
            }
451
        }]
452
453
        stored_flow = {
454
            "id": 1,
455
            "flow": {
456
                "table_id": 0,
457
                "cookie": 84114964,
458
                "hard_timeout": 0,
459
                "idle_timeout": 0,
460
                "priority": 10,
461
                "match": {"dl_vlan": 100, "in_port": 1},
462
                "actions": [
463
                    {"action_type": "pop_vlan"},
464
                    {"action_type": "output", "port": 2},
465
                ],
466
            }
467
        }
468
469
        mock_stored_flows.return_value = {
470
            "00:00:00:00:00:00:00:01": [stored_flow]
471
        }
472
473
        response = api.put(
474
            url, data=json.dumps(payload), content_type="application/json"
475
        )
476
        current_data = json.loads(response.data)
477
        result1 = current_data["result"]
478
479
        assert len(result1) == 1
480
        assert len(result1[0]) == 1
481
        assert result1[0][0]["dpid"] == "00:00:00:00:00:00:00:01"
482
        assert result1[0][0]["port"] == 1
483
        assert result1[0][0]["type"] == "last"
484
        assert result1[0][0]["vlan"] == 100
485
        assert result1[0][0]["out"] == {"port": 2}
486
487
    @patch("napps.amlight.sdntrace_cp.main.get_stored_flows")
488
    def test_traces(self, mock_stored_flows):
489
        """Test traces rest call"""
490
        api = get_test_client(get_controller_mock(), self.napp)
491
        url = f"{self.server_name_url}/traces/"
492
493
        payload = [
494
                    {
495
                        "trace": {
496
                            "switch": {
497
                                "dpid": "00:00:00:00:00:00:00:01",
498
                                "in_port": 1
499
                            },
500
                            "eth": {
501
                                "dl_vlan": 100
502
                            }
503
                        }
504
                    },
505
                    {
506
                        "trace": {
507
                            "switch": {
508
                                "dpid": "00:00:00:00:00:00:00:0a",
509
                                "in_port": 1
510
                            },
511
                            "eth": {
512
                                "dl_vlan": 100
513
                            }
514
                        }
515
                    },
516
                    {
517
                        "trace": {
518
                            "switch": {
519
                                "dpid": "00:00:00:00:00:00:00:02",
520
                            }
521
                        }
522
                    },
523
                    {
524
                        "trace": {
525
                            "switch": {
526
                                "dpid": "00:00:00:00:00:00:00:02",
527
                                "in_port": 2
528
                            },
529
                            "eth": {
530
                                "dl_vlan": 100
531
                            }
532
                        }
533
                    }
534
                ]
535
536
        stored_flow = {
537
            "id": 1,
538
            "flow": {
539
                "table_id": 0,
540
                "cookie": 84114964,
541
                "hard_timeout": 0,
542
                "idle_timeout": 0,
543
                "priority": 10,
544
                "match": {"dl_vlan": 100, "in_port": 1},
545
                "actions": [{"action_type": "output", "port": 2}],
546
            }
547
        }
548
549
        mock_stored_flows.return_value = {
550
            "00:00:00:00:00:00:00:01": [stored_flow],
551
            "00:00:00:00:00:00:00:02": [stored_flow],
552
        }
553
554
        response = api.put(
555
            url, data=json.dumps(payload), content_type="application/json"
556
        )
557
        current_data = json.loads(response.data)
558
        result = current_data["result"]
559
        assert len(result) == 4
560
561
        assert result[0][0]["dpid"] == "00:00:00:00:00:00:00:01"
562
        assert result[0][0]["port"] == 1
563
        assert result[0][0]["type"] == "last"
564
        assert result[0][0]["vlan"] == 100
565
        assert result[0][0]["out"] == {"port": 2, "vlan": 100}
566
567
        assert result[1][0]["dpid"] == "00:00:00:00:00:00:00:0a"
568
        assert result[1][0]["port"] == 1
569
        assert result[1][0]["type"] == "last"
570
        assert result[1][0]["vlan"] == 100
571
        assert result[1][0]["out"] is None
572
573
        assert result[2] == []
574
575
        assert result[3][0]["dpid"] == "00:00:00:00:00:00:00:02"
576
        assert result[3][0]["port"] == 2
577
        assert result[3][0]["type"] == "incomplete"
578
        assert result[3][0]["vlan"] == 100
579
        assert result[3][0]["out"] is None
580
581 View Code Duplication
    @patch("napps.amlight.sdntrace_cp.main.get_stored_flows")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
582
    def test_traces_with_loop(self, mock_stored_flows):
583
        """Test traces rest call"""
584
        api = get_test_client(get_controller_mock(), self.napp)
585
        url = f"{self.server_name_url}/traces/"
586
587
        payload = [
588
                    {
589
                        "trace": {
590
                            "switch": {
591
                                "dpid": "00:00:00:00:00:00:00:01",
592
                                "in_port": 1
593
                            },
594
                            "eth": {
595
                                "dl_vlan": 100
596
                            }
597
                        }
598
                    }
599
                ]
600
601
        stored_flow = {
602
            "id": 1,
603
            "flow": {
604
                "table_id": 0,
605
                "cookie": 84114964,
606
                "hard_timeout": 0,
607
                "idle_timeout": 0,
608
                "priority": 10,
609
                "match": {"dl_vlan": 100, "in_port": 1},
610
                "actions": [{"action_type": "output", "port": 1}],
611
            }
612
        }
613
614
        mock_stored_flows.return_value = {
615
            "00:00:00:00:00:00:00:01": [stored_flow],
616
        }
617
618
        response = api.put(
619
            url, data=json.dumps(payload), content_type="application/json"
620
        )
621
        current_data = json.loads(response.data)
622
        result = current_data["result"]
623
        assert len(result) == 1
624
        assert result[0][0]["dpid"] == "00:00:00:00:00:00:00:01"
625
        assert result[0][0]["port"] == 1
626
        assert result[0][0]["type"] == "loop"
627
        assert result[0][0]["vlan"] == 100
628
        assert result[0][0]["out"] == {"port": 1, "vlan": 100}
629
630
    @patch("napps.amlight.sdntrace_cp.main.get_stored_flows")
631
    def test_traces_no_action(self, mock_stored_flows):
632
        """Test traces rest call for two traces with different switches."""
633
        api = get_test_client(get_controller_mock(), self.napp)
634
        url = f"{self.server_name_url}/traces/"
635
636
        payload = [
637
            {
638
                "trace": {
639
                    "switch": {
640
                        "dpid": "00:00:00:00:00:00:00:01",
641
                        "in_port": 1
642
                        },
643
                    "eth": {"dl_vlan": 100},
644
                }
645
            },
646
            {
647
                "trace": {
648
                    "switch": {
649
                        "dpid": "00:00:00:00:00:00:00:02",
650
                        "in_port": 1},
651
                    "eth": {"dl_vlan": 100},
652
                }
653
            }
654
        ]
655
656
        stored_flow1 = {
657
            "id": 1,
658
            "flow": {
659
                "table_id": 0,
660
                "cookie": 84114964,
661
                "hard_timeout": 0,
662
                "idle_timeout": 0,
663
                "priority": 10,
664
                "match": {"dl_vlan": 100, "in_port": 1}
665
            }
666
        }
667
        stored_flow2 = {
668
            "id": 1,
669
            "flow": {
670
                "table_id": 0,
671
                "cookie": 84114964,
672
                "hard_timeout": 0,
673
                "idle_timeout": 0,
674
                "priority": 10,
675
                "match": {"dl_vlan": 100, "in_port": 1},
676
                "actions": []
677
            }
678
        }
679
680
        mock_stored_flows.return_value = {
681
            "00:00:00:00:00:00:00:01": [stored_flow1],
682
            "00:00:00:00:00:00:00:02": [stored_flow2]
683
        }
684
685
        response = api.put(
686
            url, data=json.dumps(payload), content_type="application/json"
687
        )
688
        current_data = json.loads(response.data)
689
        result = current_data["result"]
690
        assert len(result) == 2
691
        assert result[0][-1]['type'] == "incomplete"
692
        assert result[1][-1]['type'] == "incomplete"
693