App-Test-Generator

 view release on metacpan or  search on metacpan

t/function.t  view on Meta::CPAN


	# DB::DB() resolves $file through abs_path() before normalising, so
	# the cache/comparison key here must be built the same way -- using
	# _normalize(__FILE__) directly (skipping abs_path) would compare a
	# relative path against DB::DB()'s absolute one and never match
	my $this_file = Devel::App::Test::Generator::LCSAJ::Runtime::_normalize(Cwd::abs_path(__FILE__));
	ok(
		!exists $Devel::App::Test::Generator::LCSAJ::Runtime::HITS{$this_file},
		'a populated %TARGET that excludes this file suppresses recording for it'
	);

	%Devel::App::Test::Generator::LCSAJ::Runtime::TARGET = ($this_file => 1);
	my $hit_line = __LINE__ + 1;
	DB::DB();
	is(
		$Devel::App::Test::Generator::LCSAJ::Runtime::HITS{$this_file}{$hit_line},
		1,
		'adding this file to %TARGET allows the next call to record a hit for it'
	);
};

subtest 'Devel::App::Test::Generator::LCSAJ::Runtime::_write_results - skips IO when %HITS is empty' => sub {
	local %Devel::App::Test::Generator::LCSAJ::Runtime::HITS = ();

	my $make_path_calls = 0;
	Test::Mockingbird::mock(
		'Devel::App::Test::Generator::LCSAJ::Runtime',
		'make_path',
		sub { $make_path_calls++; return 1 },
	);
	Devel::App::Test::Generator::LCSAJ::Runtime::_write_results();
	is($make_path_calls, 0, '_write_results() returns immediately, before touching the filesystem, when %HITS is empty');
	Test::Mockingbird::unmock('Devel::App::Test::Generator::LCSAJ::Runtime', 'make_path');
};

subtest 'Devel::App::Test::Generator::LCSAJ::Runtime::_write_results - writes a well-formed per-PID JSON file' => sub {
	my $orig_cwd = getcwd();
	my $tmp = File::Temp::tempdir(CLEANUP => 1);

	local %Devel::App::Test::Generator::LCSAJ::Runtime::HITS = (
		'lib/Foo/Bar.pm' => { 10 => 3, 12 => 1 },
	);

	eval {
		chdir $tmp or die "cannot chdir to $tmp: $!";
		Devel::App::Test::Generator::LCSAJ::Runtime::_write_results();
	};
	my $err = $@;
	chdir $orig_cwd or croak "cannot chdir back to $orig_cwd: $!";
	is($err, '', '_write_results() does not die for a normal, writable target') or diag($err);

	my $out_file = "$tmp/cover_html/lcsaj_hits/hits_$$.json";
	ok(-e $out_file, 'a per-PID JSON file is written under cover_html/lcsaj_hits, named with the current PID');

	no autodie qw(open);
	open my $fh, '<', $out_file or croak "cannot read back $out_file: $!";
	local $/;
	my $content = <$fh>;
	close $fh;

	my $decoded = decode_json($content);
	is_deeply(
		$decoded,
		{ 'lib/Foo/Bar.pm' => { 10 => 3, 12 => 1 } },
		'the written JSON round-trips back to the exact %HITS structure that was serialised'
	);

	memory_cycle_ok(\%Devel::App::Test::Generator::LCSAJ::Runtime::HITS, '%HITS holds no circular references');
};

subtest 'Devel::App::Test::Generator::LCSAJ::Runtime::_write_results - croaks with the documented message when open() fails' => sub {
	my $orig_cwd = getcwd();
	my $tmp = File::Temp::tempdir(CLEANUP => 1);

	local %Devel::App::Test::Generator::LCSAJ::Runtime::HITS = (
		'lib/Foo/Bar.pm' => { 10 => 1 },
	);

	eval {
		chdir $tmp or die "cannot chdir to $tmp: $!";

		# Pre-create the exact target path as a directory, not a file --
		# open() for write against a directory portably fails on every
		# platform this module supports, without relying on chmod-based
		# permission tricks that behave differently when run as root
		my $out_file = "cover_html/lcsaj_hits/hits_$$.json";
		make_path($out_file);

		throws_ok { Devel::App::Test::Generator::LCSAJ::Runtime::_write_results() }
			qr/^Cannot write \Q$out_file\E: /,
			'_write_results() croaks with the exact documented message when open() fails';
	};
	my $err = $@;
	chdir $orig_cwd or croak "cannot chdir back to $orig_cwd: $!";
	is($err, '', 'no unexpected exception escaped the eval wrapper') or diag($err);
};

subtest 'Devel::App::Test::Generator::LCSAJ::Runtime - BEGIN-time LCSAJ_TARGETS parsing' => sub {
	# %TARGET is populated once, at compile time, from $ENV{LCSAJ_TARGETS}.
	# That BEGIN block cannot be re-run against this already-loaded module,
	# so it is exercised by loading a fresh copy of the module in a child
	# process with the env var pre-set, then reading back %TARGET -- a
	# single bounded perl -e invocation, not a recursive test/prove run
	my $targets = '/build/blib/lib/Foo.pm:/home/user/proj/lib/Bar.pm:' . "\n";
	local $ENV{LCSAJ_TARGETS} = $targets;

	# List-form system() under capture_merged(), not qx{}/backticks --
	# qx{} always goes through a shell, and a shell-quoted '...' -e
	# argument that works under sh/bash is invalid syntax for
	# cmd.exe on Windows (single quotes are not its quoting
	# character), which previously made the child perl process fail
	# to parse its own -e script.
	my @inc_args = map { ('-I', $_) } @INC;
	my $code = 'print join(q{,}, sort keys %Devel::App::Test::Generator::LCSAJ::Runtime::TARGET)';
	my ($output, $exit) = capture_merged {
		system($^X, @inc_args, '-MDevel::App::Test::Generator::LCSAJ::Runtime', '-e', $code);
	};
	is($exit, 0, 'the child process exits cleanly') or diag($output);
	is(
		$output,
		'lib/Bar.pm,lib/Foo.pm',
		'LCSAJ_TARGETS entries are normalised (blib/lib and lib stripped) and stray trailing newlines are removed'
	);



( run in 1.056 second using v1.01-cache-2.11-cpan-7fcb06a456a )