File: configs_tree_view.py

package info (click to toggle)
exhale 0.3.7-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,816 kB
  • sloc: python: 9,048; cpp: 1,260; javascript: 915; f90: 29; ansic: 18; makefile: 16
file content (359 lines) | stat: -rw-r--r-- 16,162 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# -*- 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).
@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>`.
    """

    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")

    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")

    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)

    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)]

    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))

    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
                )
            )
        )

    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])

    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()
        ])

    @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)
        )

    @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)

    @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)

    @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)

    @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)