1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mozbase/mozprocess/tests/test_mozprocess.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,340 @@ 1.4 +#!/usr/bin/env python 1.5 + 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 +# You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +import optparse 1.11 +import os 1.12 +import subprocess 1.13 +import sys 1.14 +import unittest 1.15 +from mozprocess import processhandler 1.16 +from time import sleep 1.17 + 1.18 +here = os.path.dirname(os.path.abspath(__file__)) 1.19 + 1.20 +def make_proclaunch(aDir): 1.21 + """ 1.22 + Makes the proclaunch executable. 1.23 + Params: 1.24 + aDir - the directory in which to issue the make commands 1.25 + Returns: 1.26 + the path to the proclaunch executable that is generated 1.27 + """ 1.28 + 1.29 + if sys.platform == "win32": 1.30 + exepath = os.path.join(aDir, "proclaunch.exe") 1.31 + else: 1.32 + exepath = os.path.join(aDir, "proclaunch") 1.33 + 1.34 + # remove the launcher, if it already exists 1.35 + # otherwise, if the make fails you may not notice 1.36 + if os.path.exists(exepath): 1.37 + os.remove(exepath) 1.38 + 1.39 + # Ideally make should take care of both calls through recursion, but since it doesn't, 1.40 + # on windows anyway (to file?), let's just call out both targets explicitly. 1.41 + for command in [["make", "-C", "iniparser"], 1.42 + ["make"]]: 1.43 + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=aDir) 1.44 + stdout, stderr = process.communicate() 1.45 + if process.returncode: 1.46 + # SomethingBadHappen; print all the things 1.47 + print "%s: exit %d" % (command, process.returncode) 1.48 + print "stdout:\n%s" % stdout 1.49 + print "stderr:\n%s" % stderr 1.50 + raise subprocess.CalledProcessError(process.returncode, command, stdout) 1.51 + 1.52 + # ensure the launcher now exists 1.53 + if not os.path.exists(exepath): 1.54 + raise AssertionError("proclaunch executable '%s' does not exist (sys.platform=%s)" % (exepath, sys.platform)) 1.55 + return exepath 1.56 + 1.57 +def check_for_process(processName): 1.58 + """ 1.59 + Use to determine if process of the given name is still running. 1.60 + 1.61 + Returns: 1.62 + detected -- True if process is detected to exist, False otherwise 1.63 + output -- if process exists, stdout of the process, '' otherwise 1.64 + """ 1.65 + # TODO: replace with 1.66 + # https://github.com/mozilla/mozbase/blob/master/mozprocess/mozprocess/pid.py 1.67 + # which should be augmented from talos 1.68 + # see https://bugzilla.mozilla.org/show_bug.cgi?id=705864 1.69 + output = '' 1.70 + if sys.platform == "win32": 1.71 + # On windows we use tasklist 1.72 + p1 = subprocess.Popen(["tasklist"], stdout=subprocess.PIPE) 1.73 + output = p1.communicate()[0] 1.74 + detected = False 1.75 + for line in output.splitlines(): 1.76 + if processName in line: 1.77 + detected = True 1.78 + break 1.79 + else: 1.80 + p1 = subprocess.Popen(["ps", "-ef"], stdout=subprocess.PIPE) 1.81 + p2 = subprocess.Popen(["grep", processName], stdin=p1.stdout, stdout=subprocess.PIPE) 1.82 + p1.stdout.close() 1.83 + output = p2.communicate()[0] 1.84 + detected = False 1.85 + for line in output.splitlines(): 1.86 + if "grep %s" % processName in line: 1.87 + continue 1.88 + elif processName in line and not 'defunct' in line: 1.89 + detected = True 1.90 + break 1.91 + 1.92 + return detected, output 1.93 + 1.94 + 1.95 +class ProcTest(unittest.TestCase): 1.96 + 1.97 + # whether to remove created files on exit 1.98 + cleanup = os.environ.get('CLEANUP', 'true').lower() in ('1', 'true') 1.99 + 1.100 + @classmethod 1.101 + def setUpClass(cls): 1.102 + cls.proclaunch = make_proclaunch(here) 1.103 + 1.104 + @classmethod 1.105 + def tearDownClass(cls): 1.106 + del cls.proclaunch 1.107 + if not cls.cleanup: 1.108 + return 1.109 + files = [('proclaunch',), 1.110 + ('proclaunch.exe',), 1.111 + ('iniparser', 'dictionary.o'), 1.112 + ('iniparser', 'iniparser.lib'), 1.113 + ('iniparser', 'iniparser.o'), 1.114 + ('iniparser', 'libiniparser.a'), 1.115 + ('iniparser', 'libiniparser.so.0'), 1.116 + ] 1.117 + files = [os.path.join(here, *path) for path in files] 1.118 + errors = [] 1.119 + for path in files: 1.120 + if os.path.exists(path): 1.121 + try: 1.122 + os.remove(path) 1.123 + except OSError as e: 1.124 + errors.append(str(e)) 1.125 + if errors: 1.126 + raise OSError("Error(s) encountered tearing down %s.%s:\n%s" % (cls.__module__, cls.__name__, '\n'.join(errors))) 1.127 + 1.128 + def test_process_normal_finish(self): 1.129 + """Process is started, runs to completion while we wait for it""" 1.130 + 1.131 + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], 1.132 + cwd=here) 1.133 + p.run() 1.134 + p.wait() 1.135 + 1.136 + detected, output = check_for_process(self.proclaunch) 1.137 + self.determine_status(detected, 1.138 + output, 1.139 + p.proc.returncode, 1.140 + p.didTimeout) 1.141 + 1.142 + def test_commandline_no_args(self): 1.143 + """Command line is reported correctly when no arguments are specified""" 1.144 + p = processhandler.ProcessHandler(self.proclaunch, cwd=here) 1.145 + self.assertEqual(p.commandline, self.proclaunch) 1.146 + 1.147 + def test_commandline_overspecified(self): 1.148 + """Command line raises an exception when the arguments are specified ambiguously""" 1.149 + err = None 1.150 + try: 1.151 + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], 1.152 + args=["1", "2", "3"], 1.153 + cwd=here) 1.154 + except TypeError, e: 1.155 + err = e 1.156 + 1.157 + self.assertTrue(err) 1.158 + 1.159 + def test_commandline_from_list(self): 1.160 + """Command line is reported correctly when command and arguments are specified in a list""" 1.161 + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], 1.162 + cwd=here) 1.163 + self.assertEqual(p.commandline, self.proclaunch + ' process_normal_finish.ini') 1.164 + 1.165 + def test_commandline_over_specified(self): 1.166 + """Command line raises an exception when the arguments are specified ambiguously""" 1.167 + err = None 1.168 + try: 1.169 + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], 1.170 + args=["1", "2", "3"], 1.171 + cwd=here) 1.172 + except TypeError, e: 1.173 + err = e 1.174 + 1.175 + self.assertTrue(err) 1.176 + 1.177 + def test_commandline_from_args(self): 1.178 + """Command line is reported correctly when arguments are specified in a dedicated list""" 1.179 + p = processhandler.ProcessHandler(self.proclaunch, 1.180 + args=["1", "2", "3"], 1.181 + cwd=here) 1.182 + self.assertEqual(p.commandline, self.proclaunch + ' 1 2 3') 1.183 + 1.184 + def test_process_wait(self): 1.185 + """Process is started runs to completion while we wait indefinitely""" 1.186 + 1.187 + p = processhandler.ProcessHandler([self.proclaunch, 1.188 + "process_waittimeout_10s.ini"], 1.189 + cwd=here) 1.190 + p.run() 1.191 + p.wait() 1.192 + 1.193 + detected, output = check_for_process(self.proclaunch) 1.194 + self.determine_status(detected, 1.195 + output, 1.196 + p.proc.returncode, 1.197 + p.didTimeout) 1.198 + 1.199 + def test_process_timeout(self): 1.200 + """ Process is started, runs but we time out waiting on it 1.201 + to complete 1.202 + """ 1.203 + p = processhandler.ProcessHandler([self.proclaunch, "process_waittimeout.ini"], 1.204 + cwd=here) 1.205 + p.run(timeout=10) 1.206 + p.wait() 1.207 + 1.208 + detected, output = check_for_process(self.proclaunch) 1.209 + self.determine_status(detected, 1.210 + output, 1.211 + p.proc.returncode, 1.212 + p.didTimeout, 1.213 + False, 1.214 + ['returncode', 'didtimeout']) 1.215 + 1.216 + def test_process_timeout_no_kill(self): 1.217 + """ Process is started, runs but we time out waiting on it 1.218 + to complete. Process should not be killed. 1.219 + """ 1.220 + p = None 1.221 + def timeout_handler(): 1.222 + self.assertEqual(p.proc.poll(), None) 1.223 + p.kill() 1.224 + p = processhandler.ProcessHandler([self.proclaunch, "process_waittimeout.ini"], 1.225 + cwd=here, 1.226 + onTimeout=(timeout_handler,), 1.227 + kill_on_timeout=False) 1.228 + p.run(timeout=1) 1.229 + p.wait() 1.230 + self.assertTrue(p.didTimeout) 1.231 + 1.232 + detected, output = check_for_process(self.proclaunch) 1.233 + self.determine_status(detected, 1.234 + output, 1.235 + p.proc.returncode, 1.236 + p.didTimeout, 1.237 + False, 1.238 + ['returncode', 'didtimeout']) 1.239 + 1.240 + def test_process_waittimeout(self): 1.241 + """ 1.242 + Process is started, then wait is called and times out. 1.243 + Process is still running and didn't timeout 1.244 + """ 1.245 + p = processhandler.ProcessHandler([self.proclaunch, 1.246 + "process_waittimeout_10s.ini"], 1.247 + cwd=here) 1.248 + 1.249 + p.run() 1.250 + p.wait(timeout=5) 1.251 + 1.252 + detected, output = check_for_process(self.proclaunch) 1.253 + self.determine_status(detected, 1.254 + output, 1.255 + p.proc.returncode, 1.256 + p.didTimeout, 1.257 + True, 1.258 + ()) 1.259 + 1.260 + def test_process_waitnotimeout(self): 1.261 + """ Process is started, runs to completion before our wait times out 1.262 + """ 1.263 + p = processhandler.ProcessHandler([self.proclaunch, 1.264 + "process_waittimeout_10s.ini"], 1.265 + cwd=here) 1.266 + p.run(timeout=30) 1.267 + p.wait() 1.268 + 1.269 + detected, output = check_for_process(self.proclaunch) 1.270 + self.determine_status(detected, 1.271 + output, 1.272 + p.proc.returncode, 1.273 + p.didTimeout) 1.274 + 1.275 + def test_process_kill(self): 1.276 + """Process is started, we kill it""" 1.277 + 1.278 + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], 1.279 + cwd=here) 1.280 + p.run() 1.281 + p.kill() 1.282 + 1.283 + detected, output = check_for_process(self.proclaunch) 1.284 + self.determine_status(detected, 1.285 + output, 1.286 + p.proc.returncode, 1.287 + p.didTimeout) 1.288 + 1.289 + def test_process_output_twice(self): 1.290 + """ 1.291 + Process is started, then processOutput is called a second time explicitly 1.292 + """ 1.293 + p = processhandler.ProcessHandler([self.proclaunch, 1.294 + "process_waittimeout_10s.ini"], 1.295 + cwd=here) 1.296 + 1.297 + p.run() 1.298 + p.processOutput(timeout=5) 1.299 + p.wait() 1.300 + 1.301 + detected, output = check_for_process(self.proclaunch) 1.302 + self.determine_status(detected, 1.303 + output, 1.304 + p.proc.returncode, 1.305 + p.didTimeout, 1.306 + False, 1.307 + ()) 1.308 + 1.309 + def determine_status(self, 1.310 + detected=False, 1.311 + output='', 1.312 + returncode=0, 1.313 + didtimeout=False, 1.314 + isalive=False, 1.315 + expectedfail=()): 1.316 + """ 1.317 + Use to determine if the situation has failed. 1.318 + Parameters: 1.319 + detected -- value from check_for_process to determine if the process is detected 1.320 + output -- string of data from detected process, can be '' 1.321 + returncode -- return code from process, defaults to 0 1.322 + didtimeout -- True if process timed out, defaults to False 1.323 + isalive -- Use True to indicate we pass if the process exists; however, by default 1.324 + the test will pass if the process does not exist (isalive == False) 1.325 + expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail 1.326 + """ 1.327 + if 'returncode' in expectedfail: 1.328 + self.assertTrue(returncode, "Detected an unexpected return code of: %s" % returncode) 1.329 + elif not isalive: 1.330 + self.assertTrue(returncode == 0, "Detected non-zero return code of: %d" % returncode) 1.331 + 1.332 + if 'didtimeout' in expectedfail: 1.333 + self.assertTrue(didtimeout, "Detected that process didn't time out") 1.334 + else: 1.335 + self.assertTrue(not didtimeout, "Detected that process timed out") 1.336 + 1.337 + if isalive: 1.338 + self.assertTrue(detected, "Detected process is not running, process output: %s" % output) 1.339 + else: 1.340 + self.assertTrue(not detected, "Detected process is still running, process output: %s" % output) 1.341 + 1.342 +if __name__ == '__main__': 1.343 + unittest.main()