Jonathan
Jonathan

Reputation: 43

Perl automated file testing

So I'm taking a Perl programming class and our teacher gave us our first assignment with very little talk about how to actually program perl. Here's exactly what our teacher assigned:

"You should write a script (you may name you script whatever you deem appropriate) that accepts 3 filenames as arguments. The first filename corresponds to the program source code written in C++. The second filename corresponds to an input file that is to be used by the C++ program listed as the first filename. The third filename corresponds to a text file that contains the expected, correct output for the program in question. A directory path may be provided with any of the filenames.

If the program doesn’t require an input file, the second parameter on the command line should be the filename "/dev/null".

All of the files - the program source, the input file, and expected output file - should be copied to a scratch test directory before any of them are used by the script so that there is little chance that the originals will be modified by the test procedure. If the scratch directory doesn’t exist, your script should create one as a subdirectory of the current working directory.

Your script should then compile and link the scratch copy of the source using the GNU g++ compiler. The script should then run the program – saving the output to a temporary file stored in the scratch test directory.

After running the program, your script should then use the UNIX command diff to compare the actual output generated in the previous step with the expected output file and report either that the output conforms to specifications or report any differences as reported by the diff program.

After completion, your script should remove all of the temporary copies and scratch files. Do not remove the original program, the original input file, the original expected output file, or the scratch directory itself."

I have this so far:

#!/usr/bin/perl -w

use strict;

my ($line, $program, $input, $output);

print "Give the program, input, and standart output for testing. ";

$line = <>;
chomp $line;

($program, $input, $output) = split/\s+/, $line; # split/\s+/ is to separate spaces from the input

my($o_test) = $output + "_test";

print "$program ";
print "$input ";
print "$output ";

system("mkdir test_scratch") == 0
or die "failed to create test_scratch. exiting...."

system("cp $program, /test_scratch/"); # error
system("cp $input, /test_scratch/");
system("cp $output, /test_scratch/");

system("cd test_scratch");

system("g++ $program");
system("chmod +x a.out");

system("./a.out < $input > $o_test");

my($DIFF) = system("diff $output $o_test") # error

if[ $DIFF != ""]
    print ("Output conforms to specifications."); # error
then
    print ("$DIFF");
system("cd ..");

I'm getting errors at the # in code. I don't even know how to do the "/dev/null". I've spent a lot of time looking things up online and searching stackoverflow, but I just don't know what else to do. I realize this is an extremely long question but I don't know what else to do. Thank you for ANY help you can give me.

Upvotes: 1

Views: 599

Answers (2)

Joel Berger
Joel Berger

Reputation: 20280

There are several modules which can help you here. The first one I would recommend is ExtUtils::CBuilder which can manage a build process for you. Then you might also use File::Copy for moving things intot the temporary folder, and even File::chdir for managing the working directory. Since the prof specifies that you should use diff perhaps you should, but there are modules which do that task, or you could use Test::More to check that the output is what is expected.


Just for future reference, this is how I would accomplish a similar task (don't keep temp dir, don't need diff):

#!/usr/bin/env perl

use strict;
use warnings;

use File::chdir;
use File::Temp;
use File::Copy;
use File::Basename;
use File::Slurp;

use ExtUtils::CBuilder;
use Test::More tests => 1;

die "Not enough inputs\n" unless @ARGV >= 3;

# create a temporary directory
my $temp = File::Temp->newdir;

# copy all arguments to that temporary directory
copy $_, $temp for @ARGV;

# store only the filename (not path) of each argument
my ($cpp_file, $in_file, $expected_file) = map { scalar basename $_ } @ARGV;

# change working directory to temporary one (via File::chdir)
local $CWD = $temp;

# build the executable
my $builder = ExtUtils::CBuilder->new(config => {cc => 'g++'});

my $obj = $builder->compile( source => $cpp_file, 'C++' => 1 );
my $exe = $builder->link_executable( objects => 'hello.o' );

# run the executable
my $output = `./$exe $in_file`;

# read in the expected file
my $expected = read_file $expected_file; 

# test the resulting output
is $output, $expected, 'Got expected output from executable';

Note that you may need to be careful with newlines on the output and expected files.

Upvotes: 1

Adam Mihalcin
Adam Mihalcin

Reputation: 14468

I'm getting errors at the # in code.

The easy fix for this error is to remove the comma. The standard Linux cp command doesn't take a comma between the source and destination. You might also consider File::Copy to copy files in Perl.

I don't even know how to do the "/dev/null".

/dev/null is a standard Linux file(*), which points to emptiness. You can write bytes to this file, and they disappear. You can try to read bytes from this file, and nothing's ever there.

This leaves you with two options:

  1. Just use /dev/null directly, and the program will be unable to read anything from an empty byte stream.
  2. Check if the input file is equal to /dev/null, and don't give the C++ program an input value if the input is /dev/null.

Either method would work. If you copy /dev/null to a directory, you get a new zero-length file called 'null', which is a perfectly valid empty input file for the C++ program.

(*) No, /dev/null is not a file in the sense you're used to if you come from the Windows world. It doesn't exist on disk - never has and never will. But the classic Unix philosophy is to make every data source a file, even if that file doesn't exist on disk. As an example, see the Linux /proc filesystem, which allows you to see information about CPUs, processes, etc. in a directory tree structure. That philosophy held up somewhat until the sockets API took a completely different route. If you're looking for an operating system that does make basically everything into a file, including network connections and the screen, look up Plan 9. I would not recommend doing your homework assignments on Plan 9 and expecting them to work on Linux, though.


N.B. Don't forget to check return codes from system, since if g++ fails to compile the C++ program there will be no way to run a compiled program that doesn't exist.

Upvotes: 0

Related Questions