Source code for testing.tests.configs

# -*- coding: utf8 -*-
########################################################################################
# This file is part of exhale.  Copyright (c) 2017-2019, Stephen McDowell.             #
# Full BSD 3-Clause license available here:                                            #
#                                                                                      #
#                https://github.com/svenevs/exhale/blob/master/LICENSE                 #
########################################################################################
"""
Tests for validating error handling with configs set in ``conf.py``.
"""
from __future__ import unicode_literals
import re
import textwrap

import pytest
from sphinx.errors import ConfigError

from testing import get_exhale_root
from testing.base import ExhaleTestCase
from testing.decorators import confoverrides


[docs]def assert_message_not_present(test, message, text, flags=0): """ Assert that ``message`` **is** found in ``text``. This method is useful for creating consistent error messages when a test fails, including printing out what the contents of ``message`` and ``text`` were. **Parameters** ``test`` (:class:`ExhaleTestCase <testing.base.ExhaleTestCase>`) The test case to call ``assertTrue`` with, should just be ``self`` for most test cases in the framework. ``message`` (:class:`python:str`) The message to search for in ``text``. This will be the ``pattern`` parameter for a call to :func:`python:re.search`. ``text`` (:class:`python:str`) The text to be searched. This will be the ``string`` parameter for a call to :func:`python:re.search`. ``flags`` (:class:`python:int`) Optional flags to supply as ``flags`` to :func:`python:re.search`. Default of ``0`` means no special flags sent. """ test.assertTrue( re.search(message, text, flags) is None, textwrap.dedent('''\ Sphinx message stream contained '{message}' but should not have. Stream contents: {vsep} {text} {vsep} ''').format( message=message, vsep=("*" * 44), text=text ) )
[docs]def assert_message_present(test, message, text, flags=0): """ Assert that ``message`` is **not** found in ``text``. This method is useful for creating consistent error messages when a test fails, including printing out what the contents of ``message`` and ``text`` were. **Parameters** ``test`` (:class:`ExhaleTestCase <testing.base.ExhaleTestCase>`) The test case to call ``assertTrue`` with, should just be ``self`` for most test cases in the framework. ``message`` (:class:`python:str`) The message to search for in ``text``. This will be the ``pattern`` parameter for a call to :func:`python:re.search`. ``text`` (:class:`python:str`) The text to be searched. This will be the ``string`` parameter for a call to :func:`python:re.search`. ``flags`` (:class:`python:int`) Optional flags to supply as ``flags`` to :func:`python:re.search`. Default of ``0`` means no special flags sent. """ test.assertTrue( re.search(message, text, flags) is not None, textwrap.dedent('''\ Sphinx message stream did not contain '{message}' but should have. Stream contents: {vsep} {text} {vsep} ''').format( message=message, vsep=("*" * 44), text=text ) )
[docs]class ConfigurationStatusTests(ExhaleTestCase): """ Tests to ensure expected status messages are displayed. """ 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>`. """ treeview_add_start_message = r"Exhale: adding tree view css / javascript\." """ Start message displayed only when :data:`createTreeView <exhale.configs.createTreeView>` is ``True``. """ treeview_add_close_message = r"Exhale: added tree view css / javascript\." """ Closing message displayed only when :data:`createTreeView <exhale.configs.createTreeView>` is ``True``. """
[docs] def test_no_treeview(self): """ Verify no notification for adding CSS / JavaScript issued when no Tree View requested. """ sphinx_status = self.app._status.getvalue() assert_message_not_present(self, self.treeview_add_start_message, sphinx_status) assert_message_not_present(self, self.treeview_add_close_message, sphinx_status)
[docs] @confoverrides(exhale_args={"createTreeView": True}) def test_treeview(self): """ Verify notification for adding CSS / JavaScript issued when Tree View requested. """ sphinx_status = self.app._status.getvalue() assert_message_present(self, self.treeview_add_start_message, sphinx_status) assert_message_present(self, self.treeview_add_close_message, sphinx_status)
[docs]class ConfigurationWarningTests(ExhaleTestCase): """ Tests to ensure non-fatal configuration discrepancies receive warnings. """ 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] @confoverrides(exhale_args={"createTreeView": False, "treeViewIsBootstrap": True}) def test_treeview_mismatch(self): """ Verify warning issued with ``createTreeView=False`` but ``treeViewIsBootstrap=True``. """ sphinx_warnings = self.app._warning.getvalue() expected = "Exhale: `treeViewIsBootstrap=True` ignored since `createTreeView=False`" self.assertTrue( expected in sphinx_warnings, "Sphinx Warnings did not contain '{0}'.".format(expected) )
[docs]class ListingExcludeTests(ExhaleTestCase): """Test for expected failures when invalid configurations are given in ``conf.py``.""" 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] class BadStr(object): """ Helper for :func:`ListingExcludeTests.test_invalid_report_index`. """ def __str__(self): """Raise :class:`python:AttributeError`.""" raise AttributeError("No string for you!")
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"listingExclude item at index 1 cannot be unpacked as `pattern, flags = item`:" ) @confoverrides(exhale_args={ "listingExclude": [r"valid", BadStr()] }) def test_invalid_report_index(self): """Verify list index is indicated when item cannot be converted to string.""" pass
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"Unable to compile specified listingExclude" ) @confoverrides(exhale_args={ "listingExclude": [(r"some_string", "an invalid flag")] }) def test_invalid_regex_flags(self): """Verify invalid regex ``flags`` are rejected.""" pass
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"Unable to compile specified listingExclude" ) @confoverrides(exhale_args={ "listingExclude": [ r"valid", r"*I don't compile$" ] }) def test_bad_regex(self): """Verify string pattern that does not compile is gracefully rejected.""" pass
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"listingExclude item .* cannot be unpacked as `pattern, flags = item`:" ) @confoverrides(exhale_args={ "listingExclude": [ r"valid", (r"valid", 0), (r"invalid", 0, 1) # length 3 and longer cannot unpack ] }) def test_too_many(self): """ Verify that length three item is rejected. Only ``pattern:str`` or ``(pattern:str, flags:int)`` are allowed. """ pass
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"listingExclude item .* cannot be unpacked as `pattern, flags = item`:" ) @confoverrides(exhale_args={ "listingExclude": [1] }) def test_invalid_pattern(self): """Verify that non-string argument for pattern is rejected.""" pass
[docs]class UnabridgedOrphanKindsTests(ExhaleTestCase): """Test various values of :data:`~exhale.configs.unabridgedOrphanKinds`.""" test_project = "cpp_long_names" """ .. testproject:: cpp_long_names The ``cpp_long_names`` project essentially has 1 compound of each kind, which makes it an ideal project to reuse here. """
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"^The type of the value for key `unabridgedOrphanKinds`" ) @confoverrides(exhale_args={ "unabridgedOrphanKinds": False }) def test_not_iterable_fails(self): """Verify that non-list/set values raise a configuration error.""" pass
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"^`unabridgedOrphanKinds` must be a list of strings." ) @confoverrides(exhale_args={ "unabridgedOrphanKinds": ["file", 22, "dir"] }) def test_non_string_fails(self): """Verify that non-string entries raise a configuration error.""" pass
[docs] @pytest.mark.setup_raises( exception=ConfigError, match=r"^Unknown kind `jabooty` given in `unabridgedOrphanKinds`." ) @confoverrides(exhale_args={ "unabridgedOrphanKinds": ["file", "jabooty", "dir"] }) def test_invalid_kind(self): """Verify that invalid ``kind`` raises a configuration error.""" pass
[docs] def total(self, root): """Count all nodes that are not ``enumvalue`` and ``group``.""" # TODO: probably should make this available in ExhaleRoot... return sum(n.kind not in {"group", "enumvalue"} for n in root.all_nodes)
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": []}) def test_no_exclusion(self): """Verify empty list means no exlusions from full API.""" full_names, orphan_names = self.checkAllFilesIncluded() root = get_exhale_root(self) total = self.total(root) assert len(full_names) == total assert len(orphan_names) == 0
def _validate_diff_for_kinds(self, diff, *kinds): """Check if ``diff`` items got orphaned.""" # Initial check: verify expected lengths. full_names, orphan_names = self.checkAllFilesIncluded() root = get_exhale_root(self) total = self.total(root) assert len(full_names) == total - diff assert len(orphan_names) == diff # Verify the right things actually got excluded. for node in root.all_nodes: if node.kind in kinds: assert node.file_name in orphan_names else: assert node.file_name not in orphan_names
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"namespace"}}) def test_orphan_namespace(self): """Verify excluding ``namespace`` behaves as expected.""" self._validate_diff_for_kinds(1, "namespace")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"class"}}) def test_orphan_class(self): """Verify excluding ``class`` behaves as expected.""" self._validate_diff_for_kinds(2, "class", "struct")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"struct"}}) def test_orphan_struct(self): """Verify excluding ``struct`` behaves as expected.""" self._validate_diff_for_kinds(2, "struct", "class")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"class", "struct"}}) def test_orphan_class_struct(self): """Verify excluding ``class`` and ``struct`` behaves as expected.""" self._validate_diff_for_kinds(2, "class", "struct")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"enum"}}) def test_orphan_enum(self): """Verify excluding ``enum`` behaves as expected.""" self._validate_diff_for_kinds(1, "enum")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"union"}}) def test_orphan_union(self): """Verify excluding ``union`` behaves as expected.""" self._validate_diff_for_kinds(1, "union")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"function"}}) def test_orphan_function(self): """Verify excluding ``function`` behaves as expected.""" self._validate_diff_for_kinds(1, "function")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"variable"}}) def test_orphan_variable(self): """Verify excluding ``variable`` behaves as expected.""" # There are two variables in the project. self._validate_diff_for_kinds(2, "variable")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"define"}}) def test_orphan_define(self): """Verify excluding ``define`` behaves as expected.""" # There are two define's in the project. self._validate_diff_for_kinds(2, "define")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"typedef"}}) def test_orphan_typedef(self): """Verify excluding ``typedef`` behaves as expected.""" self._validate_diff_for_kinds(1, "typedef")
[docs] @confoverrides(exhale_args={"unabridgedOrphanKinds": {"file"}}) def test_orphan_file(self): """Verify excluding ``file`` behaves as expected.""" self._validate_diff_for_kinds(1, "file")