示例#1
0
    def setUp(self):
        here = os.path.dirname(__file__)
        self.__md_dir = os.path.abspath(
            os.path.join(here, 'tmp-markdown-files'))
        self.__priv_dir = os.path.abspath(os.path.join(here, 'tmp-private'))
        self.__src_dir = os.path.abspath(os.path.join(here, 'tmp-src-files'))
        self.__output_dir = os.path.abspath(os.path.join(here, 'tmp-output'))
        self.__remove_tmp_dirs()
        os.mkdir(self.__md_dir)
        os.mkdir(self.__priv_dir)
        os.mkdir(self.__src_dir)
        os.mkdir(self.get_generated_doc_folder())
        self.include_paths = OrderedSet([self.__md_dir])
        self.include_paths.add(self.get_generated_doc_folder())

        # Using the real doc database is too costly, tests should be lightning
        # fast (and they are)
        self.doc_database = DocDatabase()
        self.doc_database.setup(self.__priv_dir)
        self.link_resolver = LinkResolver(self.doc_database)

        self.change_tracker = ChangeTracker()

        self.sitemap_parser = SitemapParser()

        self.test_ext = TestExtension(self)
        self.core_ext = CoreExtension(self)
示例#2
0
 def setUp(self):
     here = os.path.dirname(__file__)
     self.__tmp_dir = os.path.abspath(os.path.join(here, 'sitemap-test'))
     shutil.rmtree(self.__tmp_dir, ignore_errors=True)
     os.mkdir(self.__tmp_dir)
     self.parser = SitemapParser()
     Logger.silent = True
示例#3
0
    def setUp(self):
        here = os.path.dirname(__file__)
        self.__md_dir = os.path.abspath(os.path.join(
            here, 'tmp-markdown-files'))
        self.__priv_dir = os.path.abspath(os.path.join(
            here, 'tmp-private'))
        self.__src_dir = os.path.abspath(os.path.join(
            here, 'tmp-src-files'))
        self.__output_dir = os.path.abspath(os.path.join(
            here, 'tmp-output'))
        self.__remove_tmp_dirs()
        os.mkdir(self.__md_dir)
        os.mkdir(self.__priv_dir)
        os.mkdir(self.__src_dir)
        os.mkdir(self.get_generated_doc_folder())
        self.include_paths = OrderedSet([self.__md_dir])
        self.include_paths.add(self.get_generated_doc_folder())

        # Using the real doc database is too costly, tests should be lightning
        # fast (and they are)
        self.doc_database = DocDatabase()
        self.doc_database.setup(self.__priv_dir)
        self.link_resolver = LinkResolver(self.doc_database)

        self.change_tracker = ChangeTracker()

        self.sitemap_parser = SitemapParser()

        self.test_ext = TestExtension(self)
        self.core_ext = CoreExtension(self)
示例#4
0
    def setup(self):
        """
        Banana banana
        """
        configurable_classes = all_subclasses(Configurable)

        configured = set()
        for subclass in configurable_classes:
            if subclass.parse_config not in configured:
                subclass.parse_config(self, self.config)
                configured.add(subclass.parse_config)
        self.__parse_config()

        self.doc_tree = DocTree(self.get_private_folder(), self.include_paths)

        for extension in self.extensions.values():
            info('Setting up %s' % extension.extension_name)
            extension.setup()
            self.doc_database.flush()

        sitemap = SitemapParser().parse(self.sitemap_path)
        self.doc_tree.parse_sitemap(self.change_tracker, sitemap)

        info("Resolving symbols", 'resolution')
        self.doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        self.doc_database.flush()
示例#5
0
 def setUp(self):
     here = os.path.dirname(__file__)
     self.__tmp_dir = os.path.abspath(os.path.join(
         here, 'sitemap-test'))
     shutil.rmtree(self.__tmp_dir, ignore_errors=True)
     os.mkdir(self.__tmp_dir)
     self.parser = SitemapParser()
     Logger.silent = True
示例#6
0
class TestSitemapParser(unittest.TestCase):
    def setUp(self):
        here = os.path.dirname(__file__)
        self.__tmp_dir = os.path.abspath(os.path.join(here, 'sitemap-test'))
        shutil.rmtree(self.__tmp_dir, ignore_errors=True)
        os.mkdir(self.__tmp_dir)
        self.parser = SitemapParser()
        Logger.silent = True

    def tearDown(self):
        shutil.rmtree(self.__tmp_dir, ignore_errors=True)

    def parse(self, text):
        path = os.path.join(self.__tmp_dir, 'sitemap.txt')
        with io.open(path, 'w', encoding='utf-8') as _:
            _.write(text)
        return self.parser.parse(path)

    def test_basic(self):
        inp = (u'index.markdown\n' '\tsection.markdown')
        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 2)
        self.assertEqual(all_sources['index.markdown'], ['section.markdown'])

    def test_nesting(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown\n'
               '\t\tsubsection.markdown')

        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 3)
        self.assertEqual(all_sources['index.markdown'], ['section.markdown'])
        self.assertEqual(all_sources['section.markdown'],
                         ['subsection.markdown'])

    def test_unnesting(self):
        inp = (u'index.markdown\n'
               '\tsection1.markdown\n'
               '\t\tsubsection.markdown\n'
               '\tsection2.markdown')

        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 4)
        self.assertEqual(all_sources['index.markdown'],
                         ['section1.markdown', 'section2.markdown'])

    def test_empty_lines(self):
        inp = (u'index.markdown\n' '\n' '\tsection.markdown')
        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 2)
        self.assertEqual(all_sources['index.markdown'], ['section.markdown'])

    def test_quoting(self):
        inp = (u'index.markdown\n' '\t" section with spaces.markdown "')
        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 2)
        self.assertEqual(all_sources['index.markdown'],
                         [' section with spaces.markdown '])

    def test_invalid_indentation(self):
        inp = (u'index.markdown\n'
               '\tsection1.markdown\n'
               '\tsection2.markdown\n'
               '\t invalid.markdown\n'
               '\tsection3.markdown\n'
               '\tsection4.markdown\n'
               '\tsection5.markdown')
        with self.assertRaises(IndentError) as cm:
            self.parse(inp)
        self.assertEqual(cm.exception.lineno, 3)
        self.assertEqual(cm.exception.column, 9)
        self.assertEqual(cm.exception.filename,
                         os.path.join(self.__tmp_dir, 'sitemap.txt'))

    def test_duplicate_file(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown\n'
               '\tsection.markdown\n')

        with self.assertRaises(SitemapDuplicateError) as cm:
            self.parse(inp)
        self.assertEqual(cm.exception.lineno, 2)
        self.assertEqual(cm.exception.column, 9)

    def test_multiple_roots(self):
        inp = (u'index.markdown\n' 'other_index.markdown\n')

        with self.assertRaises(SitemapError) as cm:
            self.parse(inp)
        self.assertEqual(cm.exception.lineno, 1)
        self.assertEqual(cm.exception.column, 0)
示例#7
0
class TestSitemapParser(unittest.TestCase):

    def setUp(self):
        here = os.path.dirname(__file__)
        self.__tmp_dir = os.path.abspath(os.path.join(
            here, 'sitemap-test'))
        shutil.rmtree(self.__tmp_dir, ignore_errors=True)
        os.mkdir(self.__tmp_dir)
        self.parser = SitemapParser()
        Logger.silent = True

    def tearDown(self):
        shutil.rmtree(self.__tmp_dir, ignore_errors=True)

    def parse(self, text):
        path = os.path.join(self.__tmp_dir, 'sitemap.txt')
        with io.open(path, 'w', encoding='utf-8') as _:
            _.write(text)
        return self.parser.parse(path)

    def test_basic(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown')
        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 2)
        self.assertEqual(all_sources['index.markdown'],
                         ['section.markdown'])

    def test_nesting(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown\n'
               '\t\tsubsection.markdown')

        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 3)
        self.assertEqual(all_sources['index.markdown'],
                         ['section.markdown'])
        self.assertEqual(all_sources['section.markdown'],
                         ['subsection.markdown'])

    def test_unnesting(self):
        inp = (u'index.markdown\n'
               '\tsection1.markdown\n'
               '\t\tsubsection.markdown\n'
               '\tsection2.markdown')

        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 4)
        self.assertEqual(all_sources['index.markdown'],
                         ['section1.markdown', 'section2.markdown'])

    def test_empty_lines(self):
        inp = (u'index.markdown\n'
               '\n'
               '\tsection.markdown')
        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 2)
        self.assertEqual(all_sources['index.markdown'],
                         ['section.markdown'])

    def test_quoting(self):
        inp = (u'index.markdown\n'
               '\t" section with spaces.markdown "')
        sitemap = self.parse(inp)
        all_sources = sitemap.get_all_sources()
        self.assertEqual(len(all_sources), 2)
        self.assertEqual(all_sources['index.markdown'],
                         [' section with spaces.markdown '])

    def test_invalid_indentation(self):
        inp = (u'index.markdown\n'
               '\tsection1.markdown\n'
               '\tsection2.markdown\n'
               '\t invalid.markdown\n'
               '\tsection3.markdown\n'
               '\tsection4.markdown\n'
               '\tsection5.markdown')
        with self.assertRaises(IndentError) as cm:
            self.parse(inp)
        self.assertEqual(cm.exception.lineno, 3)
        self.assertEqual(cm.exception.column, 9)
        self.assertEqual(cm.exception.filename,
                         os.path.join(self.__tmp_dir, 'sitemap.txt'))

    def test_duplicate_file(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown\n'
               '\tsection.markdown\n')

        with self.assertRaises(SitemapDuplicateError) as cm:
            self.parse(inp)
        self.assertEqual(cm.exception.lineno, 2)
        self.assertEqual(cm.exception.column, 9)

    def test_multiple_roots(self):
        inp = (u'index.markdown\n'
               'other_index.markdown\n')

        with self.assertRaises(SitemapError) as cm:
            self.parse(inp)
        self.assertEqual(cm.exception.lineno, 1)
        self.assertEqual(cm.exception.column, 0)
示例#8
0
class TestDocTree(unittest.TestCase):
    def setUp(self):
        here = os.path.dirname(__file__)
        self.__md_dir = os.path.abspath(os.path.join(
            here, 'tmp-markdown-files'))
        self.__priv_dir = os.path.abspath(os.path.join(
            here, 'tmp-private'))
        self.__src_dir = os.path.abspath(os.path.join(
            here, 'tmp-src-files'))
        self.__output_dir = os.path.abspath(os.path.join(
            here, 'tmp-output'))
        self.__remove_tmp_dirs()
        os.mkdir(self.__md_dir)
        os.mkdir(self.__priv_dir)
        os.mkdir(self.__src_dir)
        os.mkdir(self.get_generated_doc_folder())
        self.include_paths = OrderedSet([self.__md_dir])
        self.include_paths.add(self.get_generated_doc_folder())

        # Using the real doc database is too costly, tests should be lightning
        # fast (and they are)
        self.doc_database = DocDatabase()
        self.doc_database.setup(self.__priv_dir)
        self.link_resolver = LinkResolver(self.doc_database)

        self.change_tracker = ChangeTracker()

        self.sitemap_parser = SitemapParser()

        self.test_ext = TestExtension(self)
        self.core_ext = CoreExtension(self)

    def tearDown(self):
        self.__remove_tmp_dirs()
        del self.test_ext
        del self.core_ext

    def get_generated_doc_folder(self):
        return os.path.join(self.__priv_dir, 'generated')

    def get_base_doc_folder(self):
        return self.__md_dir

    def get_private_folder(self):
        return self.__priv_dir

    def __parse_sitemap(self, text):
        path = os.path.join(self.__md_dir, 'sitemap.txt')
        with io.open(path, 'w', encoding='utf-8') as _:
            _.write(text)
        return self.sitemap_parser.parse(path)

    def __create_md_file(self, name, contents):
        path = os.path.join(self.__md_dir, name)
        with open(path, 'w') as _:
            _.write(contents)

        # Just making sure we don't hit a race condition,
        # in real world situations it is assumed users
        # will not update source files twice in the same
        # microsecond
        touch(path)

    def __create_src_file(self, name, symbols):
        path = os.path.join(self.__md_dir, name)
        with open(path, 'w') as _:
            for symbol in symbols:
                _.write('%s\n' % symbol)

        # Just making sure we don't hit a race condition,
        # in real world situations it is assumed users
        # will not update source files twice in the same
        # microsecond
        touch(path)

        return path

    def __remove_src_file(self, name):
        path = os.path.join(self.__md_dir, name)
        os.unlink(path)

    def __remove_md_file(self, name):
        path = os.path.join(self.__md_dir, name)
        os.unlink(path)

    def __touch_src_file(self, name):
        path = os.path.join(self.__md_dir, name)
        touch(path)

    def __remove_tmp_dirs(self):
        shutil.rmtree(self.__md_dir, ignore_errors=True)
        shutil.rmtree(self.__priv_dir, ignore_errors=True)
        shutil.rmtree(self.__src_dir, ignore_errors=True)
        shutil.rmtree(self.__output_dir, ignore_errors=True)
        shutil.rmtree(self.get_generated_doc_folder(), ignore_errors=True)

    def test_basic(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))
        self.__create_md_file(
            'section.markdown',
            (u'# My section\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        pages = doc_tree.get_pages()

        # We do not care about ordering
        self.assertSetEqual(
            set(pages.keys()),
            set([u'index.markdown',
                 u'section.markdown']))

        index = pages.get('index.markdown')
        self.assertEqual(index.title, u'My documentation')

    def test_basic_incremental(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))
        self.__create_md_file(
            'section.markdown',
            (u'# My section\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        # Building from scratch, all pages are stale
        self.assertSetEqual(
            set(doc_tree.get_stale_pages()),
            set([u'index.markdown',
                 u'section.markdown']))

        doc_tree.persist()

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        # Nothing changed, no page is stale
        self.assertSetEqual(
            set(doc_tree.get_stale_pages()),
            set({}))

        # But we still have our pages
        self.assertSetEqual(
            set(doc_tree.get_pages()),
            set([u'index.markdown',
                 u'section.markdown']))

        touch(os.path.join(self.__md_dir, u'section.markdown'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        self.assertSetEqual(
            set(doc_tree.get_stale_pages()),
            set([u'section.markdown']))

    def __assert_extension_names(self, doc_tree, name_map):
        pages = doc_tree.get_pages()
        for name, ext_name in name_map.items():
            page = pages[name]
            self.assertEqual(ext_name, page.extension_name)

    def __assert_stale(self, doc_tree, expected_stale):
        stale_pages = doc_tree.get_stale_pages()
        for pagename in expected_stale:
            self.assertIn(pagename, stale_pages)
            stale_pages.pop(pagename)
        self.assertEqual(len(stale_pages), 0)

    def __create_test_layout(self):
        inp = (u'index.markdown\n'
               '\ttest-index\n'
               '\t\ttest-section.markdown\n'
               '\t\t\tsource_a.test\n'
               '\t\tpage_x.markdown\n'
               '\t\tpage_y.markdown\n'
               '\tcore_page.markdown\n')

        sources = []

        sources.append(self.__create_src_file(
            'source_a.test',
            ['symbol_1',
             'symbol_2']))

        sources.append(self.__create_src_file(
            'source_b.test',
            ['symbol_3',
             'symbol_4']))

        self.test_ext.index = 'test-index.markdown'
        self.test_ext.sources = sources
        self.test_ext.setup()

        sitemap = self.__parse_sitemap(inp)

        self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))
        self.__create_md_file(
            'core_page.markdown',
            (u'# My non-extension page\n'))
        self.__create_md_file(
            'test-index.markdown',
            (u'# My test index\n'))
        self.__create_md_file(
            'test-section.markdown',
            (u'# My test section\n'
             '\n'
             'Linking to [a generated page](source_a.test)\n'))
        self.__create_md_file(
            'page_x.markdown',
            (u'---\n'
             'symbols: [symbol_3]\n'
             '...\n'
             '# Page X\n'))
        self.__create_md_file(
            'page_y.markdown',
            (u'# Page Y\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        return doc_tree, sitemap

    def __update_test_layout(self, doc_tree, sitemap):
        self.test_ext.reset()
        self.test_ext.setup()

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        return doc_tree

    def test_extension_basic(self):
        doc_tree, _ = self.__create_test_layout()
        self.__assert_extension_names(
            doc_tree,
            {u'index.markdown': 'core',
             u'test-index': 'test-extension',
             u'test-section.markdown': 'test-extension',
             u'source_a.test': 'test-extension',
             u'source_b.test': 'test-extension',
             u'page_x.markdown': 'test-extension',
             u'page_y.markdown': 'test-extension',
             u'core_page.markdown': 'core'})

        all_pages = doc_tree.get_pages()
        self.assertEqual(len(all_pages), 8)
        self.__assert_stale(doc_tree, all_pages)
        self.assertNotIn('source_a.test', all_pages['test-index'].subpages)
        self.assertIn('source_a.test',
                      all_pages['test-section.markdown'].subpages)

    def test_extension_override(self):
        self.__create_md_file(
            'source_a.test.markdown',
            (u'# My override\n'))
        doc_tree, _ = self.__create_test_layout()
        page = doc_tree.get_pages()['source_a.test']

        self.assertEqual(
            page.symbol_names,
            OrderedSet(['symbol_1',
                        'symbol_2']))

        self.assertEqual(
            os.path.basename(page.source_file),
            'source_a.test.markdown')

        self.assertEqual(
            cmark.ast_to_html(page.ast, None),
            u'<h1>My override</h1>\n')

    def test_parse_yaml(self):
        inp = (u'index.markdown\n')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file(
            'index.markdown',
            (u'---\n'
             'title: A random title\n'
             'symbols: [symbol_1, symbol_2]\n'
             '...\n'
             '# My documentation\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        pages = doc_tree.get_pages()
        page = pages.get('index.markdown')
        self.assertEqual(
            cmark.ast_to_html(page.ast, None),
            u'<h1>My documentation</h1>\n')

        self.assertEqual(page.title, u'A random title')

        self.assertEqual(
            page.symbol_names,
            OrderedSet(['symbol_1',
                        'symbol_2']))

    def test_empty_link_resolution(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))
        self.__create_md_file(
            'section.markdown',
            (u'# My section\n'
             '\n'
             '[](index.markdown)\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        doc_tree.format(
            self.link_resolver, self.__output_dir,
            {self.core_ext.extension_name: self.core_ext})

        pages = doc_tree.get_pages()
        page = pages.get('section.markdown')
        self.assertEqual(
            page.formatted_contents,
            u'<h1>My section</h1>\n'
            '<p><a href="index.html">My documentation</a></p>\n')

    def test_labeled_link_resolution(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))
        self.__create_md_file(
            'section.markdown',
            (u'# My section\n'
             '\n'
             '[a label](index.markdown)\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        doc_tree.format(
            self.link_resolver, self.__output_dir,
            {self.core_ext.extension_name: self.core_ext})

        pages = doc_tree.get_pages()
        page = pages.get('section.markdown')
        self.assertEqual(
            page.formatted_contents,
            u'<h1>My section</h1>\n'
            '<p><a href="index.html">a label</a></p>\n')

    def test_anchored_link_resolution(self):
        inp = (u'index.markdown\n'
               '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))
        self.__create_md_file(
            'section.markdown',
            (u'# My section\n'
             '\n'
             '[](index.markdown#subsection)\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        doc_tree.format(
            self.link_resolver, self.__output_dir,
            {self.core_ext.extension_name: self.core_ext})

        pages = doc_tree.get_pages()
        page = pages.get('section.markdown')
        self.assertEqual(
            page.formatted_contents,
            u'<h1>My section</h1>\n'
            '<p><a href="index.html#subsection">My documentation</a></p>\n')

    # pylint: disable=too-many-statements
    def test_extension_incremental(self):
        doc_tree, sitemap = self.__create_test_layout()
        doc_tree.persist()

        # Here we touch source_a.test, as its symbols were
        # all contained in a generated page, only that page
        # should now be stale
        self.__touch_src_file('source_a.test')
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree, set(['source_a.test']))
        doc_tree.persist()

        # We now touch source_b.test, which symbols are contained
        # both in a generated page and a user-provided one.
        # We expect both pages to be stale
        self.__touch_src_file('source_b.test')
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree,
                            set(['source_b.test',
                                 'page_x.markdown']))
        doc_tree.persist()

        # This one is trickier: we unlist symbol_3 from
        # page_x, which means the symbol should now be
        # documented in the generated page for source_b.test.
        # We expect both pages to be stale, and make sure
        # they contain the right symbols
        self.__create_md_file(
            'page_x.markdown',
            (u'# Page X\n'))
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree,
                            set(['source_b.test',
                                 'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet())

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(
            source_b_page.symbol_names,
            OrderedSet(['symbol_4', 'symbol_3']))

        doc_tree.persist()

        # Let's make sure the opposite use case works as well,
        # we relocate symbol_3 in page_x , both page_x and
        # the generated page for source_b.test should be stale
        # and the symbols should be back to their original
        # layout.
        self.__create_md_file(
            'page_x.markdown',
            (u'---\n'
             'symbols: [symbol_3]\n'
             '...\n'
             '# Page X\n'))

        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree,
                            set(['source_b.test',
                                 'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(
            source_b_page.symbol_names,
            OrderedSet(['symbol_4']))

        doc_tree.persist()

        # We now move the definition of symbol_3 to source_a.test,
        # we thus expect the generated page for source_a.test to be
        # stale because its source changed, same for source_b.test,
        # and page_x.markdown should be stale as well because the
        # definition of symbol_3 may have changed. The final
        # symbol layout should not have changed however.
        self.__create_src_file(
            'source_a.test',
            ['symbol_1',
             'symbol_2',
             'symbol_3'])
        self.__create_src_file(
            'source_b.test',
            ['symbol_4'])
        doc_tree = self.__update_test_layout(doc_tree, sitemap)

        self.__assert_stale(doc_tree,
                            set(['source_a.test',
                                 'source_b.test',
                                 'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(
            source_b_page.symbol_names,
            OrderedSet(['symbol_4']))

        source_a_page = doc_tree.get_pages()['source_a.test']
        self.assertEqual(
            source_a_page.symbol_names,
            OrderedSet(['symbol_1',
                        'symbol_2']))

        doc_tree.persist()

        # And we rollback again
        self.__create_src_file(
            'source_a.test',
            ['symbol_1',
             'symbol_2'])
        self.__create_src_file(
            'source_b.test',
            ['symbol_3',
             'symbol_4'])
        doc_tree = self.__update_test_layout(doc_tree, sitemap)

        self.__assert_stale(doc_tree,
                            set(['source_a.test',
                                 'source_b.test',
                                 'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(
            source_b_page.symbol_names,
            OrderedSet(['symbol_4']))

        source_a_page = doc_tree.get_pages()['source_a.test']
        self.assertEqual(
            source_a_page.symbol_names,
            OrderedSet(['symbol_1',
                        'symbol_2']))

        doc_tree.persist()

        # Now we'll try removing page_x altogether
        self.__remove_md_file('page_x.markdown')
        inp = (u'index.markdown\n'
               '\ttest-index\n'
               '\t\ttest-section.markdown\n'
               '\t\t\tsource_a.test\n'
               '\t\tpage_y.markdown\n'
               '\tcore_page.markdown\n')

        new_sitemap = self.__parse_sitemap(inp)
        doc_tree = self.__update_test_layout(doc_tree, new_sitemap)
        self.__assert_stale(doc_tree,
                            set(['source_b.test']))
        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(
            source_b_page.symbol_names,
            OrderedSet(['symbol_4', 'symbol_3']))
        doc_tree.persist()

        # And rollback again
        self.__create_md_file(
            'page_x.markdown',
            (u'---\n'
             'symbols: [symbol_3]\n'
             '...\n'
             '# Page X\n'))
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree,
                            set(['page_x.markdown',
                                 'source_b.test']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(
            source_b_page.symbol_names,
            OrderedSet(['symbol_4']))

        doc_tree.persist()
示例#9
0
class TestDocTree(unittest.TestCase):
    def setUp(self):
        here = os.path.dirname(__file__)
        self.__md_dir = os.path.abspath(
            os.path.join(here, 'tmp-markdown-files'))
        self.__priv_dir = os.path.abspath(os.path.join(here, 'tmp-private'))
        self.__src_dir = os.path.abspath(os.path.join(here, 'tmp-src-files'))
        self.__output_dir = os.path.abspath(os.path.join(here, 'tmp-output'))
        self.__remove_tmp_dirs()
        os.mkdir(self.__md_dir)
        os.mkdir(self.__priv_dir)
        os.mkdir(self.__src_dir)
        os.mkdir(self.get_generated_doc_folder())
        self.include_paths = OrderedSet([self.__md_dir])
        self.include_paths.add(self.get_generated_doc_folder())

        # Using the real doc database is too costly, tests should be lightning
        # fast (and they are)
        self.doc_database = DocDatabase()
        self.doc_database.setup(self.__priv_dir)
        self.link_resolver = LinkResolver(self.doc_database)

        self.change_tracker = ChangeTracker()

        self.sitemap_parser = SitemapParser()

        self.test_ext = TestExtension(self)
        self.core_ext = CoreExtension(self)

    def tearDown(self):
        self.__remove_tmp_dirs()
        del self.test_ext
        del self.core_ext

    def get_generated_doc_folder(self):
        return os.path.join(self.__priv_dir, 'generated')

    def get_base_doc_folder(self):
        return self.__md_dir

    def get_private_folder(self):
        return self.__priv_dir

    def __parse_sitemap(self, text):
        path = os.path.join(self.__md_dir, 'sitemap.txt')
        with io.open(path, 'w', encoding='utf-8') as _:
            _.write(text)
        return self.sitemap_parser.parse(path)

    def __create_md_file(self, name, contents):
        path = os.path.join(self.__md_dir, name)
        with open(path, 'w') as _:
            _.write(contents)

        # Just making sure we don't hit a race condition,
        # in real world situations it is assumed users
        # will not update source files twice in the same
        # microsecond
        touch(path)

    def __create_src_file(self, name, symbols):
        path = os.path.join(self.__md_dir, name)
        with open(path, 'w') as _:
            for symbol in symbols:
                _.write('%s\n' % symbol)

        # Just making sure we don't hit a race condition,
        # in real world situations it is assumed users
        # will not update source files twice in the same
        # microsecond
        touch(path)

        return path

    def __remove_src_file(self, name):
        path = os.path.join(self.__md_dir, name)
        os.unlink(path)

    def __remove_md_file(self, name):
        path = os.path.join(self.__md_dir, name)
        os.unlink(path)

    def __touch_src_file(self, name):
        path = os.path.join(self.__md_dir, name)
        touch(path)

    def __remove_tmp_dirs(self):
        shutil.rmtree(self.__md_dir, ignore_errors=True)
        shutil.rmtree(self.__priv_dir, ignore_errors=True)
        shutil.rmtree(self.__src_dir, ignore_errors=True)
        shutil.rmtree(self.__output_dir, ignore_errors=True)
        shutil.rmtree(self.get_generated_doc_folder(), ignore_errors=True)

    def test_basic(self):
        inp = (u'index.markdown\n' '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file('index.markdown', (u'# My documentation\n'))
        self.__create_md_file('section.markdown', (u'# My section\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        pages = doc_tree.get_pages()

        # We do not care about ordering
        self.assertSetEqual(set(pages.keys()),
                            set([u'index.markdown', u'section.markdown']))

        index = pages.get('index.markdown')
        self.assertEqual(index.title, u'My documentation')

    def test_basic_incremental(self):
        inp = (u'index.markdown\n' '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file('index.markdown', (u'# My documentation\n'))
        self.__create_md_file('section.markdown', (u'# My section\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        # Building from scratch, all pages are stale
        self.assertSetEqual(set(doc_tree.get_stale_pages()),
                            set([u'index.markdown', u'section.markdown']))

        doc_tree.persist()

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        # Nothing changed, no page is stale
        self.assertSetEqual(set(doc_tree.get_stale_pages()), set({}))

        # But we still have our pages
        self.assertSetEqual(set(doc_tree.get_pages()),
                            set([u'index.markdown', u'section.markdown']))

        touch(os.path.join(self.__md_dir, u'section.markdown'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        self.assertSetEqual(set(doc_tree.get_stale_pages()),
                            set([u'section.markdown']))

    def __assert_extension_names(self, doc_tree, name_map):
        pages = doc_tree.get_pages()
        for name, ext_name in name_map.items():
            page = pages[name]
            self.assertEqual(ext_name, page.extension_name)

    def __assert_stale(self, doc_tree, expected_stale):
        stale_pages = doc_tree.get_stale_pages()
        for pagename in expected_stale:
            self.assertIn(pagename, stale_pages)
            stale_pages.pop(pagename)
        self.assertEqual(len(stale_pages), 0)

    def __create_test_layout(self):
        inp = (u'index.markdown\n'
               '\ttest-index\n'
               '\t\ttest-section.markdown\n'
               '\t\t\tsource_a.test\n'
               '\t\tpage_x.markdown\n'
               '\t\tpage_y.markdown\n'
               '\tcore_page.markdown\n')

        sources = []

        sources.append(
            self.__create_src_file('source_a.test', ['symbol_1', 'symbol_2']))

        sources.append(
            self.__create_src_file('source_b.test', ['symbol_3', 'symbol_4']))

        self.test_ext.index = 'test-index.markdown'
        self.test_ext.sources = sources
        self.test_ext.setup()

        sitemap = self.__parse_sitemap(inp)

        self.__create_md_file('index.markdown', (u'# My documentation\n'))
        self.__create_md_file('core_page.markdown',
                              (u'# My non-extension page\n'))
        self.__create_md_file('test-index.markdown', (u'# My test index\n'))
        self.__create_md_file(
            'test-section.markdown',
            (u'# My test section\n'
             '\n'
             'Linking to [a generated page](source_a.test)\n'))
        self.__create_md_file('page_x.markdown', (u'---\n'
                                                  'symbols: [symbol_3]\n'
                                                  '...\n'
                                                  '# Page X\n'))
        self.__create_md_file('page_y.markdown', (u'# Page Y\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        return doc_tree, sitemap

    def __update_test_layout(self, doc_tree, sitemap):
        self.test_ext.reset()
        self.test_ext.setup()

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        return doc_tree

    def test_extension_basic(self):
        doc_tree, _ = self.__create_test_layout()
        self.__assert_extension_names(
            doc_tree, {
                u'index.markdown': 'core',
                u'test-index': 'test-extension',
                u'test-section.markdown': 'test-extension',
                u'source_a.test': 'test-extension',
                u'source_b.test': 'test-extension',
                u'page_x.markdown': 'test-extension',
                u'page_y.markdown': 'test-extension',
                u'core_page.markdown': 'core'
            })

        all_pages = doc_tree.get_pages()
        self.assertEqual(len(all_pages), 8)
        self.__assert_stale(doc_tree, all_pages)
        self.assertNotIn('source_a.test', all_pages['test-index'].subpages)
        self.assertIn('source_a.test',
                      all_pages['test-section.markdown'].subpages)

    def test_extension_override(self):
        self.__create_md_file('source_a.test.markdown', (u'# My override\n'))
        doc_tree, _ = self.__create_test_layout()
        page = doc_tree.get_pages()['source_a.test']

        self.assertEqual(page.symbol_names,
                         OrderedSet(['symbol_1', 'symbol_2']))

        self.assertEqual(os.path.basename(page.source_file),
                         'source_a.test.markdown')

        out, _ = cmark.ast_to_html(page.ast, None)

        self.assertEqual(out, u'<h1>My override</h1>\n')

    def test_parse_yaml(self):
        inp = (u'index.markdown\n')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file('index.markdown',
                              (u'---\n'
                               'title: A random title\n'
                               'symbols: [symbol_1, symbol_2]\n'
                               '...\n'
                               '# My documentation\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)

        pages = doc_tree.get_pages()
        page = pages.get('index.markdown')

        out, _ = cmark.ast_to_html(page.ast, None)

        self.assertEqual(out, u'<h1>My documentation</h1>\n')

        self.assertEqual(page.title, u'A random title')

        self.assertEqual(page.symbol_names,
                         OrderedSet(['symbol_1', 'symbol_2']))

    def test_empty_link_resolution(self):
        inp = (u'index.markdown\n' '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file('index.markdown', (u'# My documentation\n'))
        self.__create_md_file('section.markdown', (u'# My section\n'
                                                   '\n'
                                                   '[](index.markdown)\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        doc_tree.format(self.link_resolver, self.__output_dir,
                        {self.core_ext.extension_name: self.core_ext})

        pages = doc_tree.get_pages()
        page = pages.get('section.markdown')
        self.assertEqual(
            page.formatted_contents, u'<h1>My section</h1>\n'
            '<p><a href="index.html">My documentation</a></p>\n')

    def test_labeled_link_resolution(self):
        inp = (u'index.markdown\n' '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file('index.markdown', (u'# My documentation\n'))
        self.__create_md_file('section.markdown',
                              (u'# My section\n'
                               '\n'
                               '[a label](index.markdown)\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        doc_tree.format(self.link_resolver, self.__output_dir,
                        {self.core_ext.extension_name: self.core_ext})

        pages = doc_tree.get_pages()
        page = pages.get('section.markdown')
        self.assertEqual(
            page.formatted_contents, u'<h1>My section</h1>\n'
            '<p><a href="index.html">a label</a></p>\n')

    def test_anchored_link_resolution(self):
        inp = (u'index.markdown\n' '\tsection.markdown')
        sitemap = self.__parse_sitemap(inp)
        self.__create_md_file('index.markdown', (u'# My documentation\n'))
        self.__create_md_file('section.markdown',
                              (u'# My section\n'
                               '\n'
                               '[](index.markdown#subsection)\n'))

        doc_tree = DocTree(self.__priv_dir, self.include_paths)
        doc_tree.parse_sitemap(self.change_tracker, sitemap)
        doc_tree.resolve_symbols(self.doc_database, self.link_resolver)
        doc_tree.format(self.link_resolver, self.__output_dir,
                        {self.core_ext.extension_name: self.core_ext})

        pages = doc_tree.get_pages()
        page = pages.get('section.markdown')
        self.assertEqual(
            page.formatted_contents, u'<h1>My section</h1>\n'
            '<p><a href="index.html#subsection">My documentation</a></p>\n')

    # pylint: disable=too-many-statements
    def test_extension_incremental(self):
        doc_tree, sitemap = self.__create_test_layout()
        doc_tree.persist()

        # Here we touch source_a.test, as its symbols were
        # all contained in a generated page, only that page
        # should now be stale
        self.__touch_src_file('source_a.test')
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree, set(['source_a.test']))
        doc_tree.persist()

        # We now touch source_b.test, which symbols are contained
        # both in a generated page and a user-provided one.
        # We expect both pages to be stale
        self.__touch_src_file('source_b.test')
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree, set(['source_b.test',
                                           'page_x.markdown']))
        doc_tree.persist()

        # This one is trickier: we unlist symbol_3 from
        # page_x, which means the symbol should now be
        # documented in the generated page for source_b.test.
        # We expect both pages to be stale, and make sure
        # they contain the right symbols
        self.__create_md_file('page_x.markdown', (u'# Page X\n'))
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree, set(['source_b.test',
                                           'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet())

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(source_b_page.symbol_names,
                         OrderedSet(['symbol_4', 'symbol_3']))

        doc_tree.persist()

        # Let's make sure the opposite use case works as well,
        # we relocate symbol_3 in page_x , both page_x and
        # the generated page for source_b.test should be stale
        # and the symbols should be back to their original
        # layout.
        self.__create_md_file('page_x.markdown', (u'---\n'
                                                  'symbols: [symbol_3]\n'
                                                  '...\n'
                                                  '# Page X\n'))

        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree, set(['source_b.test',
                                           'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(source_b_page.symbol_names, OrderedSet(['symbol_4']))

        doc_tree.persist()

        # We now move the definition of symbol_3 to source_a.test,
        # we thus expect the generated page for source_a.test to be
        # stale because its source changed, same for source_b.test,
        # and page_x.markdown should be stale as well because the
        # definition of symbol_3 may have changed. The final
        # symbol layout should not have changed however.
        self.__create_src_file('source_a.test',
                               ['symbol_1', 'symbol_2', 'symbol_3'])
        self.__create_src_file('source_b.test', ['symbol_4'])
        doc_tree = self.__update_test_layout(doc_tree, sitemap)

        self.__assert_stale(
            doc_tree,
            set(['source_a.test', 'source_b.test', 'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(source_b_page.symbol_names, OrderedSet(['symbol_4']))

        source_a_page = doc_tree.get_pages()['source_a.test']
        self.assertEqual(source_a_page.symbol_names,
                         OrderedSet(['symbol_1', 'symbol_2']))

        doc_tree.persist()

        # And we rollback again
        self.__create_src_file('source_a.test', ['symbol_1', 'symbol_2'])
        self.__create_src_file('source_b.test', ['symbol_3', 'symbol_4'])
        doc_tree = self.__update_test_layout(doc_tree, sitemap)

        self.__assert_stale(
            doc_tree,
            set(['source_a.test', 'source_b.test', 'page_x.markdown']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(source_b_page.symbol_names, OrderedSet(['symbol_4']))

        source_a_page = doc_tree.get_pages()['source_a.test']
        self.assertEqual(source_a_page.symbol_names,
                         OrderedSet(['symbol_1', 'symbol_2']))

        doc_tree.persist()

        # Now we'll try removing page_x altogether
        self.__remove_md_file('page_x.markdown')
        inp = (u'index.markdown\n'
               '\ttest-index\n'
               '\t\ttest-section.markdown\n'
               '\t\t\tsource_a.test\n'
               '\t\tpage_y.markdown\n'
               '\tcore_page.markdown\n')

        new_sitemap = self.__parse_sitemap(inp)
        doc_tree = self.__update_test_layout(doc_tree, new_sitemap)
        self.__assert_stale(doc_tree, set(['source_b.test']))
        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(source_b_page.symbol_names,
                         OrderedSet(['symbol_4', 'symbol_3']))
        doc_tree.persist()

        # And rollback again
        self.__create_md_file('page_x.markdown', (u'---\n'
                                                  'symbols: [symbol_3]\n'
                                                  '...\n'
                                                  '# Page X\n'))
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        self.__assert_stale(doc_tree, set(['page_x.markdown',
                                           'source_b.test']))

        page_x = doc_tree.get_pages()['page_x.markdown']
        self.assertEqual(page_x.symbol_names, OrderedSet(['symbol_3']))

        source_b_page = doc_tree.get_pages()['source_b.test']
        self.assertEqual(source_b_page.symbol_names, OrderedSet(['symbol_4']))

        doc_tree.persist()

    def test_index_override_incremental(self):
        doc_tree, sitemap = self.__create_test_layout()
        doc_tree.persist()
        index_page = doc_tree.get_pages()['test-index']
        self.assertIn('source_b.test', index_page.subpages)

        self.__touch_src_file('test-index.markdown')
        doc_tree = self.__update_test_layout(doc_tree, sitemap)
        index_page = doc_tree.get_pages()['test-index']
        self.assertIn('source_b.test', index_page.subpages)
        doc_tree.persist()