diff --git a/lib/cros_build_lib.py b/lib/cros_build_lib.py index d7da384e09..018bc10cf8 100644 --- a/lib/cros_build_lib.py +++ b/lib/cros_build_lib.py @@ -70,28 +70,35 @@ def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, (GetCallerName(), cmd, cwd)) for retry_count in range(num_retries + 1): - try: - proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, - stdout=stdout, stderr=stderr) - (output, error) = proc.communicate(input) - if exit_code and retry_count == num_retries: - return proc.returncode - if proc.returncode == 0: - break + # If it's not the first attempt, it's a retry + if retry_count > 0 and print_cmd: + Info('PROGRAM(%s) -> RunCommand: retrying %r in dir %s' % + (GetCallerName(), cmd, cwd)) + + proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, + stdout=stdout, stderr=stderr) + (output, error) = proc.communicate(input) + # if the command worked, don't retry any more. + if proc.returncode == 0: + break + + # If the command (and all retries) failed, handle error result + if proc.returncode != 0: + if error_ok: + if print_cmd: + Warning('Command "%r" failed.\n' % (cmd) + + (error_message or error or output or '')) + else: raise RunCommandException('Command "%r" failed.\n' % (cmd) + (error_message or error or output or '')) - except RunCommandException as e: - if not error_ok and retry_count == num_retries: - raise e - else: - Warning(str(e)) - if print_cmd: - Info('PROGRAM(%s) -> RunCommand: retrying %r in dir %s' % - (GetCallerName(), cmd, cwd)) - return output + # return final result + if exit_code: + return proc.returncode + else: + return output def RunCommandCaptureOutput(cmd, print_cmd=True, cwd=None, input=None, diff --git a/lib/cros_build_lib_unittest.py b/lib/cros_build_lib_unittest.py new file mode 100755 index 0000000000..a66424b814 --- /dev/null +++ b/lib/cros_build_lib_unittest.py @@ -0,0 +1,76 @@ +#!/usr/bin/python +# +# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Unit tests for cros_build_lib.""" + +import unittest + +import cros_build_lib + +class CrosBuildLibTest(unittest.TestCase): + """Test class for cros_build_lib.""" + + def testRunCommandSimple(self): + """Test that RunCommand can run a simple successful command.""" + result = cros_build_lib.RunCommand(['ls'], + # Keep the test quiet options + print_cmd=False, + redirect_stdout=True, + redirect_stderr=True, + # Test specific options + exit_code=True) + self.assertEqual(result, 0) + + def testRunCommandError(self): + """Test that RunCommand can return an error code for a failed command.""" + result = cros_build_lib.RunCommand(['ls', '/nosuchdir'], + # Keep the test quiet options + print_cmd=False, + redirect_stdout=True, + redirect_stderr=True, + # Test specific options + error_ok=True, + exit_code=True) + self.assertNotEqual(result, 0) + + def testRunCommandErrorRetries(self): + """Test that RunCommand can retry a failed command that always fails.""" + + # We don't actually check that it's retrying, just exercise the code path. + result = cros_build_lib.RunCommand(['ls', '/nosuchdir'], + # Keep the test quiet options + print_cmd=False, + redirect_stdout=True, + redirect_stderr=True, + # Test specific options + num_retries=2, + error_ok=True, + exit_code=True) + self.assertNotEqual(result, 0) + + def testRunCommandErrorException(self): + """Test that RunCommand can throw an exception when a command fails.""" + + function = lambda : cros_build_lib.RunCommand(['ls', '/nosuchdir'], + # Keep the test quiet options + print_cmd=False, + redirect_stdout=True, + redirect_stderr=True) + self.assertRaises(cros_build_lib.RunCommandException, function) + + def testRunCommandCaptureOutput(self): + """Test that RunCommand can capture stdout if a command succeeds.""" + + result = cros_build_lib.RunCommand(['echo', '-n', 'Hi'], + # Keep the test quiet options + print_cmd=False, + redirect_stdout=True, + redirect_stderr=True) + self.assertEqual(result, 'Hi') + + +if __name__ == '__main__': + unittest.main()