processor/cognitive_nesting_test.go GO 195 lines View on github.com → Search inside
1// SPDX-License-Identifier: MIT23package processor45import "testing"67// These tests pin the core promise of cognitive complexity: the SAME number of8// branch points (identical cyclomatic Complexity) must score HIGHER the more9// deeply they are nested. Cyclomatic counts branches flatly; cognitive weights10// each branch by 1+nesting, so shape — not just count — drives the number.11//12// All fixtures below contain exactly four `if`s (Complexity == 4) arranged from13// maximally nested to maximally flat, and assert both the exact cognitive values14// (derived from the 1+nesting rule) and the strict ordering between them.1516// deepChain: four ifs each nested one level deeper than the last.17//18//	if          nesting 1 -> +219//	  if        nesting 2 -> +320//	    if      nesting 3 -> +421//	      if    nesting 4 -> +5      total 1422const deepChain = "func main() {\n" +23	"    if a {\n" +24	"        if b {\n" +25	"            if c {\n" +26	"                if d {\n" +27	"                }\n" +28	"            }\n" +29	"        }\n" +30	"    }\n" +31	"}\n"3233// deNested: two independent 2-deep pairs (the user's "de-nested" example).34//35//	if          nesting 1 -> +236//	  if        nesting 2 -> +337//	if          nesting 1 -> +238//	  if        nesting 2 -> +3      total 1039const deNested = "func main() {\n" +40	"    if a {\n" +41	"        if b {\n" +42	"        }\n" +43	"    }\n" +44	"    if c {\n" +45	"        if d {\n" +46	"        }\n" +47	"    }\n" +48	"}\n"4950// flat: all four ifs as siblings at the same level.51//52//	if  if  if  if   each nesting 1 -> +2 each   total 853const flat = "func main() {\n" +54	"    if a {\n    }\n" +55	"    if b {\n    }\n" +56	"    if c {\n    }\n" +57	"    if d {\n    }\n" +58	"}\n"5960// TestCognitiveDeepVsDeNestedVsFlat is the headline test: same four branches,61// three shapes, strictly decreasing cognitive as they flatten — while the flat62// cyclomatic Complexity stays 4 throughout.63func TestCognitiveDeepVsDeNestedVsFlat(t *testing.T) {64	Cognitive = true65	defer func() { Cognitive = false }()6667	deep := countCognitive(t, "Go", deepChain)68	mid := countCognitive(t, "Go", deNested)69	shallow := countCognitive(t, "Go", flat)7071	// Same number of branch points: cyclomatic cannot tell these apart.72	for name, job := range map[string]FileJob{"deep": deep, "deNested": mid, "flat": shallow} {73		if job.Complexity != 4 {74			t.Errorf("%s: expected Complexity 4 (four ifs), got %d", name, job.Complexity)75		}76	}7778	// Exact cognitive values from the 1+nesting rule.79	if deep.Cognitive != 14 {80		t.Errorf("deepChain Cognitive: expected 14, got %d", deep.Cognitive)81	}82	if mid.Cognitive != 10 {83		t.Errorf("deNested Cognitive: expected 10, got %d", mid.Cognitive)84	}85	if shallow.Cognitive != 8 {86		t.Errorf("flat Cognitive: expected 8, got %d", shallow.Cognitive)87	}8889	// The relationship the metric exists to express: deeper nesting scores90	// strictly higher, even at identical cyclomatic complexity.91	if !(deep.Cognitive > mid.Cognitive && mid.Cognitive > shallow.Cognitive) {92		t.Errorf("expected deep > deNested > flat, got %d, %d, %d",93			deep.Cognitive, mid.Cognitive, shallow.Cognitive)94	}95}9697// TestCognitiveNestedHigherThanDeNested is the user's literal example, written98// with top-level (column 0) ifs and 2-space indentation to show the ranking is99// independent of the wrapping function and the indent unit.100//101//	nested:            de-nested:102//	if                 if103//	  if                 if104//	    if             if105//	      if             if106func TestCognitiveNestedHigherThanDeNested(t *testing.T) {107	Cognitive = true108	defer func() { Cognitive = false }()109110	nested := "if a {\n" +111		"  if b {\n" +112		"    if c {\n" +113		"      if d {\n" +114		"      }\n" +115		"    }\n" +116		"  }\n" +117		"}\n"118119	splitNested := "if a {\n" +120		"  if b {\n" +121		"  }\n" +122		"}\n" +123		"if c {\n" +124		"  if d {\n" +125		"  }\n" +126		"}\n"127128	nestedJob := countCognitive(t, "Go", nested)129	splitJob := countCognitive(t, "Go", splitNested)130131	if nestedJob.Complexity != splitJob.Complexity {132		t.Fatalf("fixtures must have equal cyclomatic Complexity, got nested=%d split=%d",133			nestedJob.Complexity, splitJob.Complexity)134	}135	// nested: 1+2+3+4 = 10 ; de-nested: 1+2+1+2 = 6136	if nestedJob.Cognitive != 10 {137		t.Errorf("nested Cognitive: expected 10, got %d", nestedJob.Cognitive)138	}139	if splitJob.Cognitive != 6 {140		t.Errorf("de-nested Cognitive: expected 6, got %d", splitJob.Cognitive)141	}142	if nestedJob.Cognitive <= splitJob.Cognitive {143		t.Errorf("nested (%d) should score strictly higher than de-nested (%d)",144			nestedJob.Cognitive, splitJob.Cognitive)145	}146}147148// TestCognitiveMonotonicWithDepth: a single chain of N nested ifs. Each added149// level of depth adds strictly more cognitive weight than the previous level did150// (the increments grow 2,3,4,... as nesting deepens), unlike cyclomatic which151// would add a flat 1 per branch.152func TestCognitiveMonotonicWithDepth(t *testing.T) {153	Cognitive = true154	defer func() { Cognitive = false }()155156	// Build chains of depth 1..5 and record cognitive at each depth.157	chain := func(depth int) string {158		var b string159		b = "func main() {\n"160		indent := "    "161		pad := ""162		for i := 0; i < depth; i++ {163			pad += indent164			b += pad + "if x {\n"165		}166		for i := 0; i < depth; i++ {167			b += pad + "}\n"168			pad = pad[:len(pad)-len(indent)]169		}170		b += "}\n"171		return b172	}173174	prev := int64(-1)175	prevDelta := int64(-1)176	for depth := 1; depth <= 5; depth++ {177		job := countCognitive(t, "Go", chain(depth))178		if int64(job.Complexity) != int64(depth) {179			t.Errorf("depth %d: expected Complexity %d, got %d", depth, depth, job.Complexity)180		}181		if prev >= 0 {182			delta := job.Cognitive - prev183			// Each deeper level contributes more than the one before it.184			if delta <= prevDelta {185				t.Errorf("depth %d: cognitive delta %d should exceed previous delta %d (values grow super-linearly with depth)", depth, delta, prevDelta)186			}187			prevDelta = delta188		}189		if job.Cognitive <= prev {190			t.Errorf("depth %d: cognitive %d should exceed shallower depth's %d", depth, job.Cognitive, prev)191		}192		prev = job.Cognitive193	}194}

Code quality findings 16

Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer func() { Cognitive = false }()
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer func() { Cognitive = false }()
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// if nesting 1 -> +2
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// if nesting 2 -> +3
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// if nesting 3 -> +4
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// if nesting 4 -> +5 total 14
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if a {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if b {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if c {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if d {\n" +
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for name, job := range map[string]FileJob{"deep": deep, "deNested": mid, "flat": shallow} {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
nested := "if a {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if b {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if c {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
" if d {\n" +
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
for i := 0; i < depth; i++ {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.