reduce loop counters to more practical levels
[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
6 #    it under the terms of the GNU General Public License as published
7 #    by the Free Software Foundation; either version 2, or (at your
8 #    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 #    General Public License for more details.
14 #
15 #    You should have received a copy of the GNU General Public License
16 #    along with GNUnet; see the file COPYING.  If not, write to the
17 #    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 #    Boston, MA 02110-1301, USA.
19 #
20 # Functions for integration testing
21 import os
22 import subprocess
23 import sys
24 import shutil
25 import time
26 from gnunet_pyexpect import pexpect
27
28
29 class Check:
30     def __init__(self, test):
31         self.fulfilled = False
32         self.conditions = list()
33         self.test = test
34
35     def add(self, condition):
36         self.conditions.append(condition)
37
38     def run(self):
39         fulfilled = True
40         pos = 0
41         neg = 0
42         for c in self.conditions:
43             if (False == c.check()):
44                 fulfilled = False
45                 neg += 1
46             else:
47                 pos += 1
48         return fulfilled
49
50     def run_blocking(self, timeout, pos_cont, neg_cont):
51         execs = 0
52         res = False
53         while ((False == res) and (execs < timeout)):
54             res = self.run()
55             time.sleep(1)
56             execs += 1
57         if ((False == res) and (execs >= timeout)):
58             print(('Check had timeout after ' + str(timeout) + ' seconds'))
59             neg_cont(self)
60         elif ((False == res) and (execs < timeout)):
61             if (None != neg_cont):
62                 neg_cont(self)
63         else:
64             if (None != pos_cont):
65                 pos_cont(self)
66         return res
67
68     def run_once(self, pos_cont, neg_cont):
69         execs = 0
70         res = False
71         res = self.run()
72         if ((res == False) and (neg_cont != None)):
73             neg_cont(self)
74         if ((res == True) and (pos_cont != None)):
75             pos_cont(self)
76         return res
77
78     def evaluate(self, failed_only):
79         pos = 0
80         neg = 0
81         for c in self.conditions:
82             if (False == c.evaluate(failed_only)):
83                 neg += 1
84             else:
85                 pos += 1
86         print((str(pos) + ' out of ' + str(pos+neg) + ' conditions fulfilled'))
87         return self.fulfilled
88
89     def reset(self):
90         self.fulfilled = False
91         for c in self.conditions:
92             c.fulfilled = False
93
94
95 class Condition:
96     def __init__(self):
97         self.fulfilled = False
98         self.type = 'generic'
99
100     def __init__(self, type):
101         self.fulfilled = False
102         self.type = type
103
104     def check(self):
105         return False
106
107     def evaluate(self, failed_only):
108         if ((self.fulfilled == False) and (failed_only == True)):
109             print(str(self.type) + 'condition for was ' + str(self.fulfilled))
110         elif (failed_only == False):
111             print(str(self.type) + 'condition for was ' + str(self.fulfilled))
112         return self.fulfilled
113
114
115 class FileExistCondition(Condition):
116     def __init__(self, file):
117         self.fulfilled = False
118         self.type = 'file'
119         self.file = file
120
121     def check(self):
122         if (self.fulfilled == False):
123             res = os.path.isfile(self.file)
124             if (res == True):
125                 self.fulfilled = True
126                 return True
127             else:
128                 return False
129         else:
130             return True
131
132     def evaluate(self, failed_only):
133         if ((self.fulfilled == False) and (failed_only == True)):
134             print(str(self.type) + 'condition for file '+self.file+' was ' + str(self.fulfilled))
135         elif (failed_only == False):
136             print(str(self.type) + 'condition for file '+self.file+' was ' + str(self.fulfilled))
137         return self.fulfilled
138
139
140 class StatisticsCondition (Condition):
141     def __init__(self, peer, subsystem, name, value):
142         self.fulfilled = False
143         self.type = 'statistics'
144         self.peer = peer
145         self.subsystem = subsystem
146         self.name = name
147         self.value = value
148         self.result = -1
149
150     def check(self):
151         if (self.fulfilled == False):
152             self.result = self.peer.get_statistics_value(self.subsystem, self.name)
153             if (str(self.result) == str(self.value)):
154                 self.fulfilled = True
155                 return True
156             else:
157                 return False
158         else:
159             return True
160
161     def evaluate(self, failed_only):
162         if (self.result == -1):
163             res = 'NaN'
164         else:
165             res = str(self.result)
166         if (self.fulfilled == False):
167             fail = " FAIL!"
168             op = " != "
169         else:
170             fail = ""
171             op = " == "
172         if (((self.fulfilled == False) and (failed_only == True)) or (failed_only == False)):
173             print(self.peer.id[:4] + " " + self.peer.cfg + " " + str(self.type) + ' condition in subsystem "' + self.subsystem.ljust(12) + '" : "' + self.name.ljust(30) + '" : (expected/real value) ' + str(self.value) + op + res + fail)
174         return self.fulfilled
175
176
177 # Specify two statistic values and check if they are equal
178 class EqualStatisticsCondition (Condition):
179     def __init__(self, peer, subsystem, name, peer2, subsystem2, name2):
180         self.fulfilled = False
181         self.type = 'equalstatistics'
182         self.peer = peer
183         self.subsystem = subsystem
184         self.name = name
185         self.result = -1
186         self.peer2 = peer2
187         self.subsystem2 = subsystem2
188         self.name2 = name2
189         self.result2 = -1
190
191     def check(self):
192         if (self.fulfilled == False):
193             self.result = self.peer.get_statistics_value(self.subsystem, self.name)
194             self.result2 = self.peer2.get_statistics_value(self.subsystem2, self.name2)
195             if (str(self.result) == str(self.result2)):
196                 self.fulfilled = True
197                 return True
198             else:
199                 return False
200         else:
201             return True
202
203     def evaluate(self, failed_only):
204         if (self.result == -1):
205             res = 'NaN'
206         else:
207             res = str(self.result)
208         if (self.result2 == -1):
209             res2 = 'NaN'
210         else:
211             res2 = str(self.result2)
212         if (self.fulfilled == False):
213             fail = " FAIL!"
214             op = " != "
215         else:
216             fail = ""
217             op = " == "
218         if (((self.fulfilled == False) and (failed_only == True)) or (failed_only == False)):
219             print(self.peer.id[:4] + ' "' + self.subsystem.ljust(12) + '" "' + self.name.ljust(30) + '" == ' + str(self.result) + " " + self.peer2.id[:4] + ' "' + self.subsystem2.ljust(12) + '" ' + self.name2.ljust(30) + '" ' + str(self.result2))
220         return self.fulfilled
221
222
223 class Test:
224     def __init__(self, testname, verbose):
225         self.peers = list()
226         self.verbose = verbose
227         self.name = testname
228         srcdir = "../.."
229         gnunet_pyexpect_dir = os.path.join(srcdir, "contrib/scripts")
230         if gnunet_pyexpect_dir not in sys.path:
231             sys.path.append(gnunet_pyexpect_dir)
232         self.gnunetarm = ''
233         self.gnunetstatistics = ''
234         if os.name == 'posix':
235             self.gnunetarm = 'gnunet-arm'
236             self.gnunetstatistics = 'gnunet-statistics'
237             self.gnunetpeerinfo = 'gnunet-peerinfo'
238         elif os.name == 'nt':
239             self.gnunetarm = 'gnunet-arm.exe'
240             self.gnunetstatistics = 'gnunet-statistics.exe'
241             self.gnunetpeerinfo = 'gnunet-peerinfo.exe'
242         if os.name == "nt":
243             shutil.rmtree(os.path.join(os.getenv("TEMP"), testname), True)
244         else:
245             shutil.rmtree("/tmp/" + testname, True)
246
247     def add_peer(self, peer):
248         self.peers.append(peer)
249
250     def p(self, msg):
251         if (self.verbose == True):
252             print(msg)
253
254
255 class Peer:
256     def __init__(self, test, cfg_file):
257         if (False == os.path.isfile(cfg_file)):
258             print(("Peer cfg " + cfg_file + ": FILE NOT FOUND"))
259         self.id = "<NaN>"
260         self.test = test
261         self.started = False
262         self.cfg = cfg_file
263
264     def __del__(self):
265         if (self.started == True):
266             print('ERROR! Peer using cfg ' + self.cfg + ' was not stopped')
267             ret = self.stop()
268             if (False == ret):
269                 print('ERROR! Peer using cfg ' + self.cfg + ' could not be stopped')
270                 self.started = False
271             return ret
272         else:
273             return False
274
275     def start(self):
276         self.test.p("Starting peer using cfg " + self.cfg)
277         try:
278             server = subprocess.Popen([self.test.gnunetarm, '-sq', '-c', self.cfg])
279             server.communicate()
280         except OSError:
281             print("Can not start peer")
282             self.started = False
283             return False
284         self.started = True
285         test = ''
286         try:
287             server = pexpect()
288             server.spawn(None, [self.test.gnunetpeerinfo, '-c', self.cfg, '-s'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
289             test = server.read("stdout", 1024)
290         except OSError:
291             print("Can not get peer identity")
292         test = (test.split('`')[1])
293         self.id = test.split('\'')[0]
294         return True
295
296     def stop(self):
297         if (self.started == False):
298             return False
299         self.test.p("Stopping peer using cfg " + self.cfg)
300         try:
301             server = subprocess.Popen([self.test.gnunetarm, '-eq', '-c', self.cfg])
302             server.communicate()
303         except OSError:
304             print("Can not stop peer")
305             return False
306         self.started = False
307         return True
308
309     def get_statistics_value(self, subsystem, name):
310         server = pexpect()
311         server.spawn(None, [self.test.gnunetstatistics, '-c', self.cfg, '-q', '-n', name, '-s', subsystem], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
312         # server.expect ("stdout", re.compile (r""))
313         test = server.read("stdout", 10240)
314         tests = test.partition('\n')
315         # On W32 GNUnet outputs with \r\n, rather than \n
316         if os.name == 'nt' and tests[1] == '\n' and tests[0][-1] == '\r':
317             tests = (tests[0][:-1], tests[1], tests[2])
318         tests = tests[0]
319         if (tests.isdigit() == True):
320             return tests
321         else:
322             return -1