fishertools 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- tests/test_structure_enhancements.py +115 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property-based tests for visual documentation consistency.
|
|
3
|
+
|
|
4
|
+
Feature: fishertools-enhancements
|
|
5
|
+
Property 4: Visual Documentation Consistency
|
|
6
|
+
Validates: Requirements 4.1, 4.2, 4.3, 4.4, 4.5
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
from hypothesis import given, strategies as st, assume
|
|
11
|
+
from fishertools.documentation import VisualDocumentation
|
|
12
|
+
from fishertools.documentation.models import (
|
|
13
|
+
FunctionInfo, MermaidDiagram, DiagramType,
|
|
14
|
+
FlowDiagram, Flowchart, StructureDiagram
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Test data generators
|
|
19
|
+
@st.composite
|
|
20
|
+
def generate_function_info(draw):
|
|
21
|
+
"""Generate valid FunctionInfo for testing."""
|
|
22
|
+
name = draw(st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=('Lu', 'Ll', 'Nd'), blacklist_characters='_')))
|
|
23
|
+
assume(name.isidentifier() and not name.startswith('_'))
|
|
24
|
+
|
|
25
|
+
docstring = draw(st.one_of(
|
|
26
|
+
st.none(),
|
|
27
|
+
st.text(min_size=10, max_size=200)
|
|
28
|
+
))
|
|
29
|
+
|
|
30
|
+
# Generate parameters with type annotations
|
|
31
|
+
param_count = draw(st.integers(min_value=0, max_value=5))
|
|
32
|
+
parameters = {}
|
|
33
|
+
for i in range(param_count):
|
|
34
|
+
param_name = f"param_{i}"
|
|
35
|
+
param_type = draw(st.sampled_from(['str', 'int', 'float', 'bool', 'List[str]', 'Dict[str, Any]', 'Optional[str]']))
|
|
36
|
+
parameters[param_name] = param_type
|
|
37
|
+
|
|
38
|
+
return_type = draw(st.one_of(
|
|
39
|
+
st.none(),
|
|
40
|
+
st.sampled_from(['str', 'int', 'bool', 'List[str]', 'None'])
|
|
41
|
+
))
|
|
42
|
+
|
|
43
|
+
return FunctionInfo(
|
|
44
|
+
name=name,
|
|
45
|
+
docstring=docstring,
|
|
46
|
+
parameters=parameters,
|
|
47
|
+
return_type=return_type,
|
|
48
|
+
module_path="/fake/path/module.py",
|
|
49
|
+
line_number=draw(st.integers(min_value=1, max_value=1000))
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@st.composite
|
|
54
|
+
def generate_code_sample(draw):
|
|
55
|
+
"""Generate valid Python code samples for testing."""
|
|
56
|
+
# Generate simple but valid Python code structures
|
|
57
|
+
code_templates = [
|
|
58
|
+
"def {name}():\n return {value}",
|
|
59
|
+
"if {condition}:\n print('{message}')\nelse:\n print('alternative')",
|
|
60
|
+
"for i in range({count}):\n print(i)",
|
|
61
|
+
"while {condition}:\n {action}\n break",
|
|
62
|
+
"x = {value}\nprint(x)\nreturn x"
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
template = draw(st.sampled_from(code_templates))
|
|
66
|
+
|
|
67
|
+
# Fill in template variables
|
|
68
|
+
name = draw(st.text(min_size=3, max_size=15, alphabet=st.characters(whitelist_categories=('Lu', 'Ll'))))
|
|
69
|
+
assume(name.isidentifier())
|
|
70
|
+
|
|
71
|
+
value = draw(st.one_of(
|
|
72
|
+
st.integers(min_value=1, max_value=100),
|
|
73
|
+
st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=('Lu', 'Ll', 'Nd'))),
|
|
74
|
+
st.sampled_from([True, False])
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
condition = draw(st.sampled_from(['True', 'False', 'x > 0', 'len(data) > 0']))
|
|
78
|
+
message = draw(st.text(min_size=5, max_size=30, alphabet=st.characters(whitelist_categories=('Lu', 'Ll', 'Nd', 'Zs'))))
|
|
79
|
+
count = draw(st.integers(min_value=1, max_value=10))
|
|
80
|
+
action = draw(st.sampled_from(['x += 1', 'print("loop")', 'data.append(i)']))
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
code = template.format(
|
|
84
|
+
name=name,
|
|
85
|
+
value=repr(value),
|
|
86
|
+
condition=condition,
|
|
87
|
+
message=message,
|
|
88
|
+
count=count,
|
|
89
|
+
action=action
|
|
90
|
+
)
|
|
91
|
+
return code
|
|
92
|
+
except (KeyError, ValueError):
|
|
93
|
+
# If template formatting fails, return a simple default
|
|
94
|
+
return f"def {name}():\n return {repr(value)}"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@st.composite
|
|
98
|
+
def generate_data_structure(draw):
|
|
99
|
+
"""Generate various data structures for visualization testing."""
|
|
100
|
+
data_type = draw(st.sampled_from(['list', 'dict', 'set', 'string', 'int', 'float', 'bool', 'none']))
|
|
101
|
+
|
|
102
|
+
if data_type == 'list':
|
|
103
|
+
return draw(st.lists(st.integers(min_value=0, max_value=100), max_size=15))
|
|
104
|
+
elif data_type == 'dict':
|
|
105
|
+
return draw(st.dictionaries(
|
|
106
|
+
st.text(min_size=1, max_size=10, alphabet=st.characters(whitelist_categories=('Lu', 'Ll'))),
|
|
107
|
+
st.integers(min_value=0, max_value=100),
|
|
108
|
+
max_size=10
|
|
109
|
+
))
|
|
110
|
+
elif data_type == 'set':
|
|
111
|
+
return draw(st.sets(st.integers(min_value=0, max_value=50), max_size=10))
|
|
112
|
+
elif data_type == 'string':
|
|
113
|
+
return draw(st.text(max_size=100))
|
|
114
|
+
elif data_type == 'int':
|
|
115
|
+
return draw(st.integers(min_value=-1000, max_value=1000))
|
|
116
|
+
elif data_type == 'float':
|
|
117
|
+
return draw(st.floats(min_value=-100.0, max_value=100.0, allow_nan=False, allow_infinity=False))
|
|
118
|
+
elif data_type == 'bool':
|
|
119
|
+
return draw(st.booleans())
|
|
120
|
+
else: # none
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class TestVisualDocumentationProperties:
|
|
125
|
+
"""Property-based tests for visual documentation consistency."""
|
|
126
|
+
|
|
127
|
+
@given(
|
|
128
|
+
st.lists(st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=('Lu', 'Ll', 'Nd'), blacklist_characters='_')), min_size=1, max_size=10),
|
|
129
|
+
st.sampled_from(['modern', 'classic', 'minimal'])
|
|
130
|
+
)
|
|
131
|
+
def test_architecture_diagram_consistency(self, modules, style):
|
|
132
|
+
"""
|
|
133
|
+
Property 4.1: Architecture diagram consistency
|
|
134
|
+
|
|
135
|
+
For any list of modules and any style, the Visual_Documentation should
|
|
136
|
+
display architecture diagrams with consistent styling.
|
|
137
|
+
|
|
138
|
+
**Validates: Requirements 4.1, 4.5**
|
|
139
|
+
"""
|
|
140
|
+
# Filter out invalid module names
|
|
141
|
+
valid_modules = [m for m in modules if m.isidentifier() and not m.startswith('_')]
|
|
142
|
+
assume(len(valid_modules) > 0)
|
|
143
|
+
|
|
144
|
+
visual_doc = VisualDocumentation(style=style)
|
|
145
|
+
|
|
146
|
+
# Generate architecture diagram
|
|
147
|
+
diagram = visual_doc.create_architecture_diagram(valid_modules)
|
|
148
|
+
|
|
149
|
+
# Verify diagram structure (4.1)
|
|
150
|
+
assert isinstance(diagram, MermaidDiagram)
|
|
151
|
+
assert diagram.diagram_type == DiagramType.ARCHITECTURE
|
|
152
|
+
assert diagram.content is not None
|
|
153
|
+
assert len(diagram.content.strip()) > 0
|
|
154
|
+
assert diagram.title == "Module Architecture"
|
|
155
|
+
|
|
156
|
+
# Verify all modules are included in the diagram
|
|
157
|
+
for module in valid_modules:
|
|
158
|
+
assert module in diagram.content
|
|
159
|
+
|
|
160
|
+
# Verify Mermaid syntax is valid (basic check)
|
|
161
|
+
assert "graph TB" in diagram.content or "graph TD" in diagram.content
|
|
162
|
+
|
|
163
|
+
# Apply consistent styling and verify consistency (4.5)
|
|
164
|
+
styled_diagram = visual_doc.apply_consistent_styling(diagram)
|
|
165
|
+
|
|
166
|
+
# Verify styling is applied consistently
|
|
167
|
+
assert isinstance(styled_diagram, MermaidDiagram)
|
|
168
|
+
assert styled_diagram.diagram_type == diagram.diagram_type
|
|
169
|
+
assert styled_diagram.title == diagram.title
|
|
170
|
+
|
|
171
|
+
# Verify style-specific elements are present
|
|
172
|
+
if style == "modern":
|
|
173
|
+
assert "#e1f5fe" in styled_diagram.content or "#01579b" in styled_diagram.content
|
|
174
|
+
elif style == "classic":
|
|
175
|
+
assert "#fff3e0" in styled_diagram.content or "#e65100" in styled_diagram.content
|
|
176
|
+
|
|
177
|
+
# Verify theme configuration is added
|
|
178
|
+
assert "%%{init:" in styled_diagram.content
|
|
179
|
+
|
|
180
|
+
@given(generate_function_info(), st.sampled_from(['modern', 'classic', 'minimal']))
|
|
181
|
+
def test_data_flow_diagram_consistency(self, function_info, style):
|
|
182
|
+
"""
|
|
183
|
+
Property 4.2: Data flow diagram consistency
|
|
184
|
+
|
|
185
|
+
For any function and any style, the Visual_Documentation should
|
|
186
|
+
show data flow schemes with consistent styling.
|
|
187
|
+
|
|
188
|
+
**Validates: Requirements 4.2, 4.5**
|
|
189
|
+
"""
|
|
190
|
+
visual_doc = VisualDocumentation(style=style)
|
|
191
|
+
|
|
192
|
+
# Generate data flow diagram
|
|
193
|
+
flow_diagram = visual_doc.generate_data_flow_diagram(function_info)
|
|
194
|
+
|
|
195
|
+
# Verify diagram structure (4.2)
|
|
196
|
+
assert isinstance(flow_diagram, FlowDiagram)
|
|
197
|
+
assert flow_diagram.title == f"Data Flow: {function_info.name}"
|
|
198
|
+
assert len(flow_diagram.nodes) >= 3 # At least input, process, output
|
|
199
|
+
assert len(flow_diagram.edges) >= 2 # At least input->process, process->output
|
|
200
|
+
|
|
201
|
+
# Verify required nodes are present
|
|
202
|
+
node_types = [node.get('type') for node in flow_diagram.nodes]
|
|
203
|
+
assert 'input' in node_types
|
|
204
|
+
assert 'process' in node_types
|
|
205
|
+
assert 'output' in node_types
|
|
206
|
+
|
|
207
|
+
# Verify function name appears in process node
|
|
208
|
+
process_nodes = [node for node in flow_diagram.nodes if node.get('type') == 'process']
|
|
209
|
+
assert any(function_info.name in node.get('label', '') for node in process_nodes)
|
|
210
|
+
|
|
211
|
+
# Verify edges connect properly
|
|
212
|
+
edge_froms = [edge.get('from') for edge in flow_diagram.edges]
|
|
213
|
+
edge_tos = [edge.get('to') for edge in flow_diagram.edges]
|
|
214
|
+
assert 'input' in edge_froms or any('param_' in ef for ef in edge_froms)
|
|
215
|
+
assert 'output' in edge_tos
|
|
216
|
+
assert 'process' in edge_tos
|
|
217
|
+
|
|
218
|
+
# If function has parameters, verify they are represented
|
|
219
|
+
if function_info.parameters:
|
|
220
|
+
param_nodes = [node for node in flow_diagram.nodes if node.get('type') == 'parameter']
|
|
221
|
+
assert len(param_nodes) == len(function_info.parameters)
|
|
222
|
+
|
|
223
|
+
@given(generate_code_sample(), st.sampled_from(['modern', 'classic', 'minimal']))
|
|
224
|
+
def test_algorithm_flowchart_consistency(self, code, style):
|
|
225
|
+
"""
|
|
226
|
+
Property 4.4: Algorithm flowchart consistency
|
|
227
|
+
|
|
228
|
+
For any algorithm code and any style, the Visual_Documentation should
|
|
229
|
+
create flowcharts with consistent styling.
|
|
230
|
+
|
|
231
|
+
**Validates: Requirements 4.4, 4.5**
|
|
232
|
+
"""
|
|
233
|
+
visual_doc = VisualDocumentation(style=style)
|
|
234
|
+
|
|
235
|
+
# Generate algorithm flowchart
|
|
236
|
+
flowchart = visual_doc.create_algorithm_flowchart(code)
|
|
237
|
+
|
|
238
|
+
# Verify flowchart structure (4.4)
|
|
239
|
+
assert isinstance(flowchart, Flowchart)
|
|
240
|
+
assert flowchart.title is not None
|
|
241
|
+
assert len(flowchart.title.strip()) > 0
|
|
242
|
+
assert len(flowchart.steps) > 0
|
|
243
|
+
|
|
244
|
+
# Verify each step has required fields
|
|
245
|
+
for step in flowchart.steps:
|
|
246
|
+
assert 'id' in step
|
|
247
|
+
assert 'type' in step
|
|
248
|
+
assert 'description' in step
|
|
249
|
+
assert 'line_number' in step
|
|
250
|
+
assert step['line_number'] > 0
|
|
251
|
+
|
|
252
|
+
# Verify connections are logical
|
|
253
|
+
if len(flowchart.steps) > 1:
|
|
254
|
+
assert len(flowchart.connections) >= len(flowchart.steps) - 1
|
|
255
|
+
|
|
256
|
+
# Verify connection structure
|
|
257
|
+
for connection in flowchart.connections:
|
|
258
|
+
assert 'from' in connection
|
|
259
|
+
assert 'to' in connection
|
|
260
|
+
|
|
261
|
+
# Verify referenced steps exist
|
|
262
|
+
step_ids = [step['id'] for step in flowchart.steps]
|
|
263
|
+
assert connection['from'] in step_ids
|
|
264
|
+
assert connection['to'] in step_ids
|
|
265
|
+
|
|
266
|
+
# Verify step types are appropriate
|
|
267
|
+
valid_step_types = ['start', 'end', 'process', 'decision', 'loop', 'output']
|
|
268
|
+
for step in flowchart.steps:
|
|
269
|
+
assert step['type'] in valid_step_types
|
|
270
|
+
|
|
271
|
+
@given(generate_data_structure(), st.sampled_from(['modern', 'classic', 'minimal']))
|
|
272
|
+
def test_data_structure_visualization_consistency(self, data, style):
|
|
273
|
+
"""
|
|
274
|
+
Property 4.3: Data structure visualization consistency
|
|
275
|
+
|
|
276
|
+
For any data structure and any style, the Visual_Documentation should
|
|
277
|
+
provide visual representations with consistent styling.
|
|
278
|
+
|
|
279
|
+
**Validates: Requirements 4.3, 4.5**
|
|
280
|
+
"""
|
|
281
|
+
visual_doc = VisualDocumentation(style=style)
|
|
282
|
+
|
|
283
|
+
# Generate data structure visualization
|
|
284
|
+
struct_diagram = visual_doc.visualize_data_structure(data)
|
|
285
|
+
|
|
286
|
+
# Verify diagram structure (4.3)
|
|
287
|
+
assert isinstance(struct_diagram, StructureDiagram)
|
|
288
|
+
assert struct_diagram.structure_type is not None
|
|
289
|
+
assert struct_diagram.visualization is not None
|
|
290
|
+
assert struct_diagram.title is not None
|
|
291
|
+
|
|
292
|
+
# Verify structure type matches data type
|
|
293
|
+
if data is None:
|
|
294
|
+
assert struct_diagram.structure_type == "None"
|
|
295
|
+
elif isinstance(data, bool): # Check bool before int since bool is subclass of int
|
|
296
|
+
assert struct_diagram.structure_type == "bool"
|
|
297
|
+
elif isinstance(data, int):
|
|
298
|
+
assert struct_diagram.structure_type == "int"
|
|
299
|
+
elif isinstance(data, list):
|
|
300
|
+
assert struct_diagram.structure_type == "list"
|
|
301
|
+
elif isinstance(data, tuple):
|
|
302
|
+
assert struct_diagram.structure_type == "tuple"
|
|
303
|
+
elif isinstance(data, dict):
|
|
304
|
+
assert struct_diagram.structure_type == "dict"
|
|
305
|
+
elif isinstance(data, set):
|
|
306
|
+
assert struct_diagram.structure_type == "set"
|
|
307
|
+
elif isinstance(data, str):
|
|
308
|
+
assert struct_diagram.structure_type == "string"
|
|
309
|
+
elif isinstance(data, float):
|
|
310
|
+
assert struct_diagram.structure_type == "float"
|
|
311
|
+
|
|
312
|
+
# Verify visualization is meaningful
|
|
313
|
+
assert len(struct_diagram.visualization.strip()) > 0
|
|
314
|
+
|
|
315
|
+
# For collections, verify size information is included when appropriate
|
|
316
|
+
if isinstance(data, (list, tuple, dict, set)) and len(data) > 10:
|
|
317
|
+
assert "..." in struct_diagram.visualization
|
|
318
|
+
assert "total" in struct_diagram.visualization
|
|
319
|
+
|
|
320
|
+
@given(generate_code_sample(), generate_data_structure(), st.sampled_from(['modern', 'classic', 'minimal']))
|
|
321
|
+
def test_example_visualization_consistency(self, code, result, style):
|
|
322
|
+
"""
|
|
323
|
+
Property 4.3: Example visualization consistency
|
|
324
|
+
|
|
325
|
+
For any code example and result, the Visual_Documentation should
|
|
326
|
+
create consistent visual representations.
|
|
327
|
+
|
|
328
|
+
**Validates: Requirements 4.3, 4.5**
|
|
329
|
+
"""
|
|
330
|
+
visual_doc = VisualDocumentation(style=style)
|
|
331
|
+
|
|
332
|
+
# Generate example visualization
|
|
333
|
+
html_viz = visual_doc.create_example_visualization(code, result)
|
|
334
|
+
|
|
335
|
+
# Verify HTML structure (4.3)
|
|
336
|
+
assert isinstance(html_viz, str)
|
|
337
|
+
assert len(html_viz.strip()) > 0
|
|
338
|
+
|
|
339
|
+
# Verify required HTML elements are present
|
|
340
|
+
assert '<div class="example-visualization">' in html_viz
|
|
341
|
+
assert '<div class="example-header">' in html_viz
|
|
342
|
+
assert '<div class="code-section">' in html_viz
|
|
343
|
+
assert '<div class="result-section">' in html_viz
|
|
344
|
+
assert '<style>' in html_viz
|
|
345
|
+
|
|
346
|
+
# Verify code is properly escaped and included
|
|
347
|
+
# The _escape_html method escapes single quotes as '
|
|
348
|
+
escaped_code = code.replace('&', '&').replace('<', '<').replace('>', '>').replace("'", ''').replace('"', '"')
|
|
349
|
+
assert escaped_code in html_viz
|
|
350
|
+
|
|
351
|
+
# Verify result visualization is included
|
|
352
|
+
result_viz = visual_doc.visualize_data_structure(result)
|
|
353
|
+
escaped_result = result_viz.visualization.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''')
|
|
354
|
+
assert escaped_result in html_viz
|
|
355
|
+
|
|
356
|
+
# Verify style-specific CSS is applied (4.5)
|
|
357
|
+
if style == "modern":
|
|
358
|
+
assert "#01579b" in html_viz or "#4a148c" in html_viz
|
|
359
|
+
elif style == "classic":
|
|
360
|
+
assert "#e65100" in html_viz or "#880e4f" in html_viz
|
|
361
|
+
|
|
362
|
+
# Verify consistent structure across all styles
|
|
363
|
+
assert 'font-family:' in html_viz
|
|
364
|
+
assert 'background:' in html_viz
|
|
365
|
+
assert 'border:' in html_viz
|
|
366
|
+
|
|
367
|
+
@given(
|
|
368
|
+
st.lists(st.text(min_size=1, max_size=15, alphabet=st.characters(whitelist_categories=('Lu', 'Ll'))), min_size=1, max_size=5),
|
|
369
|
+
st.sampled_from(['modern', 'classic', 'minimal'])
|
|
370
|
+
)
|
|
371
|
+
def test_consistent_styling_across_diagram_types(self, modules, style):
|
|
372
|
+
"""
|
|
373
|
+
Property 4.5: Consistent styling across all visual elements
|
|
374
|
+
|
|
375
|
+
For any visual documentation elements and any style, all diagrams
|
|
376
|
+
should maintain consistent styling themes.
|
|
377
|
+
|
|
378
|
+
**Validates: Requirements 4.5**
|
|
379
|
+
"""
|
|
380
|
+
# Filter valid modules
|
|
381
|
+
valid_modules = [m for m in modules if m.isidentifier()]
|
|
382
|
+
assume(len(valid_modules) > 0)
|
|
383
|
+
|
|
384
|
+
visual_doc = VisualDocumentation(style=style)
|
|
385
|
+
|
|
386
|
+
# Generate different types of diagrams
|
|
387
|
+
arch_diagram = visual_doc.create_architecture_diagram(valid_modules)
|
|
388
|
+
|
|
389
|
+
# Apply styling to all diagrams
|
|
390
|
+
styled_arch = visual_doc.apply_consistent_styling(arch_diagram)
|
|
391
|
+
|
|
392
|
+
# Verify consistent theme application
|
|
393
|
+
diagrams = [styled_arch]
|
|
394
|
+
|
|
395
|
+
# All diagrams should have theme configuration
|
|
396
|
+
for diagram in diagrams:
|
|
397
|
+
assert "%%{init:" in diagram.content
|
|
398
|
+
|
|
399
|
+
# Verify style-specific consistency
|
|
400
|
+
if style == "modern":
|
|
401
|
+
color_palette = ["#e1f5fe", "#01579b", "#4a148c", "#f3e5f5"]
|
|
402
|
+
for diagram in diagrams:
|
|
403
|
+
# At least one color from the modern palette should be present
|
|
404
|
+
assert any(color in diagram.content for color in color_palette)
|
|
405
|
+
elif style == "classic":
|
|
406
|
+
color_palette = ["#fff3e0", "#e65100", "#880e4f", "#fce4ec"]
|
|
407
|
+
for diagram in diagrams:
|
|
408
|
+
# At least one color from the classic palette should be present
|
|
409
|
+
assert any(color in diagram.content for color in color_palette)
|
|
410
|
+
else: # minimal
|
|
411
|
+
# Minimal style should have more neutral colors
|
|
412
|
+
neutral_indicators = ["#f5f5f5", "#333", "#666", "#999"]
|
|
413
|
+
for diagram in diagrams:
|
|
414
|
+
assert any(indicator in diagram.content for indicator in neutral_indicators)
|
|
415
|
+
|
|
416
|
+
def test_visual_documentation_error_handling(self):
|
|
417
|
+
"""
|
|
418
|
+
Test that visual documentation handles edge cases gracefully.
|
|
419
|
+
|
|
420
|
+
Validates that the system provides meaningful errors for invalid inputs
|
|
421
|
+
while maintaining consistency.
|
|
422
|
+
"""
|
|
423
|
+
visual_doc = VisualDocumentation()
|
|
424
|
+
|
|
425
|
+
# Test empty module list
|
|
426
|
+
with pytest.raises(ValueError, match="At least one module must be provided"):
|
|
427
|
+
visual_doc.create_architecture_diagram([])
|
|
428
|
+
|
|
429
|
+
# Test empty code
|
|
430
|
+
with pytest.raises(ValueError, match="Code cannot be empty"):
|
|
431
|
+
visual_doc.create_algorithm_flowchart("")
|
|
432
|
+
|
|
433
|
+
with pytest.raises(ValueError, match="Code cannot be empty"):
|
|
434
|
+
visual_doc.create_example_visualization("", "result")
|
|
435
|
+
|
|
436
|
+
# Test empty diagram content
|
|
437
|
+
empty_diagram = MermaidDiagram(DiagramType.ARCHITECTURE, "", "Test")
|
|
438
|
+
with pytest.raises(ValueError, match="Diagram content cannot be empty"):
|
|
439
|
+
visual_doc.apply_consistent_styling(empty_diagram)
|
|
440
|
+
|
|
441
|
+
# Test function with empty name
|
|
442
|
+
empty_func = FunctionInfo("", None, {}, None, "/path", 1)
|
|
443
|
+
with pytest.raises(ValueError, match="Function must have a name"):
|
|
444
|
+
visual_doc.generate_data_flow_diagram(empty_func)
|