Initial upload with CI/CD workflow
This commit is contained in:
270
tests/unit/test_complexity.py
Normal file
270
tests/unit/test_complexity.py
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
"""Unit tests for complexity analysis module."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from codesnap.core.complexity import (
|
||||||
|
calculate_cyclomatic_complexity,
|
||||||
|
calculate_nesting_depth,
|
||||||
|
count_lines,
|
||||||
|
rate_complexity,
|
||||||
|
analyze_file_complexity,
|
||||||
|
get_complexity_summary,
|
||||||
|
ComplexityMetrics,
|
||||||
|
)
|
||||||
|
from codesnap.core.parser import FunctionInfo
|
||||||
|
|
||||||
|
|
||||||
|
class TestCalculateCyclomaticComplexity:
|
||||||
|
"""Tests for cyclomatic complexity calculation."""
|
||||||
|
|
||||||
|
def test_empty_content(self):
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity("")
|
||||||
|
assert complexity == 1
|
||||||
|
assert decisions == 0
|
||||||
|
|
||||||
|
def test_simple_function(self):
|
||||||
|
content = "def test():\n pass"
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity == 1
|
||||||
|
|
||||||
|
def test_if_statement(self):
|
||||||
|
content = "if x > 0:\n pass"
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 1
|
||||||
|
|
||||||
|
def test_multiple_if_statements(self):
|
||||||
|
content = """
|
||||||
|
if x > 0:
|
||||||
|
pass
|
||||||
|
elif x < 0:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 3
|
||||||
|
|
||||||
|
def test_for_loop(self):
|
||||||
|
content = "for i in range(10):\n pass"
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 1
|
||||||
|
|
||||||
|
def test_while_loop(self):
|
||||||
|
content = "while True:\n pass"
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 1
|
||||||
|
|
||||||
|
def test_try_except(self):
|
||||||
|
content = """
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 1
|
||||||
|
|
||||||
|
def test_and_or_operators(self):
|
||||||
|
content = "if x > 0 and y > 0:\n pass"
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 2
|
||||||
|
|
||||||
|
def test_ternary_operator(self):
|
||||||
|
content = "x = 1 if cond else 2"
|
||||||
|
complexity, decisions = calculate_cyclomatic_complexity(content)
|
||||||
|
assert complexity >= 1
|
||||||
|
|
||||||
|
|
||||||
|
class TestCalculateNestingDepth:
|
||||||
|
"""Tests for nesting depth calculation."""
|
||||||
|
|
||||||
|
def test_flat_code(self):
|
||||||
|
depth = calculate_nesting_depth("x = 1\ny = 2")
|
||||||
|
assert depth >= 0
|
||||||
|
|
||||||
|
def test_single_brace_level(self):
|
||||||
|
depth = calculate_nesting_depth("if x: { y = 1 }")
|
||||||
|
assert depth >= 0
|
||||||
|
|
||||||
|
def test_nested_braces(self):
|
||||||
|
content = """
|
||||||
|
if x:
|
||||||
|
if y:
|
||||||
|
if z:
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
depth = calculate_nesting_depth(content)
|
||||||
|
assert depth >= 0
|
||||||
|
|
||||||
|
def test_mixed_brackets(self):
|
||||||
|
content = """
|
||||||
|
def test():
|
||||||
|
data = [
|
||||||
|
[1, 2],
|
||||||
|
{a: b}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
depth = calculate_nesting_depth(content)
|
||||||
|
assert depth >= 1
|
||||||
|
|
||||||
|
def test_balanced_brackets(self):
|
||||||
|
content = "[](){}"
|
||||||
|
depth = calculate_nesting_depth(content)
|
||||||
|
assert depth >= 1
|
||||||
|
|
||||||
|
def test_unbalanced_close(self):
|
||||||
|
content = "x = 1]"
|
||||||
|
depth = calculate_nesting_depth(content)
|
||||||
|
assert depth >= 0
|
||||||
|
|
||||||
|
|
||||||
|
class TestCountLines:
|
||||||
|
"""Tests for line counting."""
|
||||||
|
|
||||||
|
def test_empty_content(self):
|
||||||
|
total, comments = count_lines("")
|
||||||
|
assert total >= 0
|
||||||
|
assert comments >= 0
|
||||||
|
|
||||||
|
def test_single_line(self):
|
||||||
|
total, comments = count_lines("x = 1")
|
||||||
|
assert total >= 1
|
||||||
|
assert comments >= 0
|
||||||
|
|
||||||
|
def test_python_comments(self):
|
||||||
|
content = "# This is a comment\nx = 1\n# Another comment"
|
||||||
|
total, comments = count_lines(content)
|
||||||
|
assert total >= 3
|
||||||
|
assert comments >= 2
|
||||||
|
|
||||||
|
def test_python_docstring(self):
|
||||||
|
content = '"""This is a docstring"""'
|
||||||
|
total, comments = count_lines(content)
|
||||||
|
assert total >= 1
|
||||||
|
|
||||||
|
def test_multiline_python_comment(self):
|
||||||
|
content = """
|
||||||
|
'''
|
||||||
|
Multiline
|
||||||
|
Comment
|
||||||
|
'''
|
||||||
|
x = 1
|
||||||
|
"""
|
||||||
|
total, comments = count_lines(content)
|
||||||
|
assert total >= 5
|
||||||
|
|
||||||
|
def test_cpp_comments(self):
|
||||||
|
content = "// Single line comment\nx = 1;"
|
||||||
|
total, comments = count_lines(content)
|
||||||
|
assert total >= 2
|
||||||
|
assert comments >= 1
|
||||||
|
|
||||||
|
def test_c_multiline_comment(self):
|
||||||
|
content = "/* Multi\n Line */\nx = 1;"
|
||||||
|
total, comments = count_lines(content)
|
||||||
|
assert total >= 3
|
||||||
|
assert comments >= 1
|
||||||
|
|
||||||
|
|
||||||
|
class TestRateComplexity:
|
||||||
|
"""Tests for complexity rating."""
|
||||||
|
|
||||||
|
def test_low_complexity(self):
|
||||||
|
assert rate_complexity(1, 1) == "low"
|
||||||
|
assert rate_complexity(5, 2) == "low"
|
||||||
|
assert rate_complexity(9, 3) == "low"
|
||||||
|
|
||||||
|
def test_medium_complexity(self):
|
||||||
|
assert rate_complexity(10, 3) == "medium"
|
||||||
|
assert rate_complexity(15, 4) == "medium"
|
||||||
|
assert rate_complexity(19, 5) == "medium"
|
||||||
|
|
||||||
|
def test_high_complexity(self):
|
||||||
|
assert rate_complexity(20, 3) == "high"
|
||||||
|
assert rate_complexity(25, 6) == "high"
|
||||||
|
assert rate_complexity(50, 2) == "high"
|
||||||
|
|
||||||
|
def test_high_nesting(self):
|
||||||
|
result = rate_complexity(5, 6)
|
||||||
|
assert result in ["low", "medium", "high"]
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnalyzeFileComplexity:
|
||||||
|
"""Tests for file complexity analysis."""
|
||||||
|
|
||||||
|
def test_empty_file(self):
|
||||||
|
metrics, func_complexities = analyze_file_complexity("", [], "python")
|
||||||
|
assert metrics.cyclomatic_complexity >= 1
|
||||||
|
assert len(func_complexities) == 0
|
||||||
|
|
||||||
|
def test_simple_file(self):
|
||||||
|
content = "x = 1\ny = 2"
|
||||||
|
metrics, func_complexities = analyze_file_complexity(content, [], "python")
|
||||||
|
assert metrics.complexity_rating in ["low", "medium", "high"]
|
||||||
|
|
||||||
|
def test_complex_file(self):
|
||||||
|
content = """
|
||||||
|
def test():
|
||||||
|
if x > 0:
|
||||||
|
if y > 0:
|
||||||
|
if z > 0:
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
func = FunctionInfo(
|
||||||
|
name="test",
|
||||||
|
node_type="function",
|
||||||
|
start_line=1,
|
||||||
|
end_line=6,
|
||||||
|
parameters=[],
|
||||||
|
)
|
||||||
|
metrics, func_complexities = analyze_file_complexity(content, [func], "python")
|
||||||
|
assert metrics.complexity_rating in ["low", "medium", "high"]
|
||||||
|
assert len(func_complexities) >= 0
|
||||||
|
|
||||||
|
def test_suggestions_generated(self):
|
||||||
|
content = """
|
||||||
|
def test():
|
||||||
|
pass
|
||||||
|
""" * 25
|
||||||
|
metrics, func_complexities = analyze_file_complexity(content, [], "python")
|
||||||
|
assert isinstance(metrics.suggestions, list)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetComplexitySummary:
|
||||||
|
"""Tests for complexity summary generation."""
|
||||||
|
|
||||||
|
def test_empty_list(self):
|
||||||
|
summary = get_complexity_summary([])
|
||||||
|
assert summary["total_files"] == 0
|
||||||
|
assert summary["avg_complexity"] == 0
|
||||||
|
|
||||||
|
def test_single_file(self):
|
||||||
|
metrics = ComplexityMetrics(
|
||||||
|
cyclomatic_complexity=10,
|
||||||
|
nesting_depth=2,
|
||||||
|
lines_of_code=50,
|
||||||
|
)
|
||||||
|
summary = get_complexity_summary([metrics])
|
||||||
|
assert summary["total_files"] == 1
|
||||||
|
assert summary["avg_complexity"] == 10
|
||||||
|
|
||||||
|
def test_multiple_files(self):
|
||||||
|
metrics_list = [
|
||||||
|
ComplexityMetrics(cyclomatic_complexity=5),
|
||||||
|
ComplexityMetrics(cyclomatic_complexity=15),
|
||||||
|
ComplexityMetrics(cyclomatic_complexity=10),
|
||||||
|
]
|
||||||
|
summary = get_complexity_summary(metrics_list)
|
||||||
|
assert summary["total_files"] == 3
|
||||||
|
assert summary["avg_complexity"] == 10
|
||||||
|
|
||||||
|
def test_rating_distribution(self):
|
||||||
|
metrics_list = [
|
||||||
|
ComplexityMetrics(cyclomatic_complexity=5),
|
||||||
|
ComplexityMetrics(cyclomatic_complexity=15),
|
||||||
|
ComplexityMetrics(cyclomatic_complexity=25),
|
||||||
|
]
|
||||||
|
summary = get_complexity_summary(metrics_list)
|
||||||
|
assert summary["rating_distribution"]["low"] >= 0
|
||||||
|
assert summary["rating_distribution"]["medium"] >= 0
|
||||||
|
assert summary["rating_distribution"]["high"] >= 0
|
||||||
|
assert summary["rating_distribution"]["low"] + summary["rating_distribution"]["medium"] + summary["rating_distribution"]["high"] == 3
|
||||||
Reference in New Issue
Block a user