Example #1
0
 def setUp(self):
     document = Document(SYS)
     self.tree = Tree(document)
     document.tree = self.tree
     document = Document(FILES)
     self.tree._place(document)  # pylint: disable=W0212
     document.tree = self.tree
Example #2
0
 def setUpClass(cls):
     a = Tree('a', root='.')
     b1 = Tree('b1', parent=a, root='.')
     b2 = Tree('b2', parent=a, root='.')
     c1 = Tree('c1', parent=b2, root='.')
     c2 = Tree('c2', parent=b2, root='.')
     a.children = [b1, b2]
     b2.children = [c1, c2]
     cls.tree = a
Example #3
0
def build(cwd=None, root=None):
    """Build a tree from the current working directory or explicit root.

    :param cwd: current working directory
    :param root: path to root of the working copy

    :raises: :class:`~doorstop.common.DoorstopError` when the tree
        cannot be built

    :return: new :class:`~doorstop.core.tree.Tree`

    """
    documents = []

    # Find the root of the working copy
    cwd = cwd or os.getcwd()
    root = root or vcs.find_root(cwd)

    # Find all documents in the working copy
    log.info("looking for documents in {}...".format(root))
    _document_from_path(root, root, documents)
    for dirpath, dirnames, _filenames in os.walk(root):
        for dirname in dirnames:
            path = os.path.join(dirpath, dirname)
            _document_from_path(path, root, documents)

    # Build the tree
    if not documents:
        log.info("no documents found in: {}".format(root))
    log.info("building tree...")
    tree = Tree.from_list(documents, root=root)
    log.info("built tree: {}".format(tree))
    return tree
Example #4
0
 def setUp(self):
     # Create default item attributes
     self.uid = 'PREFIX-00042'
     # Ensure the tree is reloaded
     mock_document = Mock()
     mock_document.root = self.root
     mock_document.prefix = self.prefix
     self.mock_tree = Tree(mock_document)
     _set_tree(self.mock_tree)
Example #5
0
 def test_create_document_explicit_tree(self, mock_new, mock_get_tree):
     """Verify a new document can be created for import (explicit tree)."""
     mock_document = Mock()
     mock_document.root = None
     tree = Tree(document=mock_document)
     importer.create_document(self.prefix, self.path, tree=tree)
     self.assertFalse(mock_get_tree.called)
     mock_new.assert_called_once_with(self.path, self.prefix, parent=None)
     self.assertIn(mock_document, tree)
Example #6
0
    def test_place_no_parent(self):
        """Verify an error occurs when a node is missing a parent."""

        a = MockDocumentSkip(EMPTY)
        a.prefix = 'A'
        b = MockDocumentSkip(EMPTY)
        b.prefix = 'B'
        tree = Tree(a)
        self.assertRaises(DoorstopError, tree._place, b)  # pylint: disable=W0212
Example #7
0
 def setUp(self):
     # Create default document options
     self.prefix = 'PREFIX'
     self.root = 'ROOT'
     self.path = os.path.join(self.root, 'DIRECTORY')
     self.parent = 'PARENT_PREFIX'
     # Ensure the tree is reloaded
     mock_document = Mock()
     mock_document.root = self.root
     self.mock_tree = Tree(mock_document)
     _set_tree(self.mock_tree)
Example #8
0
 def setUpClass(cls):
     a = Tree('a', root='.')
     b1 = Tree('b1', parent=a, root='.')
     d = Tree('d', parent=b1, root='.')
     e = Tree('e', parent=d, root='.')
     b2 = Tree('b2', parent=a, root='.')
     c1 = Tree('c1', parent=b2, root='.')
     c2 = Tree('c2', parent=b2, root='.')
     a.children = [b1, b2]
     b1.children = [d]
     d.children = [e]
     b2.children = [c1, c2]
     cls.tree = a
Example #9
0
class TestModule(unittest.TestCase):
    """Unit tests for the doorstop.core.builder module."""
    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_run_empty(self):
        """Verify an empty directory is an empty hierarchy."""
        tree = build(EMPTY)
        self.assertEqual(0, len(tree))

    @patch('doorstop.core.document.Document', MockDocumentNoSkip)
    @patch('doorstop.core.vcs.find_root', Mock(return_value=FILES))
    def test_build(self):
        """Verify a tree can be built."""
        tree = build(FILES)
        self.assertEqual(4, len(tree))

    @patch('doorstop.core.document.Document', MockDocumentSkip)
    @patch('doorstop.core.vcs.find_root', Mock(return_value=FILES))
    def test_build_with_skips(self):
        """Verify documents can be skipped while building a tree."""
        tree = build(FILES)
        self.assertEqual(0, len(tree))

    @patch('doorstop.core.builder.build', Mock(return_value=Tree(Mock())))
    @patch('doorstop.core.tree.Tree.find_document')
    def test_find_document(self, mock_find_document):  # pylint: disable=R0201
        """Verify documents can be found using a convenience function."""
        _clear_tree()
        prefix = 'req'
        find_document(prefix)
        mock_find_document.assert_called_once_with(prefix)

    @patch('doorstop.core.builder.build', Mock(return_value=Tree(Mock())))
    @patch('doorstop.core.tree.Tree.find_item')
    def test_find_item(self, mock_find_item):  # pylint: disable=R0201
        """Verify items can be found using a convenience function."""
        _clear_tree()
        uid = 'req1'
        find_item(uid)
        mock_find_item.assert_called_once_with(uid)
Example #10
0
 def test_from_list(self):
     """Verify a tree can be created from a list."""
     a = MockDocumentSkip(EMPTY)
     a.prefix = 'A'
     b = MockDocumentSkip(EMPTY)
     b.prefix = 'B'
     b.parent = 'A'
     c = MockDocumentSkip(EMPTY)
     c.prefix = 'C'
     c.parent = 'B'
     docs = [a, b, c]
     tree = Tree.from_list(docs)
     self.assertEqual(3, len(tree))
     self.assertTrue(tree.validate())
Example #11
0
 def test_from_list(self):
     """Verify a tree can be created from a list."""
     a = MockDocumentSkip(EMPTY)
     a.prefix = 'A'
     b = MockDocumentSkip(EMPTY)
     b.prefix = 'B'
     b.parent = 'A'
     c = MockDocumentSkip(EMPTY)
     c.prefix = 'C'
     c.parent = 'B'
     docs = [a, b, c]
     tree = Tree.from_list(docs)
     self.assertEqual(3, len(tree))
     self.assertTrue(tree.validate())
Example #12
0
def build(cwd=None, root=None, request_next_number=None) -> Tree:
    """Build a tree from the current working directory or explicit root.

    :param cwd: current working directory
    :param root: path to root of the working copy
    :param request_next_number: server method to get a document's next number

    :raises: :class:`~doorstop.common.DoorstopError` when the tree
        cannot be built

    :return: new :class:`~doorstop.core.tree.Tree`

    """
    documents: List[Document] = []

    # Find the root of the working copy
    cwd = cwd or os.getcwd()
    root = root or vcs.find_root(cwd)

    # Find all documents in the working copy
    log.info("looking for documents in {}...".format(root))
    skip_file_name = '.doorstop.skip-all'
    if not os.path.isfile(os.path.join(root, skip_file_name)):
        _document_from_path(root, root, documents)
    exclude_dirnames = {'.git'}
    if not os.path.isfile(os.path.join(root, skip_file_name)):
        for dirpath, dirnames, _ in os.walk(root, topdown=True):
            whilelist_dirnames = []
            for dirname in dirnames:
                if dirname in exclude_dirnames:
                    continue
                path = os.path.join(dirpath, dirname)
                if os.path.isfile(os.path.join(path, skip_file_name)):
                    continue
                whilelist_dirnames.append(dirname)
                _document_from_path(path, root, documents)
            dirnames[:] = whilelist_dirnames

    # Build the tree
    if not documents:
        log.info("no documents found in: {}".format(root))
    log.info("building tree...")
    tree = Tree.from_list(documents, root=root)
    tree.request_next_number = request_next_number
    if len(tree):  # pylint: disable=len-as-condition
        log.info("built tree: {}".format(tree))
    else:
        log.info("tree is empty")
    return tree
Example #13
0
def build(cwd=None, root=None, request_next_number=None):
    """Build a tree from the current working directory or explicit root.

    :param cwd: current working directory
    :param root: path to root of the working copy
    :param request_next_number: server method to get a document's next number

    :raises: :class:`~doorstop.common.DoorstopError` when the tree
        cannot be built

    :return: new :class:`~doorstop.core.tree.Tree`

    """
    documents = []

    # Find the root of the working copy
    cwd = cwd or os.getcwd()
    root = root or vcs.find_root(cwd)

    # Find all documents in the working copy
    log.info("looking for documents in {}...".format(root))
    _document_from_path(root, root, documents)
    for dirpath, dirnames, _ in os.walk(root):
        for dirname in dirnames:
            path = os.path.join(dirpath, dirname)
            _document_from_path(path, root, documents)

    # Build the tree
    if not documents:
        log.info("no documents found in: {}".format(root))
    log.info("building tree...")
    tree = Tree.from_list(documents, root=root)
    tree.request_next_number = request_next_number
    if len(tree):  # pylint: disable=len-as-condition
        log.info("built tree: {}".format(tree))
    else:
        log.info("tree is empty")
    return tree
Example #14
0
class TestTree(unittest.TestCase):
    """Unit tests for the Tree class."""
    def setUp(self):
        document = Document(SYS)
        self.tree = Tree(document)
        document.tree = self.tree
        document = Document(FILES)
        self.tree._place(document)  # pylint: disable=W0212
        document.tree = self.tree
        self.tree._vcs = Mock()  # pylint: disable=W0212

    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_palce_empty(self):
        """Verify a document can be placed in an empty tree."""
        tree = build(EMPTY)
        doc = MockDocumentSkip.new(tree, os.path.join(EMPTY, 'temp'), EMPTY,
                                   'TEMP')
        tree._place(doc)  # pylint: disable=W0212
        self.assertEqual(1, len(tree))

    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_palce_empty_no_parent(self):
        """Verify a document with parent cannot be placed in an empty tree."""
        tree = build(EMPTY)
        doc = MockDocumentSkip.new(tree,
                                   os.path.join(EMPTY, 'temp'),
                                   EMPTY,
                                   'TEMP',
                                   parent='REQ')
        self.assertRaises(DoorstopError, tree._place, doc)  # pylint: disable=W0212

    def test_documents(self):
        """Verify the documents in a tree can be accessed."""
        documents = self.tree.documents
        self.assertEqual(2, len(documents))
        for document in self.tree:
            logging.debug("document: {}".format(document))
            self.assertIs(self.tree, document.tree)

    @patch('doorstop.core.document.Document.get_issues')
    def test_validate(self, mock_get_issues):
        """Verify trees can be checked."""
        logging.info("tree: {}".format(self.tree))
        self.assertTrue(self.tree.validate())
        self.assertEqual(2, mock_get_issues.call_count)

    def test_validate_no_documents(self):
        """Verify an empty tree can be checked."""
        tree = Tree(None, root='.')
        self.assertTrue(tree.validate())

    @patch('doorstop.settings.REORDER', False)
    @patch('doorstop.core.document.Document.get_issues',
           Mock(return_value=[
               DoorstopError('error'),
               DoorstopWarning('warning'),
               DoorstopInfo('info')
           ]))
    def test_validate_document(self):
        """Verify a document error fails the tree validation."""
        self.assertFalse(self.tree.validate())

    @patch('doorstop.core.document.Document.get_issues', Mock(return_value=[]))
    def test_validate_hook(self):
        """Verify a document hook can be called."""
        mock_hook = MagicMock()
        self.tree.validate(document_hook=mock_hook)
        self.assertEqual(2, mock_hook.call_count)

    @patch('doorstop.core.tree.Tree.get_issues', Mock(return_value=[]))
    def test_issues(self):
        """Verify an tree's issues convenience property can be accessed."""
        self.assertEqual(0, len(self.tree.issues))

    def test_get_traceability(self):
        """Verify traceability rows are correct."""
        rows = [
            (self.tree.find_item('SYS001'), self.tree.find_item('REQ001')),
            (self.tree.find_item('SYS002'), self.tree.find_item('REQ001')),
            (None, self.tree.find_item('REQ002')),
            (None, self.tree.find_item('REQ004')),
        ]
        # Act
        rows2 = self.tree.get_traceability()
        # Assert
        self.maxDiff = None
        self.assertListEqual(rows, rows2)

    def test_new_document(self):
        """Verify a new document can be created on a tree."""
        self.tree.create_document(EMPTY, '_TEST', parent='REQ')

    def test_new_document_unknown_parent(self):
        """Verify an exception is raised for an unknown parent."""
        temp = tempfile.mkdtemp()
        self.assertRaises(DoorstopError,
                          self.tree.create_document,
                          temp,
                          '_TEST',
                          parent='UNKNOWN')
        self.assertFalse(os.path.exists(temp))

    @patch('doorstop.core.document.Document.add_item')
    def test_add_item(self, mock_add_item):
        """Verify an item can be added to a document."""
        self.tree.add_item('REQ')
        mock_add_item.assert_called_once_with(number=None,
                                              level=None,
                                              reorder=True)

    @patch('doorstop.core.document.Document.add_item')
    def test_add_item_level(self, mock_add):
        """Verify an item can be added to a document with a level."""
        self.tree.add_item('REQ', level='1.2.3')
        mock_add.assert_called_once_with(number=None,
                                         level='1.2.3',
                                         reorder=True)

    def test_add_item_unknown_prefix(self):
        """Verify an exception is raised for an unknown prefix (item)."""
        # Cache miss
        self.assertRaises(DoorstopError, self.tree.add_item, 'UNKNOWN')
        # Cache hit
        self.assertRaises(DoorstopError, self.tree.add_item, 'UNKNOWN')

    @patch('doorstop.settings.REORDER', False)
    @patch('doorstop.core.item.Item.delete')
    def test_remove_item(self, mock_delete):
        """Verify an item can be removed from a document."""
        self.tree.remove_item('req1', reorder=False)
        mock_delete.assert_called_once_with()

    def test_remove_item_unknown_item(self):
        """Verify an exception is raised removing an unknown item."""
        self.assertRaises(DoorstopError, self.tree.remove_item, 'req9999')

    @patch('doorstop.core.item.Item.link')
    def test_link_items(self, mock_link):
        """Verify two items can be linked."""
        self.tree.link_items('req1', 'req2')
        mock_link.assert_called_once_with('REQ002')

    def test_link_items_unknown_child_prefix(self):
        """Verify an exception is raised with an unknown child prefix."""
        self.assertRaises(DoorstopError, self.tree.link_items, 'unknown1',
                          'req2')

    def test_link_items_unknown_child_number(self):
        """Verify an exception is raised with an unknown child number."""
        self.assertRaises(DoorstopError, self.tree.link_items, 'req9999',
                          'req2')

    def test_link_items_unknown_parent_prefix(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError, self.tree.link_items, 'req1',
                          'unknown1')

    def test_link_items_unknown_parent_number(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError, self.tree.link_items, 'req1',
                          'req9999')

    @patch('doorstop.core.item.Item.unlink')
    def test_unlink_items(self, mock_unlink):
        """Verify two items can be unlinked."""
        self.tree.unlink_items('req3', 'req1')
        mock_unlink.assert_called_once_with('REQ001')

    def test_unlink_items_unknown_child_prefix(self):
        """Verify an exception is raised with an unknown child prefix."""
        self.assertRaises(DoorstopError, self.tree.unlink_items, 'unknown1',
                          'req1')

    def test_unlink_items_unknown_child_number(self):
        """Verify an exception is raised with an unknown child number."""
        self.assertRaises(DoorstopError, self.tree.unlink_items, 'req9999',
                          'req1')

    def test_unlink_items_unknown_parent_prefix(self):
        """Verify an exception is raised with an unknown parent prefix."""
        # Cache miss
        self.assertRaises(DoorstopError, self.tree.unlink_items, 'req3',
                          'unknown1')
        # Cache hit
        self.assertRaises(DoorstopError, self.tree.unlink_items, 'req3',
                          'unknown1')

    def test_unlink_items_unknown_parent_number(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError, self.tree.unlink_items, 'req3',
                          'req9999')

    @patch('doorstop.core.editor.launch')
    def test_edit_item(self, mock_launch):
        """Verify an item can be edited in a tree."""
        self.tree.edit_item('req2', launch=True)
        path = os.path.join(FILES, 'REQ002.yml')
        mock_launch.assert_called_once_with(path, tool=None)

    def test_edit_item_unknown_prefix(self):
        """Verify an exception is raised for an unknown prefix (document)."""
        self.assertRaises(DoorstopError, self.tree.edit_item, 'unknown1')

    def test_edit_item_unknown_number(self):
        """Verify an exception is raised for an unknown number."""
        self.assertRaises(DoorstopError, self.tree.edit_item, 'req9999')

    def test_find_item(self):
        """Verify an item can be found by exact UID."""
        # Cache miss
        item = self.tree.find_item('req2-001')
        self.assertIsNot(None, item)
        # Cache hit
        item2 = self.tree.find_item('req2-001')
        self.assertIs(item2, item)

    def test_find_document(self):
        """Verify an document can be found by prefix"""
        # Cache miss
        document = self.tree.find_document('req')
        self.assertIsNot(None, document)
        # Cache hit
        document2 = self.tree.find_document('req')
        self.assertIs(document2, document)

    def test_load(self):
        """Verify an a tree can be reloaded."""
        self.tree.load()
        self.tree.load()  # should return immediately

    @patch('doorstop.core.document.Document.delete')
    def test_delete(self, mock_delete):
        """Verify a tree can be deleted."""
        self.tree.delete()
        self.assertEqual(0, len(self.tree))
        self.assertEqual(2, mock_delete.call_count)
        self.tree.delete()  # ensure a second delete is ignored
Example #15
0
class TestModule(unittest.TestCase):
    """Unit tests for the doorstop.core.builder module."""
    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_run_empty(self):
        """Verify an empty directory is an empty hierarchy."""
        tree = build(EMPTY)
        self.assertEqual(0, len(tree))

    @patch('doorstop.core.document.Document', MockDocumentNoSkip)
    @patch('doorstop.core.vcs.find_root', Mock(return_value=FILES))
    def test_build(self):
        """Verify a tree can be built."""
        tree = build(FILES)
        self.assertEqual(4, len(tree))

    @patch('doorstop.core.document.Document', MockDocumentSkip)
    @patch('doorstop.core.vcs.find_root', Mock(return_value=FILES))
    def test_build_with_skips(self):
        """Verify documents can be skipped while building a tree."""
        tree = build(FILES)
        self.assertEqual(0, len(tree))

    @patch('doorstop.core.builder.build', Mock(return_value=Tree(Mock())))
    @patch('doorstop.core.tree.Tree.find_document')
    def test_find_document(self, mock_find_document):  # pylint: disable=R0201
        """Verify documents can be found using a convenience function."""
        _clear_tree()
        prefix = 'req'
        find_document(prefix)
        mock_find_document.assert_called_once_with(prefix)

    @patch('doorstop.core.builder.build', Mock(return_value=Tree(Mock())))
    @patch('doorstop.core.tree.Tree.find_item')
    def test_find_item(self, mock_find_item):  # pylint: disable=R0201
        """Verify items can be found using a convenience function."""
        _clear_tree()
        uid = 'req1'
        find_item(uid)
        mock_find_item.assert_called_once_with(uid)

    def test_tree_finds_documents(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')
        item = Item.new(tree, document, cwd, cwd, "TST-001")
        item.save()

        # Step 2: Find a newly created tree
        same_tree_again = build(cwd, root)

        # Verify that that the tree, document and its item can be found.
        self.assertEqual(1, len(same_tree_again.documents))
        document = same_tree_again.document
        self.assertIsNotNone(document)
        self.assertEqual(1, len(document.items))
        item = document.items[0]
        self.assertEqual('TST-001', item.uid)

    def test_tree_does_not_find_documents_when_skipall_file_present(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')
        item = Item.new(tree, document, cwd, cwd, "TST-001")
        item.save()

        # Step 2: Put a .doorstop.skip-all to the root of the tree.
        path = os.path.join(temp, '.doorstop.skip-all')
        open(path, 'a').close()

        # Step 3: Find a newly created tree
        same_tree_again = build(cwd, root)

        # Verify that the tree does not have a document because it was ignored.
        document = same_tree_again.document
        self.assertIsNone(document)

    def test_tree_finds_subdocuments(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item in a document in a subfolder.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')

        subfolder = os.path.join(temp, 'SUBFOLDER')
        os.makedirs(os.path.join(temp, subfolder))

        sub_document = tree.create_document(subfolder, 'TST_SUB', parent='TST')

        item = Item.new(tree, sub_document, subfolder, cwd, "TST_SUB-001")
        item.save()

        # Step 2: Read existing tree
        same_tree_again = build(cwd, root)

        # Verify that the tree has:
        # - both root-level and subfolder documents
        # - item in a subdocument
        self.assertEqual(2, len(same_tree_again.documents))
        sub_document = same_tree_again.documents[1]
        self.assertIsNotNone(document)
        self.assertEqual(1, len(sub_document.items))
        item = sub_document.items[0]
        self.assertEqual('TST_SUB-001', item.uid)

    def test_tree_skips_subdocuments_when_skipall_file_present(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item in a document in a subfolder.
        tree = build(cwd, root)
        tree.create_document(cwd, 'TST')

        subfolder = os.path.join(temp, 'SUBFOLDER')
        os.makedirs(os.path.join(temp, subfolder))

        sub_document = tree.create_document(subfolder, 'TST_SUB', parent='TST')

        item = Item.new(tree, sub_document, subfolder, cwd, "TST_SUB-001")
        item.save()

        # Step 2: Put a .doorstop.skip-all into the subfolder.
        path = os.path.join(subfolder, '.doorstop.skip-all')
        open(path, 'a').close()

        # Verify that building tree ignores subfolder's document.
        same_tree_again = build(cwd, root)
        self.assertEqual(1, len(same_tree_again.documents))
Example #16
0
 def test_validate_no_documents(self):
     """Verify an empty tree can be checked."""
     tree = Tree(None, root='.')
     self.assertTrue(tree.validate())
Example #17
0
class TestTree(unittest.TestCase):
    """Unit tests for the Tree class."""

    def setUp(self):
        document = Document(SYS)
        self.tree = Tree(document)
        document.tree = self.tree
        document = Document(FILES)
        self.tree._place(document)  # pylint: disable=W0212
        document.tree = self.tree
        self.tree._vcs = Mock()  # pylint: disable=W0212

    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_palce_empty(self):
        """Verify a document can be placed in an empty tree."""
        tree = build(EMPTY)
        doc = MockDocumentSkip.new(tree,
                                   os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP')
        tree._place(doc)  # pylint: disable=W0212
        self.assertEqual(1, len(tree))

    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_palce_empty_no_parent(self):
        """Verify a document with parent cannot be placed in an empty tree."""
        tree = build(EMPTY)
        doc = MockDocumentSkip.new(tree,
                                   os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP',
                                   parent='REQ')
        self.assertRaises(DoorstopError, tree._place, doc)  # pylint: disable=W0212

    def test_documents(self):
        """Verify the documents in a tree can be accessed."""
        documents = self.tree.documents
        self.assertEqual(2, len(documents))
        for document in self.tree:
            logging.debug("document: {}".format(document))
            self.assertIs(self.tree, document.tree)

    @patch('doorstop.core.document.Document.get_issues')
    def test_validate(self, mock_get_issues):
        """Verify trees can be checked."""
        logging.info("tree: {}".format(self.tree))
        self.assertTrue(self.tree.validate())
        self.assertEqual(2, mock_get_issues.call_count)

    def test_validate_no_documents(self):
        """Verify an empty tree can be checked."""
        tree = Tree(None, root='.')
        self.assertTrue(tree.validate())

    @patch('doorstop.settings.REORDER', False)
    @patch('doorstop.core.document.Document.get_issues',
           Mock(return_value=[DoorstopError('error'),
                              DoorstopWarning('warning'),
                              DoorstopInfo('info')]))
    def test_validate_document(self):
        """Verify a document error fails the tree validation."""
        self.assertFalse(self.tree.validate())

    @patch('doorstop.core.document.Document.get_issues', Mock(return_value=[]))
    def test_validate_hook(self):
        """Verify a document hook can be called."""
        mock_hook = MagicMock()
        self.tree.validate(document_hook=mock_hook)
        self.assertEqual(2, mock_hook.call_count)

    @patch('doorstop.core.tree.Tree.get_issues', Mock(return_value=[]))
    def test_issues(self):
        """Verify an tree's issues convenience property can be accessed."""
        self.assertEqual(0, len(self.tree.issues))

    def test_get_traceability(self):
        """Verify traceability rows are correct."""
        rows = [
            (self.tree.find_item('SYS001'), self.tree.find_item('REQ001')),
            (self.tree.find_item('SYS002'), self.tree.find_item('REQ001')),
            (None, self.tree.find_item('REQ002')),
            (None, self.tree.find_item('REQ004')),
        ]
        # Act
        rows2 = self.tree.get_traceability()
        # Assert
        self.maxDiff = None
        self.assertListEqual(rows, rows2)

    def test_new_document(self):
        """Verify a new document can be created on a tree."""
        self.tree.create_document(EMPTY, '_TEST', parent='REQ')

    def test_new_document_unknown_parent(self):
        """Verify an exception is raised for an unknown parent."""
        temp = tempfile.mkdtemp()
        self.assertRaises(DoorstopError, self.tree.create_document,
                          temp, '_TEST', parent='UNKNOWN')
        self.assertFalse(os.path.exists(temp))

    @patch('doorstop.core.document.Document.add_item')
    def test_add_item(self, mock_add_item):
        """Verify an item can be added to a document."""
        self.tree.add_item('REQ')
        mock_add_item.assert_called_once_with(number=None, level=None,
                                              reorder=True)

    @patch('doorstop.core.document.Document.add_item')
    def test_add_item_level(self, mock_add):
        """Verify an item can be added to a document with a level."""
        self.tree.add_item('REQ', level='1.2.3')
        mock_add.assert_called_once_with(number=None, level='1.2.3',
                                         reorder=True)

    def test_add_item_unknown_prefix(self):
        """Verify an exception is raised for an unknown prefix (item)."""
        # Cache miss
        self.assertRaises(DoorstopError, self.tree.add_item, 'UNKNOWN')
        # Cache hit
        self.assertRaises(DoorstopError, self.tree.add_item, 'UNKNOWN')

    @patch('doorstop.settings.REORDER', False)
    @patch('doorstop.core.item.Item.delete')
    def test_remove_item(self, mock_delete):
        """Verify an item can be removed from a document."""
        self.tree.remove_item('req1', reorder=False)
        mock_delete.assert_called_once_with()

    def test_remove_item_unknown_item(self):
        """Verify an exception is raised removing an unknown item."""
        self.assertRaises(DoorstopError, self.tree.remove_item, 'req9999')

    @patch('doorstop.core.item.Item.link')
    def test_link_items(self, mock_link):
        """Verify two items can be linked."""
        self.tree.link_items('req1', 'req2')
        mock_link.assert_called_once_with('REQ002')

    def test_link_items_unknown_child_prefix(self):
        """Verify an exception is raised with an unknown child prefix."""
        self.assertRaises(DoorstopError,
                          self.tree.link_items, 'unknown1', 'req2')

    def test_link_items_unknown_child_number(self):
        """Verify an exception is raised with an unknown child number."""
        self.assertRaises(DoorstopError,
                          self.tree.link_items, 'req9999', 'req2')

    def test_link_items_unknown_parent_prefix(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError,
                          self.tree.link_items, 'req1', 'unknown1')

    def test_link_items_unknown_parent_number(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError,
                          self.tree.link_items, 'req1', 'req9999')

    @patch('doorstop.core.item.Item.unlink')
    def test_unlink_items(self, mock_unlink):
        """Verify two items can be unlinked."""
        self.tree.unlink_items('req3', 'req1')
        mock_unlink.assert_called_once_with('REQ001')

    def test_unlink_items_unknown_child_prefix(self):
        """Verify an exception is raised with an unknown child prefix."""
        self.assertRaises(DoorstopError,
                          self.tree.unlink_items, 'unknown1', 'req1')

    def test_unlink_items_unknown_child_number(self):
        """Verify an exception is raised with an unknown child number."""
        self.assertRaises(DoorstopError,
                          self.tree.unlink_items, 'req9999', 'req1')

    def test_unlink_items_unknown_parent_prefix(self):
        """Verify an exception is raised with an unknown parent prefix."""
        # Cache miss
        self.assertRaises(DoorstopError,
                          self.tree.unlink_items, 'req3', 'unknown1')
        # Cache hit
        self.assertRaises(DoorstopError,
                          self.tree.unlink_items, 'req3', 'unknown1')

    def test_unlink_items_unknown_parent_number(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError,
                          self.tree.unlink_items, 'req3', 'req9999')

    @patch('doorstop.core.editor.launch')
    def test_edit_item(self, mock_launch):
        """Verify an item can be edited in a tree."""
        self.tree.edit_item('req2', launch=True)
        path = os.path.join(FILES, 'REQ002.yml')
        mock_launch.assert_called_once_with(path, tool=None)

    def test_edit_item_unknown_prefix(self):
        """Verify an exception is raised for an unknown prefix (document)."""
        self.assertRaises(DoorstopError, self.tree.edit_item, 'unknown1')

    def test_edit_item_unknown_number(self):
        """Verify an exception is raised for an unknown number."""
        self.assertRaises(DoorstopError, self.tree.edit_item, 'req9999')

    def test_find_item(self):
        """Verify an item can be found by exact UID."""
        # Cache miss
        item = self.tree.find_item('req2-001')
        self.assertIsNot(None, item)
        # Cache hit
        item2 = self.tree.find_item('req2-001')
        self.assertIs(item2, item)

    def test_find_document(self):
        """Verify an document can be found by prefix"""
        # Cache miss
        document = self.tree.find_document('req')
        self.assertIsNot(None, document)
        # Cache hit
        document2 = self.tree.find_document('req')
        self.assertIs(document2, document)

    def test_load(self):
        """Verify an a tree can be reloaded."""
        self.tree.load()
        self.tree.load()  # should return immediately

    @patch('doorstop.core.document.Document.delete')
    def test_delete(self, mock_delete):
        """Verify a tree can be deleted."""
        self.tree.delete()
        self.assertEqual(0, len(self.tree))
        self.assertEqual(2, mock_delete.call_count)
        self.tree.delete()  # ensure a second delete is ignored
Example #18
0
 def setUp(self):
     self.tree = Tree(Document(SYS))
     self.tree._place(Document(FILES))  # pylint: disable=W0212
Example #19
0
 def test_validate_no_documents(self):
     """Verify an empty tree can be checked."""
     tree = Tree(None, root='.')
     self.assertTrue(tree.validate())
Example #20
0
 def setUpClass(cls):
     a = Tree('a', root='.')
     b1 = Tree('b1', parent=a, root='.')
     d = Tree('d', parent=b1, root='.')
     e = Tree('e', parent=d, root='.')
     b2 = Tree('b2', parent=a, root='.')
     c1 = Tree('c1', parent=b2, root='.')
     c2 = Tree('c2', parent=b2, root='.')
     a.children = [b1, b2]
     b1.children = [d]
     d.children = [e]
     b2.children = [c1, c2]
     cls.tree = a
Example #21
0
class TestTree(unittest.TestCase):  # pylint: disable=R0904

    """Unit tests for the Tree class."""  # pylint: disable=C0103

    def setUp(self):
        self.tree = Tree(Document(SYS))
        self.tree._place(Document(FILES))  # pylint: disable=W0212

    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_palce_empty(self):
        """Verify a document can be placed in an empty tree."""
        tree = build(EMPTY)
        doc = MockDocument.new(os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP')
        tree._place(doc)  # pylint: disable=W0212
        self.assertEqual(1, len(tree))

    @patch('doorstop.core.vcs.find_root', Mock(return_value=EMPTY))
    def test_palce_empty_no_parent(self):
        """Verify a document with parent cannot be placed in an empty tree."""
        tree = build(EMPTY)
        doc = MockDocument.new(os.path.join(EMPTY, 'temp'), EMPTY, 'TEMP',
                               parent='REQ')
        self.assertRaises(DoorstopError, tree._place, doc)  # pylint: disable=W0212

    @patch('doorstop.core.document.Document.issues')
    def test_valid(self, mock_issues):
        """Verify trees can be checked."""
        logging.info("tree: {}".format(self.tree))
        self.assertTrue(self.tree.valid())
        self.assertEqual(2, mock_issues.call_count)

    @unittest.skipUnless(os.getenv(ENV), REASON)
    def test_valid_long(self):
        """Verify trees can be checked (long)."""
        logging.info("tree: {}".format(self.tree))
        self.assertTrue(self.tree.valid())

    def test_valid_no_documents(self):
        """Verify an empty tree can be checked."""
        tree = Tree(None, root='.')
        self.assertTrue(tree.valid())

    def test_valid_document(self):
        """Verify an document error fails the tree valid."""
        mock_issues = Mock(return_value=[DoorstopError('e'),
                                         DoorstopWarning('w'),
                                         DoorstopInfo('i')])
        with patch.object(self.tree, 'issues', mock_issues):
            self.assertFalse(self.tree.valid())

    @patch('doorstop.core.document.Document.issues', Mock(return_value=[]))
    def test_valid_hook(self):
        """Verify a document hook can be called."""
        mock_hook = MagicMock()
        self.tree.valid(document_hook=mock_hook)
        self.assertEqual(2, mock_hook.call_count)

    def test_new(self):
        """Verify a new document can be created on a tree."""
        self.tree.new(EMPTY, '_TEST', parent='REQ')

    def test_new_unknown_parent(self):
        """Verify an exception is raised for an unknown parent."""
        temp = tempfile.mkdtemp()
        self.assertRaises(DoorstopError, self.tree.new,
                          temp, '_TEST', parent='UNKNOWN')
        self.assertFalse(os.path.exists(temp))

    @patch('doorstop.core.vcs.git.WorkingCopy.lock')
    @patch('doorstop.core.document.Document.add')
    def test_add(self, mock_add, mock_lock):
        """Verify an item can be added to a document."""
        self.tree.add('REQ')
        mock_add.assert_called_once_with()
        path = os.path.join(FILES, '.doorstop.yml')
        mock_lock.assert_called_once_with(path)

    def test_add_unknown_prefix(self):
        """Verify an exception is raised for an unknown prefix (item)."""
        # Cache miss
        self.assertRaises(DoorstopError, self.tree.add, 'UNKNOWN')
        # Cache hit
        self.assertRaises(DoorstopError, self.tree.add, 'UNKNOWN')

    @patch('doorstop.core.item.Item.delete')
    def test_remove(self, mock_delete):
        """Verify an item can be removed from a document."""
        self.tree.remove('req1')
        mock_delete.assert_called_once_with()

    def test_remove_unknown_item(self):
        """Verify an exception is raised removing an unknown item."""
        self.assertRaises(DoorstopError, self.tree.remove, 'req9999')

    @patch('doorstop.core.item.Item.add_link')
    def test_link(self, mock_add_link):
        """Verify two items can be linked."""
        self.tree.link('req1', 'req2')
        mock_add_link.assert_called_once_with('REQ002')

    def test_link_unknown_child_prefix(self):
        """Verify an exception is raised with an unknown child prefix."""
        self.assertRaises(DoorstopError, self.tree.link, 'unknown1', 'req2')

    def test_link_unknown_child_number(self):
        """Verify an exception is raised with an unknown child number."""
        self.assertRaises(DoorstopError, self.tree.link, 'req9999', 'req2')

    def test_link_unknown_parent_prefix(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError, self.tree.link, 'req1', 'unknown1')

    def test_link_unknown_parent_number(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError, self.tree.link, 'req1', 'req9999')

    @patch('doorstop.core.item.Item.remove_link')
    def test_unlink(self, mock_add_link):
        """Verify two items can be unlinked."""
        self.tree.unlink('req3', 'req1')
        mock_add_link.assert_called_once_with('REQ001')

    def test_unlink_unknown_child_prefix(self):
        """Verify an exception is raised with an unknown child prefix."""
        self.assertRaises(DoorstopError, self.tree.unlink, 'unknown1', 'req1')

    def test_unlink_unknown_child_number(self):
        """Verify an exception is raised with an unknown child number."""
        self.assertRaises(DoorstopError, self.tree.unlink, 'req9999', 'req1')

    def test_unlink_unknown_parent_prefix(self):
        """Verify an exception is raised with an unknown parent prefix."""
        # Cache miss
        self.assertRaises(DoorstopError, self.tree.unlink, 'req3', 'unknown1')
        # Cache hit
        self.assertRaises(DoorstopError, self.tree.unlink, 'req3', 'unknown1')

    def test_unlink_unknown_parent_number(self):
        """Verify an exception is raised with an unknown parent prefix."""
        self.assertRaises(DoorstopError, self.tree.unlink, 'req3', 'req9999')

    @patch('doorstop.core.vcs.git.WorkingCopy.lock')
    @patch('doorstop.core.tree._open')
    def test_edit(self, mock_open, mock_lock):
        """Verify an item can be edited in a tree."""
        self.tree.edit('req2', launch=True)
        path = os.path.join(FILES, 'REQ002.yml')
        mock_open.assert_called_once_with(path, tool=None)
        mock_lock.assert_called_once_with(path)

    def test_edit_unknown_prefix(self):
        """Verify an exception is raised for an unknown prefix (document)."""
        self.assertRaises(DoorstopError, self.tree.edit, 'unknown1')

    def test_edit_unknown_number(self):
        """Verify an exception is raised for an unknown number."""
        self.assertRaises(DoorstopError, self.tree.edit, 'req9999')

    def test_find_item(self):
        """Verify an item can be found by exact ID."""
        # Cache miss
        item = self.tree.find_item('req2-001')
        self.assertIsNot(None, item)
        # Cache hit
        item2 = self.tree.find_item('req2-001')
        self.assertIs(item2, item)

    def test_find_document(self):
        """Verify an document can be found by prefix"""
        # Cache miss
        document = self.tree.find_document('req')
        self.assertIsNot(None, document)
        # Cache hit
        document2 = self.tree.find_document('req')
        self.assertIs(document2, document)

    def test_load(self):
        """Verify an a tree can be reloaded."""
        self.tree.load()
        self.tree.load()  # should return immediately

    @patch('doorstop.core.document.Document.delete')
    def test_delete(self, mock_delete):
        """Verify a tree can be deleted."""
        self.tree.delete()
        self.assertEqual(0, len(self.tree))
        self.assertEqual(2, mock_delete.call_count)
        self.tree.delete()  # ensure a second delete is ignored