// distribution boxbackup-0.10 (svn version: 494)
//
// Copyright (c) 2003 - 2006
// Ben Summers and contributors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. All use of this software and associated advertising materials must
// display the following acknowledgment:
// This product includes software developed by Ben Summers.
// 4. The names of the Authors may not be used to endorse or promote
// products derived from this software without specific prior written
// permission.
//
// [Where legally impermissible the Authors do not disclaim liability for
// direct physical injury or death caused solely by defects in the software
// unless it is modified by a third party.]
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//
//
// --------------------------------------------------------------------------
//
// File
// Name: testcommon.cpp
// Purpose: Tests for the code in lib/common
// Created: 2003/07/23
//
// --------------------------------------------------------------------------
#include "Box.h"
#include <stdio.h>
#include "Test.h"
#include "Configuration.h"
#include "FdGetLine.h"
#include "Guards.h"
#include "FileStream.h"
#include "IOStreamGetLine.h"
#include "NamedLock.h"
#include "ReadGatherStream.h"
#include "MemBlockStream.h"
#include "ExcludeList.h"
#include "CommonException.h"
#include "Conversion.h"
#include "autogen_ConversionException.h"
#include "MemLeakFindOn.h"
using namespace BoxConvert;
void test_conversions()
{
TEST_THAT((Convert<int32_t, const std::string &>(std::string("32"))) == 32);
TEST_THAT((Convert<int32_t, const char *>("42")) == 42);
TEST_THAT((Convert<int32_t, const char *>("-42")) == -42);
TEST_CHECK_THROWS((Convert<int8_t, const char *>("500")), ConversionException, IntOverflowInConvertFromString);
TEST_CHECK_THROWS((Convert<int8_t, const char *>("pants")), ConversionException, BadStringRepresentationOfInt);
TEST_CHECK_THROWS((Convert<int8_t, const char *>("")), ConversionException, CannotConvertEmptyStringToInt);
std::string a(Convert<std::string, int32_t>(63));
TEST_THAT(a == "63");
std::string b(Convert<std::string, int32_t>(-3473463));
TEST_THAT(b == "-3473463");
std::string c(Convert<std::string, int16_t>(344));
TEST_THAT(c == "344");
}
ConfigurationVerifyKey verifykeys1_1_1[] =
{
{"bing", 0, ConfigTest_Exists, 0},
{"carrots", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"terrible", 0, ConfigTest_Exists | ConfigTest_LastEntry, 0}
};
ConfigurationVerifyKey verifykeys1_1_2[] =
{
{"fish", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"string", 0, ConfigTest_Exists | ConfigTest_LastEntry, 0}
};
ConfigurationVerify verifysub1_1[] =
{
{
"*",
0,
verifykeys1_1_1,
ConfigTest_Exists,
0
},
{
"otherthing",
0,
verifykeys1_1_2,
ConfigTest_Exists | ConfigTest_LastEntry,
0
}
};
ConfigurationVerifyKey verifykeys1_1[] =
{
{"value", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"string1", 0, ConfigTest_Exists, 0},
{"string2", 0, ConfigTest_Exists | ConfigTest_LastEntry, 0}
};
ConfigurationVerifyKey verifykeys1_2[] =
{
{"carrots", 0, ConfigTest_Exists | ConfigTest_IsInt, 0},
{"string", 0, ConfigTest_Exists | ConfigTest_LastEntry, 0}
};
ConfigurationVerify verifysub1[] =
{
{
"test1",
verifysub1_1,
verifykeys1_1,
ConfigTest_Exists,
0
},
{
"ping",
0,
verifykeys1_2,
ConfigTest_Exists | ConfigTest_LastEntry,
0
}
};
ConfigurationVerifyKey verifykeys1[] =
{
{"notExpected", 0, 0, 0},
{"HasDefaultValue", "Lovely default value", 0, 0},
{"MultiValue", 0, ConfigTest_MultiValueAllowed, 0},
{"BoolTrue1", 0, ConfigTest_IsBool, 0},
{"BoolTrue2", 0, ConfigTest_IsBool, 0},
{"BoolFalse1", 0, ConfigTest_IsBool, 0},
{"BoolFalse2", 0, ConfigTest_IsBool, 0},
{"TOPlevel", 0, ConfigTest_LastEntry | ConfigTest_Exists, 0}
};
ConfigurationVerify verify =
{
"root",
verifysub1,
verifykeys1,
ConfigTest_Exists | ConfigTest_LastEntry,
0
};
int test(int argc, const char *argv[])
{
// Test memory leak detection
#ifdef BOX_MEMORY_LEAK_TESTING
{
TEST_THAT(memleakfinder_numleaks() == 0);
void *block = ::malloc(12);
TEST_THAT(memleakfinder_numleaks() == 1);
void *b2 = ::realloc(block, 128*1024);
TEST_THAT(memleakfinder_numleaks() == 1);
::free(b2);
TEST_THAT(memleakfinder_numleaks() == 0);
char *test = new char[1024];
TEST_THAT(memleakfinder_numleaks() == 1);
MemBlockStream *s = new MemBlockStream(test,12);
TEST_THAT(memleakfinder_numleaks() == 2);
delete s;
TEST_THAT(memleakfinder_numleaks() == 1);
delete [] test;
TEST_THAT(memleakfinder_numleaks() == 0);
}
#endif // BOX_MEMORY_LEAK_TESTING
static char *testfilelines[] =
{
"First line",
"Second line",
"Third",
"",
"",
"",
"sdf hjjk",
"",
"test",
"test#not comment",
"test#not comment",
"",
"nice line",
"fish",
"",
"ping",
"",
"",
"Nothing",
"Nothing",
0
};
// First, test the FdGetLine class -- rather important this works!
{
FileHandleGuard<O_RDONLY> file("testfiles"
DIRECTORY_SEPARATOR "fdgetlinetest.txt");
FdGetLine getline(file);
int l = 0;
while(testfilelines[l] != 0)
{
TEST_THAT(!getline.IsEOF());
std::string line = getline.GetLine(true);
//printf("expected |%s| got |%s|\n", lines[l], line.c_str());
TEST_THAT(strcmp(testfilelines[l], line.c_str()) == 0);
l++;
}
TEST_THAT(getline.IsEOF());
TEST_CHECK_THROWS(getline.GetLine(true), CommonException, GetLineEOF);
}
// and again without pre-processing
{
FileHandleGuard<O_RDONLY> file("testfiles"
DIRECTORY_SEPARATOR "fdgetlinetest.txt");
FILE *file2 = fopen("testfiles" DIRECTORY_SEPARATOR
"fdgetlinetest.txt", "r");
TEST_THAT_ABORTONFAIL(file2 != 0);
FdGetLine getline(file);
char ll[512];
while(!feof(file2))
{
fgets(ll, sizeof(ll), file2);
int e = strlen(ll);
while(e > 0 && (ll[e-1] == '\n' || ll[e-1] == '\r'))
{
e--;
}
ll[e] = '\0';
TEST_THAT(!getline.IsEOF());
std::string line = getline.GetLine(false);
//printf("expected |%s| got |%s|\n", ll, line.c_str());
TEST_THAT(strcmp(ll, line.c_str()) == 0);
}
TEST_THAT(getline.IsEOF());
TEST_CHECK_THROWS(getline.GetLine(true), CommonException, GetLineEOF);
fclose(file2);
}
// Then the IOStream version of get line, seeing as we're here...
{
FileStream file("testfiles" DIRECTORY_SEPARATOR
"fdgetlinetest.txt", O_RDONLY);
IOStreamGetLine getline(file);
int l = 0;
while(testfilelines[l] != 0)
{
TEST_THAT(!getline.IsEOF());
std::string line;
while(!getline.GetLine(line, true))
;
//printf("expected |%s| got |%s|\n", lines[l], line.c_str());
TEST_THAT(strcmp(testfilelines[l], line.c_str()) == 0);
l++;
}
TEST_THAT(getline.IsEOF());
std::string dummy;
TEST_CHECK_THROWS(getline.GetLine(dummy, true), CommonException, GetLineEOF);
}
// and again without pre-processing
{
FileStream file("testfiles" DIRECTORY_SEPARATOR
"fdgetlinetest.txt", O_RDONLY);
IOStreamGetLine getline(file);
FILE *file2 = fopen("testfiles" DIRECTORY_SEPARATOR
"fdgetlinetest.txt", "r");
TEST_THAT_ABORTONFAIL(file2 != 0);
char ll[512];
while(!feof(file2))
{
fgets(ll, sizeof(ll), file2);
int e = strlen(ll);
while(e > 0 && (ll[e-1] == '\n' || ll[e-1] == '\r'))
{
e--;
}
ll[e] = '\0';
TEST_THAT(!getline.IsEOF());
std::string line;
while(!getline.GetLine(line, false))
;
//printf("expected |%s| got |%s|\n", ll, line.c_str());
TEST_THAT(strcmp(ll, line.c_str()) == 0);
}
TEST_THAT(getline.IsEOF());
std::string dummy;
TEST_CHECK_THROWS(getline.GetLine(dummy, true), CommonException, GetLineEOF);
fclose(file2);
}
// Doesn't exist
{
std::string errMsg;
TEST_CHECK_THROWS(std::auto_ptr<Configuration> pconfig(
Configuration::LoadAndVerify(
"testfiles" DIRECTORY_SEPARATOR "DOESNTEXIST",
&verify, errMsg)),
CommonException, OSFileOpenError);
}
// Basic configuration test
{
std::string errMsg;
std::auto_ptr<Configuration> pconfig(
Configuration::LoadAndVerify(
"testfiles" DIRECTORY_SEPARATOR "config1.txt",
&verify, errMsg));
if(!errMsg.empty())
{
printf("UNEXPECTED error msg is:\n------\n%s------\n", errMsg.c_str());
}
TEST_THAT_ABORTONFAIL(pconfig.get() != 0);
TEST_THAT(errMsg.empty());
TEST_THAT(pconfig->KeyExists("TOPlevel"));
TEST_THAT(pconfig->GetKeyValue("TOPlevel") == "value");
TEST_THAT(pconfig->KeyExists("MultiValue"));
TEST_THAT(pconfig->GetKeyValue("MultiValue") == "single");
TEST_THAT(!pconfig->KeyExists("not exist"));
TEST_THAT(pconfig->KeyExists("HasDefaultValue"));
TEST_THAT(pconfig->GetKeyValue("HasDefaultValue") == "Lovely default value");
TEST_CHECK_THROWS(pconfig->GetKeyValue("not exist"), CommonException, ConfigNoKey);
// list of keys
std::vector<std::string> keylist(pconfig->GetKeyNames());
TEST_THAT(keylist.size() == 3);
// will be sorted alphanumerically
TEST_THAT(keylist[2] == "TOPlevel" && keylist[1] == "MultiValue" && keylist[0] == "HasDefaultValue");
// list of sub configurations
std::vector<std::string> sublist(pconfig->GetSubConfigurationNames());
TEST_THAT(sublist.size() == 2);
TEST_THAT(sublist[0] == "test1");
TEST_THAT(sublist[1] == "ping");
TEST_THAT(pconfig->SubConfigurationExists("test1"));
TEST_THAT(pconfig->SubConfigurationExists("ping"));
TEST_CHECK_THROWS(pconfig->GetSubConfiguration("nosubconfig"), CommonException, ConfigNoSubConfig);
// Get a sub configuration
const Configuration &sub1 = pconfig->GetSubConfiguration("test1");
TEST_THAT(sub1.GetKeyValueInt("value") == 12);
std::vector<std::string> sublist2(sub1.GetSubConfigurationNames());
TEST_THAT(sublist2.size() == 4);
// And the sub-sub configs
const Configuration &sub1_1 = sub1.GetSubConfiguration("subconfig");
TEST_THAT(sub1_1.GetKeyValueInt("carrots") == 0x2356);
const Configuration &sub1_2 = sub1.GetSubConfiguration("subconfig2");
TEST_THAT(sub1_2.GetKeyValueInt("carrots") == -243895);
const Configuration &sub1_3 = sub1.GetSubConfiguration("subconfig3");
TEST_THAT(sub1_3.GetKeyValueInt("carrots") == 050);
TEST_THAT(sub1_3.GetKeyValue("terrible") == "absolutely");
}
static const char *file[] =
{
"testfiles" DIRECTORY_SEPARATOR "config2.txt",
// Value missing from root
"testfiles" DIRECTORY_SEPARATOR "config3.txt",
// Unexpected {
"testfiles" DIRECTORY_SEPARATOR "config4.txt",
// Missing }
"testfiles" DIRECTORY_SEPARATOR "config5.txt",
// { expected, but wasn't there
"testfiles" DIRECTORY_SEPARATOR "config6.txt",
// Duplicate key
"testfiles" DIRECTORY_SEPARATOR "config7.txt",
// Invalid key (no name)
"testfiles" DIRECTORY_SEPARATOR "config8.txt",
// Not all sub blocks terminated
"testfiles" DIRECTORY_SEPARATOR "config9.txt",
// Not valid integer
"testfiles" DIRECTORY_SEPARATOR "config9b.txt",
// Not valid integer
"testfiles" DIRECTORY_SEPARATOR "config9c.txt",
// Not valid integer
"testfiles" DIRECTORY_SEPARATOR "config9d.txt",
// Not valid integer
"testfiles" DIRECTORY_SEPARATOR "config10.txt",
// Missing key (in subblock)
"testfiles" DIRECTORY_SEPARATOR "config11.txt",
// Unknown key
"testfiles" DIRECTORY_SEPARATOR "config12.txt",
// Missing block
"testfiles" DIRECTORY_SEPARATOR "config13.txt",
// Subconfig (wildcarded) should exist, but missing (ie nothing present)
"testfiles" DIRECTORY_SEPARATOR "config16.txt",
// bad boolean value
0
};
for(int l = 0; file[l] != 0; ++l)
{
std::string errMsg;
std::auto_ptr<Configuration> pconfig(Configuration::LoadAndVerify(file[l], &verify, errMsg));
TEST_THAT(pconfig.get() == 0);
TEST_THAT(!errMsg.empty());
printf("(%s) Error msg is:\n------\n%s------\n", file[l], errMsg.c_str());
}
// Check that multivalues happen as expected
// (single value in a multivalue already checked)
{
std::string errMsg;
std::auto_ptr<Configuration> pconfig(
Configuration::LoadAndVerify(
"testfiles" DIRECTORY_SEPARATOR "config14.txt",
&verify, errMsg));
TEST_THAT(pconfig.get() != 0);
TEST_THAT(errMsg.empty());
TEST_THAT(pconfig->KeyExists("MultiValue"));
// values are separated by a specific character
std::string expectedvalue("value1");
expectedvalue += Configuration::MultiValueSeparator;
expectedvalue += "secondvalue";
TEST_THAT(pconfig->GetKeyValue("MultiValue") == expectedvalue);
}
// Check boolean values
{
std::string errMsg;
std::auto_ptr<Configuration> pconfig(
Configuration::LoadAndVerify(
"testfiles" DIRECTORY_SEPARATOR "config15.txt",
&verify, errMsg));
TEST_THAT(pconfig.get() != 0);
TEST_THAT(errMsg.empty());
TEST_THAT(pconfig->GetKeyValueBool("BoolTrue1") == true);
TEST_THAT(pconfig->GetKeyValueBool("BoolTrue2") == true);
TEST_THAT(pconfig->GetKeyValueBool("BoolFalse1") == false);
TEST_THAT(pconfig->GetKeyValueBool("BoolFalse2") == false);
}
// Test named locks
{
NamedLock lock1;
// Try and get a lock on a name in a directory which doesn't exist
TEST_CHECK_THROWS(lock1.TryAndGetLock(
"testfiles"
DIRECTORY_SEPARATOR "non-exist"
DIRECTORY_SEPARATOR "lock"),
CommonException, OSFileError);
// And a more resonable request
TEST_THAT(lock1.TryAndGetLock(
"testfiles" DIRECTORY_SEPARATOR "lock1") == true);
// Try to lock something using the same lock
TEST_CHECK_THROWS(
lock1.TryAndGetLock(
"testfiles"
DIRECTORY_SEPARATOR "non-exist"
DIRECTORY_SEPARATOR "lock2"),
CommonException, NamedLockAlreadyLockingSomething);
#if defined(HAVE_FLOCK) || HAVE_DECL_O_EXLOCK
// And again on that name
NamedLock lock2;
TEST_THAT(lock2.TryAndGetLock(
"testfiles" DIRECTORY_SEPARATOR "lock1") == false);
#endif
}
{
// Check that it unlocked when it went out of scope
NamedLock lock3;
TEST_THAT(lock3.TryAndGetLock(
"testfiles" DIRECTORY_SEPARATOR "lock1") == true);
}
{
// And unlocking works
NamedLock lock4;
TEST_CHECK_THROWS(lock4.ReleaseLock(), CommonException,
NamedLockNotHeld);
TEST_THAT(lock4.TryAndGetLock(
"testfiles" DIRECTORY_SEPARATOR "lock4") == true);
lock4.ReleaseLock();
NamedLock lock5;
TEST_THAT(lock5.TryAndGetLock(
"testfiles" DIRECTORY_SEPARATOR "lock4") == true);
// And can reuse it
TEST_THAT(lock4.TryAndGetLock(
"testfiles" DIRECTORY_SEPARATOR "lock5") == true);
}
// Test the ReadGatherStream
{
#define GATHER_DATA1 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define GATHER_DATA2 "ZYZWVUTSRQPOMNOLKJIHGFEDCBA9876543210zyxwvutsrqpomno"
// Make two streams
MemBlockStream s1(GATHER_DATA1, sizeof(GATHER_DATA1));
MemBlockStream s2(GATHER_DATA2, sizeof(GATHER_DATA2));
// And a gather stream
ReadGatherStream gather(false /* no deletion */);
// Add the streams
int s1_c = gather.AddComponent(&s1);
int s2_c = gather.AddComponent(&s2);
TEST_THAT(s1_c == 0);
TEST_THAT(s2_c == 1);
// Set up some blocks
gather.AddBlock(s1_c, 11);
gather.AddBlock(s1_c, 2);
gather.AddBlock(s1_c, 8, true, 2);
gather.AddBlock(s2_c, 20);
gather.AddBlock(s1_c, 20);
gather.AddBlock(s2_c, 25);
gather.AddBlock(s1_c, 10, true, 0);
#define GATHER_RESULT "0123456789abc23456789ZYZWVUTSRQPOMNOLKJIHabcdefghijklmnopqrstGFEDCBA9876543210zyxwvuts0123456789"
// Read them in...
char buffer[1024];
unsigned int r = 0;
while(r < sizeof(GATHER_RESULT) - 1)
{
int s = gather.Read(buffer + r, 7);
r += s;
TEST_THAT(gather.GetPosition() == r);
if(r < sizeof(GATHER_RESULT) - 1)
{
TEST_THAT(gather.StreamDataLeft());
TEST_THAT(static_cast<size_t>(gather.BytesLeftToRead()) == sizeof(GATHER_RESULT) - 1 - r);
}
else
{
TEST_THAT(!gather.StreamDataLeft());
TEST_THAT(gather.BytesLeftToRead() == 0);
}
}
TEST_THAT(r == sizeof(GATHER_RESULT) - 1);
TEST_THAT(::memcmp(buffer, GATHER_RESULT, sizeof(GATHER_RESULT) - 1) == 0);
}
// Test ExcludeList
{
ExcludeList elist;
// Check assumption
TEST_THAT(Configuration::MultiValueSeparator == '\x01');
// Add definite entries
elist.AddDefiniteEntries(std::string("\x01"));
elist.AddDefiniteEntries(std::string(""));
elist.AddDefiniteEntries(std::string("Definite1\x01/dir/DefNumberTwo\x01\x01ThingDefThree"));
elist.AddDefiniteEntries(std::string("AnotherDef"));
TEST_THAT(elist.SizeOfDefiniteList() == 4);
// Add regex entries
#ifdef HAVE_REGEX_H
elist.AddRegexEntries(std::string("[a-d]+\\.reg$" "\x01" "EXCLUDE" "\x01" "^exclude$"));
elist.AddRegexEntries(std::string(""));
TEST_CHECK_THROWS(elist.AddRegexEntries(std::string("[:not_valid")), CommonException, BadRegularExpression);
TEST_THAT(elist.SizeOfRegexList() == 3);
#else
TEST_CHECK_THROWS(elist.AddRegexEntries(std::string("[a-d]+\\.reg$" "\x01" "EXCLUDE" "\x01" "^exclude$")), CommonException, RegexNotSupportedOnThisPlatform);
TEST_THAT(elist.SizeOfRegexList() == 0);
#endif
// Try some matches!
TEST_THAT(elist.IsExcluded(std::string("Definite1")) == true);
TEST_THAT(elist.IsExcluded(std::string("/dir/DefNumberTwo")) == true);
TEST_THAT(elist.IsExcluded(std::string("ThingDefThree")) == true);
TEST_THAT(elist.IsExcluded(std::string("AnotherDef")) == true);
TEST_THAT(elist.IsExcluded(std::string("dir/DefNumberTwo")) == false);
#ifdef HAVE_REGEX_H
TEST_THAT(elist.IsExcluded(std::string("b.reg")) == true);
TEST_THAT(elist.IsExcluded(std::string("e.reg")) == false);
TEST_THAT(elist.IsExcluded(std::string("b.Reg")) == false);
TEST_THAT(elist.IsExcluded(std::string("DEfinite1")) == false);
TEST_THAT(elist.IsExcluded(std::string("DEXCLUDEfinite1")) == true);
TEST_THAT(elist.IsExcluded(std::string("DEfinitexclude1")) == false);
TEST_THAT(elist.IsExcluded(std::string("exclude")) == true);
#endif
}
test_conversions();
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1