Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions lib/Test/MockFile.pm
Original file line number Diff line number Diff line change
Expand Up @@ -2517,11 +2517,16 @@ Calculates the block count of the file based on its size.
sub blocks {
my ($self) = @_;

my $size = $self->size;
my $size = $self->size;
return 0 unless $size;

my $blksize = abs( $self->{'blksize'} );
return int( ( $size + $blksize - 1 ) / $blksize );

# Number of filesystem blocks needed (ceiling division)
my $fs_blocks = int( ( $size + $blksize - 1 ) / $blksize );

# stat(2) st_blocks is always in 512-byte units, regardless of st_blksize
return $fs_blocks * int( $blksize / 512 );
}

=head2 chmod
Expand Down
4 changes: 2 additions & 2 deletions t/Test-MockFile_file.t
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ subtest 'size() and blocks()' => sub {

is( $mock->size(), 5000, 'size() matches content length' );

# blocks() = ceil(size / blksize) — no 512-byte conversion
my $expected_blocks = int( ( 5000 + 4096 - 1 ) / 4096 );
# blocks() = ceil(size / blksize) * (blksize / 512) — stat(2) uses 512-byte units
my $expected_blocks = int( ( 5000 + 4096 - 1 ) / 4096 ) * int( 4096 / 512 );
is( $mock->blocks(), $expected_blocks, 'blocks() computes correctly from size and blksize' );
};

Expand Down
22 changes: 14 additions & 8 deletions t/blocks.t
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use Test::MockFile qw< nostrict >;
# Assures testers don't mess up with our hard coded perms expectations.
umask 022;

note "blocks() calculation";
note "blocks() calculation — stat(2) st_blocks is always in 512-byte units";

{
note "empty file has 0 blocks";
Expand All @@ -24,34 +24,36 @@ note "blocks() calculation";
}

{
note "small file has 1 block";
note "small file with default 4096 blksize";
# 1 filesystem block = 4096 bytes = 8 * 512-byte units
my $mock = Test::MockFile->file( '/tmp/small', 'x' );
is $mock->blocks, 1, "1-byte file: 1 block";
is $mock->blocks, 8, "1-byte file: 8 blocks (1 fs block * 4096/512)";

my $mock2 = Test::MockFile->file( '/tmp/small2', 'x' x 100 );
is $mock2->blocks, 1, "100-byte file: 1 block";
is $mock2->blocks, 8, "100-byte file: 8 blocks (1 fs block * 4096/512)";
}

{
note "file exactly at blksize boundary";
my $mock = Test::MockFile->file( '/tmp/exact', 'x' x 4096 );
is $mock->blocks, 1, "4096-byte file with 4096 blksize: exactly 1 block";
is $mock->blocks, 8, "4096-byte file with 4096 blksize: exactly 8 blocks";
}

{
note "file one byte over blksize boundary";
my $mock = Test::MockFile->file( '/tmp/over', 'x' x 4097 );
is $mock->blocks, 2, "4097-byte file: 2 blocks";
is $mock->blocks, 16, "4097-byte file: 16 blocks (2 fs blocks * 4096/512)";
}

{
note "file at exactly 2x blksize";
my $mock = Test::MockFile->file( '/tmp/double', 'x' x 8192 );
is $mock->blocks, 2, "8192-byte file with 4096 blksize: exactly 2 blocks";
is $mock->blocks, 16, "8192-byte file with 4096 blksize: exactly 16 blocks";
}

{
note "custom blksize";
# blksize=512: 1 fs block = 1 * 512-byte unit
my $mock = Test::MockFile->file( '/tmp/custom_blk', 'x' x 1024, { blksize => 512 } );
is $mock->blocks, 2, "1024-byte file with 512 blksize: 2 blocks";

Expand All @@ -60,13 +62,17 @@ note "blocks() calculation";

my $mock3 = Test::MockFile->file( '/tmp/custom_blk3', 'x' x 512, { blksize => 512 } );
is $mock3->blocks, 1, "512-byte file with 512 blksize: exactly 1 block";

# blksize=1024: 1 fs block = 2 * 512-byte units
my $mock4 = Test::MockFile->file( '/tmp/custom_blk4', 'x' x 100, { blksize => 1024 } );
is $mock4->blocks, 2, "100-byte file with 1024 blksize: 2 blocks (1 fs block * 1024/512)";
}

{
note "blocks from stat()";
my $mock = Test::MockFile->file( '/tmp/stat_blocks', 'hello' );
my @stat = stat('/tmp/stat_blocks');
is $stat[12], 1, "stat[12] (blocks) is 1 for a 5-byte file";
is $stat[12], 8, "stat[12] (blocks) is 8 for a 5-byte file (1 fs block in 512-byte units)";

my $mock_empty = Test::MockFile->file( '/tmp/stat_empty', '' );
my @stat_e = stat('/tmp/stat_empty');
Expand Down
2 changes: 1 addition & 1 deletion t/mock_stat.t
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ my $symlink_lstat_return = array {
item match qr/^[0-9]{3,}$/;
item match qr/^[0-9]{3,}$/;
item 4096;
item 1;
item 8; # blocks: 1 fs block in 512-byte units (4096/512 = 8)
};

is( Test::MockFile::_mock_stat( 'lstat', '/broken_link' ), $symlink_lstat_return, "lstat on /broken_link returns the stat on the symlink itself." );
Expand Down
Loading