1
0
forked from 0ad/0ad

# Fixed Linux execution problem. Extended unit-test system.

Fix sys_get_executable_name.
Add mock object support.
Add CxxTest wrapper to support "-test TestSuitename" and "-test
TestSuitename::test_case_name" command-line arguments for running
individual tests.

This was SVN commit r7081.
This commit is contained in:
Ykkrosh 2009-08-07 15:21:39 +00:00
parent 58ee7038db
commit 6374080b60
7 changed files with 282 additions and 11 deletions

View File

@ -334,7 +334,9 @@ function setup_static_lib_package (package_name, rel_source_dirs, extern_libs, e
package_add_contents(source_root, rel_source_dirs, {}, extra_params)
package_add_extern_libs(extern_libs)
tinsert(static_lib_names, package_name)
if not extra_params["no_default_link"] then
tinsert(static_lib_names, package_name)
end
if OS == "windows" then
tinsert(package.buildflags, "no-rtti")
@ -353,8 +355,10 @@ function setup_static_lib_package (package_name, rel_source_dirs, extern_libs, e
-- correctly open these files from the IDE.
-- * precompiled.cpp (needed to "Create" the PCH) also goes in
-- the abovementioned dir.
pch_dir = source_root.."pch/"..package_name.."/"
package_setup_pch(pch_dir, "precompiled.h", "precompiled.cpp")
if not extra_params["no_default_pch"] then
pch_dir = source_root.."pch/"..package_name.."/"
package_setup_pch(pch_dir, "precompiled.h", "precompiled.cpp")
end
end
-- this is where the source tree is chopped up into static libs.
@ -486,7 +490,8 @@ function setup_all_libs ()
"vorbis",
"libjpg",
"cryptopp",
"valgrind"
"valgrind",
"cxxtest",
}
-- CPU architecture-specific
@ -518,6 +523,18 @@ function setup_all_libs ()
end
setup_static_lib_package("lowlevel", source_dirs, extern_libs, {})
-- CxxTest mock function support
extern_libs = {
"cxxtest",
}
-- 'real' implementations, to be linked against the main executable
setup_static_lib_package("mocks_real", {}, extern_libs, { no_default_link = 1, no_default_pch = 1 })
listconcat(package.files, matchfiles(source_root.."mocks/*.h", source_root.."mocks/*_real.cpp"))
-- 'test' implementations, to be linked against the test executable
setup_static_lib_package("mocks_test", {}, extern_libs, { no_default_link = 1, no_default_pch = 1 })
listconcat(package.files, matchfiles(source_root.."mocks/*.h", source_root.."mocks/*_test.cpp"))
end
@ -563,6 +580,7 @@ function setup_main_exe ()
package_add_extern_libs(used_extern_libs)
tinsert(package.links, "mocks_real")
-- Platform Specifics
if OS == "windows" then
@ -955,9 +973,9 @@ function setup_tests()
package.testoptions = "--have-std"
package.rootoptions = "--have-std"
if OS == "windows" then
package.rootoptions = package.rootoptions .. " --gui=Win32Gui --runner=Win32ODSPrinter"
package.rootoptions = package.rootoptions .. " --gui=PsTestWrapper --runner=Win32ODSPrinter"
else
package.rootoptions = package.rootoptions .. " --runner=ErrorPrinter"
package.rootoptions = package.rootoptions .. " --gui=PsTestWrapper --runner=ErrorPrinter"
end
-- precompiled headers - the header is added to all generated .cpp files
-- note that the header isn't actually precompiled here, only #included
@ -981,6 +999,7 @@ function setup_tests()
-- note: these are not relative to source_root and therefore can't be included via package_add_contents.
listconcat(package.files, src_files)
package_add_extern_libs(used_extern_libs)
tinsert(package.links, "mocks_test")
if OS == "windows" then
-- from "lowlevel" static lib; must be added here to be linked in

View File

@ -18,22 +18,124 @@
#include "precompiled.h"
#include "lib/sysdep/sysdep.h"
#include "lib/external_libraries/boost_filesystem.h"
#define GNU_SOURCE
#include <dlfcn.h>
#include "mocks/dlfcn.h"
#include "mocks/boost_filesystem.h"
// TODO: This normalization code is copied from boost::filesystem,
// where it is deprecated, presumably for good reasons (probably
// because symlinks mean "x/../y" != "y" in general), and this file
// is not an appropriate place for the code anyway, so it should be
// rewritten or removed or something. (Also, the const_cast is evil.)
namespace boost { namespace filesystem {
// Derived from boost/filesystem/path.hpp:
// "Copyright Beman Dawes 2002-2005
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)"
template<class String, class Traits>
basic_path<String, Traits> normalize_COPIED(const basic_path<String, Traits> &self)
{
typedef basic_path<String, Traits> path_type;
typedef typename path_type::string_type string_type;
typedef typename path_type::iterator iterator;
static const typename string_type::value_type dot_str[]
= { dot<path>::value, 0 };
if ( self.empty() ) return self;
path temp;
iterator start( self.begin() );
iterator last( self.end() );
iterator stop( last-- );
for ( iterator itr( start ); itr != stop; ++itr )
{
// ignore "." except at start and last
if ( itr->size() == 1
&& (*itr)[0] == dot<path_type>::value
&& itr != start
&& itr != last ) continue;
// ignore a name and following ".."
if ( !temp.empty()
&& itr->size() == 2
&& (*itr)[0] == dot<path_type>::value
&& (*itr)[1] == dot<path_type>::value ) // dot dot
{
string_type lf( temp.leaf() );
if ( lf.size() > 0
&& (lf.size() != 1
|| (lf[0] != dot<path_type>::value
&& lf[0] != slash<path_type>::value))
&& (lf.size() != 2
|| (lf[0] != dot<path_type>::value
&& lf[1] != dot<path_type>::value
# ifdef BOOST_WINDOWS_PATH
&& lf[1] != colon<path_type>::value
# endif
)
)
)
{
temp.remove_leaf();
// if not root directory, must also remove "/" if any
if ( temp.string().size() > 0
&& temp.string()[temp.string().size()-1]
== slash<path_type>::value )
{
typename string_type::size_type rds(
detail::root_directory_start<String,Traits>( temp.string(),
temp.string().size() ) );
if ( rds == string_type::npos
|| rds != temp.string().size()-1 )
{ const_cast<String&>( temp.string() ).erase( temp.string().size()-1 ); }
}
iterator next( itr );
if ( temp.empty() && ++next != stop
&& next == last && *last == dot_str ) temp /= dot_str;
continue;
}
}
temp /= *itr;
};
if ( temp.empty() ) temp /= dot_str;
return temp;
}
} }
LibError sys_get_executable_name(char* n_path, size_t max_chars)
{
const char* path;
Dl_info dl_info;
// Find the executable's filename
memset(&dl_info, 0, sizeof(dl_info));
if (!dladdr((void *)sys_get_executable_name, &dl_info) ||
if (!T::dladdr((void *)sys_get_executable_name, &dl_info) ||
!dl_info.dli_fname )
{
return ERR::NO_SYS;
}
path = dl_info.dli_fname;
strncpy(n_path, dl_info.dli_fname, max_chars);
return INFO::OK;
// If this looks like a relative path, resolve against cwd.
// If this looks like an absolute path, we still need to normalize it.
if (strchr(path, '/')) {
fs::path p = fs::complete(fs::path(path), T::Boost_Filesystem_initial_path());
fs::path n = fs::normalize_COPIED(p);
strncpy(n_path, n.string().c_str(), max_chars);
return INFO::OK;
}
// If it's not a path at all, i.e. it's just a filename, we'd
// probably have to search through PATH to find it.
// That's complex and should be uncommon, so don't bother.
return ERR::NO_SYS;
}

View File

@ -17,9 +17,17 @@
#include "lib/self_test.h"
#include "lib/lib.h"
#include "lib/sysdep/sysdep.h"
#include "lib/posix/posix.h" // fminf etc.
#if OS_LINUX
# include "mocks/dlfcn.h"
# include "mocks/boost_filesystem.h"
#endif
#include <cxxtest/PsTestWrapper.h>
class TestSysdep : public CxxTest::TestSuite
{
public:
@ -68,4 +76,106 @@ public:
TS_ASSERT_EQUALS(fmaxf(-2.0f, 1.0f), 1.0f);
TS_ASSERT_EQUALS(fmaxf(0.001f, 0.00001f), 0.001f);
}
void test_sys_get_executable_name()
{
char path[PATH_MAX] = "";
// Try it first with the real executable (i.e. the
// one that's running this test code)
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
// Check it's absolute
TSM_ASSERT(std::string("Path: ")+path, path_is_absolute(path));
// Check the file exists
struct stat s;
TSM_ASSERT_EQUALS(std::string("Path: ")+path, stat(path, &s), 0);
// Do some platform-specific tests, based on the
// implementations of sys_get_executable_name:
#if OS_LINUX
// Try with absolute paths
{
Mock_dladdr d("/example/executable");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
}
{
Mock_dladdr d("/example/./a/b/../c/../../executable");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
}
// Try with relative paths
{
Mock_dladdr d("./executable");
Mock_initial_path m("/example");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
}
{
Mock_dladdr d("./executable");
Mock_initial_path m("/example/");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/executable");
}
{
Mock_dladdr d("../d/../../e/executable");
Mock_initial_path m("/example/a/b/c");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), INFO::OK);
TS_ASSERT_STR_EQUALS(path, "/example/a/e/executable");
}
// Try with pathless names
{
Mock_dladdr d("executable");
TS_ASSERT_EQUALS(sys_get_executable_name(path, PATH_MAX), ERR::NO_SYS);
}
#endif // OS_LINUX
}
// Mock classes for test_sys_get_executable_name
#if OS_LINUX
class Mock_dladdr : public T::Base_dladdr
{
public:
Mock_dladdr(const char* fname) : fname_(fname) { }
int dladdr(void *UNUSED(addr), Dl_info *info) {
info->dli_fname = fname_;
return 1;
}
private:
const char* fname_;
};
class Mock_initial_path : public T::Base_Boost_Filesystem_initial_path
{
public:
Mock_initial_path(const char* buf) : buf_(buf) { }
fs::path Boost_Filesystem_initial_path() {
return fs::path(buf_);
}
private:
const char* buf_;
};
#endif
private:
bool path_is_absolute(const char* path)
{
// UNIX-style absolute paths
if (path[0] == '/')
return true;
// Windows UNC absolute paths
if (path[0] == '\\' && path[1] == '\\')
return true;
// Windows drive-letter absolute paths
if (isalpha(path[0]) && path[1] == ':' && (path[2] == '/' || path[2] == '\\'))
return true;
return false;
}
};

View File

@ -0,0 +1,10 @@
#include "lib/external_libraries/boost_filesystem.h"
#include <cxxtest/Mock.h>
CXXTEST_MOCK(
Boost_Filesystem_initial_path,
boost::filesystem::path,
Boost_Filesystem_initial_path,
(),
boost::filesystem::initial_path,
()
);

7
source/mocks/dlfcn.h Normal file
View File

@ -0,0 +1,7 @@
#include <dlfcn.h>
#include <cxxtest/Mock.h>
CXXTEST_MOCK_GLOBAL(
int, dladdr,
(void *addr, Dl_info *info),
(addr, info)
);

View File

@ -0,0 +1,8 @@
#define CXXTEST_MOCK_REAL_SOURCE_FILE
#include "lib/sysdep/os.h"
#include "mocks/boost_filesystem.h"
#if OS_LINUX
#include "mocks/dlfcn.h"
#endif // OS_LINUX

View File

@ -0,0 +1,15 @@
#define CXXTEST_MOCK_TEST_SOURCE_FILE
#include "lib/sysdep/os.h"
// Cause calls to be redirected to the real function by default
#define DEFAULT(name) static T::Real_##name real_##name
#include "mocks/boost_filesystem.h"
DEFAULT(Boost_Filesystem_initial_path);
#if OS_LINUX
#include "mocks/dlfcn.h"
DEFAULT(dladdr);
#endif // OS_LINUX