You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by jd...@apache.org on 2019/11/06 11:16:24 UTC

[qpid-proton] branch master updated: PROTON-2115 [c] fix prlimit test and do PEP8 reformat in related files (#198)

This is an automated email from the ASF dual-hosted git repository.

jdanek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/qpid-proton.git


The following commit(s) were added to refs/heads/master by this push:
     new bdf9b1a  PROTON-2115 [c] fix prlimit test and do PEP8 reformat in related files (#198)
bdf9b1a is described below

commit bdf9b1a4a0b3f080387362f7391617199af6b203
Author: Jiří Daněk <jd...@redhat.com>
AuthorDate: Wed Nov 6 12:16:12 2019 +0100

    PROTON-2115 [c] fix prlimit test and do PEP8 reformat in related files (#198)
    
    This fixes prlimit test, adds some more useful 2.7 unittest methods,
    and does some PEP8 formatting in related files.
---
 c/tests/CMakeLists.txt      |  7 +++-
 c/tests/fdlimit.py          | 94 +++++++++++++++++++++++++++------------------
 scripts/env.py              |  8 ++--
 tests/py/test_subprocess.py | 15 ++++++--
 tests/py/test_unittest.py   | 31 +++++++++++++--
 5 files changed, 106 insertions(+), 49 deletions(-)

diff --git a/c/tests/CMakeLists.txt b/c/tests/CMakeLists.txt
index 457022f..6a754c7 100644
--- a/c/tests/CMakeLists.txt
+++ b/c/tests/CMakeLists.txt
@@ -97,7 +97,12 @@ if (CMAKE_CXX_COMPILER)
       set(path "$<TARGET_FILE_DIR:c-broker>:$ENV{PATH}")
     endif(WIN32)
 
-    add_test(NAME c-fdlimit-tests COMMAND ${PN_ENV_SCRIPT} -- "PATH=${path}" "PYTHONPATH=${pypath}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/fdlimit.py)
+    set(pypath "${CMAKE_SOURCE_DIR}/tests/py")
+
+    add_test(
+        NAME c-fdlimit-tests
+        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+        COMMAND ${PN_ENV_SCRIPT} -- "PATH=${path}" "PYTHONPATH=${pypath}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/fdlimit.py)
   endif(HAS_PROACTOR)
 else (CMAKE_CXX_COMPILER)
   message(WARNING "No C++ compiler, some C library tests were not built")
diff --git a/c/tests/fdlimit.py b/c/tests/fdlimit.py
index e5cedac..c4408b9 100644
--- a/c/tests/fdlimit.py
+++ b/c/tests/fdlimit.py
@@ -16,58 +16,76 @@
 # specific language governing permissions and limitations
 # under the License
 #
+from __future__ import absolute_import
+from __future__ import division
 from __future__ import print_function
 
-import os, sys
-from subprocess import Popen, PIPE
+import os
+import subprocess
+import time
 
-def wait_listening(p):
-    return re.search(b"listening on ([0-9]+)$", p.stdout.readline()).group(1)
+import test_subprocess
+from test_unittest import unittest
 
-class LimitedBroker(Popen):
-    def __init__(self, fdlimit):
-        super(LimitedBroker, self).__init__(["broker", "", "0"], stdout=PIPE, stderr=open(os.devnull))
-        self.fdlimit = fdlimit
+# Check if we can run prlimit to control resources
+try:
+    assert subprocess.check_call(["prlimit"], stdout=open(os.devnull, 'w')) == 0, 'prlimit is present, but broken'
+    prlimit_available = True
+except OSError:
+    prlimit_available = False
 
-    def __enter__(self):
-        self.port = wait_listening(self)
-        return self
 
-    def __exit__(self, *args):
-        self.kill()
+class PRLimitedBroker(test_subprocess.Server):
+    def __init__(self, fdlimit, *args, **kwargs):
+        super(PRLimitedBroker, self).__init__(
+            ['prlimit', '-n{0:d}'.format(fdlimit), "broker", "", "0"],
+            stdout=subprocess.PIPE, universal_newlines=True, *args, **kwargs)
+        self.fdlimit = fdlimit
 
-# Check if we can run prlimit to control resources
-try:
-    Proc(["prlimit"]).wait_exit()
-except:
-    print("Skipping test: prlimit not available")
-    sys.exit(0)
 
-class FdLimitTest(ProcTestCase):
+class FdLimitTest(unittest.TestCase):
+    devnull = open(os.devnull, 'w')
+
+    @classmethod
+    def tearDownClass(cls):
+        if cls.devnull:
+            cls.devnull.close()
 
+    @unittest.skipUnless(prlimit_available, "prlimit not available")
     def test_fd_limit_broker(self):
         """Check behaviour when running out of file descriptors on accept"""
         # Not too many FDs but not too few either, some are used for system purposes.
         fdlimit = 256
-        with LimitedBroker(fdlimit) as b:
+        with PRLimitedBroker(fdlimit, kill_me=True) as b:
             receivers = []
-            # Start enough receivers to use all FDs, make sure the broker logs an error
-            for i in range(fdlimit+1):
-                receivers.append(Popen(["receive", "", b.port, str(i)], stdout=PIPE))
-
-            # All FDs are now in use, send attempt should fail or hang
-            self.assertIn(Popen(["send", "", b.port, "x"], stdout=PIPE, stderr=STDOUT).poll(), [1, None])
-
-            # Kill receivers to free up FDs
-            for r in receivers:
-                r.kill()
-            for r in receivers:
-                r.wait()
-            # send/receive should succeed now
-            self.assertIn("10 messages sent", check_output(["send", "", b.port]))
-            self.assertIn("10 messages received", check_output(["receive", "", b.port]))
 
-if __name__ == "__main__":
-    main()
+            # Start enough receivers to use all FDs
+            # NOTE: broker does not log a file descriptor related error at any point in the test, only
+            #  PN_TRANSPORT_CLOSED: amqp:connection:framing-error: connection aborted
+            #  PN_TRANSPORT_CLOSED: proton:io: Connection reset by peer - disconnected :5672 (connection aborted)
+            for i in range(fdlimit + 1):
+                receiver = test_subprocess.Popen(["receive", "", b.port, str(i)], stdout=self.devnull)
+                receivers.append(receiver)
+
+            # All FDs are now in use, send attempt will (with present implementation) hang
+            with test_subprocess.Popen(["send", "", b.port, "x"],
+                                       stdout=self.devnull, stderr=subprocess.STDOUT) as sender:
+                time.sleep(1)  # polling for None immediately would always succeed, regardless whether send hangs or not
+                self.assertIsNone(sender.poll())
+
+                # Kill receivers to free up FDs
+                for r in receivers:
+                    r.kill()
+                for r in receivers:
+                    r.wait()
 
+                # Sender now succeeded and exited
+                self.assertEqual(sender.wait(), 0)
 
+            # Additional send/receive should succeed now
+            self.assertIn("10 messages sent", test_subprocess.check_output(["send", "", b.port], universal_newlines=True))
+            self.assertIn("10 messages received", test_subprocess.check_output(["receive", "", b.port], universal_newlines=True))
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/scripts/env.py b/scripts/env.py
index 14df6d1..443bdca 100644
--- a/scripts/env.py
+++ b/scripts/env.py
@@ -26,6 +26,7 @@ import os
 import subprocess
 from optparse import OptionParser
 
+
 def main(argv=None):
 
     parser = OptionParser(usage="Usage: %prog [options] [--] VAR=VALUE... command [options] arg1 arg2...")
@@ -41,10 +42,10 @@ def main(argv=None):
         new_env = os.environ.copy()
 
     # pull out each name value pair
-    while (len(args)):
-        z = args[0].split("=",1)
+    while len(args):
+        z = args[0].split("=", 1)
         if len(z) != 2:
-            break;  # done with env args
+            break  # done with env args
         if len(z[0]) == 0:
             raise Exception("Error: incorrect format for env var: '%s'" % str(args[x]))
         del args[0]
@@ -61,5 +62,6 @@ def main(argv=None):
     p = subprocess.Popen(args, env=new_env)
     return p.wait()
 
+
 if __name__ == "__main__":
     sys.exit(main())
diff --git a/tests/py/test_subprocess.py b/tests/py/test_subprocess.py
index 4905107..6e84f01 100644
--- a/tests/py/test_subprocess.py
+++ b/tests/py/test_subprocess.py
@@ -19,10 +19,13 @@
 
 # Extends the subprocess module to use runtime checkers, and report stderr output.
 
-import subprocess, re, os, tempfile
-
+import os
+import re
+import subprocess
+import tempfile
 from subprocess import PIPE
 
+
 def in_path(name):
     """Look for name in the PATH""" 
     for path in os.environ["PATH"].split(os.pathsep):
@@ -30,6 +33,7 @@ def in_path(name):
         if os.path.isfile(f) and os.access(f, os.X_OK):
             return f
 
+
 class TestProcessError(Exception):
     def __init__(self, proc, what, output=None):
         self.output = output
@@ -38,6 +42,7 @@ class TestProcessError(Exception):
         super(TestProcessError, self).__init__("%s pid=%s exit=%s: %s%s" % (
             proc.cmd, proc.pid, proc.returncode, what, error))
 
+
 class Popen(subprocess.Popen):
     """
     Add TEST_EXE_PREFIX to the command, check stderr for runtime checker output.
@@ -92,8 +97,8 @@ class Popen(subprocess.Popen):
         """Return stderr as string, may only be used after process has terminated."""
         assert(self.poll is not None)
         if not hasattr(self, "_error"):
-            self.errfile.close() # Not auto-deleted
-            with open(self.errfile.name) as f: # Re-open to read
+            self.errfile.close()  # Not auto-deleted
+            with open(self.errfile.name) as f:  # Re-open to read
                 self._error = f.read().strip()
             os.unlink(self.errfile.name)
         return self._error
@@ -104,9 +109,11 @@ class Popen(subprocess.Popen):
     def __exit__(self, *args):
         self.on_exit()
 
+
 def check_output(*args, **kwargs):
     return Popen(*args, **kwargs).communicate()[0]
 
+
 class Server(Popen):
     """A process that prints 'listening on <port>' to stdout"""
     def __init__(self, *args, **kwargs):
diff --git a/tests/py/test_unittest.py b/tests/py/test_unittest.py
index ebb8efc..bc372f6 100644
--- a/tests/py/test_unittest.py
+++ b/tests/py/test_unittest.py
@@ -17,19 +17,44 @@
 # under the License
 #
 
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
 import unittest
 
 # Monkey-patch a few unittest 2.7 features for Python 2.6.
 #
-# These are note the pretty versions provided by 2.7 but they do the
+# These are not the pretty versions provided by 2.7 but they do the
 # same job as far as correctness is concerned.
 
 if not hasattr(unittest.TestCase, "assertMultiLineEqual"):
-    def assertMultiLineEqual(self, a, b, msg=None): self.assertEqual(a,b,msg)
+    def assertMultiLineEqual(self, a, b, msg=None): self.assertEqual(a, b, msg)
     unittest.TestCase.assertMultiLineEqual = assertMultiLineEqual
 
 if not hasattr(unittest.TestCase, "assertIn"):
-    def assertIn(self, a, b, msg=None): self.assertTrue(a in b,msg)
+    def assertIn(self, a, b, msg=None): self.assertTrue(a in b, msg)
     unittest.TestCase.assertIn = assertIn
 
+if not hasattr(unittest.TestCase, "assertIsNone"):
+    def assertIsNone(self, obj, msg=None): self.assertEqual(obj, None, msg)
+    unittest.TestCase.assertIsNone = assertIsNone
+
+if not hasattr(unittest, "skip"):
+    def skip(reason="Test skipped"):
+        return lambda f: print(reason)
+    unittest.skip = skip
+
+if not hasattr(unittest, "skipIf"):
+    def skipIf(condition, reason):
+        if condition:
+            return skip(reason)
+        return lambda f: f
+    unittest.skipUnless = skipIf
 
+if not hasattr(unittest, "skipUnless"):
+    def skipUnless(condition, reason):
+        if not condition:
+            return skip(reason)
+        return lambda f: f
+    unittest.skipUnless = skipUnless


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org