Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / test_driver / happy / test-templates / ChipInetMulticast.py
1 #!/usr/bin/env python3
2
3 #
4 #    Copyright (c) 2020 Project CHIP Authors
5 #    Copyright (c) 2018-2019 Google LLC.
6 #    All rights reserved.
7 #
8 #    Licensed under the Apache License, Version 2.0 (the "License");
9 #    you may not use this file except in compliance with the License.
10 #    You may obtain a copy of the License at
11 #
12 #        http://www.apache.org/licenses/LICENSE-2.0
13 #
14 #    Unless required by applicable law or agreed to in writing, software
15 #    distributed under the License is distributed on an "AS IS" BASIS,
16 #    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 #    See the License for the specific language governing permissions and
18 #    limitations under the License.
19 #
20
21 #
22 #    @file
23 #       Implements ChipInet class that tests Inet Layer multicast
24 #       among multiple nodes.
25 #
26
27 import os
28 import sys
29 import time
30
31 from happy.ReturnMsg import ReturnMsg
32 from happy.Utils import *
33 from happy.utils.IP import IP
34 from happy.HappyNode import HappyNode
35 from happy.HappyNetwork import HappyNetwork
36
37 from ChipTest import ChipTest
38
39
40 options = {}
41 options["configuration"] = None
42 options["interface"] = None
43 options["ipversion"] = None
44 options["transport"] = None
45 options["interval"] = None
46 options["quiet"] = False
47 options["tap"] = None
48
49
50 def option():
51     return options.copy()
52
53
54 class ChipInetMulticast(HappyNode, HappyNetwork, ChipTest):
55     """
56     Note:
57         Supports two networks: IPv4 and IPv6
58         Supports two transports: raw IP and UDP
59     return:
60         0   success
61         1   failure
62     """
63
64     def __init__(self, opts=options):
65         HappyNode.__init__(self)
66         HappyNetwork.__init__(self)
67         ChipTest.__init__(self)
68
69         # Dictionary that will eventually contain 'node' and 'tag' for
70         # the sender node process.
71
72         self.sender = {}
73
74         # Array that will eventually contain 'node' and 'tag'
75         # dictionaries for the receiver node processes.
76
77         self.receivers = []
78
79         self.configuration = opts["configuration"]
80         self.interface = opts["interface"]
81         self.ipversion = opts["ipversion"]
82         self.transport = opts["transport"]
83         self.interval = opts["interval"]
84         self.quiet = opts["quiet"]
85         self.tap = opts["tap"]
86
87     def __log_error_and_exit(self, error):
88         self.logger.error("[localhost] ChipInetMulticast: %s" % (error))
89         sys.exit(1)
90
91     def __checkNodeExists(self, node, description):
92         if not self._nodeExists(node):
93             emsg = "The %s '%s' does not exist in the test topology." % (
94                 description, node)
95             self.__log_error_and_exit(emsg)
96
97     def __pre_check(self):
98         """Sanity check the instantiated options and configuration"""
99         # Sanity check the transport
100         if self.transport != "udp" and self.transport != "raw":
101             emsg = "Transport type must be one of 'raw' or 'udp'."
102             self.__log_error_and_exit(emsg)
103
104         # Sanity check the IP version
105         if self.ipversion != "4" and self.ipversion != "6":
106             emsg = "The IP version must be one of '4' or '6'."
107             self.__log_error_and_exit(emsg)
108
109         # Sanity check the configuration
110         if self.configuration == None:
111             emsg = "The test configuration is missing."
112             self.__log_error_and_exit(emsg)
113
114         # There must be exactly one sender
115         if self.configuration['sender'] == None or len(self.configuration['sender']) != 1:
116             emsg = "The test configuration must have exactly one sender, 'sender'."
117             self.__log_error_and_exit(emsg)
118
119         # There must be at least one receiver
120         if self.configuration["receivers"] == None or len(self.configuration["receivers"]) < 1:
121             emsg = "The test configuration must at least one receiver in 'receivers'."
122             self.__log_error_and_exit(emsg)
123
124         # Each specified sender and receiver node must exist in the
125         # loaded Happy configuration.
126
127         for node in self.configuration['sender'].keys():
128             self.__checkNodeExists(node, "sender")
129
130         for node in self.configuration['receivers'].keys():
131             self.__checkNodeExists(node, "receiver")
132
133         if not self.interval == None and not self.interval.isdigit():
134             emsg = "Please specify a valid send interval in milliseconds."
135             self.__log_error_and_exit(emsg)
136
137     def __gather_process_results(self, process, quiet):
138         """Gather and return the exit status and output as a tuple for the
139         specified process node and tag.
140         """
141         node = process['node']
142         tag = process['tag']
143
144         status, output = self.get_test_output(node, tag, quiet)
145
146         return (status, output)
147
148     def __gather_sender_results(self, quiet):
149         """Gather and return the exit status and output as a tuple for the
150         sender process.
151         """
152         status, output = self.__gather_process_results(self.sender, quiet)
153
154         return (status, output)
155
156     def __gather_receiver_results(self, receiver, quiet):
157         """Gather and return the exit status and output as a tuple for the
158         specified receiver process.
159         """
160         status, output = self.__gather_process_results(receiver, quiet)
161
162         return (status, output)
163
164     def __gather_receivers_results(self, quiet):
165         """Gather and return the exit status and output as a tuple for all
166         receiver processes.
167         """
168         receiver_results = {}
169         receivers_results = []
170
171         for receiver in self.receivers:
172             receiver_results['status'], receiver_results['output'] = \
173                 self.__gather_receiver_results(receiver, quiet)
174             receivers_results.append(receiver_results)
175
176         return (receivers_results)
177
178     def __gather_results(self):
179         """Gather and return the exit status and output as a dictionary for all
180         sender and receiver processes.
181         """
182         quiet = True
183         results = {}
184         sender_results = {}
185         receivers_results = []
186
187         sender_results['status'], sender_results['output'] = \
188             self.__gather_sender_results(quiet)
189
190         receivers_results = self.__gather_receivers_results(quiet)
191
192         results['sender'] = sender_results
193         results['receivers'] = receivers_results
194
195         return (results)
196
197     def __process_results(self, results):
198         status = True
199         output = {}
200         output['sender'] = ""
201         output['receivers'] = []
202
203         # Iterate through the sender and receivers return status. If
204         # all had successful (0) status, then the cumulative return
205         # status is successful (True). If any had unsuccessful status,
206         # then the cumulative return status is unsuccessful (False).
207         #
208         # For the output, simply key it by sender and receivers.
209
210         # Sender
211
212         status = (results['sender']['status'] == 0)
213
214         output['sender'] = results['sender']['output']
215
216         # Receivers
217
218         for receiver_results in results['receivers']:
219             status = (status and (receiver_results['status'] == 0))
220             output['receivers'].append(receiver_results['output'])
221
222         return (status, output)
223
224     def __start_node(self, node, attributes, extra, tag):
225         """Start the test process on the specified node with the provided
226         tag using the provided attributes and extra command line
227         options.
228         """
229         cmd = self.getChipInetLayerMulticastPath()
230
231         # Generate and accumulate the multicast group-related command
232         # line options.
233
234         for group in attributes['groups']:
235             cmd += " --group %u" % (group['id'])
236             cmd += " --group-expected-tx-packets %u" % (group['send'])
237             cmd += " --group-expected-rx-packets %u" % (group['receive'])
238
239         # Generate and accumulate the transport and IP version command
240         # line arguments.
241
242         cmd += " --ipv" + self.ipversion
243
244         cmd += " --" + self.transport
245
246         # If present, generate and accumulate the LwIP hosted OS
247         # network tap device interface.
248
249         if self.tap:
250             cmd += " --tap-device " + self.tap
251
252             # If present, generate and accumulate the LwIP hosted OS
253             # network local IPv4 address.
254
255             if self.ipversion == "4" and attributes.has_key('tap-ipv4-local-addr'):
256                 cmd += " --local-addr " + attributes['tap-ipv4-local-addr']
257
258         # Generate and accumulate, if present, the bound network
259         # interface command line option.
260
261         if not self.interface == None:
262             cmd += " --interface " + self.interface
263
264         # Accumulate any extra command line options specified.
265
266         cmd += extra
267
268         self.logger.debug(
269             "[localhost] ChipInetMulticast: Will start process on node '%s' with tag '%s' and command '%s'" % (node, tag, cmd))
270
271         self.start_chip_process(
272             node, cmd, tag, sync_on_output=self.ready_to_service_events_str)
273
274     def __start_receiver(self, node, attributes, identifier):
275         """Start a receiver test process on the specified node with the
276         provided attributes and tag identifier.
277         """
278         receiver = {}
279         tag = "INET-MCAST-RX-%u" % (identifier)
280
281         extra_cmd = " --listen"
282
283         self.__start_node(node, attributes, extra_cmd, tag)
284
285         receiver['node'] = node
286         receiver['tag'] = tag
287
288         self.receivers.append(receiver)
289
290     def __start_receivers(self):
291         """Start all receiver test processes."""
292         receiver = {}
293         identifier = 0
294         for receiver in self.configuration['receivers']:
295             self.__start_receiver(
296                 receiver, self.configuration['receivers'][receiver], identifier)
297             identifier += 1
298
299     def __start_sender(self):
300         """Start the sender test process."""
301         node = list(self.configuration['sender'])[0]
302         attributes = self.configuration['sender'][node]
303         tag = "INET-MCAST-TX-0"
304
305         extra_cmd = ""
306
307         if not self.interval == None:
308             extra_cmd += " --interval " + str(self.interval)
309
310         extra_cmd += " --no-loopback"
311
312         self.__start_node(node, attributes, extra_cmd, tag)
313
314         self.sender['node'] = node
315         self.sender['tag'] = tag
316
317     def __wait_for_sender(self):
318         """Block and wait for the sender test process."""
319         node = self.sender['node']
320         tag = self.sender['tag']
321
322         self.logger.debug(
323             "[localhost] ChipInetMulticast: Will wait for sender on node %s with tag %s..." % (node, tag))
324
325         self.wait_for_test_to_end(node, tag)
326
327     def __stop_receiver(self, receiver):
328         node = receiver['node']
329         tag = receiver['tag']
330
331         self.logger.debug(
332             "[localhost] ChipInetMulticast: Will stop receiver on node %s with tag %s..." % (node, tag))
333
334         self.stop_chip_process(node, tag)
335
336     def __stop_receivers(self):
337         """Stop all receiver test processes."""
338         for receiver in self.receivers:
339             self.__stop_receiver(receiver)
340
341     def run(self):
342         results = {}
343
344         self.logger.debug("[localhost] ChipInetMulticast: Run.")
345
346         self.__pre_check()
347
348         self.__start_receivers()
349
350         self.logger.debug("[%s] ChipInetMulticast: %u receivers should be running." % (
351             "localhost", len(self.receivers)))
352
353         for receiver in self.receivers:
354             emsg = "receiver %s should be running." % (receiver['tag'])
355             self.logger.debug("[%s] ChipInetMulticast: %s" %
356                               (receiver['node'], emsg))
357
358         self.__start_sender()
359
360         self.__wait_for_sender()
361
362         self.__stop_receivers()
363
364         # Gather results from the sender and receivers
365
366         results = self.__gather_results()
367
368         # Process the results from the sender and receivers into a
369         # singular status value and a results dictionary containing
370         # process output.
371
372         status, results = self.__process_results(results)
373
374         self.logger.debug("[localhost] ChipInetMulticast: Done.")
375
376         return ReturnMsg(status, results)