tests: Port bash tests to perl

This integrates the tests written in bash into the existing perl infrastructure.
This commit is contained in:
Jakob Unterwurzacher 2014-10-19 21:05:51 +02:00
parent d9c7d52b89
commit 38970c75bd
6 changed files with 136 additions and 268 deletions

View File

@ -2,15 +2,15 @@
use Test::More qw( no_plan );
use File::Path;
use File::Copy;
use IO::Handle;
use Digest::MD5;
use Digest::MD5 qw(md5_hex);
my $tempDir = $ENV{'TMPDIR'} || "/tmp";
my $raw = "$tempDir/crypt-raw-$$";
my $crypt = "$tempDir/crypt-$$";
# test filesystem in standard config mode
&runTests('standard');
@ -18,7 +18,7 @@ my $crypt = "$tempDir/crypt-$$";
&runTests('paranoia');
# Wrapper function - runs all tests in the specified mode
sub runTests
{
my $mode = shift;
@ -31,6 +31,7 @@ sub runTests
{
&mount("--paranoia");
$hardlinks = 0; # no hardlinks in paranoia mode
&corruption;
} else
{
die "invalid test mode";
@ -41,10 +42,66 @@ sub runTests
&links($hardlinks);
&truncate;
&renames;
&internalModification;
&grow;
&cleanup;
}
# Test Corruption
# Modify the encrypted file and verify that the MAC check detects it
sub corruption
{
ok( open(OUT, "+> $crypt/corrupt") && print(OUT "12345678901234567890")
&& close(OUT), "create corruption-test file" );
$e = encName("corrupt");
ok( open(OUT, ">> $raw/$e") && print(OUT "garbage") && close(OUT),
"corrupting raw file");
ok( open(IN, "< $crypt/corrupt"), "open corrupted file");
my $content;
$result = read(IN, $content, 20);
ok(! defined $result, "corrupted file with MAC returns read error: $!");
}
# Test internal modification
# Create a file of fixed size and overwrite data at different offsets
# (like a database would do)
sub internalModification
{
$ofile = "$tempDir/crypt-internal-$$";
qx(dd if=/dev/urandom of=$ofile bs=2k count=2 2> /dev/null);
ok(copy($ofile, "$crypt/internal"), "copying crypt-internal file");
open(my $out1, "+<", "$crypt/internal");
open(my $out2, "+<", $ofile);
@fhs = ($out1, $out2);
$ori = md5fh($out1);
$b = md5fh($out2);
ok( $ori eq $b, "random file md5 matches");
my @offsets = (10, 30, 1020, 1200);
foreach my $o (@offsets)
{
foreach my $fh(@fhs) {
seek($fh, $o, 0);
print($fh "garbagegarbagegarbagegarbagegarbage");
}
$a=md5fh($out1);
$b=md5fh($out2);
ok( ($a eq $b) && ($a ne $ori), "internal modification at $o");
}
close($out1);
close($out2);
}
# Test renames
sub renames
{
ok( open(F, ">$crypt/orig-name") && close F, "create file for rename test");
@ -73,6 +130,7 @@ sub renames
is( (stat "$crypt/3rd-name")[9], $olderTime, "time unchanged by rename");
}
# Test truncate and grow
sub truncate
{
# write to file, then truncate it
@ -95,26 +153,25 @@ sub truncate
print OUT "12345";
is( -s "$crypt/trunc", 35, "truncated file size");
seek(OUT, 0, 0);
is( Digest::MD5->new->addfile(*OUT)->hexdigest,
"5f170cc34b1944d75d86cc01496292df", "content digest");
is( md5fh(*OUT), "5f170cc34b1944d75d86cc01496292df",
"content digest");
# try crossing block boundaries
seek(OUT, 10000,0);
print OUT "abcde";
seek(OUT, 0, 0);
is( Digest::MD5->new->addfile(*OUT)->hexdigest,
"117a51c980b64dcd21df097d02206f98", "content digest");
is( md5fh(*OUT), "117a51c980b64dcd21df097d02206f98",
"content digest");
# then truncate back to 35 chars
truncate(OUT, 35);
seek(OUT, 0, 0);
is( Digest::MD5->new->addfile(*OUT)->hexdigest,
"5f170cc34b1944d75d86cc01496292df", "content digest");
is( md5fh(*OUT), "5f170cc34b1944d75d86cc01496292df",
"content digest");
close OUT;
}
# Test file creation and removal
sub fileCreation
{
# create a file
@ -122,8 +179,7 @@ sub fileCreation
ok( -f "$crypt/df.txt", "file created" );
# ensure there is an encrypted version.
my $c = qx(./encfsctl encode --extpass="echo test" $raw df.txt);
chomp($c);
my $c = encName("df.txt");
cmp_ok( length($c), '>', 8, "encrypted name ok" );
ok( -f "$raw/$c", "encrypted file created" );
@ -136,6 +192,46 @@ sub fileCreation
ok( ! -f "$raw/$c", "file removal" );
}
# Test file growth
sub grow
{
open(my $fh_a, "+>$crypt/grow");
open(my $fh_b, "+>$tempDir/grow");
my $d = "1234567"; # Length 7 so we are not aligned to the block size
my $len = 7;
my $old = "";
my $errs = 0;
my $i;
for($i=1; $i<1000; $i++)
{
print($fh_a $d);
print($fh_b $d);
my $a = md5fh($fh_a);
my $b = md5fh($fh_b);
my $size = $len * $i;
# md5sums must be identical but must have changed
if($a ne $b || $a eq $old)
{
$errs++;
}
$old = $a;
}
ok($errs == 0, "grow file by $len bytes, $i times");
close($fh_a);
close($fh_b);
}
# Helper function
# Check a file's content
sub checkContents
{
my ($file, $expected, $testName) = @_;
@ -147,6 +243,28 @@ sub checkContents
close IN;
}
# Helper function
# Convert plain-text filename to encrypted filename
sub encName
{
my $plain = shift;
my $enc = qx(./encfsctl encode --extpass="echo test" $raw $plain);
chomp($enc);
return $enc;
}
# Helper function
# Get the MD5 sum of the file open at the filehandle
sub md5fh
{
my $fh_orig = shift;
open(my $fh, "<&", $fh_orig); # Duplicate the file handle so the seek
seek($fh, 0, 0); # does not affect the caller
return Digest::MD5->new->addfile($fh)->hexdigest;
close($fh);
}
# Test symlinks & hardlinks
sub links
{
my $hardlinkTests = shift;
@ -173,6 +291,8 @@ sub links
};
}
# Test mount
# Leaves the filesystem mounted - also used as a helper function
sub mount
{
my $args = shift;
@ -190,6 +310,8 @@ sub mount
ok( -f "$raw/.encfs6.xml", "created control file");
}
# Helper function
# Unmount and delete mountpoint
sub cleanup
{
my $fusermount = qx(which fusermount);

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="10">
<cfg class_id="0" tracking_level="0" version="20">
<version>20100713</version>
<creator>EncFS 1.7.5</creator>
<cipherAlg class_id="1" tracking_level="0" version="0">
<name>ssl/aes</name>
<major>3</major>
<minor>0</minor>
</cipherAlg>
<nameAlg>
<name>nameio/block</name>
<major>4</major>
<minor>0</minor>
</nameAlg>
<keySize>192</keySize>
<blockSize>1024</blockSize>
<uniqueIV>1</uniqueIV>
<chainedNameIV>1</chainedNameIV>
<externalIVChaining>0</externalIVChaining>
<blockMACBytes>0</blockMACBytes>
<blockMACRandBytes>0</blockMACRandBytes>
<allowHoles>1</allowHoles>
<encodedKeySize>44</encodedKeySize>
<encodedKeyData>
AL2iRMLYyuf7RXcFCK0CcegQ/+enbaaqTq1OY3WULMLpcz0yvo8K4KtVw7c=
</encodedKeyData>
<saltLen>20</saltLen>
<saltData>
RVCTqJyxxp7ZN0AsLJhgpvSuUB8=
</saltData>
<kdfIterations>170562</kdfIterations>
<desiredKDFDuration>500</desiredKDFDuration>
</cfg>
</boost_serialization>

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="10">
<cfg class_id="0" tracking_level="0" version="20">
<version>20100713</version>
<creator>EncFS 1.7.5</creator>
<cipherAlg class_id="1" tracking_level="0" version="0">
<name>ssl/aes</name>
<major>3</major>
<minor>0</minor>
</cipherAlg>
<nameAlg>
<name>nameio/block</name>
<major>4</major>
<minor>0</minor>
</nameAlg>
<keySize>192</keySize>
<blockSize>1024</blockSize>
<uniqueIV>1</uniqueIV>
<chainedNameIV>1</chainedNameIV>
<externalIVChaining>0</externalIVChaining>
<blockMACBytes>8</blockMACBytes>
<blockMACRandBytes>0</blockMACRandBytes>
<allowHoles>1</allowHoles>
<encodedKeySize>44</encodedKeySize>
<encodedKeyData>
AL2iRMLYyuf7RXcFCK0CcegQ/+enbaaqTq1OY3WULMLpcz0yvo8K4KtVw7c=
</encodedKeyData>
<saltLen>20</saltLen>
<saltData>
RVCTqJyxxp7ZN0AsLJhgpvSuUB8=
</saltData>
<kdfIterations>170562</kdfIterations>
<desiredKDFDuration>500</desiredKDFDuration>
</cfg>
</boost_serialization>

View File

@ -1,109 +0,0 @@
#!/bin/bash
# This is the wrapper script for all tests.
# It calls the unit tests and all integration tests.
#
# Functions that are useful in more than one integration test
# should be defined here.
set -eu
# Set up envrionment variables, cd to the correct directory
function init {
# Make sure we are in the "tests" directory
TESTDIR=$(dirname $(realpath "$0"))
cd $TESTDIR
# Full path to encfs binary
ENCFS=$(realpath ../encfs/encfs)
# Directory for temporary files (scratch)
SCRATCH=$(realpath scratch)
LOWER=$SCRATCH/lower
UPPER=$SCRATCH/upper
# Test number counter
TESTNO=1
trap 'echo -e "***\e[31m test(s) FAILED\e[0m"' ERR
}
# Run all test_*.sh files
function run_cli_tests {
cd $TESTDIR
for i in $(echo test_*.sh)
do
cleanup
mkdir $SCRATCH
echo "*** running $i"
source $i
done
}
# fresh_mount CONFIG
#
# Mount a fresh, empty encfs filesystem using the encfs config file
# CONFIG as .encfs6.xml. The password must be set to "a".
# The backing files will be at $LOWER, the mounted filesystem at $UPPER.
function fresh_mount {
cd $TESTDIR
fusermount -q -u $UPPER 2> /dev/null || true
wait
rm -Rf $LOWER $UPPER
mkdir -p $LOWER $UPPER
touch $UPPER/not_yet_mounted
cp $TESTDIR/$1 $SCRATCH/lower/.encfs6.xml
echo a | $ENCFS -f -S -o nonempty $LOWER $UPPER 2> /dev/null &
while [ -e $UPPER/not_yet_mounted ]
do
sleep 0.1s
done
}
# Clean up scratch directory
function cleanup {
test -d $SCRATCH || return 0
cd $SCRATCH/..
fusermount -q -u $UPPER 2> /dev/null || true
wait
rm -Rf $SCRATCH
}
# Get the plain MD5 sum of a file, without the filename that is output
# by md5sum
# md5sum foo: 5f47bbbd6db883f93f5d00fd05f149ff foo
# plain_md5 foo: 5f47bbbd6db883f93f5d00fd05f149ff
function md5 {
OUTPUT=$(md5sum "$1") # Capture md5sum output
ARRAY=($OUTPUT) # Split into array
echo ${ARRAY[0]} # Return first element
}
# Indicate beginning of a test
# Prints test number and title
function test_begin {
echo -n "$TESTNO $1: "
let TESTNO++
}
# Indicate successful completion of a test
function test_ok {
echo "OK"
}
init
test_begin "Running unit tests"
../encfs/test 2> /dev/null
test_ok
run_cli_tests
echo -e "***\e[32m All tests OK\e[0m"
cleanup

View File

@ -1,31 +0,0 @@
fresh_mount default.xml
cd $SCRATCH
dd if=/dev/urandom of=foo bs=1M count=1 2> /dev/null
A=$(md5 foo)
cp foo $UPPER
cd $UPPER
test_begin "Reading file"
B=$(md5 foo)
test $A = $B
test_ok
test_begin "Reading corrupted file"
echo DEADBEEF >> $LOWER/$(ls $LOWER)
B=$(md5 foo)
test $A != $B
test_ok
fresh_mount mac.xml
cd $SCRATCH
cp foo $UPPER
cd $UPPER
test_begin "Reading file with MAC"
B=$(md5 foo)
test $A = $B
test_ok
test_begin "Corruption with MAC returns IO error"
echo DEADBEEF >> $LOWER/$(ls $LOWER)
md5 foo 2>&1 | grep "Input/output error" > /dev/null
test_ok

View File

@ -1,40 +0,0 @@
fresh_mount default.xml
cd $UPPER
test_begin "Creating files of different sizes: "
for i in `seq 0 50` `seq 1000 1050`
do
OUTPUT=$(dd if=/dev/zero bs=$i count=1 2> /dev/null | tee $i | md5sum)
ARRAY=($OUTPUT)
A=${ARRAY[0]} # Remove filename
B=$(md5 $i)
test $A = $B
done
test_ok
test_begin "Growing file"
rm -f ../grow
for i in `seq 0 300`
do
echo -n "abcdefg" >> ../grow
echo -n "abcdefg" >> grow
A=$(md5 ../grow)
B=$(md5 grow)
test "$A" = "$B"
done
test_ok
test_begin "Internal modification"
dd if=/dev/urandom of=../internal bs=1M count=2 2> /dev/null
cp ../internal internal
for i in 0 30 1020 1200
do
dd if=/dev/zero of=../internal bs=1 count=1 skip=$i 2> /dev/null
dd if=/dev/zero of=internal bs=1 count=1 skip=$i 2> /dev/null
A=$(md5 ../internal)
B=$(md5 internal)
test "$A" = "$B"
done
test_ok