# Path: seaworthy/ps.py
# class PsRow:
#     """
#     Representation of a process list entry, containing the details of a single
#     process.
#     """
# 
#     pid = attr.ib(converter=int)
#     ppid = attr.ib(converter=int)
#     ruser = attr.ib()
#     args = attr.ib()
# 
#     @classmethod
#     def columns(cls):
#         """
#         List the columns required to construct a suitable ``ps`` command.
#         """
#         return [a.name for a in attr.fields(cls)]
# 
# class PsTree:
#     """
#     Node in a process tree, linking a :class:`PsRow` to its child processes.
#     """
#     row = attr.ib()
#     children = attr.ib(default=attr.Factory(list))
# 
#     def count(self):
#         """
#         Return the number of processes in this subtree.
#         """
#         return 1 + sum(row.count() for row in self.children)
# 
# Path: seaworthy/testtools.py
# class MatchesPsTree:
#     """
#     Matches a nested PsTree object in a sensible way.
# 
#     The `ruser` and `args` fields are always checked. The `pid` and `ppid`
#     fields are only checked if non-None values are explicitly provided, because
#     real pids are essentially arbitrary integers. The `children` field is
#     always checked, but order is ignored.
#     """
# 
#     def __init__(self, ruser, args, pid=None, ppid=None, children=()):
#         self.row_fields = {'ruser': ruser, 'args': args}
#         if pid is not None:
#             self.row_fields['pid'] = pid
#         if ppid is not None:
#             self.row_fields['ppid'] = ppid
#         self.children = children
# 
#     def __str__(self):
#         rfs = ['{}={!r}'.format(k, v)
#                for k, v in sorted(self.row_fields.items())]
#         return '{}({}, children={})'.format(
#             self.__class__.__name__, ', '.join(rfs), str(self.children))
# 
#     def match(self, value):
#         """
#         Return ``None`` if this matcher matches something, a
#         :class:`PsTreeMismatch` otherwise.
#         """
#         fields_mm = MatchesStructure.byEquality(**self.row_fields).match(
#             value.row)
#         children_mm = MatchesSetwise(*self.children).match(value.children)
#         if fields_mm is not None or children_mm is not None:
#             return PsTreeMismatch(
#                 self.row_fields, len(self.children), fields_mm, children_mm)


import unittest
from testtools.assertions import assert_that
from testtools.matchers import Not
from seaworthy.ps import PsRow, PsTree
from seaworthy.testtools import MatchesPsTree
                MatchesPsTree('root', 'app2 --child'),
            ]),
        ])
        assert matcher.match(ps_tree) is None

        # Check different children in a different order.
        matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[
            MatchesPsTree('root', 'app2 --arg', children=[
                MatchesPsTree('root', 'app2 --child'),
            ]),
            MatchesPsTree('root', 'app --arg', children=[
                MatchesPsTree('appuser', 'app --child1'),
                MatchesPsTree('appuser', 'app --child2'),
            ]),
        ])
        assert matcher.match(ps_tree) is None

    def test_nested_tree_missing_child(self):
        """
        MatchesPsTree can detect a missing child process.
        """
        ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- app'), [
            PsTree(PsRow(2, 1, 'root', 'app --arg'), [
                PsTree(PsRow(3, 2, 'appuser', 'app --child1')),
            ]),
            PsTree(PsRow(5, 1, 'root', 'app2 --arg'), [
                PsTree(PsRow(6, 5, 'root', 'app2 --child')),
            ]),
        ])


# Path: seaworthy/ps.py
# class PsRow:
#     """
#     Representation of a process list entry, containing the details of a single
#     process.
#     """
# 
#     pid = attr.ib(converter=int)
#     ppid = attr.ib(converter=int)
#     ruser = attr.ib()
#     args = attr.ib()
# 
#     @classmethod
#     def columns(cls):
#         """
#         List the columns required to construct a suitable ``ps`` command.
#         """
#         return [a.name for a in attr.fields(cls)]
# 
# class PsTree:
#     """
#     Node in a process tree, linking a :class:`PsRow` to its child processes.
#     """
#     row = attr.ib()
#     children = attr.ib(default=attr.Factory(list))
# 
#     def count(self):
#         """
#         Return the number of processes in this subtree.
#         """
#         return 1 + sum(row.count() for row in self.children)
# 
# Path: seaworthy/testtools.py
# class MatchesPsTree:
#     """
#     Matches a nested PsTree object in a sensible way.
# 
#     The `ruser` and `args` fields are always checked. The `pid` and `ppid`
#     fields are only checked if non-None values are explicitly provided, because
#     real pids are essentially arbitrary integers. The `children` field is
#     always checked, but order is ignored.
#     """
# 
#     def __init__(self, ruser, args, pid=None, ppid=None, children=()):
#         self.row_fields = {'ruser': ruser, 'args': args}
#         if pid is not None:
#             self.row_fields['pid'] = pid
#         if ppid is not None:
#             self.row_fields['ppid'] = ppid
#         self.children = children
# 
#     def __str__(self):
#         rfs = ['{}={!r}'.format(k, v)
#                for k, v in sorted(self.row_fields.items())]
#         return '{}({}, children={})'.format(
#             self.__class__.__name__, ', '.join(rfs), str(self.children))
# 
#     def match(self, value):
#         """
#         Return ``None`` if this matcher matches something, a
#         :class:`PsTreeMismatch` otherwise.
#         """
#         fields_mm = MatchesStructure.byEquality(**self.row_fields).match(
#             value.row)
#         children_mm = MatchesSetwise(*self.children).match(value.children)
#         if fields_mm is not None or children_mm is not None:
#             return PsTreeMismatch(
#                 self.row_fields, len(self.children), fields_mm, children_mm)


# Path: seaworthy/tests-testtools/test_testtools.py
import unittest
from testtools.assertions import assert_that
from testtools.matchers import Not
from seaworthy.ps import PsRow, PsTree
from seaworthy.testtools import MatchesPsTree

                MatchesPsTree('root', 'app2 --child'),
            ]),
        ])
        assert matcher.match(ps_tree) is None

        # Check different children in a different order.
        matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[
            MatchesPsTree('root', 'app2 --arg', children=[
                MatchesPsTree('root', 'app2 --child'),
            ]),
            MatchesPsTree('root', 'app --arg', children=[
                MatchesPsTree('appuser', 'app --child1'),
                MatchesPsTree('appuser', 'app --child2'),
            ]),
        ])
        assert matcher.match(ps_tree) is None

    def test_nested_tree_missing_child(self):
        """
        MatchesPsTree can detect a missing child process.
        """
        ps_tree = PsTree(PsRow(1, 0, 'root', 'tini -- app'), [
            PsTree(PsRow(2, 1, 'root', 'app --arg'), [
                PsTree(PsRow(3, 2, 'appuser', 'app --child1')),
            ]),
            PsTree(PsRow(5, 1, 'root', 'app2 --arg'), [
                PsTree(PsRow(6, 5, 'root', 'app2 --child')),
            ]),
        ])


        matcher = MatchesPsTree('root', 'tini -- app', pid=1, children=[