Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / tests / cdash / builder.py
1 #
2 # builder.py - PJSIP test scenarios builder
3 #
4 # Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 #
20
21 import ccdash
22 import os
23 import platform
24 import re
25 import subprocess
26 import sys
27 import time
28
29 class Operation:
30     """\
31     The Operation class describes the individual ccdash operation to be 
32     performed.
33
34     """
35     # Types:
36     UPDATE = "update"           # Update operation
37     CONFIGURE = "configure"     # Configure operation
38     BUILD = "build"             # Build operation
39     TEST = "test"               # Unit test operation
40
41     def __init__(self, type, cmdline, name="", wdir=""):
42         self.type = type
43         self.cmdline = cmdline
44         self.name = name
45         self.wdir = wdir
46         if self.type==self.TEST and not self.name:
47             raise "name required for tests"
48
49     def encode(self, base_dir):
50         s = [self.type]
51         if self.type == self.TEST:
52             s.append(self.name)
53         if self.type != self.UPDATE:
54             s.append(self.cmdline)
55         s.append("-w")
56         if self.wdir:
57             s.append(base_dir + "/" + self.wdir)
58         else:
59             s.append(base_dir)
60         return s
61
62
63 #
64 # Update operation
65 #
66 update_ops = [Operation(Operation.UPDATE, "")]
67
68 #
69 # The standard library tests (e.g. pjlib-test, pjsip-test, etc.)
70 #
71 std_test_ops= [
72     Operation(Operation.TEST, "./pjlib-test$SUFFIX", name="pjlib test",
73               wdir="pjlib/bin"),
74     Operation(Operation.TEST, "./pjlib-util-test$SUFFIX", 
75               name="pjlib-util test", wdir="pjlib-util/bin"),
76     Operation(Operation.TEST, "./pjnath-test$SUFFIX", name="pjnath test",
77               wdir="pjnath/bin"),
78     Operation(Operation.TEST, "./pjmedia-test$SUFFIX", name="pjmedia test",
79               wdir="pjmedia/bin"),
80     Operation(Operation.TEST, "./pjsip-test$SUFFIX", name="pjsip test",
81               wdir="pjsip/bin")
82 ]
83
84 #
85 # These are pjsua Python based unit test operations
86 #
87 def build_pjsua_test_ops(pjsua_exe=""):
88     ops = []
89     if pjsua_exe:
90         exe = " -e ../../pjsip-apps/bin/" + pjsua_exe
91     else:
92         exe = ""
93     cwd = os.getcwd()
94     os.chdir("../pjsua")
95     os.system("python runall.py --list > list")
96     f = open("list", "r")
97     for e in f:
98         e = e.rstrip("\r\n ")
99         (mod,param) = e.split(None,2)
100         name = mod[4:mod.find(".py")] + "_" + \
101                param[param.find("/")+1:param.find(".py")]
102         ops.append(Operation(Operation.TEST, "python run.py" + exe + " " + \
103                              e, name=name, wdir="tests/pjsua"))
104     f.close()
105     os.remove("list") 
106     os.chdir(cwd)
107     return ops
108
109 #
110 # Get gcc version
111 #
112 def gcc_version(gcc):
113     proc = subprocess.Popen(gcc + " -v", stdout=subprocess.PIPE,
114                             stderr=subprocess.STDOUT, shell=True)
115     ver = ""
116     while True:
117         s = proc.stdout.readline()
118         if not s:
119             break
120         if s.find("gcc version") >= 0:
121             ver = s.split(None, 3)[2]
122             break
123     proc.wait()
124     return "gcc-" + ver
125
126 #
127 # Get Visual Studio version
128 #
129 def vs_get_version():
130     proc = subprocess.Popen("cl", stdout=subprocess.PIPE,
131                             stderr=subprocess.STDOUT)
132     while True:
133         s = proc.stdout.readline()
134         if s=="":
135             break
136         pos = s.find("Version")
137         if pos > 0:
138             proc.wait()
139             s = s[pos+8:]
140             ver = s.split(None, 1)[0]
141             major = ver[0:2]
142             if major=="12":
143                 return "vs6"
144             elif major=="13":
145                 return "vs2003"
146             elif major=="14":
147                 return "vs2005"
148             elif major=="15":
149                 return "vs2008"
150             else:
151                 return "vs-" + major
152     proc.wait()
153     return "vs-unknown"
154     
155
156 #
157 # Test config
158 #
159 class BaseConfig:
160     def __init__(self, base_dir, url, site, group, options=None):
161         self.base_dir = base_dir
162         self.url = url
163         self.site = site
164         self.group = group
165         self.options = options
166
167 #
168 # Base class for test configurator
169 #
170 class TestBuilder:
171     def __init__(self, config, build_config_name="",
172                  user_mak="", config_site="", exclude=[], not_exclude=[]):
173         self.config = config                        # BaseConfig instance
174         self.build_config_name = build_config_name  # Optional build suffix
175         self.user_mak = user_mak                    # To be put in user.mak
176         self.config_site = config_site              # To be put in config_s..
177         self.saved_user_mak = ""                    # To restore user.mak
178         self.saved_config_site = ""                 # To restore config_s..
179         self.exclude = exclude                      # List of exclude pattern
180         self.not_exclude = not_exclude              # List of include pattern
181         self.ccdash_args = []                       # ccdash cmd line
182
183     def stamp(self):
184         return time.strftime("%Y%m%d-%H%M", time.localtime())
185
186     def pre_action(self):
187         # Override user.mak
188         name = self.config.base_dir + "/user.mak"
189         if os.access(name, os.F_OK):
190             f = open(name, "r")
191             self.saved_user_mak = f.read()
192             f.close()
193         if True:
194             f = open(name, "w")
195             f.write(self.user_mak)
196             f.close()
197         # Override config_site.h
198         name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
199         if os.access(name, os.F_OK):
200             f = open(name, "r")
201             self.saved_config_site= f.read()
202             f.close()
203         if True:
204             f = open(name, "wt")
205             f.write(self.config_site)
206             f.close()
207
208
209     def post_action(self):
210         # Restore user.mak
211         name = self.config.base_dir + "/user.mak"
212         f = open(name, "wt")
213         f.write(self.saved_user_mak)
214         f.close()
215         # Restore config_site.h
216         name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
217         f = open(name, "wt")
218         f.write(self.saved_config_site)
219         f.close()
220
221     def build_tests(self):
222         # This should be overridden by subclasses
223         pass
224
225     def execute(self):
226         if len(self.ccdash_args)==0:
227             self.build_tests()
228         self.pre_action()
229         mandatory_op = ["update", "configure", "build"]
230         counter = 0
231         for a in self.ccdash_args:
232             # Check if this test is in exclusion list
233             fullcmd = " ".join(a)
234             excluded = False
235             included = False
236             for pat in self.exclude:
237                 if pat and re.search(pat, fullcmd) != None:
238                     excluded = True
239                     break
240             if excluded:
241                 for pat in self.not_exclude:
242                     if pat and re.search(pat, fullcmd) != None:
243                         included = True
244                         break
245             if excluded and not included:
246                 if len(fullcmd)>60:
247                     fullcmd = fullcmd[0:60] + ".."
248                 print "Skipping '%s'" % (fullcmd)
249                 continue
250
251             b = ["ccdash.py"]
252             b.extend(a)
253             a = b
254             #print a
255             try:
256                 rc = ccdash.main(a)
257             except Exception, e:
258                 errmsg = str(e)
259                 print "**** Error: ccdash got exception %s ****" % errmsg
260                 rc = -1
261             except:
262                 print "**** Error: ccdash got unknown exception ****"
263                 rc = -1
264                 
265             if rc!=0 and a[1] in mandatory_op:
266                 print "Stopping because of error.."
267                 break
268             counter = counter + 1
269         self.post_action()
270
271
272 #
273 # GNU test configurator
274 #
275 class GNUTestBuilder(TestBuilder):
276     """\
277     This class creates list of tests suitable for GNU targets.
278
279     """
280     def __init__(self, config, build_config_name="", user_mak="", \
281                  config_site="", cross_compile="", exclude=[], not_exclude=[]):
282         """\
283         Parameters:
284         config              - BaseConfig instance
285         build_config_name   - Optional name to be added as suffix to the build
286                               name. Sample: "min-size", "O4", "TLS", etc.
287         user_mak            - Contents to be put on user.mak
288         config_site         - Contents to be put on config_site.h
289         cross_compile       - Optional cross-compile prefix. Must include the
290                               trailing dash, e.g. "arm-unknown-linux-"
291         exclude             - List of regular expression patterns for tests
292                               that will be excluded from the run
293         not_exclude         - List of regular expression patterns for tests
294                               that will be run regardless of whether they
295                               match the excluded pattern.
296
297         """
298         TestBuilder.__init__(self, config, build_config_name=build_config_name,
299                              user_mak=user_mak, config_site=config_site,
300                              exclude=exclude, not_exclude=not_exclude)
301         self.cross_compile = cross_compile
302         if self.cross_compile and self.cross_compile[-1] != '-':
303             self.cross_compile.append("-")
304
305     def build_tests(self):
306         if self.cross_compile:
307             suffix = "-" + self.cross_compile[0:-1]
308             build_name =  self.cross_compile + \
309                           gcc_version(self.cross_compile + "gcc")
310         else:
311             proc = subprocess.Popen("sh "+self.config.base_dir+"/config.guess",
312                                     shell=True, stdout=subprocess.PIPE)
313             plat = proc.stdout.readline().rstrip(" \r\n")
314             build_name =  plat + "-"+gcc_version(self.cross_compile + "gcc")
315             suffix = "-" + plat
316
317         if self.build_config_name:
318             build_name = build_name + "-" + self.build_config_name
319         cmds = []
320         cmds.extend(update_ops)
321         cmds.append(Operation(Operation.CONFIGURE, "sh ./configure"))
322         if sys.platform=="win32":
323             # Don't build python module on Mingw
324             cmds.append(Operation(Operation.BUILD, 
325                             "sh -c 'make distclean && make dep && make'"))
326         else:
327             cmds.append(Operation(Operation.BUILD, 
328                             "sh -c 'make distclean && make dep && make" + \
329                             " && cd pjsip-apps/src/python && " + \
330                             "python setup.py clean build'"))
331
332         cmds.extend(std_test_ops)
333         cmds.extend(build_pjsua_test_ops())
334         self.ccdash_args = []
335         for c in cmds:
336             c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
337             args = c.encode(self.config.base_dir)
338             args.extend(["-U", self.config.url, 
339                          "-S", self.config.site, 
340                          "-T", self.stamp(), 
341                          "-B", build_name, 
342                          "-G", self.config.group])
343             args.extend(self.config.options)
344             self.ccdash_args.append(args)
345
346 #
347 # MSVC test configurator
348 #
349 class MSVCTestBuilder(TestBuilder):
350     """\
351     This class creates list of tests suitable for Visual Studio builds. 
352     You need to set the MSVC environment variables (typically by calling
353     vcvars32.bat) prior to running this class.
354     
355     """
356     def __init__(self, config, target="Release|Win32", build_config_name="", 
357                  config_site="", exclude=[], not_exclude=[]):
358         """\
359         Parameters:
360         config              - BaseConfig instance
361         target              - Visual Studio build configuration to build.
362                               Sample: "Debug|Win32", "Release|Win32".
363         build_config_name   - Optional name to be added as suffix to the build
364                               name. Sample: "Debug", "Release", "IPv6", etc.
365         config_site         - Contents to be put on config_site.h
366         exclude             - List of regular expression patterns for tests
367                               that will be excluded from the run
368         not_exclude         - List of regular expression patterns for tests
369                               that will be run regardless of whether they
370                               match the excluded pattern.
371
372         """
373         TestBuilder.__init__(self, config, build_config_name=build_config_name,
374                              config_site=config_site, exclude=exclude, 
375                              not_exclude=not_exclude)
376         self.target = target.lower()
377
378     def build_tests(self):
379        
380         (vsbuild,sys) = self.target.split("|",2)
381         
382         build_name = sys + "-" + vs_get_version() + "-" + vsbuild
383
384         if self.build_config_name:
385             build_name = build_name + "-" + self.build_config_name
386
387         vccmd = "vcbuild.exe /nologo /nohtmllog /nocolor /rebuild " + \
388                 "pjproject-vs8.sln " + " \"" + self.target + "\""
389         
390         suffix = "-i386-win32-vc8-" + vsbuild
391         pjsua = "pjsua_vc8"
392         if vsbuild=="debug":
393             pjsua = pjsua + "d"
394         
395         cmds = []
396         cmds.extend(update_ops)
397         cmds.append(Operation(Operation.CONFIGURE, "CMD /C echo Nothing to do"))
398         cmds.append(Operation(Operation.BUILD, vccmd))
399         cmds.extend(std_test_ops)
400         cmds.extend(build_pjsua_test_ops(pjsua))
401
402         self.ccdash_args = []
403         for c in cmds:
404             c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
405             args = c.encode(self.config.base_dir)
406             args.extend(["-U", self.config.url, 
407                          "-S", self.config.site, 
408                          "-T", self.stamp(), 
409                          "-B", build_name, 
410                          "-G", self.config.group])
411             args.extend(self.config.options)
412             self.ccdash_args.append(args)
413
414
415 #
416 # Symbian test configurator
417 #
418 class SymbianTestBuilder(TestBuilder):
419     """\
420     This class creates list of tests suitable for Symbian builds. You need to
421     set the command line build settings prior to running this class (typically
422     that involves setting the EPOCROOT variable and current device).
423     
424     """
425     def __init__(self, config, target="gcce urel", build_config_name="", 
426                  config_site="", exclude=[], not_exclude=[]):
427         """\
428         Parameters:
429         config              - BaseConfig instance
430         target              - Symbian target to build. Default is "gcce urel".
431         build_config_name   - Optional name to be added as suffix to the build
432                               name. Sample: "APS", "VAS", etc.
433         config_site         - Contents to be put on config_site.h
434         exclude             - List of regular expression patterns for tests
435                               that will be excluded from the run
436         not_exclude         - List of regular expression patterns for tests
437                               that will be run regardless of whether they
438                               match the excluded pattern.
439
440         """
441         TestBuilder.__init__(self, config, build_config_name=build_config_name,
442                              config_site=config_site, exclude=exclude, 
443                              not_exclude=not_exclude)
444         self.target = target.lower()
445         
446     def build_tests(self):
447        
448         # Check that EPOCROOT is set
449         if not "EPOCROOT" in os.environ:
450             print "Error: EPOCROOT environment variable is not set"
451             sys.exit(1)
452         epocroot = os.environ["EPOCROOT"]
453         # EPOCROOT must have trailing backslash
454         if epocroot[-1] != "\\":
455             epocroot = epocroot + "\\"
456             os.environ["EPOCROOT"] = epocroot
457         sdk1 = epocroot.split("\\")[-2]
458
459         # Check that correct device is set
460         proc = subprocess.Popen("devices", stdout=subprocess.PIPE,
461                                 stderr=subprocess.STDOUT, shell=True)
462         sdk2 = ""
463         while True:
464             line = proc.stdout.readline()
465             if line.find("- default") > 0:
466                 sdk2 = line.split(":",1)[0]
467                 break
468         proc.wait()
469
470         if sdk1 != sdk2:
471             print "Error: default SDK in device doesn't match EPOCROOT"
472             print "Default device SDK =", sdk2
473             print "EPOCROOT SDK =", sdk1
474             sys.exit(1)
475
476         build_name = sdk2.replace("_", "-") + "-" + \
477                      self.target.replace(" ", "-")
478
479         if self.build_config_name:
480             build_name = build_name + "-" + self.build_config_name
481
482         cmdline = "cmd /C \"cd build.symbian && bldmake bldfiles && abld build %s\"" % (self.target)
483         
484         cmds = []
485         cmds.extend(update_ops)
486         cmds.append(Operation(Operation.CONFIGURE, "CMD /C echo Nothing to do"))
487         cmds.extend([Operation(Operation.BUILD, cmdline)])
488
489         self.ccdash_args = []
490         suffix = ""
491         for c in cmds:
492             c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
493             args = c.encode(self.config.base_dir)
494             args.extend(["-U", self.config.url, 
495                          "-S", self.config.site, 
496                          "-T", self.stamp(), 
497                          "-B", build_name, 
498                          "-G", self.config.group])
499             args.extend(self.config.options)
500             self.ccdash_args.append(args)
501