App-Test-Generator

 view release on metacpan or  search on metacpan

t/Analyzer-Complexity.t  view on Meta::CPAN

	done_testing();
};

# ==================================================================
# analyze -- early_returns detection
# --------------------------------------------------
# The first return does not count as early.
# Each additional return beyond the first increments
# early_returns and cyclomatic_score.
# ==================================================================
subtest 'analyze: early_returns detection' => sub {
	# Zero returns -- no early returns
	my $report = _analyze_body('sub foo { my $x = 1; }');
	is($report->{early_returns}, 0, 'no return: zero early returns');

	# Exactly one return -- not early
	$report = _analyze_body('sub foo { return 1; }');
	is($report->{early_returns}, 0, 'single return: zero early returns');

	# Two returns -- one early
	$report = _analyze_body('sub foo { return undef unless $x; return 1; }');
	is($report->{early_returns}, 1, 'two returns: one early return');
	is($report->{cyclomatic_score},
		$CYCLOMATIC_BASE + 1 + 1,	# unless + one early return
		'two returns: score incremented for early return');

	# Three returns -- two early
	$report = _analyze_body(
		'sub foo { return 0 if $a; return undef if $b; return 1; }'
	);
	is($report->{early_returns}, 2, 'three returns: two early returns');

	done_testing();
};

# ==================================================================
# analyze -- exception_paths detection
# --------------------------------------------------
# die, croak, confess, try, catch, eval each increment
# exception_paths and cyclomatic_score
# ==================================================================
subtest 'analyze: exception_paths detection' => sub {
	# die
	my $report = _analyze_body('die "error" unless $x;');
	is($report->{exception_paths}, 1, 'die: one exception path');
	is($report->{cyclomatic_score}, $CYCLOMATIC_BASE + 1 + 1,
		'die + unless: correct score');

	# croak
	$report = _analyze_body('croak "bad input" unless defined $x;');
	is($report->{exception_paths}, 1, 'croak: one exception path');

	# confess
	$report = _analyze_body('confess "deep error";');
	is($report->{exception_paths}, 1, 'confess: one exception path');

	# eval block
	$report = _analyze_body('eval { do_something(); };');
	is($report->{exception_paths}, 1, 'eval: one exception path');

	# try/catch (e.g. Try::Tiny style)
	$report = _analyze_body('try { do_something(); } catch { handle($_); };');
	is($report->{exception_paths}, 2, 'try + catch: two exception paths');

	# Multiple exception keywords are additive
	$report = _analyze_body('eval { die "err"; }');
	is($report->{exception_paths}, 2, 'eval + die: two exception paths');

	done_testing();
};

# ==================================================================
# analyze -- nesting_depth detection
# --------------------------------------------------
# Maximum brace depth is tracked by naive counting.
# Note: braces in strings/regexes are also counted
# (documented limitation).
# ==================================================================
subtest 'analyze: nesting_depth detection' => sub {
	# No braces -- depth is 0
	my $report = _analyze_body('return 1;');
	is($report->{nesting_depth}, 0, 'no braces: depth 0');

	# Single level -- depth is 1
	$report = _analyze_body('{ my $x = 1; }');
	is($report->{nesting_depth}, 1, 'single brace pair: depth 1');

	# Two levels -- depth is 2
	$report = _analyze_body('{ if($x) { return 1; } }');
	is($report->{nesting_depth}, 2, 'nested pair: depth 2');

	# Three levels -- depth is 3
	$report = _analyze_body('{ for(@a) { if($b) { do_thing(); } } }');
	is($report->{nesting_depth}, 3, 'three-level nesting: depth 3');

	# Sequential (not nested) pairs -- depth is still 1
	$report = _analyze_body('{ my $x = 1; } { my $y = 2; }');
	is($report->{nesting_depth}, 1,
		'sequential pairs not nested: depth 1');

	done_testing();
};

# ==================================================================
# analyze -- complexity_level classification
# --------------------------------------------------
# low: score <= LOW_THRESHOLD (3)
# moderate: score <= HIGH_THRESHOLD (7)
# high: score > HIGH_THRESHOLD (7)
# ==================================================================
subtest 'analyze: complexity_level classification' => sub {
	# Score of 1 (base) -- low
	my $report = _analyze_body('return 1;');
	is($report->{complexity_level}, $LEVEL_LOW,
		'score 1: low complexity');

	# Score of 3 (base + 2 branches) -- still low (boundary)
	$report = _analyze_body('if($a) { 1; } if($b) { 2; }');
	is($report->{cyclomatic_score}, $CYCLOMATIC_BASE + 2,
		'two ifs give score 3');
	is($report->{complexity_level}, $LEVEL_LOW,



( run in 0.826 second using v1.01-cache-2.11-cpan-5fbc6bb55f2 )