Repository: giampaolo/psutil
Pull request #1535 (2019-06-14 07:33:38)

Connection family/type are not converted to enums
...plus refactoring of connection routines and tests.


Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index b9e08da1..044b7bc0 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -5,14 +5,12 @@
 
 XXXX-XX-XX
 
-**Enhancements**
-
-- 1530_: [NetBSD] add process cwd() support.  (patch by Kamil Rytarowski)
-
 **Bug fixes**
 
 - 1528_: [AIX] compilation error on AIX 7.2 due to 32 vs 64 bit differences.
   (patch by Arnon Yaari)
+- 1535_: 'type' and 'family' fields returned by net_connections() are not
+  always turned into enums.
 
 5.6.3
 =====
diff --git a/docs/index.rst b/docs/index.rst
index f068967e..8c0ee74a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -513,7 +513,8 @@ Network
     to obtain a usable socket object.
     On Windows and SunOS this is always set to ``-1``.
   - **family**: the address family, either `AF_INET`_, `AF_INET6`_ or `AF_UNIX`_.
-  - **type**: the address type, either `SOCK_STREAM`_ or `SOCK_DGRAM`_.
+  - **type**: the address type, either `SOCK_STREAM`_, `SOCK_DGRAM`_ or
+    `SOCK_SEQPACKET`_.
   - **laddr**: the local address as a ``(ip, port)`` named tuple or a ``path``
     in case of AF_UNIX sockets. For UNIX sockets see notes below.
   - **raddr**: the remote address as a ``(ip, port)`` named tuple or an
@@ -1784,7 +1785,8 @@ Process class
       always set to ``-1``.
     - **family**: the address family, either `AF_INET`_, `AF_INET6`_ or
       `AF_UNIX`_.
-    - **type**: the address type, either `SOCK_STREAM`_ or `SOCK_DGRAM`_.
+    - **type**: the address type, either `SOCK_STREAM`_, `SOCK_DGRAM`_ or
+      `SOCK_SEQPACKET`_.  .
     - **laddr**: the local address as a ``(ip, port)`` named tuple or a ``path``
       in case of AF_UNIX sockets. For UNIX sockets see notes below.
     - **raddr**: the remote address as a ``(ip, port)`` named tuple or an
@@ -2605,7 +2607,11 @@ take a look at the `development guide`_.
 Timeline
 ========
 
-- 2019-0426:
+- 2019-06-11:
+  `5.6.3 <https://pypi.org/project/psutil/5.6.3/#files>`__ -
+  `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#563>`__ -
+  `diff <https://github.com/giampaolo/psutil/compare/release-5.6.2...release-5.6.3#files_bucket>`__
+- 2019-04-26:
   `5.6.2 <https://pypi.org/project/psutil/5.6.2/#files>`__ -
   `what's new <https://github.com/giampaolo/psutil/blob/master/HISTORY.rst#562>`__ -
   `diff <https://github.com/giampaolo/psutil/compare/release-5.6.1...release-5.6.2#files_bucket>`__
@@ -2946,6 +2952,7 @@ Timeline
 .. _`shutil.disk_usage`: https://docs.python.org/3/library/shutil.html#shutil.disk_usage.
 .. _`signal module`: https://docs.python.org//library/signal.html
 .. _`SOCK_DGRAM`: https://docs.python.org/3/library/socket.html#socket.SOCK_DGRAM
+.. _`SOCK_SEQPACKET`: https://docs.python.org/3/library/socket.html#socket.SOCK_SEQPACKET
 .. _`SOCK_STREAM`: https://docs.python.org/3/library/socket.html#socket.SOCK_STREAM
 .. _`socket.fromfd`: https://docs.python.org/3/library/socket.html#socket.fromfd
 .. _`subprocess.Popen`: https://docs.python.org/3/library/subprocess.html#subprocess.Popen
diff --git a/psutil/_common.py b/psutil/_common.py
index e3b45417..4a006c1d 100644
--- a/psutil/_common.py
+++ b/psutil/_common.py
@@ -64,7 +64,7 @@ __all__ = [
     'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
     'parse_environ_block', 'path_exists_strict', 'usage_percent',
     'supports_ipv6', 'sockfam_to_enum', 'socktype_to_enum', "wrap_numbers",
-    'bytes2human',
+    'bytes2human', 'conn_to_ntuple',
 ]
 
 
@@ -257,8 +257,6 @@ if AF_UNIX is not None:
         "unix": ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
     })
 
-del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
-
 
 # ===================================================================
 # --- utils
@@ -447,7 +445,7 @@ def sockfam_to_enum(num):
     else:  # pragma: no cover
         try:
             return socket.AddressFamily(num)
-        except (ValueError, AttributeError):
+        except ValueError:
             return num
 
 
@@ -459,11 +457,30 @@ def socktype_to_enum(num):
         return num
     else:  # pragma: no cover
         try:
-            return socket.AddressType(num)
-        except (ValueError, AttributeError):
+            return socket.SocketKind(num)
+        except ValueError:
             return num
 
 
+def conn_to_ntuple(fd, fam, type_, laddr, raddr, status, status_map, pid=None):
+    """Convert a raw connection tuple to a proper ntuple."""
+    if fam in (socket.AF_INET, AF_INET6):
+        if laddr:
+            laddr = addr(*laddr)
+        if raddr:
+            raddr = addr(*raddr)
+    if type_ == socket.SOCK_STREAM and fam in (AF_INET, AF_INET6):
+        status = status_map.get(status, CONN_NONE)
+    else:
+        status = CONN_NONE  # ignore whatever C returned to us
+    fam = sockfam_to_enum(fam)
+    type_ = socktype_to_enum(type_)
+    if pid is None:
+        return pconn(fd, fam, type_, laddr, raddr, status)
+    else:
+        return sconn(fd, fam, type_, laddr, raddr, status, pid)
+
+
 def deprecated_method(replacement):
     """A decorator which can be used to mark a method as deprecated
     'replcement' is the method name which will be called instead.
diff --git a/psutil/_psaix.py b/psutil/_psaix.py
index b24325d1..3a949d25 100644
--- a/psutil/_psaix.py
+++ b/psutil/_psaix.py
@@ -14,20 +14,17 @@ import re
 import subprocess
 import sys
 from collections import namedtuple
-from socket import AF_INET
 
 from . import _common
 from . import _psposix
 from . import _psutil_aix as cext
 from . import _psutil_posix as cext_posix
-from ._common import AF_INET6
+from ._common import conn_to_ntuple
 from ._common import get_procfs_path
 from ._common import memoize_when_activated
 from ._common import NIC_DUPLEX_FULL
 from ._common import NIC_DUPLEX_HALF
 from ._common import NIC_DUPLEX_UNKNOWN
-from ._common import sockfam_to_enum
-from ._common import socktype_to_enum
 from ._common import usage_percent
 from ._compat import PY3
 
@@ -220,27 +217,17 @@ def net_connections(kind, _pid=-1):
                          % (kind, ', '.join([repr(x) for x in cmap])))
     families, types = _common.conn_tmap[kind]
     rawlist = cext.net_connections(_pid)
-    ret = set()
+    ret = []
     for item in rawlist:
         fd, fam, type_, laddr, raddr, status, pid = item
         if fam not in families:
             continue
         if type_ not in types:
             continue
-        status = TCP_STATUSES[status]
-        if fam in (AF_INET, AF_INET6):
-            if laddr:
-                laddr = _common.addr(*laddr)
-            if raddr:
-                raddr = _common.addr(*raddr)
-        fam = sockfam_to_enum(fam)
-        type_ = socktype_to_enum(type_)
-        if _pid == -1:
-            nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid)
-        else:
-            nt = _common.pconn(fd, fam, type_, laddr, raddr, status)
-        ret.add(nt)
-    return list(ret)
+        nt = conn_to_ntuple(fd, fam, type_, laddr, raddr, status,
+                            TCP_STATUSES, pid=pid if _pid == -1 else None)
+        ret.append(nt)
+    return ret
 
 
 def net_if_stats():
diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py
index e90bdc84..9964f5ff 100644
--- a/psutil/_psbsd.py
+++ b/psutil/_psbsd.py
@@ -10,22 +10,19 @@ import functools
 import os
 import xml.etree.ElementTree as ET
 from collections import namedtuple
-from socket import AF_INET
 from collections import defaultdict
 
 from . import _common
 from . import _psposix
 from . import _psutil_bsd as cext
 from . import _psutil_posix as cext_posix
-from ._common import AF_INET6
 from ._common import conn_tmap
+from ._common import conn_to_ntuple
 from ._common import FREEBSD
 from ._common import memoize
 from ._common import memoize_when_activated
 from ._common import NETBSD
 from ._common import OPENBSD
-from ._common import sockfam_to_enum
-from ._common import socktype_to_enum
 from ._common import usage_percent
 from ._compat import which
 
@@ -399,22 +396,8 @@ def net_connections(kind):
         fd, fam, type, laddr, raddr, status, pid = item
         # TODO: apply filter at C level
         if fam in families and type in types:
-            try:
-                status = TCP_STATUSES[status]
-            except KeyError:
-                # XXX: Not sure why this happens. I saw this occurring
-                # with IPv6 sockets opened by 'vim'. Those sockets
-                # have a very short lifetime so maybe the kernel
-                # can't initialize their status?
-                status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
-            if fam in (AF_INET, AF_INET6):
-                if laddr:
-                    laddr = _common.addr(*laddr)
-                if raddr:
-                    raddr = _common.addr(*raddr)
-            fam = sockfam_to_enum(fam)
-            type = socktype_to_enum(type)
-            nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
+            nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
+                                TCP_STATUSES, pid)
             ret.add(nt)
     return list(ret)
 
@@ -769,25 +752,15 @@ class Process(object):
 
         if NETBSD:
             families, types = conn_tmap[kind]
-            ret = set()
+            ret = []
             rawlist = cext.net_connections(self.pid)
             for item in rawlist:
                 fd, fam, type, laddr, raddr, status, pid = item
                 assert pid == self.pid
                 if fam in families and type in types:
-                    try:
-                        status = TCP_STATUSES[status]
-                    except KeyError:
-                        status = TCP_STATUSES[cext.PSUTIL_CONN_NONE]
-                    if fam in (AF_INET, AF_INET6):
-                        if laddr:
-                            laddr = _common.addr(*laddr)
-                        if raddr:
-                            raddr = _common.addr(*raddr)
-                    fam = sockfam_to_enum(fam)
-                    type = socktype_to_enum(type)
-                    nt = _common.pconn(fd, fam, type, laddr, raddr, status)
-                    ret.add(nt)
+                    nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
+                                        TCP_STATUSES)
+                    ret.append(nt)
             self._assert_alive()
             return list(ret)
 
@@ -796,18 +769,14 @@ class Process(object):
         ret = []
         for item in rawlist:
             fd, fam, type, laddr, raddr, status = item
-            if fam in (AF_INET, AF_INET6):
-                if laddr:
-                    laddr = _common.addr(*laddr)
-                if raddr:
-                    raddr = _common.addr(*raddr)
-            fam = sockfam_to_enum(fam)
-            type = socktype_to_enum(type)
             status = TCP_STATUSES[status]
-            nt = _common.pconn(fd, fam, type, laddr, raddr, status)
+            nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
+                                TCP_STATUSES)
             ret.append(nt)
+
         if OPENBSD:
             self._assert_alive()
+
         return ret
 
     @wrap_exceptions
diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py
index e4bc7d75..50094b40 100644
--- a/psutil/_pslinux.py
+++ b/psutil/_pslinux.py
@@ -933,7 +933,7 @@ class Connections:
                             path = tokens[-1]
                         else:
                             path = ""
-                        type_ = int(type_)
+                        type_ = _common.socktype_to_enum(int(type_))
                         # XXX: determining the remote endpoint of a
                         # UNIX socket on Linux is not possible, see:
                         # https://serverfault.com/questions/252723/
diff --git a/psutil/_psosx.py b/psutil/_psosx.py
index 7459a0f3..54e57202 100644
--- a/psutil/_psosx.py
+++ b/psutil/_psosx.py
@@ -8,20 +8,17 @@ import contextlib
 import errno
 import functools
 import os
-from socket import AF_INET
 from collections import namedtuple
 
 from . import _common
 from . import _psposix
 from . import _psutil_osx as cext
 from . import _psutil_posix as cext_posix
-from ._common import AF_INET6
 from ._common import conn_tmap
+from ._common import conn_to_ntuple
 from ._common import isfile_strict
 from ._common import memoize_when_activated
 from ._common import parse_environ_block
-from ._common import sockfam_to_enum
-from ._common import socktype_to_enum
 from ._common import usage_percent
 
 
@@ -529,15 +526,8 @@ class Process(object):
         ret = []
         for item in rawlist:
             fd, fam, type, laddr, raddr, status = item
-            status = TCP_STATUSES[status]
-            fam = sockfam_to_enum(fam)
-            type = socktype_to_enum(type)
-            if fam in (AF_INET, AF_INET6):
-                if laddr:
-                    laddr = _common.addr(*laddr)
-                if raddr:
-                    raddr = _common.addr(*raddr)
-            nt = _common.pconn(fd, fam, type, laddr, raddr, status)
+            nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status,
+                                TCP_STATUSES)
             ret.append(nt)
         return ret
 
diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py
index 6d7fda85..07298a73 100644
--- a/psutil/_pssunos.py
+++ b/psutil/_pssunos.py
@@ -262,6 +262,7 @@ def net_connections(kind, _pid=-1):
             continue
         if type_ not in types:
             continue
+        # TODO: refactor and use _common.conn_to_ntuple.
         if fam in (AF_INET, AF_INET6):
             if laddr:
                 laddr = _common.addr(*laddr)
diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py
index 3f131980..cf938a6f 100644
--- a/psutil/_pswindows.py
+++ b/psutil/_pswindows.py
@@ -33,14 +33,13 @@ except ImportError as err:
         raise
 
 from ._common import conn_tmap
+from ._common import conn_to_ntuple
 from ._common import ENCODING
 from ._common import ENCODING_ERRS
 from ._common import isfile_strict
 from ._common import memoize
 from ._common import memoize_when_activated
 from ._common import parse_environ_block
-from ._common import sockfam_to_enum
-from ._common import socktype_to_enum
 from ._common import usage_percent
 from ._compat import long
 from ._compat import lru_cache
@@ -388,17 +387,8 @@ def net_connections(kind, _pid=-1):
     ret = set()
     for item in rawlist:
         fd, fam, type, laddr, raddr, status, pid = item
-        if laddr:
-            laddr = _common.addr(*laddr)
-        if raddr:
-            raddr = _common.addr(*raddr)
-        status = TCP_STATUSES[status]
-        fam = sockfam_to_enum(fam)
-        type = socktype_to_enum(type)
-        if _pid == -1:
-            nt = _common.sconn(fd, fam, type, laddr, raddr, status, pid)
-        else:
-            nt = _common.pconn(fd, fam, type, laddr, raddr, status)
+        nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES,
+                            pid=pid if _pid == -1 else None)
         ret.add(nt)
     return list(ret)
 


Test patch:
diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py
index 79681719..5e4f37b3 100644
--- a/psutil/tests/__init__.py
+++ b/psutil/tests/__init__.py
@@ -32,10 +32,10 @@ import traceback
 import warnings
 from socket import AF_INET
 from socket import AF_INET6
-from socket import SOCK_DGRAM
 from socket import SOCK_STREAM
 
 import psutil
+from psutil import AIX
 from psutil import MACOS
 from psutil import POSIX
 from psutil import SUNOS
@@ -91,7 +91,7 @@ __all__ = [
     # sync primitives
     'call_until', 'wait_for_pid', 'wait_for_file',
     # network
-    'check_connection_ntuple', 'check_net_address',
+    'check_net_address',
     'get_free_port', 'unix_socket_path', 'bind_socket', 'bind_unix_socket',
     'tcp_socketpair', 'unix_socketpair', 'create_sockets',
     # compat
@@ -175,6 +175,7 @@ except Exception:
 HAS_SENSORS_FANS = hasattr(psutil, "sensors_fans")
 HAS_SENSORS_TEMPERATURES = hasattr(psutil, "sensors_temperatures")
 HAS_THREADS = hasattr(psutil.Process, "threads")
+SKIP_SYSCONS = (MACOS or AIX) and os.getuid() != 0
 
 # --- misc
 
@@ -212,7 +213,6 @@ DEVNULL = open(os.devnull, 'r+')
 VALID_PROC_STATUSES = [getattr(psutil, x) for x in dir(psutil)
                        if x.startswith('STATUS_')]
 AF_UNIX = getattr(socket, "AF_UNIX", object())
-SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object())
 
 _subprocesses_started = set()
 _pids_started = set()
@@ -403,7 +403,7 @@ def create_zombie_proc():
     with contextlib.closing(socket.socket(socket.AF_UNIX)) as sock:
         sock.settimeout(GLOBAL_TIMEOUT)
         sock.bind(unix_file)
-        sock.listen(1)
+        sock.listen(5)
         pyrun(src)
         conn, _ = sock.accept()
         try:
@@ -898,7 +898,7 @@ def bind_socket(family=AF_INET, type=SOCK_STREAM, addr=None):
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         sock.bind(addr)
         if type == socket.SOCK_STREAM:
-            sock.listen(10)
+            sock.listen(5)
         return sock
     except Exception:
         sock.close()
@@ -913,7 +913,7 @@ def bind_unix_socket(name, type=socket.SOCK_STREAM):
     try:
         sock.bind(name)
         if type == socket.SOCK_STREAM:
-            sock.listen(10)
+            sock.listen(5)
     except Exception:
         sock.close()
         raise
@@ -926,7 +926,7 @@ def tcp_socketpair(family, addr=("", 0)):
     """
     with contextlib.closing(socket.socket(family, SOCK_STREAM)) as ll:
         ll.bind(addr)
-        ll.listen(10)
+        ll.listen(5)
         addr = ll.getsockname()
         c = socket.socket(family, SOCK_STREAM)
         try:
@@ -1022,77 +1022,6 @@ def check_net_address(addr, family):
         raise ValueError("unknown family %r", family)
 
 
-def check_connection_ntuple(conn):
-    """Check validity of a connection namedtuple."""
-    # check ntuple
-    assert len(conn) in (6, 7), conn
-    has_pid = len(conn) == 7
-    has_fd = getattr(conn, 'fd', -1) != -1
-    assert conn[0] == conn.fd
-    assert conn[1] == conn.family
-    assert conn[2] == conn.type
-    assert conn[3] == conn.laddr
-    assert conn[4] == conn.raddr
-    assert conn[5] == conn.status
-    if has_pid:
-        assert conn[6] == conn.pid
-
-    # check fd
-    if has_fd:
-        assert conn.fd >= 0, conn
-        if hasattr(socket, 'fromfd') and not WINDOWS:
-            try:
-                dupsock = socket.fromfd(conn.fd, conn.family, conn.type)
-            except (socket.error, OSError) as err:
-                if err.args[0] != errno.EBADF:
-                    raise
-            else:
-                with contextlib.closing(dupsock):
-                    assert dupsock.family == conn.family
-                    assert dupsock.type == conn.type
-
-    # check family
-    assert conn.family in (AF_INET, AF_INET6, AF_UNIX), repr(conn.family)
-    if conn.family in (AF_INET, AF_INET6):
-        # actually try to bind the local socket; ignore IPv6
-        # sockets as their address might be represented as
-        # an IPv4-mapped-address (e.g. "::127.0.0.1")
-        # and that's rejected by bind()
-        if conn.family == AF_INET:
-            s = socket.socket(conn.family, conn.type)
-            with contextlib.closing(s):
-                try:
-                    s.bind((conn.laddr[0], 0))
-                except socket.error as err:
-                    if err.errno != errno.EADDRNOTAVAIL:
-                        raise
-    elif conn.family == AF_UNIX:
-        assert conn.status == psutil.CONN_NONE, conn.status
-
-    # check type (SOCK_SEQPACKET may happen in case of AF_UNIX socks)
-    assert conn.type in (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET), \
-        repr(conn.type)
-    if conn.type == SOCK_DGRAM:
-        assert conn.status == psutil.CONN_NONE, conn.status
-
-    # check laddr (IP address and port sanity)
-    for addr in (conn.laddr, conn.raddr):
-        if conn.family in (AF_INET, AF_INET6):
-            assert isinstance(addr, tuple), addr
-            if not addr:
-                continue
-            assert isinstance(addr.port, int), addr.port
-            assert 0 <= addr.port <= 65535, addr.port
-            check_net_address(addr.ip, conn.family)
-        elif conn.family == AF_UNIX:
-            assert isinstance(addr, str), addr
-
-    # check status
-    assert isinstance(conn.status, str), conn
-    valids = [getattr(psutil, x) for x in dir(psutil) if x.startswith('CONN_')]
-    assert conn.status in valids, conn
-
-
 # ===================================================================
 # --- compatibility
 # ===================================================================
diff --git a/psutil/tests/test_connections.py b/psutil/tests/test_connections.py
index 68eea784..5fda78cb 100755
--- a/psutil/tests/test_connections.py
+++ b/psutil/tests/test_connections.py
@@ -6,6 +6,8 @@
 
 """Tests for net_connections() and Process.connections() APIs."""
 
+import contextlib
+import errno
 import os
 import socket
 import textwrap
@@ -29,14 +31,16 @@ from psutil._compat import PY3
 from psutil.tests import AF_UNIX
 from psutil.tests import bind_socket
 from psutil.tests import bind_unix_socket
-from psutil.tests import check_connection_ntuple
+from psutil.tests import check_net_address
 from psutil.tests import create_sockets
+from psutil.tests import enum
 from psutil.tests import get_free_port
 from psutil.tests import HAS_CONNECTIONS_UNIX
 from psutil.tests import pyrun
 from psutil.tests import reap_children
 from psutil.tests import safe_rmpath
 from psutil.tests import skip_on_access_denied
+from psutil.tests import SKIP_SYSCONS
 from psutil.tests import tcp_socketpair
 from psutil.tests import TESTFN
 from psutil.tests import TRAVIS
@@ -47,6 +51,7 @@ from psutil.tests import wait_for_file
 
 
 thisproc = psutil.Process()
+SOCK_SEQPACKET = getattr(socket, "SOCK_SEQPACKET", object())
 
 
 class Base(object):
@@ -67,6 +72,122 @@ class Base(object):
             cons = thisproc.connections(kind='all')
             assert not cons, cons
 
+    def compare_procsys_connections(self, pid, proc_cons, kind='all'):
+        """Given a process PID and its list of connections compare
+        those against system-wide connections retrieved via
+        psutil.net_connections.
+        """
+        try:
+            sys_cons = psutil.net_connections(kind=kind)
+        except psutil.AccessDenied:
+            # On MACOS, system-wide connections are retrieved by iterating
+            # over all processes
+            if MACOS:
+                return
+            else:
+                raise
+        # Filter for this proc PID and exlucde PIDs from the tuple.
+        sys_cons = [c[:-1] for c in sys_cons if c.pid == pid]
+        sys_cons.sort()
+        proc_cons.sort()
+        self.assertEqual(proc_cons, sys_cons)
+
+    def check_connection_ntuple(self, conn):
+        """Check validity of a connection namedtuple."""
+        def check_ntuple(conn):
+            has_pid = len(conn) == 7
+            self.assertIn(len(conn), (6, 7))
+            self.assertEqual(conn[0], conn.fd)
+            self.assertEqual(conn[1], conn.family)
+            self.assertEqual(conn[2], conn.type)
+            self.assertEqual(conn[3], conn.laddr)
+            self.assertEqual(conn[4], conn.raddr)
+            self.assertEqual(conn[5], conn.status)
+            if has_pid:
+                self.assertEqual(conn[6], conn.pid)
+
+        def check_family(conn):
+            self.assertIn(conn.family, (AF_INET, AF_INET6, AF_UNIX))
+            if enum is not None:
+                assert isinstance(conn.family, enum.IntEnum), conn
+            else:
+                assert isinstance(conn.family, int), conn
+            if conn.family == AF_INET:
+                # actually try to bind the local socket; ignore IPv6
+                # sockets as their address might be represented as
+                # an IPv4-mapped-address (e.g. "::127.0.0.1")
+                # and that's rejected by bind()
+                s = socket.socket(conn.family, conn.type)
+                with contextlib.closing(s):
+                    try:
+                        s.bind((conn.laddr[0], 0))
+                    except socket.error as err:
+                        if err.errno != errno.EADDRNOTAVAIL:
+                            raise
+            elif conn.family == AF_UNIX:
+                self.assertEqual(conn.status, psutil.CONN_NONE)
+
+        def check_type(conn):
+            # SOCK_SEQPACKET may happen in case of AF_UNIX socks
+            self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET))
+            if enum is not None:
+                assert isinstance(conn.type, enum.IntEnum), conn
+            else:
+                assert isinstance(conn.type, int), conn
+            if conn.type == SOCK_DGRAM:
+                self.assertEqual(conn.status, psutil.CONN_NONE)
+
+        def check_addrs(conn):
+            # check IP address and port sanity
+            for addr in (conn.laddr, conn.raddr):
+                if conn.family in (AF_INET, AF_INET6):
+                    self.assertIsInstance(addr, tuple)
+                    if not addr:
+                        continue
+                    self.assertIsInstance(addr.port, int)
+                    assert 0 <= addr.port <= 65535, addr.port
+                    check_net_address(addr.ip, conn.family)
+                elif conn.family == AF_UNIX:
+                    self.assertIsInstance(addr, str)
+
+        def check_status(conn):
+            self.assertIsInstance(conn.status, str)
+            valids = [getattr(psutil, x) for x in dir(psutil)
+                      if x.startswith('CONN_')]
+            self.assertIn(conn.status, valids)
+            if conn.family in (AF_INET, AF_INET6) and conn.type == SOCK_STREAM:
+                self.assertNotEqual(conn.status, psutil.CONN_NONE)
+            else:
+                self.assertEqual(conn.status, psutil.CONN_NONE)
+
+        check_ntuple(conn)
+        check_family(conn)
+        check_type(conn)
+        check_addrs(conn)
+        check_status(conn)
+
+
+class TestBase(Base, unittest.TestCase):
+
+    @unittest.skipIf(SKIP_SYSCONS, "requires root")
+    def test_system(self):
+        with create_sockets():
+            for conn in psutil.net_connections(kind='all'):
+                self.check_connection_ntuple(conn)
+
+    def test_process(self):
+        with create_sockets():
+            for conn in psutil.Process().connections(kind='all'):
+                self.check_connection_ntuple(conn)
+
+    def test_invalid_kind(self):
+        self.assertRaises(ValueError, thisproc.connections, kind='???')
+        self.assertRaises(ValueError, psutil.net_connections, kind='???')
+
+
+class TestUnconnectedSockets(Base, unittest.TestCase):
+    """Tests sockets which are open but not connected to anything."""
+
     def get_conn_from_sock(self, sock):
         cons = thisproc.connections(kind='all')
         smap = dict([(c.fd, c) for c in cons])
@@ -80,14 +201,13 @@ class Base(object):
                 self.assertEqual(smap[sock.fileno()].fd, sock.fileno())
             return cons[0]
 
-    def check_socket(self, sock, conn=None):
+    def check_socket(self, sock):
         """Given a socket, makes sure it matches the one obtained
         via psutil. It assumes this process created one connection
         only (the one supposed to be checked).
         """
-        if conn is None:
-            conn = self.get_conn_from_sock(sock)
-        check_connection_ntuple(conn)
+        conn = self.get_conn_from_sock(sock)
+        self.check_connection_ntuple(conn)
 
         # fd, family, type
         if conn.fd != -1:
@@ -113,38 +233,9 @@ class Base(object):
         # XXX Solaris can't retrieve system-wide UNIX sockets
         if sock.family == AF_UNIX and HAS_CONNECTIONS_UNIX:
             cons = thisproc.connections(kind='all')
-            self.compare_procsys_connections(os.getpid(), cons)
+            self.compare_procsys_connections(os.getpid(), cons, kind='all')
         return conn
 
-    def compare_procsys_connections(self, pid, proc_cons, kind='all'):
-        """Given a process PID and its list of connections compare
-        those against system-wide connections retrieved via
-        psutil.net_connections.
-        """
-        try:
-            sys_cons = psutil.net_connections(kind=kind)
-        except psutil.AccessDenied:
-            # On MACOS, system-wide connections are retrieved by iterating
-            # over all processes
-            if MACOS:
-                return
-            else:
-                raise
-        # Filter for this proc PID and exlucde PIDs from the tuple.
-        sys_cons = [c[:-1] for c in sys_cons if c.pid == pid]
-        sys_cons.sort()
-        proc_cons.sort()
-        self.assertEqual(proc_cons, sys_cons)
-
-
-# =====================================================================
-# --- Test unconnected sockets
-# =====================================================================
-
-
-class TestUnconnectedSockets(Base, unittest.TestCase):
-    """Tests sockets which are open but not connected to anything."""
-
     def test_tcp_v4(self):
         addr = ("127.0.0.1", get_free_port())
         with closing(bind_socket(AF_INET, SOCK_STREAM, addr=addr)) as sock:
@@ -192,12 +283,7 @@ class TestUnconnectedSockets(Base, unittest.TestCase):
                 self.assertEqual(conn.status, psutil.CONN_NONE)
 
 
-# =====================================================================
-# --- Test connected sockets
-# =====================================================================
-
-
-class TestConnectedSocketPairs(Base, unittest.TestCase):
+class TestConnectedSocket(Base, unittest.TestCase):
     """Test socket pairs which are are actually connected to
     each other.
     """
@@ -257,12 +343,58 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
                 server.close()
                 client.close()
 
+
+class TestFilters(Base, unittest.TestCase):
+
+    def test_filters(self):
+        def check(kind, families, types):
+            for conn in thisproc.connections(kind=kind):
+                self.assertIn(conn.family, families)
+                self.assertIn(conn.type, types)
+            if not SKIP_SYSCONS:
+                for conn in psutil.net_connections(kind=kind):
+                    self.assertIn(conn.family, families)
+                    self.assertIn(conn.type, types)
+
+        with create_sockets():
+            check('all',
+                  [AF_INET, AF_INET6, AF_UNIX],
+                  [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET])
+            check('inet',
+                  [AF_INET, AF_INET6],
+                  [SOCK_STREAM, SOCK_DGRAM])
+            check('inet4',
+                  [AF_INET],
+                  [SOCK_STREAM, SOCK_DGRAM])
+            check('tcp',
+                  [AF_INET, AF_INET6],
+                  [SOCK_STREAM])
+            check('tcp4',
+                  [AF_INET],
+                  [SOCK_STREAM])
+            check('tcp6',
+                  [AF_INET6],
+                  [SOCK_STREAM])
+            check('udp',
+                  [AF_INET, AF_INET6],
+                  [SOCK_DGRAM])
+            check('udp4',
+                  [AF_INET],
+                  [SOCK_DGRAM])
+            check('udp6',
+                  [AF_INET6],
+                  [SOCK_DGRAM])
+            if HAS_CONNECTIONS_UNIX:
+                check('unix',
+                      [AF_UNIX],
+                      [SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET])
+
     @skip_on_access_denied(only_if=MACOS)
     def test_combos(self):
         def check_conn(proc, conn, family, type, laddr, raddr, status, kinds):
             all_kinds = ("all", "inet", "inet4", "inet6", "tcp", "tcp4",
                          "tcp6", "udp", "udp4", "udp6")
-            check_connection_ntuple(conn)
+            self.check_connection_ntuple(conn)
             self.assertEqual(conn.family, family)
             self.assertEqual(conn.type, type)
             self.assertEqual(conn.laddr, laddr)
@@ -284,7 +416,7 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
             import socket, time
             s = socket.socket($family, socket.SOCK_STREAM)
             s.bind(('$addr', 0))
-            s.listen(1)
+            s.listen(5)
             with open('$testfn', 'w') as f:
                 f.write(str(s.getsockname()[:2]))
             time.sleep(60)
@@ -352,13 +484,8 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
                                psutil.CONN_NONE,
                                ("all", "inet", "inet6", "udp", "udp6"))
 
-        # err
-        self.assertRaises(ValueError, p.connections, kind='???')
-
-    def test_multi_sockets_filtering(self):
-        with create_sockets() as socks:
-            cons = thisproc.connections(kind='all')
-            self.assertEqual(len(cons), len(socks))
+    def test_count(self):
+        with create_sockets():
             # tcp
             cons = thisproc.connections(kind='tcp')
             self.assertEqual(len(cons), 2 if supports_ipv6() else 1)
@@ -406,8 +533,9 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
                 for conn in cons:
                     self.assertEqual(conn.family, AF_INET6)
                     self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
-            # unix
-            if HAS_CONNECTIONS_UNIX:
+            # unix (skipped on NetBSD because by default the Python process
+            # creates a connection to '/var/run/log' UNIX socket)
+            if HAS_CONNECTIONS_UNIX and not NETBSD:
                 cons = thisproc.connections(kind='unix')
                 self.assertEqual(len(cons), 3)
                 for conn in cons:
@@ -415,23 +543,17 @@ class TestConnectedSocketPairs(Base, unittest.TestCase):
                     self.assertIn(conn.type, (SOCK_STREAM, SOCK_DGRAM))
 
 
-# =====================================================================
-# --- Miscellaneous tests
-# =====================================================================
-
-
+@unittest.skipIf(SKIP_SYSCONS, "requires root")
 class TestSystemWideConnections(Base, unittest.TestCase):
     """Tests for net_connections()."""
 
-    @skip_on_access_denied()
     def test_it(self):
         def check(cons, families, types_):
-            AF_UNIX = getattr(socket, 'AF_UNIX', object())
             for conn in cons:
                 self.assertIn(conn.family, families, msg=conn)
                 if conn.family != AF_UNIX:
                     self.assertIn(conn.type, types_, msg=conn)
-                check_connection_ntuple(conn)
+                self.check_connection_ntuple(conn)
 
         with create_sockets():
             from psutil._common import conn_tmap
@@ -444,16 +566,6 @@ class TestSystemWideConnections(Base, unittest.TestCase):
                 self.assertEqual(len(cons), len(set(cons)))
                 check(cons, families, types_)
 
-            self.assertRaises(ValueError, psutil.net_connections, kind='???')
-
-    @skip_on_access_denied()
-    def test_multi_socks(self):
-        with create_sockets() as socks:
-            cons = [x for x in psutil.net_connections(kind='all')
-                    if x.pid == os.getpid()]
-            self.assertEqual(len(cons), len(socks))
-
-    @skip_on_access_denied()
     # See: https://travis-ci.org/giampaolo/psutil/jobs/237566297
     @unittest.skipIf(MACOS and TRAVIS, "unreliable on MACOS + TRAVIS")
     def test_multi_sockets_procs(self):
@@ -495,11 +607,6 @@ class TestSystemWideConnections(Base, unittest.TestCase):
             self.assertEqual(len(p.connections('all')), expected)
 
 
-# =====================================================================
-# --- Miscellaneous tests
-# =====================================================================
-
-
 class TestMisc(unittest.TestCase):
 
     def test_connection_constants(self):
diff --git a/psutil/tests/test_contracts.py b/psutil/tests/test_contracts.py
index 20da8241..cb4a2b96 100755
--- a/psutil/tests/test_contracts.py
+++ b/psutil/tests/test_contracts.py
@@ -15,7 +15,6 @@ import stat
 import time
 import traceback
 import warnings
-from contextlib import closing
 
 from psutil import AIX
 from psutil import BSD
@@ -29,20 +28,17 @@ from psutil import POSIX
 from psutil import SUNOS
 from psutil import WINDOWS
 from psutil._compat import long
-from psutil.tests import bind_unix_socket
-from psutil.tests import check_connection_ntuple
+from psutil.tests import create_sockets
 from psutil.tests import get_kernel_version
-from psutil.tests import HAS_CONNECTIONS_UNIX
 from psutil.tests import HAS_NET_IO_COUNTERS
 from psutil.tests import HAS_RLIMIT
 from psutil.tests import HAS_SENSORS_FANS
 from psutil.tests import HAS_SENSORS_TEMPERATURES
 from psutil.tests import is_namedtuple
 from psutil.tests import safe_rmpath
-from psutil.tests import skip_on_access_denied
+from psutil.tests import SKIP_SYSCONS
 from psutil.tests import TESTFN
 from psutil.tests import unittest
-from psutil.tests import unix_socket_path
 from psutil.tests import VALID_PROC_STATUSES
 from psutil.tests import warn
 import psutil
@@ -226,16 +222,13 @@ class TestSystem(unittest.TestCase):
             self.assertIsInstance(disk.fstype, str)
             self.assertIsInstance(disk.opts, str)
 
-    @unittest.skipIf(not POSIX, 'POSIX only')
-    @unittest.skipIf(not HAS_CONNECTIONS_UNIX, "can't list UNIX sockets")
-    @skip_on_access_denied(only_if=MACOS)
+    @unittest.skipIf(SKIP_SYSCONS, "requires root")
     def test_net_connections(self):
-        with unix_socket_path() as name:
-            with closing(bind_unix_socket(name)):
-                cons = psutil.net_connections(kind='unix')
-                assert cons
-                for conn in cons:
-                    self.assertIsInstance(conn.laddr, str)
+        with create_sockets():
+            ret = psutil.net_connections('all')
+            self.assertEqual(len(ret), len(set(ret)))
+            for conn in ret:
+                assert is_namedtuple(conn)
 
     def test_net_if_addrs(self):
         # Duplicate of test_system.py. Keep it anyway.
@@ -560,9 +553,10 @@ class TestFetchAllProcesses(unittest.TestCase):
         self.assertGreaterEqual(ret, 0)
 
     def connections(self, ret, proc):
-        self.assertEqual(len(ret), len(set(ret)))
-        for conn in ret:
-            check_connection_ntuple(conn)
+        with create_sockets():
+            self.assertEqual(len(ret), len(set(ret)))
+            for conn in ret:
+                assert is_namedtuple(conn)
 
     def cwd(self, ret, proc):
         if ret:     # 'ret' can be None or empty
diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py
index 29e1e41e..6f923bed 100755
--- a/psutil/tests/test_misc.py
+++ b/psutil/tests/test_misc.py
@@ -20,6 +20,7 @@ import socket
 import stat
 
 from psutil import LINUX
+from psutil import NETBSD
 from psutil import POSIX
 from psutil import WINDOWS
 from psutil._common import memoize
@@ -1011,6 +1012,7 @@ class TestNetUtils(unittest.TestCase):
                 self.assertNotEqual(client.getsockname(), addr)
 
     @unittest.skipIf(not POSIX, "POSIX only")
+    @unittest.skipIf(NETBSD, "/var/run/log UNIX socket opened by default")
     def test_unix_socketpair(self):
         p = psutil.Process()
         num_fds = p.num_fds()