Coverage for tests / test_cognitive / test_go.py: 100%

109 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-08 15:04 -0800

1"""Tests for Go cognitive complexity.""" 

2 

3from analyzers.go import analyze_source 

4 

5 

6def _score(code: str) -> int: 

7 """Helper: return total complexity 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].complexity 

11 

12 

13def _scores(code: str) -> dict[str, int]: 

14 """Helper: return {name: complexity} for all functions in code.""" 

15 result = analyze_source(code) 

16 return {f.name: f.complexity for f in result.functions} 

17 

18 

19# --- Basic control flow --- 

20 

21 

22def test_empty_function(): 

23 code = """ 

24package main 

25 

26func f() {} 

27""" 

28 assert _score(code) == 0 

29 

30 

31def test_single_if(): 

32 code = """ 

33package main 

34 

35func f(x int) { 

36 if x > 0 { // +1 

37 } 

38} 

39""" 

40 assert _score(code) == 1 

41 

42 

43def test_if_else(): 

44 code = """ 

45package main 

46 

47func f(x int) { 

48 if x > 0 { // +1 

49 } else { // +1 

50 } 

51} 

52""" 

53 assert _score(code) == 2 

54 

55 

56def test_if_else_if_else(): 

57 code = """ 

58package main 

59 

60func f(x int) { 

61 if x > 0 { // +1 

62 } else if x < 0 { // +1 

63 } else { // +1 

64 } 

65} 

66""" 

67 assert _score(code) == 3 

68 

69 

70# --- Loops --- 

71 

72 

73def test_for_c_style(): 

74 code = """ 

75package main 

76 

77func f(n int) { 

78 for i := 0; i < n; i++ { // +1 

79 } 

80} 

81""" 

82 assert _score(code) == 1 

83 

84 

85def test_for_range(): 

86 code = """ 

87package main 

88 

89func f(items []int) { 

90 for _, v := range items { // +1 

91 } 

92} 

93""" 

94 assert _score(code) == 1 

95 

96 

97def test_for_as_while(): 

98 code = """ 

99package main 

100 

101func f(x int) { 

102 for x > 0 { // +1 

103 x-- 

104 } 

105} 

106""" 

107 assert _score(code) == 1 

108 

109 

110def test_infinite_for(): 

111 code = """ 

112package main 

113 

114func f() { 

115 for { // +1 

116 break 

117 } 

118} 

119""" 

120 assert _score(code) == 1 

121 

122 

123# --- Nesting --- 

124 

125 

126def test_nested_if(): 

127 code = """ 

128package main 

129 

130func f(a, b bool) { 

131 if a { // +1 (nesting 0) 

132 if b { // +1 + 1 (nesting 1) 

133 } 

134 } 

135} 

136""" 

137 assert _score(code) == 3 

138 

139 

140def test_deeply_nested(): 

141 code = """ 

142package main 

143 

144func f(a bool, items []int, c bool) { 

145 if a { // +1 (nesting 0) 

146 for _, v := range items { // +1 + 1 (nesting 1) 

147 if c { // +1 + 2 (nesting 2) 

148 _ = v 

149 } 

150 } 

151 } 

152} 

153""" 

154 assert _score(code) == 6 

155 

156 

157def test_if_else_nesting(): 

158 code = """ 

159package main 

160 

161func f(a, b bool) { 

162 if a { // +1 (nesting 0) 

163 } else { // +1 (no nesting bonus) 

164 if b { // +1 + 1 (nesting 1, inside else body) 

165 } 

166 } 

167} 

168""" 

169 assert _score(code) == 4 

170 

171 

172# --- Switch/Select --- 

173 

174 

175def test_expression_switch(): 

176 code = """ 

177package main 

178 

179func f(x int) { 

180 switch x { // +1 (nesting 0) 

181 case 1: 

182 case 2: 

183 default: 

184 } 

185} 

186""" 

187 assert _score(code) == 1 

188 

189 

190def test_type_switch(): 

191 code = """ 

192package main 

193 

194func f(x interface{}) { 

195 switch x.(type) { // +1 (nesting 0) 

196 case int: 

197 case string: 

198 } 

199} 

200""" 

201 assert _score(code) == 1 

202 

203 

204def test_select_statement(): 

205 code = """ 

206package main 

207 

208func f(ch1, ch2 chan int) { 

209 select { // +1 (nesting 0) 

210 case v := <-ch1: 

211 _ = v 

212 case v := <-ch2: 

213 _ = v 

214 } 

215} 

216""" 

217 assert _score(code) == 1 

218 

219 

220def test_switch_nested_in_if(): 

221 code = """ 

222package main 

223 

224func f(x int, flag bool) { 

225 if flag { // +1 (nesting 0) 

226 switch x { // +1 + 1 (nesting 1) 

227 case 1: 

228 case 2: 

229 } 

230 } 

231} 

232""" 

233 assert _score(code) == 3 

234 

235 

236# --- Boolean operators --- 

237 

238 

239def test_single_and(): 

240 code = """ 

241package main 

242 

243func f(a, b bool) { 

244 if a && b { // +1 (if) + 1 (boolean) 

245 } 

246} 

247""" 

248 assert _score(code) == 2 

249 

250 

251def test_chained_same_op(): 

252 code = """ 

253package main 

254 

255func f(a, b, c bool) { 

256 if a && b && c { // +1 (if) + 1 (boolean chain, same op) 

257 } 

258} 

259""" 

260 assert _score(code) == 2 

261 

262 

263def test_mixed_boolean(): 

264 code = """ 

265package main 

266 

267func f(a, b, c bool) { 

268 if a && b || c { // +1 (if) + 2 (&&->|| switch) 

269 } 

270} 

271""" 

272 assert _score(code) == 3 

273 

274 

275def test_complex_boolean(): 

276 code = """ 

277package main 

278 

279func f(a, b, c, d bool) { 

280 if a || b || (c && d) { // +1 (if) + 2 (|| sequence, then && subtree) 

281 } 

282} 

283""" 

284 assert _score(code) == 3 

285 

286 

287# --- Recursion --- 

288 

289 

290def test_recursion(): 

291 code = """ 

292package main 

293 

294func factorial(n int) int { 

295 if n <= 1 { // +1 

296 return 1 

297 } 

298 return n * factorial(n - 1) // +1 (recursion) 

299} 

300""" 

301 assert _score(code) == 2 

302 

303 

304def test_no_false_recursion(): 

305 code = """ 

306package main 

307 

308func f(x int) int { 

309 return g(x) 

310} 

311""" 

312 assert _score(code) == 0 

313 

314 

315# --- Nested named func_literal (separate scoring) --- 

316 

317 

318def test_nested_function_separate_scoring(): 

319 code = """ 

320package main 

321 

322func outer(x bool) { 

323 if x {} // +1 for outer 

324 inner := func(y bool) { 

325 if y {} // +1 for inner 

326 } 

327 _ = inner 

328} 

329""" 

330 scores = _scores(code) 

331 assert scores["outer"] == 1 

332 assert scores["inner"] == 1 

333 

334 

335# --- Closures --- 

336 

337 

338def test_inline_func_literal_increases_nesting(): 

339 code = """ 

340package main 

341 

342func f(x int) { 

343 apply(func(v int) int { 

344 if v > 0 { // +1 + 1 (nesting 1, inside inline closure) 

345 return v 

346 } 

347 return 0 

348 }) 

349} 

350""" 

351 assert _score(code) == 2 

352 

353 

354def test_named_func_literal_scored_separately(): 

355 code = """ 

356package main 

357 

358func outer() { 

359 if true {} // +1 for outer 

360 handler := func(x bool) { 

361 if x { // +1 for handler 

362 if true {} // +1 + 1 for handler 

363 } 

364 } 

365 _ = handler 

366} 

367""" 

368 scores = _scores(code) 

369 assert scores["outer"] == 1 

370 assert scores["handler"] == 3 

371 

372 

373# --- Method --- 

374 

375 

376def test_method_declaration(): 

377 code = """ 

378package main 

379 

380type Foo struct{} 

381 

382func (f Foo) Bar(x bool) { 

383 if x {} // +1 

384} 

385""" 

386 scores = _scores(code) 

387 assert scores["Bar"] == 1 

388 

389 

390# --- Go-specific --- 

391 

392 

393def test_go_statement_no_increment(): 

394 code = """ 

395package main 

396 

397func f(x bool) { 

398 go func() { 

399 if x {} // +1 + 1 (nesting 1 from inline closure) 

400 }() 

401} 

402""" 

403 assert _score(code) == 2 

404 

405 

406def test_defer_no_increment(): 

407 code = """ 

408package main 

409 

410func f(x bool) { 

411 defer func() { 

412 if x {} // +1 + 1 (nesting 1 from inline closure) 

413 }() 

414} 

415""" 

416 assert _score(code) == 2 

417 

418 

419def test_error_handling_pattern(): 

420 code = """ 

421package main 

422 

423import "errors" 

424 

425func f() error { 

426 err := doSomething() 

427 if err != nil { // +1 

428 return err 

429 } 

430 return nil 

431} 

432""" 

433 assert _score(code) == 1 

434 

435 

436def test_if_with_initializer(): 

437 code = """ 

438package main 

439 

440func f() { 

441 if err := doSomething(); err != nil { // +1 

442 } 

443} 

444""" 

445 assert _score(code) == 1 

446 

447 

448# --- Comprehensive example --- 

449 

450 

451def test_comprehensive(): 

452 code = """ 

453package main 

454 

455func process(items []int, flag bool) { 

456 if flag { // +1 (nesting 0) 

457 for i := 0; i < len(items); i++ { // +1 +1 (nesting 1) 

458 if items[i] > 0 { // +1 +2 (nesting 2) 

459 } else if items[i] < 0 { // +1 (no nesting bonus) 

460 } else { // +1 (no nesting bonus) 

461 switch items[i] { // +1 +3 (nesting 3) 

462 case 0: 

463 } 

464 } 

465 } 

466 } else { // +1 (no nesting bonus) 

467 } 

468} 

469""" 

470 assert _score(code) == 13 

471 

472 

473# --- Edge cases --- 

474 

475 

476def test_not_operator_no_increment(): 

477 code = """ 

478package main 

479 

480func f(x bool) { 

481 if !x { // +1 (if only) 

482 } 

483} 

484""" 

485 assert _score(code) == 1 

486 

487 

488def test_multiple_return_values(): 

489 code = """ 

490package main 

491 

492func f() (int, error) { 

493 if true { // +1 

494 return 0, nil 

495 } 

496 return 1, nil 

497} 

498""" 

499 assert _score(code) == 1