PageRenderTime 39ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/qa/run-tests.py

https://bitbucket.org/mdavid/cherokee-webserver-svnclone
Python | 489 lines | 441 code | 24 blank | 24 comment | 47 complexity | 6b884cd9c73931c81b00afe96b897e53 MD5 | raw file
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # Cherokee QA Tests
  4. #
  5. # Authors:
  6. # Alvaro Lopez Ortega <alvaro@alobbs.com>
  7. #
  8. # Copyright (C) 2001-2010 Alvaro Lopez Ortega
  9. # This file is distributed under the GPL2 license.
  10. import os
  11. import sys
  12. import time
  13. import copy
  14. import signal
  15. import shutil
  16. import thread
  17. import string
  18. import random
  19. import tempfile
  20. from base import *
  21. from help import *
  22. try:
  23. # Try to load a local configuration
  24. from conf_local import *
  25. except ImportError:
  26. # Use default settings
  27. from conf import *
  28. # Deals with UTF-8
  29. if sys.getdefaultencoding() != 'utf-8':
  30. reload (sys)
  31. sys.setdefaultencoding('utf-8')
  32. # Configuration parameters
  33. num = 1
  34. thds = 1
  35. pause = 0
  36. tpause = 0.0
  37. bpause = None
  38. srv_thds = None
  39. ssl = False
  40. clean = True
  41. kill = True
  42. quiet = False
  43. valgrind = None
  44. strace = False
  45. port = None
  46. method = None
  47. nobody = False
  48. log = False
  49. help = False
  50. memproc = False
  51. randomize = False
  52. proxy = None
  53. server = CHEROKEE_PATH
  54. delay = SERVER_DELAY
  55. # Make the DocumentRoot directory
  56. www = tempfile.mkdtemp ("cherokee_www")
  57. tmp = www + "/_tmp/"
  58. pid = tmp + "cherokee.pid"
  59. os.makedirs (tmp)
  60. map (lambda x: os.chmod (x, 0777), [www, tmp])
  61. # Make the files list
  62. files = []
  63. param = []
  64. skipped = []
  65. if len(sys.argv) > 1:
  66. argv = sys.argv[1:]
  67. files = filter (lambda x: x[0] != '-', argv)
  68. param = filter (lambda x: x[0] == '-', argv)
  69. # If not files were specified, use all of them
  70. if len(files) == 0:
  71. files = os.listdir('.')
  72. files = filter (lambda x: x[-3:] == '.py', files)
  73. files = filter (lambda x: x[3] == '-', files)
  74. files = filter (lambda x: x[2] in string.digits, files)
  75. files.sort()
  76. # Process the parameters
  77. for p in param:
  78. if p == '-c': clean = False
  79. elif p == '-k': kill = False
  80. elif p == '-q': quiet = True
  81. elif p == '-s': ssl = True
  82. elif p == '-x': strace = True
  83. elif p == '-b': nobody = True
  84. elif p == '-l': log = True
  85. elif p == '-h': help = True
  86. elif p == '-o': memproc = True
  87. elif p == '-a': randomize = True
  88. elif p[:2] == '-n': num = int(p[2:])
  89. elif p[:2] == '-t': thds = int(p[2:])
  90. elif p[:2] == '-p': port = int(p[2:])
  91. elif p[:2] == '-r': delay = int(p[2:])
  92. elif p[:2] == '-T': srv_thds = int(p[2:])
  93. elif p[:2] == '-j': tpause = float(p[2:])
  94. elif p[:2] == '-d': pause = p[2:]
  95. elif p[:2] == '-D': bpause = p[2:]
  96. elif p[:2] == '-m': method = p[2:]
  97. elif p[:2] == '-e': server = p[2:]
  98. elif p[:2] == '-v': valgrind = p[2:]
  99. elif p[:2] == '-P': proxy = p[2:]
  100. else:
  101. help = True
  102. # Print help
  103. if help:
  104. help_print_parameters()
  105. sys.exit(1)
  106. # Fix up pause
  107. if type(pause) == types.StringType:
  108. if len(pause) > 0:
  109. pause = int(pause)
  110. else:
  111. pause = sys.maxint
  112. # Check threads and pauses
  113. if thds > 1 and pause > 1:
  114. print "ERROR: -d and -t are incompatible with each other."
  115. sys.exit(1)
  116. # Check the interpreters
  117. php_interpreter = look_for_php()
  118. python_interpreter = look_for_python()
  119. print "Interpreters"
  120. if php_interpreter:
  121. print_key('PHP', php_interpreter)
  122. else:
  123. print_key('PHP', "Couldn't find a suitable PHP interpreter (with fastcgi support)")
  124. if python_interpreter:
  125. print_key('Python', python_interpreter)
  126. else:
  127. print_key('Python', "ERROR: Python interpreter not found")
  128. print
  129. # Might need to fake PHP
  130. fake_php = len(php_interpreter) == 0
  131. if fake_php > 0:
  132. php_interpreter = "false"
  133. # Set the panic script
  134. panic = CHEROKEE_PANIC
  135. if panic[0] != '/':
  136. panic = os.path.normpath (os.path.join (os.getcwd(), CHEROKEE_PANIC))
  137. # Proxy
  138. if not proxy:
  139. if ssl:
  140. client_port = PORT_TLS
  141. else:
  142. client_port = PORT
  143. client_host = HOST
  144. listen = '127.0.0.1'
  145. proxy_cfg_file = None
  146. else:
  147. pieces = proxy.split(':')
  148. if len(pieces) == 2:
  149. client_host, client_port = pieces
  150. public_ip = figure_public_ip()
  151. else:
  152. client_host, client_port, public_ip = pieces
  153. listen = ''
  154. PROXY_CONF = """
  155. server!chunked_encoding = 1
  156. server!keepalive_max_requests = 500
  157. server!log_flush_lapse = 0
  158. server!panic_action = /usr/bin/cherokee-panic
  159. server!bind!1!port = %(client_port)s
  160. vserver!10!nick = default
  161. vserver!10!document_root = /dev/null
  162. vserver!10!logger = combined
  163. vserver!10!logger!access!type = stderr
  164. vserver!10!logger!error!type = stderr
  165. vserver!10!rule!100!match = default
  166. vserver!10!rule!100!match!final = 1
  167. vserver!10!rule!100!handler = proxy
  168. vserver!10!rule!100!handler!balancer = round_robin
  169. vserver!10!rule!100!handler!balancer!source!1 = 1
  170. vserver!10!rule!100!handler!in_preserve_host = 1
  171. source!1!host = %(public_ip)s:%(PORT)s
  172. source!1!nick = QA
  173. source!1!type = host
  174. """ % (locals())
  175. proxy_cfg_file = tempfile.mktemp("cherokee_proxy_cfg")
  176. f = open (proxy_cfg_file, 'w')
  177. f.write (PROXY_CONF)
  178. f.close()
  179. # Configuration file base
  180. next_source = get_next_source()
  181. CONF_BASE = """
  182. #
  183. # Cherokee QA tests
  184. #
  185. server!bind!1!port = %(PORT)d
  186. server!bind!1!interface = %(listen)s
  187. server!bind!2!port = %(PORT_TLS)d
  188. server!bind!2!tls = 1
  189. server!bind!2!interface = %(listen)s
  190. server!keepalive = 1
  191. server!panic_action = %(panic)s
  192. server!pid_file = %(pid)s
  193. server!module_dir = %(CHEROKEE_MODS)s
  194. server!module_deps = %(CHEROKEE_DEPS)s
  195. server!fdlimit = 8192
  196. vserver!1!nick = default
  197. vserver!1!document_root = %(www)s
  198. vserver!1!directory_index = test_index.html,test_index.php,/super_test_index.php
  199. vserver!1!rule!1!match = default
  200. vserver!1!rule!1!handler = common
  201. source!%(next_source)d!type = interpreter
  202. source!%(next_source)d!host = localhost:%(PHP_FCGI_PORT)d
  203. source!%(next_source)d!env!PHP_FCGI_CHILDREN = 5
  204. source!%(next_source)d!interpreter = %(php_interpreter)s -b %(PHP_FCGI_PORT)d
  205. """ % (locals())
  206. php_ext = """\
  207. 10000!match = extensions
  208. 10000!match!extensions = php
  209. 10000!match!final = 0
  210. 10000!handler = fcgi
  211. 10000!handler!balancer = round_robin
  212. 10000!handler!balancer!source!1 = %(next_source)d\
  213. """ % (locals())
  214. for php in php_ext.split("\n"):
  215. CONF_BASE += "vserver!1!rule!%s\n" % (php)
  216. if method:
  217. CONF_BASE += "server!poll_method = %s" % (method)
  218. if ssl:
  219. CONF_BASE += """
  220. server!tls = libssl
  221. vserver!1!ssl_certificate_file = %s
  222. vserver!1!ssl_certificate_key_file = %s
  223. vserver!1!ssl_ca_list_file = %s
  224. """ % (SSL_CERT_FILE, SSL_CERT_KEY_FILE, SSL_CA_FILE)
  225. if log:
  226. CONF_BASE += """
  227. vserver!1!logger = %s
  228. vserver!1!logger!access!type = file
  229. vserver!1!logger!access!filename = %s
  230. vserver!1!logger!error!type = stderr
  231. """ % (LOGGER_TYPE, LOGGER_ACCESS, LOGGER_ERROR)
  232. if srv_thds:
  233. CONF_BASE += """
  234. server!thread_number = %d
  235. """ % (srv_thds)
  236. # Import modules
  237. mods = []
  238. for f in files:
  239. mod = importfile (f)
  240. mods.append (mod)
  241. # Build objects
  242. objs = []
  243. for m in mods:
  244. obj = m.Test()
  245. # Update 'base.py': TestCollection if new
  246. # properties are added here!
  247. obj.tmp = tmp
  248. obj.nobody = nobody
  249. obj.php_conf = php_ext
  250. objs.append(obj)
  251. # Prepare www files
  252. for obj in objs:
  253. if obj.Precondition():
  254. obj.Prepare(www)
  255. # Generate configuration
  256. mod_conf = ""
  257. for obj in objs:
  258. if obj.conf is not None:
  259. mod_conf += obj.conf+"\n"
  260. # Write down the configuration file
  261. cfg = CONF_BASE + mod_conf
  262. cfg_file = tempfile.mktemp("cherokee_tmp_cfg")
  263. cfg_fd = open (cfg_file, 'w')
  264. cfg_fd.write (cfg)
  265. cfg_fd.close()
  266. # Launch Cherokee
  267. pid = -1
  268. if port is None:
  269. pid = os.fork()
  270. if pid == 0:
  271. if valgrind != None:
  272. if valgrind[:3] == 'hel':
  273. os.execl (VALGRIND_PATH, "valgrind", "--tool=helgrind", server, "-C", cfg_file)
  274. elif valgrind[:3] == 'cac':
  275. os.execl (VALGRIND_PATH, "valgrind", "--tool=cachegrind", server, "-C", cfg_file)
  276. elif valgrind[:3] == 'cal':
  277. os.execl (VALGRIND_PATH, "valgrind", "--tool=callgrind", "--dump-instr=yes", "--trace-jump=yes", "-v", server, "-C", cfg_file)
  278. else:
  279. os.execl (VALGRIND_PATH, "valgrind", "--leak-check=full", "--num-callers=40", "-v", "--leak-resolution=high", server, "-C", cfg_file)
  280. elif strace:
  281. if sys.platform.startswith('darwin') or \
  282. sys.platform.startswith('sunos'):
  283. os.execl (DTRUSS_PATH, "dtruss", server, "-C", cfg_file)
  284. else:
  285. os.execl (STRACE_PATH, "strace", server, "-C", cfg_file)
  286. else:
  287. name = server[server.rfind('/') + 1:]
  288. env = os.environ
  289. if not env.has_key('CHEROKEE_PANIC_OUTPUT'):
  290. env['CHEROKEE_PANIC_OUTPUT'] = 'stdout'
  291. os.execle (server, name, "-C", cfg_file, env)
  292. else:
  293. print "Server"
  294. print_key ('PID', str(pid));
  295. print_key ('Path', CHEROKEE_PATH)
  296. print_key ('Mods', CHEROKEE_MODS)
  297. print_key ('Deps', CHEROKEE_DEPS)
  298. print_key ('Panic', CHEROKEE_PANIC)
  299. if proxy_cfg_file:
  300. print_key ('Proxy conf', proxy_cfg_file)
  301. print
  302. if memproc:
  303. cmd = 'xterm -e " \
  304. ( while :; do \
  305. [ -d /proc/%d ] || break; \
  306. date; \
  307. cat /proc/%d/status 2>/dev/null | egrep \'(VmSize|VmData|SleepAVG)\'; \
  308. echo; \
  309. sleep 1; \
  310. done; ) | less -b1024 -O/tmp/cherokee_mem.txt" &' % (pid, pid)
  311. os.system(cmd)
  312. # Count down
  313. count_down ("Tests will start in %d secs", delay)
  314. its_clean = False
  315. def clean_up():
  316. global clean
  317. global its_clean
  318. if its_clean: return
  319. its_clean = True
  320. print
  321. # Clean up
  322. if clean:
  323. os.unlink (cfg_file)
  324. shutil.rmtree (www, True)
  325. else:
  326. print_key ("Testdir", www)
  327. print_key ("Config", cfg_file)
  328. print
  329. # Skipped tests
  330. if not quiet:
  331. print "Skipped tests: %d" % (len(skipped))
  332. # Kill the server
  333. if kill and pid > 0:
  334. print "Sending SIGTERM.."
  335. os.kill (pid, signal.SIGTERM)
  336. if valgrind != None:
  337. linger = 8
  338. else:
  339. linger = 4
  340. count_down ("Will kill the server in %d secs", linger)
  341. print "Sending SIGKILL.."
  342. os.kill (pid, signal.SIGKILL)
  343. def do_pause():
  344. global pause
  345. print "Press <Enter> to continue.."
  346. sys.stdin.readline()
  347. pause = pause - 1
  348. def mainloop_iterator(objs, main_thread=True):
  349. global port
  350. global pause
  351. global its_clean
  352. time.sleep (.2)
  353. for n in range(num):
  354. # Randomize files
  355. if randomize:
  356. random.shuffle(objs)
  357. # Iterate
  358. for obj in objs:
  359. if obj.disabled:
  360. go_ahead = False
  361. else:
  362. go_ahead = obj.Precondition()
  363. if proxy and not obj.proxy_suitable:
  364. go_ahead = False
  365. if go_ahead and main_thread:
  366. if pause > 0:
  367. do_pause()
  368. elif bpause and obj.file.startswith(bpause):
  369. do_pause()
  370. if not quiet:
  371. if ssl: print "SSL:",
  372. print "%s: " % (obj.name) + " "*(40 - len(obj.name)),
  373. sys.stdout.flush()
  374. if not go_ahead:
  375. if not quiet:
  376. if obj.disabled:
  377. print MESSAGE_DISABLED
  378. else:
  379. print MESSAGE_SKIPPED
  380. if not obj in skipped:
  381. skipped.append(obj)
  382. continue
  383. try:
  384. obj.JustBefore(www)
  385. ret = obj.Run (client_host, client_port, ssl)
  386. obj.JustAfter(www)
  387. except Exception, e:
  388. if not its_clean:
  389. print e
  390. print_sec(obj)
  391. clean_up()
  392. sys.exit(1)
  393. if ret is not 0:
  394. if not its_clean:
  395. print MESSAGE_FAILED
  396. print_sec (obj)
  397. clean_up()
  398. sys.exit(1)
  399. elif not quiet:
  400. print MESSAGE_SUCCESS
  401. obj.Clean()
  402. if main_thread and tpause > 0.0:
  403. if not quiet:
  404. print "Sleeping %2.2f seconds..\r" % (tpause),
  405. sys.stdout.flush()
  406. time.sleep (tpause)
  407. # If we want to pause once do it before launching the threads
  408. if pause == 1:
  409. do_pause()
  410. # Maybe launch some threads
  411. for n in range(thds-1):
  412. time.sleep (random.randint(0,50) / 100.0)
  413. thread.start_new_thread (mainloop_iterator, (copy.deepcopy(objs), False))
  414. # Execute the tests
  415. mainloop_iterator(objs)
  416. # It's time to kill Cherokee.. :-(
  417. clean_up()