Skip to content

Commit

Permalink
Merge pull request #156 from jouvin/fileeditor_source
Browse files Browse the repository at this point in the history
FileEditor: add a 'source' option in constructor
  • Loading branch information
stdweird committed May 9, 2016
2 parents 78631f5 + 1f58e35 commit 008caf3
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 5 deletions.
66 changes: 62 additions & 4 deletions src/main/perl/FileEditor.pm
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,82 @@ the class constructor.
=item new
Returns a new object it accepts the same arguments as the constructor
for C<CAF::FileWriter>
for C<CAF::FileWriter> with one additional option:
=over
=item source
This option, when present, must be a file name whose contents will be used
as the initial contents for the edited file if the source modification time
is more recent than the edited file modification time. This allows to rebuild
the file contents based on a new version of the reference file.
The C<source> can be a pipe: in this case, it is always considered more recent
than the edited file.
=back
=cut

# FileEditor supports reading/editing a file
sub _is_valid_source
sub _is_valid_file
{
my ($self, $fn) = @_;
return -f $fn;
}

# This method is only intended to be used in the context of the constructor
# and entirely relies on the internal structure of the FileEditor object.
# 'filename` is the file name passed with instantiating the FileEditor and
# 'options/sources' the 'source' parameter value (called a reference file
# internally).
sub _is_reference_newer
{
my ($self) = @_;
my $is_newer = 0; # Assume false
if ( exists(*$self->{options}->{source}) ) {
# It is valid for the source value to be a pipe: in this case consider it
# as newer than an existing file.
if ( -p *$self->{options}->{source} ) {
$is_newer = 1
} elsif ( $self->_is_valid_file(*$self->{options}->{source}) ) {
# stat()[9] is modification time
if ( !$self->_is_valid_file(*$self->{filename}) ||
((stat(*$self->{options}->{source}))[9] > (stat(*$self->{filename}))[9]) ) {
$is_newer = 1
}
}
}
#FIXME: replace by $self->debug() after PR #154 has been merged...
if ( *$self->{LOG} ) {
if ( $is_newer ) {
*$self->{LOG}->debug(1, "File ", *$self->{filename}, " older than reference file (", *$self->{options}->{source}, ",");
} else {
*$self->{LOG}->debug(1, "Reference file (", *$self->{options}->{source}, ") older than ", *$self->{filename});
};
};
return $is_newer;
}

sub new
{
my $class = shift;
my $self = $class->SUPER::new (@_);
if ($self->_is_valid_source(*$self->{filename})) {
my $txt = LC::File::file_contents (*$self->{filename});
my $src_file;
my ($path, %opts) = @_;

*$self->{options}->{source} = $opts{source} if exists ($opts{source});
if ( $self->_is_reference_newer() ) {
# As this is a non reproducible event, be sure to log it when it happens
*$self->{LOG}->info("File ", *$self->{filename}, " contents reset to reference file (", *$self->{options}->{source}, ") contents.") if *$self->{LOG};
$src_file = *$self->{options}->{source};
} elsif ($self->_is_valid_file(*$self->{filename})) {
$src_file = *$self->{filename};
}
if ( $src_file ) {
*$self->{LOG}->debug(2, "Reading initial contents from $src_file") if *$self->{LOG};
my $txt = LC::File::file_contents ($src_file);
$self->IO::String::open ($txt);
$self->seek(IO_SEEK_END);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/perl/FileReader.pm
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ seek to the beginning and C<cancel> any (future) changes.
=cut

# FileReader supports reading a file or pipe
sub _is_valid_source
sub _is_valid_file
{
my ($self, $fn) = @_;
return -f $fn || -p $fn;
Expand Down
79 changes: 79 additions & 0 deletions src/test/perl/test-caffileeditor_source.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib "$Bin/modules";
use CAF::FileEditor;
use Test::More tests => 7;
use Test::MockModule;
use Test::Quattor::Object;
use Carp qw(confess);
use File::Path;
use File::Temp qw(tempfile);
use Readonly;

$SIG{__DIE__} = \&confess;

# FIXME:
# LC::File::file_contents (used by CAF::FileEditor constructor) doesn't work
# in the unit test context. Mock it until we find a better solution...
our $lcfile = Test::MockModule->new("LC::File");

sub read_file_contents {
my $fname = shift;
my $fh;
open($fh, "<", $fname) || die ("failed to open $fname");
my $contents = join('', <$fh>);
return $contents;
}
$lcfile->mock("file_contents", \&read_file_contents);


my $testdir = 'target/test/test_caffileeditor_source';
mkpath($testdir);
(undef, my $filename) = tempfile(DIR => $testdir);

Readonly my $TEXT => <<EOF;
En un lugar de La Mancha, de cuyo nombre no quiero acordarme
no ha tiempo que vivía un hidalgo de los de lanza en astillero...
EOF
Readonly my $ANOTHER_TEXT => "adarga antigua, rocín flaco y galgo corredor.";

my $fh;
my $obj = Test::Quattor::Object->new();


# Create a file and check that it is empty
($fh, $filename) = tempfile(DIR => $testdir);
$fh->close();
$fh = CAF::FileEditor->new($filename, log => $obj);
is("$fh","","Existing file ($filename) empty");
$fh->close();

# Check that reference file contents is used as the initial contents when it
# is newer than the file edited.
my $time=time();
utime(undef, $time - 10, $filename); # make filename old enough
my ($ref_fh, $ref_filename) = tempfile(DIR => $testdir);
print $ref_fh $TEXT;
$ref_fh->close();
$fh = CAF::FileEditor->new($filename, log => $obj, source => $ref_filename);
is(*$fh->{options}->{source},$ref_filename,"File source is correctly defined");
ok($fh->_is_reference_newer(),"Source file ($ref_filename) is newer than actual file ($filename)");
is("$fh",$TEXT,"Reference file ($filename) contents used");
$fh->close();

# Check that reference file contents is not used as the initial contents when it
# is older than the file edited.
$time=time();
utime(undef, $time - 10, $ref_filename); # make ref_filename old enough
my ($new_fh, $new_filename) = tempfile(DIR => $testdir);
print $new_fh $ANOTHER_TEXT;
$new_fh->close();
$new_fh = CAF::FileEditor->new($new_filename, log => $obj, source => $ref_filename);
is(*$new_fh->{options}->{source},$ref_filename,"File source is correctly defined");
ok(!$new_fh->_is_reference_newer(),"Source file ($ref_filename) is older than actual file ($filename)");
is("$new_fh",$ANOTHER_TEXT,"Existing file ($new_filename) contents used");
$fh->close();

done_testing();

0 comments on commit 008caf3

Please sign in to comment.