Coverage for tests/test_mi/test_python.py: 100%

1"""Tests for Maintainability Index (Python)."""

2

3 from analyzers.python import analyze_source

4

5

6 def _mi(code: str) -> float:

7 """Return MI of the first function in code."""

8 result = analyze_source(code)

9 assert result.functions, f"No functions found in:\n{code}"

10 return result.functions[0].mi

11

12

13# --- Trivial function (high MI) ---

14

15 def test_trivial_function():

16 code = """

17 def f():

18 return 1

19"""

20 mi = _mi(code)

21 assert mi > 50, f"Trivial function should have high MI, got {mi}"

22

23

24 def test_simple_add():

25 code = """

26 def add(a, b):

27 return a + b

28"""

29 mi = _mi(code)

30 assert mi > 50, f"Simple function should have high MI, got {mi}"

31

32

33# --- Complex function (low MI) ---

34

35 def test_complex_function():

36 code = """

37 def process(data, flag, mode, opt, extra):

38 result = []

39 total = 0

40 count = 0

41 for item in data:

42 if flag and mode:

43 if item > 0:

44 for sub in item:

45 if sub < 100:

46 if opt or extra:

47 result.append(sub * 2 + total)

48 count += 1

49 else:

50 result.append(sub - 1)

51 elif sub > 200:

52 total += sub

53 count -= 1

54 elif item < 0:

55 try:

56 val = int(item)

57 if val > 0 and val < 50:

58 result.append(val)

59 elif val >= 50 or val == 0:

60 result.append(-val)

61 except Exception:

62 result.append(0)

63 elif mode or opt:

64 for sub in data:

65 if sub != item:

66 result.append(sub + item)

67 total += sub

68 if count > 0:

69 return result[:count]

70 return result

71"""

72 mi = _mi(code)

73 assert mi < 50, f"Complex function should have low MI, got {mi}"

74

75

76# --- Edge case: empty body ---

77

78 def test_empty_body():

79 code = """

80 def noop():

81 pass

82"""

83 mi = _mi(code)

84 assert mi > 50, f"Empty body function should have high MI, got {mi}"

85

86

87# --- Edge case: no operators ---

88

89 def test_no_operators():

90 code = """

91 def f():

92 x = 1

93"""

94 mi = _mi(code)

95 # With minimal halstead volume, MI should be high

96 assert mi > 50, f"No-operator function should have high MI, got {mi}"

97

98

99# --- MI is in valid range ---

100

101 def test_mi_range():

102 code = """

103 def f(x):

104 if x:

105 return 1

106 return 0

107"""

108 mi = _mi(code)

109 assert 0 <= mi <= 100, f"MI should be in [0, 100], got {mi}"

110

111

112# --- Multiple functions ---

113

114 def test_multiple_functions():

115 code = """

116 def simple():

117 return 42

118

119 def moderate(x, y):

120 if x > y:

121 return x

122 elif x < y:

123 return y

124 else:

125 return x + y

126"""

127 result = analyze_source(code)

128 assert len(result.functions) == 2

129 mi_simple = result.functions[0].mi

130 mi_moderate = result.functions[1].mi

131 assert mi_simple > mi_moderate, f"Simple function should have higher MI: {mi_simple} vs {mi_moderate}"