I was writing some unit tests and decided to give Devel::Cover
a try. I'm seeing something that I can't explain when looking at the conditional coverage report, involving use of the Or (||
) and Defined-Or (//
) operators.
This is the base code that's working as expected:
use strict;
use warnings;
use 5.026;
my($DEF_MIN) = 1;
our($DEF_MAX) = 10;
sub test
{
my($hashref) = @_;
$hashref ||= {};
my($min) = $hashref->{'min'} || 1;
my($max) = $hashref->{'max'} // 10;
say qq(MIN: $min / MAX: $max);
return;
}
test();
test({});
test({ min => 14 });
test({ max => 14 });
test({ min => 10, max => 14 });
When I run that I get the following Condition report:
Pretty much what I expected.
If I make these change:
my($min) = $hashref->{'min'} || $DEF_MIN;
my($max) = $hashref->{'max'} // $DEF_MAX;
then I get this report, instead:
Why the change from just column A
to columns A
and B
? How can I complete the coverage, or mark it as uncoverable?
As can be seen, it doesn't matter whether I use ||
or //
, and it doesn't matter whether the right hand side is a lexical or a package variable.
The following defines the operation of ||
:
A | B | Result |
---|---|---|
A is false | Any value | B |
A is true | Not evaluated | A |
Your test covered both of these paths. Under this model, you'd have full coverage. However, Devel::Cover uses a different model.
A | B | Result |
---|---|---|
A is false | B is false | B |
A is false | B is true | B |
A is true | Not evaluated | A |
It only considers the expression "fully covered" if the all three of these paths were visited during the test. This makes sense in a boolean context, but the ||
is used outside of boolean context here.
Since you didn't run your code with a false value for $DEF_MIN
, the first path wasn't visited, and it deems the coverage incomplete.
When the operand is a literal, it rules out the path where it would expect a true literal to be false as impossible to cover, and removes it. (Same for when it expects a false literal to be true.) That's why there's one less column when using a literal.