Coverage for tests/test_rendering.py: 100.00%

48 statements  

« prev     ^ index     » next       coverage.py v7.10.3, created at 2025-08-14 23:22 +0200

1"""Tests for the `rendering` module.""" 

2 

3from __future__ import annotations 

4 

5import re 

6from dataclasses import dataclass 

7from typing import TYPE_CHECKING, Any, Callable 

8 

9import pytest 

10from griffe import ModulesCollection, temporary_visited_module 

11 

12from mkdocstrings_handlers.python._internal import rendering 

13 

14if TYPE_CHECKING: 

15 from markupsafe import Markup 

16 

17 

18@pytest.mark.parametrize( 

19 "code", 

20 [ 

21 "print('Hello')", 

22 "aaaaa(bbbbb, ccccc=1) + ddddd.eeeee[ffff] or {ggggg: hhhhh, iiiii: jjjjj}", 

23 ], 

24) 

25@pytest.mark.parametrize( 

26 "formatter", 

27 [ 

28 rendering._get_black_formatter(), 

29 rendering._get_ruff_formatter(), 

30 rendering._get_formatter(), 

31 ], 

32) 

33def test_format_code(code: str, formatter: Callable[[str, int], str]) -> None: 

34 """Assert code can be formatted. 

35 

36 Parameters: 

37 code: Code to format. 

38 """ 

39 for length in (5, 100): 

40 assert formatter(code, length) 

41 

42 

43@pytest.mark.parametrize( 

44 ("name", "signature"), 

45 [("Class.method", "(param: str = 'hello') -> 'OtherClass'")], 

46) 

47def test_format_signature(name: Markup, signature: str) -> None: 

48 """Assert signatures can be formatted. 

49 

50 Parameters: 

51 signature: Signature to format. 

52 """ 

53 for length in (5, 100): 

54 assert rendering._format_signature(name, signature, length) 

55 

56 

57@dataclass 

58class _FakeObject: 

59 name: str 

60 inherited: bool = False 

61 parent: None = None 

62 is_alias: bool = False 

63 

64 

65@pytest.mark.parametrize( 

66 ("names", "filter_params", "expected_names"), 

67 [ 

68 (["aa", "ab", "ac", "da"], {"filters": [(re.compile("^a[^b]"), True)]}, {"ab", "da"}), 

69 (["aa", "ab", "ac", "da"], {"members_list": ["aa", "ab"]}, {"aa", "ab"}), 

70 ], 

71) 

72def test_filter_objects(names: list[str], filter_params: dict[str, Any], expected_names: set[str]) -> None: 

73 """Assert the objects filter works correctly. 

74 

75 Parameters: 

76 names: Names of the objects. 

77 filter_params: Parameters passed to the filter function. 

78 expected_names: Names expected to be kept. 

79 """ 

80 objects = {name: _FakeObject(name) for name in names} 

81 filtered = rendering.do_filter_objects(objects, **filter_params) # type: ignore[arg-type] 

82 filtered_names = {obj.name for obj in filtered} 

83 assert set(filtered_names) == set(expected_names) 

84 

85 

86@pytest.mark.parametrize( 

87 ("members", "inherited_members", "expected_names"), 

88 [ 

89 (True, True, {"base", "main"}), 

90 (True, False, {"main"}), 

91 (True, ["base"], {"base", "main"}), 

92 (True, [], {"main"}), 

93 (False, True, {"base"}), 

94 (False, False, set()), 

95 (False, ["base"], {"base"}), 

96 (False, [], set()), 

97 ([], True, {"base"}), 

98 ([], False, set()), 

99 ([], ["base"], {"base"}), 

100 ([], [], set()), 

101 (None, True, {"base", "main"}), 

102 (None, False, {"main"}), 

103 (None, ["base"], {"base", "main"}), 

104 (None, [], {"main"}), 

105 (["base"], True, {"base"}), 

106 (["base"], False, set()), 

107 (["base"], ["base"], {"base"}), 

108 (["base"], [], set()), 

109 (["main"], True, {"main"}), 

110 (["main"], False, {"main"}), 

111 (["main"], ["base"], {"base", "main"}), 

112 (["main"], [], {"main"}), 

113 ], 

114) 

115def test_filter_inherited_members( 

116 members: bool | list[str] | None, 

117 inherited_members: bool | list[str], 

118 expected_names: list[str], 

119) -> None: 

120 """Test inherited members filtering. 

121 

122 Parameters: 

123 members: Members option (parametrized). 

124 inherited_members: Inherited members option (parametrized). 

125 expected_names: The expected result as a list of member names. 

126 """ 

127 collection = ModulesCollection() 

128 with temporary_visited_module( 

129 """ 

130 class Base: 

131 def base(self): ... 

132 

133 class Main(Base): 

134 def main(self): ... 

135 """, 

136 modules_collection=collection, 

137 ) as module: 

138 collection["module"] = module 

139 objects = module["Main"].all_members 

140 filtered = rendering.do_filter_objects(objects, members_list=members, inherited_members=inherited_members) 

141 names = {obj.name for obj in filtered} 

142 assert names == expected_names 

143 

144 

145@pytest.mark.parametrize( 

146 ("order", "members_list", "expected_names"), 

147 [ 

148 ("alphabetical", None, ["a", "b", "c"]), 

149 ("source", None, ["c", "b", "a"]), 

150 ("alphabetical", ["c", "b"], ["c", "b"]), 

151 ("source", ["a", "c"], ["a", "c"]), 

152 ("alphabetical", [], ["a", "b", "c"]), 

153 ("source", [], ["c", "b", "a"]), 

154 ("alphabetical", True, ["a", "b", "c"]), 

155 ("source", False, ["c", "b", "a"]), 

156 ], 

157) 

158def test_ordering_members(order: rendering.Order, members_list: list[str | None], expected_names: list[str]) -> None: 

159 """Assert the objects are correctly ordered. 

160 

161 Parameters: 

162 order: The order to use (alphabetical or source). 

163 members_list: The user specified members list. 

164 expected_names: The expected ordered list of object names. 

165 """ 

166 

167 class Obj: 

168 def __init__(self, name: str, lineno: int | None = None, *, is_alias: bool = False) -> None: 

169 self.name = name 

170 self.lineno = lineno 

171 self.alias_lineno = lineno 

172 self.is_alias = is_alias 

173 

174 members = [Obj("a", 10, is_alias=True), Obj("b", 9, is_alias=False), Obj("c", 8, is_alias=True)] 

175 ordered = rendering.do_order_members(members, order, members_list) # type: ignore[arg-type] 

176 assert [obj.name for obj in ordered] == expected_names