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