File: utils.py

package info (click to toggle)
exhale 0.3.7-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,616 kB
  • sloc: python: 9,057; cpp: 1,260; javascript: 915; f90: 29; ansic: 18; makefile: 16
file content (138 lines) | stat: -rw-r--r-- 5,568 bytes parent folder | download
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
# -*- 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 for validating parts of :mod:`exhale.utils`.
"""
import re

from exhale.utils import join_template_tokens, tokenize_template

import pytest

templates = [
    # Special case: empty list is returned for empty string.
    ("", []),
    # No template gets tokenized letter by letter.
    ("foo", ["foo"]),
    ("ns::foo", ["ns::foo"]),
    # Depth 1 templates.
    ("foo< 12 >", ["foo", ["12"]]),
    ("ns::foo< int, 66 >", ["ns::foo", ["int", "66"]]),
    ("ns::foo< x, y, z >", ["ns::foo", ["x", "y", "z"]]),
    # Depth 2 templates.
    ("foo< std::array< int > >", ["foo", ["std::array", ["int"]]]),
    ("bar< Cls< x, y, z > >", ["bar", ["Cls", ["x", "y", "z"]]]),
    ("baz< Cls< x >, y, z >", ["baz", ["Cls", ["x"], "y", "z"]]),
    ("baz< x, Cls< y, z >, w >", ["baz", ["x", "Cls", ["y", "z"], "w"]]),
    ("boo< a, b, c, Cls< d, e, f > >", ["boo", ["a", "b", "c", "Cls", ["d", "e", "f"]]]),
    # Depth 3 templates for good measure.  And bleeding eyes.
    ("rawr< A< B< C > > >", ["rawr", ["A", ["B", ["C"]]]]),
    (
        "f< a, B< c, D< e > >, F< g, H< i, J< k, l > > > >",
        ["f", ["a", "B", ["c", "D", ["e"]], "F", ["g", "H", ["i", "J", ["k", "l"]]]]]
    ),
    (
        "special::ImageBuffer< Image< 1920, 1080 > >::Data",
        ["special::ImageBuffer", ["Image", ["1920", "1080"]], "::Data"]
    ),
    (
        "special::unique::Nonsense< 11, snowflake::Ontology< 11 > >",
        ["special::unique::Nonsense", ["11", "snowflake::Ontology", ["11"]]]
    ),
    (
        "special::unique::Nonsense< 11, snowflake::Ontology< 11 >::test >::what",
        ["special::unique::Nonsense", ["11", "snowflake::Ontology", ["11"], '::test'], "::what"]
    )
]
"""
Shared template test case parameters, (node_name, template_tokens expected value).

.. warning::

    Test cases are unfortunately whitespace sensitive, the node_name string must have
    exactly 1 space after any ``<`` and exactly one space before any ``>``.
"""

extra_templates = [
    # Test interior whitespace is folded into a single space.
    (
        "special::complex::Fold< typename... \t\t\t   \t Ts>",
        ["special::complex::Fold", ["typename... Ts"]]
    ),
    (
        "special::complex::Fold<     typename \t  ...   Ts >",
        ["special::complex::Fold", ["typename ... Ts"]]
    ),
    (
        "special::complex::Fold<typename \t \t ...Ts>",
        ["special::complex::Fold", ["typename ...Ts"]]
    ),
    # It's so ugly it's pretty.
    (
        "has_type_member< T      , void_t  < typename     T::type  >",
        ["has_type_member", ["T", "void_t", ["typename T::type"]]]
    ),
    # Getting lazy with these tests, just care about special symbols.
    ("template <class...>", ["template", ["class..."]]),
    ("template< class, class=void >", ["template", ["class", "class=void"]]),
    (
        "template<class, class   =   void>",
        ["template", ["class", "class = void"]]
    ),
    ("template <const int* I>", ["template", ["const int* I"]]),
    ("template < const   int   *  I>", ["template", ["const int * I"]]),
    ("template <const int& I>", ["template", ["const int& I"]]),
    ("template <const int      &I>", ["template", ["const int &I"]])
    # TODO: probably more examples directly from the specs should be added, but this is
    # hopefully good enough for now.
    # https://en.cppreference.com/w/cpp/language/template_parameters
    #
    # https://github.com/svenevs/exhale/pull/159#discussion_r892041086
    # We may have issues with pointer to member of object and specializations
    # and if we do then we'll have to fix it.  First somebody can donate code.
]
"""
Only used for testing :func:`~exhale.utils.tokenize_template` (not joining back to the
original name).  These tests include some more complicated templates to ensure the
regular expression matching is valid, and some permutations of the whitespace as well
for testing how tokens are getting split.
"""


@pytest.mark.parametrize("node_name,expected", templates + extra_templates)
def test_tokenize_template(node_name, expected):
    """
    Tests for :func:`~exhale.utils.tokenize_template`.
    """
    assert tokenize_template(node_name) == expected


@pytest.mark.parametrize("node_name", [n[0] for n in templates])
def test_join_template_tokens(node_name):
    """
    Tests for :func:`~exhale.utils.join_template_tokens`.
    """
    assert join_template_tokens(tokenize_template(node_name)) == node_name


def test_join_template_tokens_edge_cases():
    """
    Edge case tests for :func:`~exhale.utils.join_template_tokens`.
    """
    with pytest.raises(ValueError) as exc_info:
        join_template_tokens("hi there")
    exc_info.match(
        "Expected tokens to be a list, but got <class 'str'> instead.")

    assert join_template_tokens([]) == ""

    with pytest.raises(ValueError) as exc_info:
        join_template_tokens([["foo"]])
    exc_info.match(re.escape(
        "The first token must be a string, but the type of tokens[0] is <class "
        "'list'>."))