attempt to fix 5560, not fixed yet. see log at https://gnunet.org/bugs/view.php?id...
[oweals/gnunet.git] / src / integration-tests / gnunet_testing.py.in
1 #!@PYTHON@
2 #    This file is part of GNUnet.
3 #    (C) 2010, 2017, 2018 Christian Grothoff (and other contributing authors)
4 #
5 #    GNUnet is free software: you can redistribute it and/or modify it
6 #    under the terms of the GNU Affero General Public License as published
7 #    by the Free Software Foundation, either version 3 of the License,
8 #    or (at your option) any later version.
9 #
10 #    GNUnet is distributed in the hope that it will be useful, but
11 #    WITHOUT ANY WARRANTY; without even the implied warranty of
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #    Affero General Public License for more details.
14 #
15 #    You should have received a copy of the GNU Affero General Public License
16 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 #    SPDX-License-Identifier: AGPL3.0-or-later
19 #
20 # Functions for integration testing
21 from __future__ import unicode_literals
22 from __future__ import print_function
23 from builtins import object
24 from builtins import str
25 import os
26 import subprocess
27 import sys
28 import shutil
29 import time
30 from gnunet_pyexpect import pexpect
31
32
33 class Check(object):
34     def __init__(self, test):
35         self.fulfilled = False
36         self.conditions = list()
37         self.test = test
38
39     def add(self, condition):
40         self.conditions.append(condition)
41
42     def run(self):
43         fulfilled = True
44         pos = 0
45         neg = 0
46         for c in self.conditions:
47             if (False == c.check()):
48                 fulfilled = False
49                 neg += 1
50             else:
51                 pos += 1
52         return fulfilled
53
54     def run_blocking(self, timeout, pos_cont, neg_cont):
55         execs = 0
56         res = False
57         while ((False == res) and (execs < timeout)):
58             res = self.run()
59             time.sleep(1)
60             execs += 1
61         if ((False == res) and (execs >= timeout)):
62             print(('Check had timeout after ' + str(timeout) + ' seconds'))
63             neg_cont(self)
64         elif ((False == res) and (execs < timeout)):
65             if (None != neg_cont):
66                 neg_cont(self)
67         else:
68             if (None != pos_cont):
69                 pos_cont(self)
70         return res
71
72     def run_once(self, pos_cont, neg_cont):
73         execs = 0
74         res = False
75         res = self.run()
76         if ((res == False) and (neg_cont != None)):
77             neg_cont(self)
78         if ((res == True) and (pos_cont != None)):
79             pos_cont(self)
80         return res
81
82     def evaluate(self, failed_only):
83         pos = 0
84         neg = 0
85         for c in self.conditions:
86             if (False == c.evaluate(failed_only)):
87                 neg += 1
88             else:
89                 pos += 1
90         print((str(pos) + ' out of ' + str(pos+neg) + ' conditions fulfilled'))
91         return self.fulfilled
92
93     def reset(self):
94         self.fulfilled = False
95         for c in self.conditions:
96             c.fulfilled = False
97
98
99 class Condition(object):
100     def __init__(self):
101         self.fulfilled = False
102         self.type = 'generic'
103
104     def __init__(self, type):
105         self.fulfilled = False
106         self.type = type
107
108     def check(self):
109         return False
110
111     def evaluate(self, failed_only):
112         if ((self.fulfilled == False) and (failed_only == True)):
113             print(str(self.type) + 'condition for was ' + str(self.fulfilled))
114         elif (failed_only == False):
115             print(str(self.type) + 'condition for was ' + str(self.fulfilled))
116         return self.fulfilled
117
118
119 class FileExistCondition(Condition):
120     def __init__(self, file):
121         self.fulfilled = False
122         self.type = 'file'
123         self.file = file
124
125     def check(self):
126         if (self.fulfilled == False):
127             res = os.path.isfile(self.file)
128             if (res == True):
129                 self.fulfilled = True
130                 return True
131             else:
132                 return False
133         else:
134             return True
135
136     def evaluate(self, failed_only):
137         if ((self.fulfilled == False) and (failed_only == True)):
138             print(str(self.type) +
139                   'condition for file ' +
140                   self.file +
141                   ' was ' +
142                   str(self.fulfilled))
143         elif (failed_only == False):
144             print(str(self.type) +
145                   'condition for file ' +
146                   self.file +
147                   ' was ' +
148                   str(self.fulfilled))
149         return self.fulfilled
150
151
152 class StatisticsCondition(Condition):
153     def __init__(self, peer, subsystem, name, value):
154         self.fulfilled = False
155         self.type = 'statistics'
156         self.peer = peer
157         self.subsystem = subsystem
158         self.name = name
159         self.value = value
160         self.result = -1
161
162     def check(self):
163         if (self.fulfilled == False):
164             self.result = self.peer.get_statistics_value(self.subsystem, self.name)
165             if (str(self.result) == str(self.value)):
166                 self.fulfilled = True
167                 return True
168             else:
169                 return False
170         else:
171             return True
172
173     def evaluate(self, failed_only):
174         if (self.result == -1):
175             res = b'NaN'
176         else:
177             res = str(self.result).encode('utf-8')
178         if (self.fulfilled == False):
179             fail = b" FAIL!"
180             op = b" != "
181         else:
182             fail = b""
183             op = b" == "
184         if (((self.fulfilled == False) and (failed_only == True)) or (failed_only == False)):
185             print(self.peer.id[:4] +
186                   b" " +
187                   self.peer.cfg.encode('utf-8') +
188                   b" " +
189                   str(self.type).encode('utf-8') +
190                   b' condition in subsystem "' +
191                   self.subsystem.encode('utf-8').ljust(12) +
192                   b'" : "' +
193                   self.name.encode('utf-8').ljust(30) +
194                   b'" : (expected/real value) ' +
195                   str(self.value).encode('utf-8') +
196                   op +
197                   res +
198                   fail)
199         return self.fulfilled
200
201
202 # Specify two statistic values and check if they are equal
203 class EqualStatisticsCondition(Condition):
204     def __init__(self, peer, subsystem, name, peer2, subsystem2, name2):
205         self.fulfilled = False
206         self.type = 'equalstatistics'
207         self.peer = peer
208         self.subsystem = subsystem
209         self.name = name
210         self.result = -1
211         self.peer2 = peer2
212         self.subsystem2 = subsystem2
213         self.name2 = name2
214         self.result2 = -1
215
216     def check(self):
217         if (self.fulfilled == False):
218             self.result = self.peer.get_statistics_value(self.subsystem, self.name)
219             self.result2 = self.peer2.get_statistics_value(self.subsystem2, self.name2)
220             if (str(self.result) == str(self.result2)):
221                 self.fulfilled = True
222                 return True
223             else:
224                 return False
225         else:
226             return True
227
228     def evaluate(self, failed_only):
229         if (self.result == -1):
230             res = b'NaN'
231         else:
232             res = str(self.result).encode('utf-8')
233         if (self.result2 == -1):
234             res2 = b'NaN'
235         else:
236             res2 = str(self.result2).encode('utf-8')
237         if (self.fulfilled == False):
238             fail = b" FAIL!"
239             op = b" != "
240         else:
241             fail = b""
242             op = b" == "
243         if (((self.fulfilled == False) and (failed_only == True)) or (failed_only == False)):
244             print(self.peer.id[:4] +
245                   b' "' +
246                   self.subsystem.encode('utf-8').ljust(12) +
247                   b'" "' +
248                   self.name.encode('utf-8').ljust(30) +
249                   b'" == ' +
250                   str(self.result).encode('utf-8') +
251                   b" " +
252                   self.peer2.id[:4] +
253                   b' "' +
254                   self.subsystem2.encode('utf-8').ljust(12) +
255                   b'" ' +
256                   self.name2.encode('utf-8').ljust(30) +
257                   b'" ' +
258                   str(self.result2).encode('utf-8'))
259         return self.fulfilled
260
261
262 class Test(object):
263     def __init__(self, testname, verbose):
264         self.peers = list()
265         self.verbose = verbose
266         self.name = testname
267         srcdir = "../.."
268         gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
269         if gnunet_pyexpect_dir not in sys.path:
270             sys.path.append(gnunet_pyexpect_dir)
271         self.gnunetarm = ''
272         self.gnunetstatistics = ''
273         if os.name == 'posix':
274             self.gnunetarm = 'gnunet-arm'
275             self.gnunetstatistics = 'gnunet-statistics'
276             self.gnunetpeerinfo = 'gnunet-peerinfo'
277         elif os.name == 'nt':
278             self.gnunetarm = 'gnunet-arm.exe'
279             self.gnunetstatistics = 'gnunet-statistics.exe'
280             self.gnunetpeerinfo = 'gnunet-peerinfo.exe'
281         if os.name == "nt":
282             shutil.rmtree(os.path.join(os.getenv("TEMP"), testname), True)
283         else:
284             shutil.rmtree("/tmp/" + testname, True)
285
286     def add_peer(self, peer):
287         self.peers.append(peer)
288
289     def p(self, msg):
290         if (self.verbose == True):
291             print(msg)
292
293
294 class Peer(object):
295     def __init__(self, test, cfg_file):
296         if (False == os.path.isfile(cfg_file)):
297             print(("Peer cfg " + cfg_file + ": FILE NOT FOUND"))
298         self.id = "<NaN>"
299         self.test = test
300         self.started = False
301         self.cfg = cfg_file
302
303     def __del__(self):
304         if (self.started == True):
305             print('ERROR! Peer using cfg ' + self.cfg + ' was not stopped')
306             ret = self.stop()
307             if (False == ret):
308                 print('ERROR! Peer using cfg ' +
309                       self.cfg +
310                       ' could not be stopped')
311                 self.started = False
312             return ret
313         else:
314             return False
315
316     def start(self):
317         self.test.p("Starting peer using cfg " + self.cfg)
318         try:
319             server = subprocess.Popen([self.test.gnunetarm, '-sq', '-c', self.cfg])
320             server.communicate()
321         except OSError:
322             print("Can not start peer")
323             self.started = False
324             return False
325         self.started = True
326         test = ''
327         try:
328             server = pexpect()
329             server.spawn(None, [self.test.gnunetpeerinfo, '-c', self.cfg, '-s'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
330             test = server.read("stdout", 1024)
331         except OSError:
332             print("Can not get peer identity")
333         test = (test.split(b'`')[1])
334         self.id = test.split(b'\'')[0]
335         return True
336
337     def stop(self):
338         if (self.started == False):
339             return False
340         self.test.p("Stopping peer using cfg " + self.cfg)
341         try:
342             server = subprocess.Popen([self.test.gnunetarm, '-eq', '-c', self.cfg])
343             server.communicate()
344         except OSError:
345             print("Can not stop peer")
346             return False
347         self.started = False
348         return True
349
350     def get_statistics_value(self, subsystem, name):
351         server = pexpect()
352         server.spawn(None, [self.test.gnunetstatistics, '-c', self.cfg, '-q', '-n', name, '-s', subsystem], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
353         # server.expect ("stdout", re.compile (r""))
354         test = server.read("stdout", 10240)
355         tests = test.partition(b'\n')
356         # On W32 GNUnet outputs with \r\n, rather than \n
357         if os.name == 'nt' and tests[1] == b'\n' and tests[0][-1] == b'\r':
358             tests = (tests[0][:-1], tests[1], tests[2])
359         tests = tests[0]
360         if (tests.isdigit() == True):
361             return tests
362         else:
363             return -1