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

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

2

3 from analyzers.go import analyze_source

4

5

6 def _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

13 def _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

22 def test_empty_function():

23 code = """

24 package main

25

26 func f() {}

27"""

28 assert _score(code) == 0

29

30

31 def test_single_if():

32 code = """

33 package main

34

35 func f(x int) {

36 if x > 0 { // +1

37 }

38}

39"""

40 assert _score(code) == 1

41

42

43 def test_if_else():

44 code = """

45 package main

46

47 func f(x int) {

48 if x > 0 { // +1

49 } else { // +1

50 }

51}

52"""

53 assert _score(code) == 2

54

55

56 def test_if_else_if_else():

57 code = """

58 package main

59

60 func 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

73 def test_for_c_style():

74 code = """

75 package main

76

77 func f(n int) {

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

79 }

80}

81"""

82 assert _score(code) == 1

83

84

85 def test_for_range():

86 code = """

87 package main

88

89 func f(items []int) {

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

91 }

92}

93"""

94 assert _score(code) == 1

95

96

97 def test_for_as_while():

98 code = """

99 package main

100

101 func f(x int) {

102 for x > 0 { // +1

103 x--

104 }

105}

106"""

107 assert _score(code) == 1

108

109

110 def test_infinite_for():

111 code = """

112 package main

113

114 func f() {

115 for { // +1

116 break

117 }

118}

119"""

120 assert _score(code) == 1

121

122

123# --- Nesting ---

124

125

126 def test_nested_if():

127 code = """

128 package main

129

130 func 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

140 def test_deeply_nested():

141 code = """

142 package main

143

144 func 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

157 def test_if_else_nesting():

158 code = """

159 package main

160

161 func 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

175 def test_expression_switch():

176 code = """

177 package main

178

179 func 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

190 def test_type_switch():

191 code = """

192 package main

193

194 func 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

204 def test_select_statement():

205 code = """

206 package main

207

208 func 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

220 def test_switch_nested_in_if():

221 code = """

222 package main

223

224 func 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

239 def test_single_and():

240 code = """

241 package main

242

243 func f(a, b bool) {

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

245 }

246}

247"""

248 assert _score(code) == 2

249

250

251 def test_chained_same_op():

252 code = """

253 package main

254

255 func 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

263 def test_mixed_boolean():

264 code = """

265 package main

266

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

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

269 }

270}

271"""

272 assert _score(code) == 3

273

274

275 def test_complex_boolean():

276 code = """

277 package main

278

279 func 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

290 def test_recursion():

291 code = """

292 package main

293

294 func 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

304 def test_no_false_recursion():

305 code = """

306 package main

307

308 func 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

318 def test_nested_function_separate_scoring():

319 code = """

320 package main

321

322 func 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

338 def test_inline_func_literal_increases_nesting():

339 code = """

340 package main

341

342 func 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

354 def test_named_func_literal_scored_separately():

355 code = """

356 package main

357

358 func 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

376 def test_method_declaration():

377 code = """

378 package main

379

380 type Foo struct{}

381

382 func (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

393 def test_go_statement_no_increment():

394 code = """

395 package main

396

397 func 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

406 def test_defer_no_increment():

407 code = """

408 package main

409

410 func 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

419 def test_error_handling_pattern():

420 code = """

421 package main

422

423 import "errors"

424

425 func 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

436 def test_if_with_initializer():

437 code = """

438 package main

439

440 func f() {

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

442 }

443}

444"""

445 assert _score(code) == 1

446

447

448# --- Comprehensive example ---

449

450

451 def test_comprehensive():

452 code = """

453 package main

454

455 func 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

476 def test_not_operator_no_increment():

477 code = """

478 package main

479

480 func f(x bool) {

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

482 }

483}

484"""

485 assert _score(code) == 1

486

487

488 def test_multiple_return_values():

489 code = """

490 package main

491

492 func f() (int, error) {

493 if true { // +1

494 return 0, nil

495 }

496 return 1, nil

497}

498"""

499 assert _score(code) == 1