/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/

 

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sal.hxx"
 
//------------------------------------------------------------------------
// include files
//------------------------------------------------------------------------
#include <osl_Module_Const.h> 

using namespace	osl;
using namespace	rtl;


//------------------------------------------------------------------------
// helper functions and classes
//------------------------------------------------------------------------

/** print Boolean value.
*/
inline void printBool( sal_Bool bOk )
{
	t_print("#printBool# " );
	( sal_True == bOk ) ? t_print("TRUE!\n" ): t_print("FALSE!\n" );		
}

/** print a UNI_CODE String.
*/
inline void printUString( const ::rtl::OUString & str )
{
	rtl::OString aString; 

	t_print("#printUString_u# " );
	aString = ::rtl::OUStringToOString( str, RTL_TEXTENCODING_ASCII_US );
	t_print("%s\n", aString.getStr( ) );
}

/** get dll file URL.
*/
inline ::rtl::OUString getDllURL( void )
{
#if ( defined WNT )	       // lib in Unix and lib in Windows are not same in file name.
	::rtl::OUString libPath( rtl::OUString::createFromAscii( "Module_DLL.dll" ) );
#else
	::rtl::OUString libPath( rtl::OUString::createFromAscii( "libModule_DLL.so" ) );
#endif
	
	::rtl::OUString dirPath, dllPath;
	osl::Module::getUrlFromAddress( ( void* ) &getDllURL, dirPath );
	dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') + 1);
	osl::FileBase::getAbsoluteFileURL( dirPath, libPath, dllPath );

	return dllPath;
}

/** print a UNI_CODE file name.
*/
inline void printFileName( const ::rtl::OUString & str )
{
	rtl::OString aString;

	t_print("#printFileName_u# " );
	aString = ::rtl::OUStringToOString( str, RTL_TEXTENCODING_ASCII_US );
	t_print("%s\n", aString.getStr( ) );
}

inline sal_Bool isURL( const ::rtl::OUString pathname )
{
	::rtl::OUString aPreURL( rtl::OUString::createFromAscii( "file:///" ) );
	return ( ( pathname.indexOf( aPreURL ) == 0 ) ? sal_True : sal_False );
}

/** create a temp test directory using OUString name of full qualified URL or system path.
*/
inline void createTestDirectory( const ::rtl::OUString dirname )
{
	::rtl::OUString     aPathURL   = dirname.copy( 0 );
	::osl::FileBase::RC	nError;
	
	if ( !isURL( dirname ) )
		::osl::FileBase::getFileURLFromSystemPath( dirname, aPathURL ); //convert if not full qualified URL	
	nError = ::osl::Directory::create( aPathURL );
	CPPUNIT_ASSERT_MESSAGE( "In createTestDirectory Function: creation: ", ( ::osl::FileBase::E_None == nError ) || ( nError == ::osl::FileBase::E_EXIST ) );	
}

/** delete a temp test directory using OUString name of full qualified URL or system path.
*/
inline void deleteTestDirectory( const ::rtl::OUString dirname )
{
	::rtl::OUString     aPathURL   = dirname.copy( 0 );
	::osl::FileBase::RC	nError;
	if ( !isURL( dirname ) )
		::osl::FileBase::getFileURLFromSystemPath( dirname, aPathURL ); //convert if not full qualified URL	

	::osl::Directory testDir( aPathURL );
	if ( testDir.isOpen( ) == sal_True )
	{
        	testDir.close( );  //close if still open.
    	}
    
	nError = ::osl::Directory::remove( aPathURL );
 	CPPUNIT_ASSERT_MESSAGE( "In deleteTestDirectory function: remove ", ( ::osl::FileBase::E_None == nError ) || ( nError == ::osl::FileBase::E_NOENT ) );	
}

//check if the file exist
inline sal_Bool ifFileExist( const ::rtl::OUString & str )
{
	::rtl::OUString 	aUStr;	
	if ( isURL( str ) )
		::osl::FileBase::getSystemPathFromFileURL( str, aUStr ); 
	else 
		return sal_False;
	
	::osl::File strFile( aUStr );
	::osl::FileBase::RC	nError = strFile.open( OpenFlag_Read );
	if ( ::File::E_NOENT == nError ) 
		return sal_False;
	else{
		strFile.close( );
		return sal_True;
	}
}

/** detete a temp test file using OUString name.
*/
inline void deleteTestFile( const ::rtl::OUString filename )
{
	::rtl::OUString	aPathURL   = filename.copy( 0 );
	::osl::FileBase::RC	nError;
	
	if ( !isURL( filename ) )
		::osl::FileBase::getFileURLFromSystemPath( filename, aPathURL ); //convert if not full qualified URL
		
	nError = ::osl::File::setAttributes( aPathURL, Attribute_GrpWrite| Attribute_OwnWrite| Attribute_OthWrite ); // if readonly, make writtenable. 
	CPPUNIT_ASSERT_MESSAGE( "In deleteTestFile Function: set writtenable ", ( ::osl::FileBase::E_None == nError ) || ( ::osl::FileBase::E_NOENT == nError ) );	
	
	nError = ::osl::File::remove( aPathURL );
	CPPUNIT_ASSERT_MESSAGE( "In deleteTestFile Function: remove ", ( ::osl::FileBase::E_None == nError ) || ( nError == ::osl::FileBase::E_NOENT ) );	
}


//------------------------------------------------------------------------
// test code start here
//------------------------------------------------------------------------

namespace osl_Module
{

	/** class and member function that is available for module test :
	*/

	class testClass
	{
	public:
		static void myFunc()
		{
			t_print("#Sun Microsystem\n");
		};
	};

	
	/** testing the methods:
		Module();
		Module( const ::rtl::OUString& strModuleName, sal_Int32 nRtldMode = SAL_LOADMODULE_DEFAULT);
	*/
	class ctors : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;
	
		void ctors_none( )
		{
			::osl::Module aMod;
			bRes = aMod.is();
			
			CPPUNIT_ASSERT_MESSAGE( "#test comment#: test constructor without parameter.", 
									sal_False == bRes  );
		}
	
		void ctors_name_mode( )
		{
			OUString aFileURL;
            bRes = osl::Module::getUrlFromAddress( ( void* ) &::osl_Module::testClass::myFunc, aFileURL );

			if ( !( bRes ) )
			{
				CPPUNIT_ASSERT_MESSAGE("Cannot locate current module.",  sal_False  );
			}
			
			::osl::Module aMod( aFileURL );
			bRes = aMod.is( );
			aMod.unload( );
			
			CPPUNIT_ASSERT_MESSAGE( "#test comment#: test constructor with load action.", 
									sal_True == bRes  );
		}

		CPPUNIT_TEST_SUITE( ctors );
		CPPUNIT_TEST( ctors_none );
		CPPUNIT_TEST( ctors_name_mode );
		CPPUNIT_TEST_SUITE_END( ); 
	}; // class ctors

	
	/** testing the methods:
		static sal_Bool getUrlFromAddress(void * addr, ::rtl::OUString & libraryUrl)
	*/
	class getUrlFromAddress : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;

		void getUrlFromAddress_001( )
		{
			OUString aFileURL;
            bRes = osl::Module::getUrlFromAddress( ( void* ) &::osl_Module::testClass::myFunc, aFileURL ) ;
			if ( !( bRes ) )
			{
				CPPUNIT_ASSERT_MESSAGE("Cannot locate current module.",  sal_False  );
			}

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: test get Module URL from address.", 
									sal_True == bRes && 0 < aFileURL.lastIndexOf('/')  );
		}
		
		void getUrlFromAddress_002( )
		{
			::osl::Module aMod( getDllURL( ) );
			FuncPtr pFunc = ( FuncPtr ) aMod.getSymbol( rtl::OUString::createFromAscii( "firstfunc" ) ); 
			
			OUString aFileURL;
            bRes = osl::Module::getUrlFromAddress( ( void* )pFunc, aFileURL );
			if ( !( bRes  ) )
			{
				CPPUNIT_ASSERT_MESSAGE("Cannot locate current module.",  sal_False  );
			}
			aMod.unload( );

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: load an external library, get its function address and get its URL.", 
									sal_True == bRes && 0 < aFileURL.lastIndexOf('/') && aFileURL.equalsIgnoreAsciiCase( getDllURL( ) ) );
		}
		
		/* tester comments: another case is getFunctionSymbol_001*/
		
		CPPUNIT_TEST_SUITE( getUrlFromAddress );
		CPPUNIT_TEST( getUrlFromAddress_001 );
		CPPUNIT_TEST( getUrlFromAddress_002 );
		CPPUNIT_TEST_SUITE_END( ); 
	}; // class getUrlFromAddress
	

	/** testing the method:
		sal_Bool SAL_CALL load( const ::rtl::OUString& strModuleName, 
        	                                     sal_Int32 nRtldMode = SAL_LOADMODULE_DEFAULT)
	*/
	class load : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;

		void load_001( )
		{
			::osl::Module aMod( getDllURL( ) );
			::osl::Module aMod1;

			aMod1.load( getDllURL( ) );
			bRes = oslModule(aMod) == oslModule(aMod1);
			aMod.unload( );
			aMod1.unload( );

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: load function should do the same thing as constructor with library name.", 
									sal_True == bRes  );
		}
		// load lib which is under a CJK directory
		void load_002( )
		{
#ifdef UNX	
			//Can not get a CJK directory already exist, so here create one. Perhaps reason is encoding problem.
			::rtl::OUString aPidDirURL = rtl::OUString::createFromAscii( "file:///tmp/" ) + ::rtl::OUString::valueOf( ( long )getpid( ) );
			::rtl::OUString aMyDirURL = aPidDirURL + aKname;
			createTestDirectory( aPidDirURL );
			createTestDirectory( aMyDirURL );
			
			::rtl::OUString aDLLURL = aMyDirURL + rtl::OUString::createFromAscii( "/libModule_DLL.so" );
			//check if the lib exist. 
			//FIXME: if assert condition is false, the case will return, so the directory will not be clean-up
			CPPUNIT_ASSERT_MESSAGE( "#Source file is not exist. please manually clean-up directory and file under /tmp", ifFileExist( getDllURL( ) ) == sal_True );
			::osl::FileBase::RC nError = ::osl::File::copy( getDllURL( ), aDLLURL );
			CPPUNIT_ASSERT_MESSAGE( "#copy failed. please manually clean-up directory and file under /tmp", nError == ::osl::FileBase::E_None );
			//ifFileExist returned false but the file exist
			CPPUNIT_ASSERT_MESSAGE( "#This file is not exist, copy failed. please manually clean-up directory and file under /tmp", ifFileExist( aDLLURL ) == sal_True );
		
			//test if can create a normal file
			::rtl::OUString aFileURL = aMyDirURL + rtl::OUString::createFromAscii( "/test_file" );
			::osl::File testFile( aFileURL );			
			nError = testFile.open( OpenFlag_Create );
			CPPUNIT_ASSERT_MESSAGE( "#create failed. please manually clean-up directory and file under /tmp", nError == ::osl::FileBase::E_None );			
			CPPUNIT_ASSERT_MESSAGE( "#This file is not exist, create failed. please manually clean-up directory and file under /tmp", ifFileExist( aFileURL ) == sal_True );
			
			//load the copied dll			
			::osl::Module aMod( aDLLURL );
			::osl::Module aMod1;

			sal_Bool bOK = aMod1.load( aDLLURL );
			bRes = oslModule(aMod) == oslModule(aMod1);
			aMod.unload( );
			aMod1.unload( );
			deleteTestFile( aFileURL );
			deleteTestFile( aDLLURL );	
			deleteTestDirectory( aMyDirURL );		
			deleteTestDirectory( aPidDirURL );			
			
			CPPUNIT_ASSERT_MESSAGE( "#test comment#: load lib which is under a CJK directory.", 
									sal_True == bRes && bOK == sal_True );
#endif
		}
		
		CPPUNIT_TEST_SUITE( load );
		CPPUNIT_TEST( load_001 );
		CPPUNIT_TEST( load_002 );
		CPPUNIT_TEST_SUITE_END( ); 
	}; // class load


	/** testing the method:
		void SAL_CALL unload()
	*/
	class unload : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;

		void unload_001( )
		{
			::osl::Module aMod( getDllURL( ) );

			aMod.unload( );
			bRes = oslModule(aMod) ==NULL;

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: unload function should do the same thing as destructor.", 
									sal_True == bRes  );
		}
		
		CPPUNIT_TEST_SUITE( unload );
		CPPUNIT_TEST( unload_001 );
		CPPUNIT_TEST_SUITE_END( ); 
	}; // class unload

	
	/** testing the methods:
		sal_Bool SAL_CALL is() const
	*/
	class is : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;

		void is_001( )
		{
			OUString aFileURL;
            bRes = osl::Module::getUrlFromAddress( ( void* ) &::osl_Module::testClass::myFunc, aFileURL );
			if ( !( bRes  ) )
			{
				CPPUNIT_ASSERT_MESSAGE("Cannot locate current module - using executable instead",  sal_False  );
			}
			
			::osl::Module aMod;
			bRes = aMod.is( );
			aMod.load( aFileURL );
			bRes1 = aMod.is( );
			aMod.unload( );

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: test if a module is a loaded module.", 
									 sal_False == bRes && sal_True == bRes1);
		}
		CPPUNIT_TEST_SUITE( is );
		CPPUNIT_TEST( is_001 );
		CPPUNIT_TEST_SUITE_END( ); 
	}; // class is


	/** testing the methods:
		void* SAL_CALL getSymbol( const ::rtl::OUString& strSymbolName)
	*/
	class getSymbol : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes;

		void getSymbol_001( )
		{
			::osl::Module aMod( getDllURL( ) );
			FuncPtr pFunc = ( FuncPtr ) aMod.getSymbol( rtl::OUString::createFromAscii( "firstfunc" ) ); 
			bRes = sal_False;
			if ( pFunc )
				bRes = pFunc( bRes );
			aMod.unload();

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: load a dll and call one function in it.", 
									 sal_True == bRes );
		}
		
		CPPUNIT_TEST_SUITE( getSymbol );
		CPPUNIT_TEST( getSymbol_001 );
		CPPUNIT_TEST_SUITE_END( );   
	}; // class getSymbol
	

	/** testing the methods:
		operator oslModule() const
	*/
	class optr_oslModule : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;

		void optr_oslModule_001( )
		{
			::osl::Module aMod;
			bRes = ( (oslModule)aMod == NULL );
				
			aMod.load( getDllURL( ) );
			bRes1 = (oslModule)aMod != NULL;

			aMod.unload( );
			
			CPPUNIT_ASSERT_MESSAGE( "#test comment#: the m_Module of a Module instance will be NULL when is not loaded, it will not be NULL after loaded.", 
									 sal_True == bRes && sal_True == bRes1);
		}
		
		void optr_oslModule_002( )
		{
			::osl::Module aMod( getDllURL( ) );
			::rtl::OUString funcName(::rtl::OUString::createFromAscii( "firstfunc" ) );
			
			FuncPtr pFunc = ( FuncPtr ) osl_getSymbol( (oslModule)aMod, funcName.pData );
			bRes = sal_False;
			if ( pFunc )
				bRes = pFunc( bRes );
			
			aMod.unload();

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: use m_Module to call osl_getSymbol() function.", 
									 sal_True == bRes  );
		}
		
		CPPUNIT_TEST_SUITE( optr_oslModule );
		CPPUNIT_TEST( optr_oslModule_001 );
		CPPUNIT_TEST( optr_oslModule_002 );
		CPPUNIT_TEST_SUITE_END( );   
	}; // class optr_oslModule
	
	/** testing the methods:
		oslGenericFunction SAL_CALL getFunctionSymbol( const ::rtl::OUString& ustrFunctionSymbolName )
	*/
	class getFunctionSymbol : public CppUnit::TestFixture
	{
	public:
		sal_Bool bRes, bRes1;

		void getFunctionSymbol_001( )
		{
			::osl::Module aMod( getDllURL( ) );
			oslGenericFunction oslFunc = aMod.getFunctionSymbol( rtl::OUString::createFromAscii( "firstfunc" ) ); 
			::rtl::OUString aLibraryURL;
			bRes = ::osl::Module::getUrlFromAddress( oslFunc, aLibraryURL);
			aMod.unload();
			printFileName( aLibraryURL );

			CPPUNIT_ASSERT_MESSAGE( "#test comment#: load a dll and get its function addr and get its URL.", 
				 sal_True == bRes && aLibraryURL.equalsIgnoreAsciiCase( getDllURL() ) );
		}
		
		CPPUNIT_TEST_SUITE( getFunctionSymbol );
		CPPUNIT_TEST( getFunctionSymbol_001 );
		//CPPUNIT_TEST( getFunctionSymbol_002 );
		CPPUNIT_TEST_SUITE_END( );   
	}; // class getFunctionSymbol
	
// -----------------------------------------------------------------------------
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::ctors, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::getUrlFromAddress, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::load, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::unload, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::is, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::getSymbol, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::optr_oslModule, "osl_Module");
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(osl_Module::getFunctionSymbol, "osl_Module");
// -----------------------------------------------------------------------------
	
} // namespace osl_Module

// -----------------------------------------------------------------------------

// this macro creates an empty function, which will called by the RegisterAllFunctions()
// to let the user the possibility to also register some functions by hand.
NOADDITIONAL;
