Source code for testing.tests.configs_tree_view

# -*- coding: utf8 -*-
########################################################################################
# This file is part of exhale.  Copyright (c) 2017-2024, Stephen McDowell.             #
# Full BSD 3-Clause license available here:                                            #
#                                                                                      #
#                https://github.com/svenevs/exhale/blob/master/LICENSE                 #
########################################################################################
"""
Tests specifically focused on the various tree view configurations.
"""
from __future__ import unicode_literals

import os
import re
from textwrap import dedent

from testing.base import ExhaleTestCase
from testing.decorators import confoverrides
from testing.tests.configs_tree_view_data import \
    class_hierarchy_ground_truth, file_hierarchy_ground_truth


# NOTE: See cpp_nesting.CPPNestingPages.{setUp,tearDown} (creates page_town_rock.hpp).
[docs] @confoverrides(exhale_args={ "exhaleDoxygenStdin": dedent("""\ INPUT = ../include EXCLUDE_PATTERNS = */page_town_rock*.hpp """)}) class TreeViewHierarchyTests(ExhaleTestCase): """ Naive tests on raw "reStructuredText" generated for tree views. """ test_project = "cpp_nesting" """ .. testproject:: cpp_nesting .. note:: The ``cpp_nesting`` project is just being recycled, the tests for that project take place in :class:`CPPNesting <testing.tests.cpp_nesting.CPPNesting>`. """
[docs] def class_view_hierarchy_file(self): """Path to ``class_view_hierarchy.rst.include`` for this test.""" return os.path.join( self.getAbsContainmentFolder(), "class_view_hierarchy.rst.include")
[docs] def file_view_hierarchy_file(self): """Path to ``file_view_hierarchy.rst.include`` for this test.""" return os.path.join( self.getAbsContainmentFolder(), "file_view_hierarchy.rst.include")
[docs] def raw_hierarchies(self): """ Raw contents of ``{class,file}_view_hierarchy.rst.include``. **Return** (Length ``2`` :class:`python:tuple` of :class:`python:str`) The string contents of ``(class_view, file_view)``, in that order. """ with open(self.class_view_hierarchy_file()) as class_view: class_view_contents = class_view.read() with open(self.file_view_hierarchy_file()) as file_view: file_view_contents = file_view.read() return (class_view_contents, file_view_contents)
[docs] def filter_empty_lines(self, lst): """ Return a copy of ``lst`` with empty / whitespace-only strings removed. **Parameters** ``lst`` (:class:`python:list` of :class:`python:str`) The input list of strings to filter. **Return** (:class:`python:list` of :class:`python:str`) The input ``lst`` in the same order, with empty strings and whitespace-only strings removed. """ empty_or_whitespace = re.compile(r"^$|^\s+$") return [line for line in lst if not empty_or_whitespace.match(line)]
[docs] def html_hierarchies(self): """ Hierarchy text from ``{class,file}_view_hierarchy.rst``. When ``createTreeView=True``, the generated page has something like: .. code-block:: rst Class View Hierarchy -------------------- .. raw:: html <ul> <li> ... </li> <!-- ... --> <li> ... </li> </ul> .. end raw html for treeView What this method does is simply search for ``.. raw:: html`` and ``.. end`` respectively, accumulating everything in between. Since we are performing direct string comparisons with "ground truth" values, we specifically accumulate ``line.strip()`` to remove the leading indentation since it is under a ``.. raw:: html`` directive. Finally, the returned output is filtered using :func:`filter_empty_lines`. **Return** (Length ``2`` :class:`python:tuple` of :class:`python:list` of :class:`python:str`) A length two tuple in the order ``(class_view, file_view)``. Each item in the tuple is a list of strings of the parsed / filtered lines. """ def strip_html_directive(hierarchy): hierarchy_lines = [] found_raw_html = False for line in hierarchy.splitlines(): if found_raw_html: if line.startswith(".. end"): break hierarchy_lines.append(line.strip()) elif line.startswith(".. raw:: html"): found_raw_html = True return self.filter_empty_lines(hierarchy_lines) class_view_raw, file_view_raw = self.raw_hierarchies() return (strip_html_directive(class_view_raw), strip_html_directive(file_view_raw))
[docs] def line_compare(self, expected_list, test_list): """ Compare two lists of strings. Performs two tests: 1. That ``len(expected_list)`` and ``len(test_list)`` are the same. 2. That the order and values of strings in ``expected_list`` are the same as ``test_list``. Mismatched values will be printed in the assertion. **Parameters** ``expected_list`` (:class:`python:list` of :class:`python:str`) The expected list of strings to compare with. ``test_list`` (:class:`python:list` of :class:`python:str`) The parsed list of strings to validate. """ num_expected_lines = len(expected_list) self.assertEqual(num_expected_lines, len(test_list)) mismatches = [] for idx in range(num_expected_lines): expected = expected_list[idx] test = test_list[idx] if test != expected: mismatches.append((expected, test)) self.assertTrue( len(mismatches) == 0, "Mismatches in line_compare:\n\n{0}".format( "\n".join( "- expected: '{0}'\n got: '{1}'".format(*item) for item in mismatches ) ) )
[docs] def line_compare_minified(self, expected_list, test_list, bootstrap=False): """ Compare two lists of tree view strings. This responsible expects the same input as :func:`line_compare`, but does some additional processing on the ``expected_list``. To explain, let's take a look at the lines involved in the actual minified output: +-------+-----------------------------------------------+------------------------------------------------------------------+ | Index | Collapsible Lists (HTML Unordered List) | Bootstrap Version (JavaScript Function Returning JSON) | +=======+===============================================+==================================================================+ | 0 | ``<ul class="treeView" id="class-treeView">`` | ``<script type="text/javascript">`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ | 1 | ``<li>`` | ``function getClassHierarchyTree() {`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ | 2 | ``<ul class="collapsibleList">`` | ``return [`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ | 3 | ``<<< really long >>>`` | ``<<< really long >>>`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ | 4 | ``</ul>`` | ``]`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ | 5 | ``</li><!-- only tree view element -->`` | ``}`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ | 6 | ``</ul><!-- /treeView class-treeView -->`` | ``</script><!-- end getClassHierarchyTree() function --></div>`` | +-------+-----------------------------------------------+------------------------------------------------------------------+ By convenience and design, line ``3`` is really the thing we want to test, because that is the meat of the tree view. For completeness indices ``[0,3)`` and ``[4,6]`` are also validated, but constructing line ``3`` from the provided ``expected_list`` (the **non**-minified ground truth) is the focus of this test function. **Parameters** ``expected_list`` (:class:`python:list` of :class:`python:str`) The expected list of strings to compare with. ``test_list`` (:class:`python:list` of :class:`python:str`) The parsed list of strings to validate. ``bootstrap`` (:class:`python:bool`) If ``False``, test is a Collapsible Lists test. If ``True``, test is a Bootstrap test. """ # noqa: E501 # First, compare the head / tail of the lists. indices = (0, 1, 2, -1, -2, -3) expected_head_tail = [expected_list[idx] for idx in indices] test_head_tail = [test_list[idx] for idx in indices] self.line_compare(expected_head_tail, test_head_tail) # Join the remaining elements to make a comparison. start = max(indices) + 1 + int(bootstrap) # TODO: uh. Why + int(bootstrap)? end = min(indices) interior = "".join(expected_list[start:end]) if bootstrap: # TODO: stop copy-pasting stuff from graph.py and clean this damn framework up................. interior = interior.replace(': ', ':').replace(",}", "}").replace(",,", ",").replace(",]", "]") self.assertEqual(interior, test_list[start])
[docs] def html_ground_truth_list(self, hierarchy, key): """ Ground truth data for html-based validation tests. **Parameters** ``hierarchy`` (:class:`python:str`) Should **only** be ``"class"`` or ``"file"``. Indexes into |class_hierarchy_ground_truth| and |file_hierarchy_ground_truth| respectively. ``key`` (:class:`python:str`) The key to lookup in either |class_hierarchy_ground_truth| or |file_hierarchy_ground_truth|. Specifically, you really only want to be using ``"collapsible_lists"`` or ``"bootstrap"``, since the raw reStructuredText version (``createTreeView=False``) can be compared "directly". .. |class_hierarchy_ground_truth| replace:: :data:`~testing.tests.configs_tree_view_data.class_hierarchy_ground_truth` .. |file_hierarchy_ground_truth| replace:: :data:`~testing.tests.configs_tree_view_data.file_hierarchy_ground_truth` **Return** (:class:`python:list` of :class:`python:str`) The text specified by ``hierarchy[key]``, with every line split and lines filtered by :func:`filter_empty_lines`. """ if hierarchy == "class": source = class_hierarchy_ground_truth elif hierarchy == "file": source = file_hierarchy_ground_truth raw_text = source[key] return self.filter_empty_lines([ line.strip() for line in raw_text.splitlines() ])
[docs] @confoverrides(exhale_args={"createTreeView": False}) def test_no_custom_html(self): """ Verify the default reStructuredText list appears as expected. """ err_fmt = dedent("""\ Expected ============================================================================ {expected} Got ============================================================================ {got} """) test_class_view, test_file_view = self.raw_hierarchies() class_default = class_hierarchy_ground_truth["default_rst_list"] self.assertTrue( class_default in test_class_view, err_fmt.format(expected=class_default, got=test_class_view)) file_default = file_hierarchy_ground_truth["default_rst_list"] self.assertTrue( file_default in test_file_view, err_fmt.format(expected=file_default, got=test_file_view) )
[docs] @confoverrides(exhale_args={ "createTreeView": True, "minifyTreeView": False, "treeViewIsBootstrap": False }) def test_collapsible_lists(self): """ Verify the *un-minified* collapsible lists html unordered list appears as expected. """ test_class_view_lines, test_file_view_lines = self.html_hierarchies() expected_class_view_lines = self.html_ground_truth_list("class", "collapsible_lists") expected_file_view_lines = self.html_ground_truth_list("file", "collapsible_lists") self.line_compare(expected_class_view_lines, test_class_view_lines) self.line_compare(expected_file_view_lines, test_file_view_lines)
[docs] @confoverrides(exhale_args={ "createTreeView": True, "minifyTreeView": True, "treeViewIsBootstrap": False }) def test_collapsible_lists_minified(self): """ Verify the *minified* collapsible lists html unordered list appears as expected. """ test_class_view_lines, test_file_view_lines = self.html_hierarchies() expected_class_view_lines = self.html_ground_truth_list("class", "collapsible_lists") expected_file_view_lines = self.html_ground_truth_list("file", "collapsible_lists") self.line_compare_minified(expected_class_view_lines, test_class_view_lines, bootstrap=False) self.line_compare_minified(expected_file_view_lines, test_file_view_lines, bootstrap=False)
[docs] @confoverrides(exhale_args={ "createTreeView": True, "minifyTreeView": False, "treeViewIsBootstrap": True }) def test_bootstrap(self): """ Verify the *un-minified* bootstrap json data list appears as expected. """ test_class_view_lines, test_file_view_lines = self.html_hierarchies() expected_class_view_lines = self.html_ground_truth_list("class", "bootstrap") expected_file_view_lines = self.html_ground_truth_list("file", "bootstrap") self.line_compare(expected_class_view_lines, test_class_view_lines) self.line_compare(expected_file_view_lines, test_file_view_lines)
[docs] @confoverrides(exhale_args={ "createTreeView": True, "treeViewIsBootstrap": True, "minifyTreeView": True }) def test_bootstrap_minified(self): """ Verify the *minified* bootstrap json data list appears as expected. """ test_class_view_lines, test_file_view_lines = self.html_hierarchies() expected_class_view_lines = self.html_ground_truth_list("class", "bootstrap") expected_file_view_lines = self.html_ground_truth_list("file", "bootstrap") self.line_compare_minified(expected_class_view_lines, test_class_view_lines, bootstrap=True) self.line_compare_minified(expected_file_view_lines, test_file_view_lines, bootstrap=True)