What's new? | Help | Directory | Sign in
Google
amop
Automatic Mock Object For C++
  
  
  
  
    
Search
for
Updated Mar 05, 2008 by rdescartes
Labels: Featured
GeneralDiscussion  
A general discussion page

Introduction

Please leave message for any question or feature request in comments or visit amop google group


Comment by onfreund, Mar 04, 2008

Refining my request for a Set() method, I think there should be 3 variants, depending on the type of the parameter, so if, for example, I have the following method: bool Interface::Method(std::string &str, std::string * pointedStr, char * const buffer) I would like to be able to write: mock.Method(&Interface::Method).Set<0>("First").SetPointer<1>("Second").Copy<2>("Third", strlen("Third") + 1).Will(true); And that will translate to: str = "First"; (*pointedStr) = "Second"; memcpy(buffer, "Third", strlen("Third") + 1; What do you think?

Comment by rdescartes, Mar 05, 2008

Let me write down your case:

class Interface
{
public:
    void Foo(
        std::string& str
        , std::string* pointedStr
        , const char* buffer
        );
};

TEST(TestMockObjectSet)
{
    mock.Method(&Interface::Foo)
        .Set<0>("First")
        .SetPointer<1>("Second")
        .SetCopy<2>("Third", strlen("Third") + 1)
        .Will(true);

    std::string first;
    std::string second;
    char buf[sizeof("Third")];

    CHECK( ((Interface*)mock)->Foo(first, &second, buf) );

    CHECK_EQUAL( "First", first.c_str() );
    CHECK_EQUAL( "Second", second.c_str()  );
    CHECK( memcmp("Third", buf, sizeof("Third") )== 0  );

}

I prefer using policy object, like this:

TEST(TestMockObjectSetPolicy)
{
    mock.Method(&Interface::Foo)
        .Set<0>("First")
        .Set<1>(Policy::Pointer("Second"))
        .Set<2>(Policy::Copy("Third", strlen("Third") + 1) )
        .Will(true);

    std::string first;
    std::string second;
    char buf[sizeof("Third")];

    CHECK( ((Interface*)mock)->Foo(first, &second, buf) );

    CHECK_EQUAL( "First", first.c_str() );
    CHECK_EQUAL( "Second", second.c_str()  );
    CHECK( memcmp("Third", buf, sizeof("Third") )== 0  );

}

What do you think??

Comment by onfreund, Mar 05, 2008

I agree. It opens the door to extensibility. BTW, why is the index a template parameter rather than a normal method parameter in Expect (and consequently in set)? It really slows down the typing. Do you happen to have any idea for the destructor problem?

Comment by rdescartes, Mar 05, 2008
  1. it is because of two reasons:
    1. I have to use that index for get the type in compile time.
    2. I think Set<1>("Time"), it better than Set(1, "Time"). It looks more clean.
  1. I have some idea about the destructor problem, i know we can explicit calling the destructor. But i don't know whether it can be called by member function pointer. I will take some time to test it.
  1. Hey, Do you know there are any free Linux shell account which i can compile code at there?? i really want to port amop to support GCC.
  1. In fact, i haven't promoted AMOP (except UnitTest?++ mailing list and MockPP forum). So how do you know this library ??

Comment by onfreund, Mar 05, 2008
  1. Well, if you have to use it then there's nothing we can do. I still think the second approach is better since it doesn't interrupt the typing flow.
  2. I tried to get the address of the constructor (&Interface::~Interface) but this produces a compilation error.
  3. I don't know such services, but google seems to think there are (http://www.google.com/search?source=ig&hl=en&rlz=&q=free+linux+shell&btnG=Google+Search)
  4. I saw a link on the MockPP home page.
Comment by rdescartes, Mar 05, 2008
  1. Yes i tried some free shell account, but all they are damn slow...
  2. MockPP is a quite good mock object library, i used to use it, until i make this ;)
Comment by rdescartes, Mar 05, 2008

I just made some test, although i cant find a way to get the destructor address. (in fact, i can, but only the address, what i want is the V-table offset..) but i can find a way to get the operator delete address and v-table offset. so i will implement that in AMOP these days. :)

Comment by rdescartes, Mar 05, 2008

The destructor and the Verify method just added in the current build (v0.22.1).. Oh, right now is 4:22am in hong kong... i should go to bed :)

Comment by onfreund, Mar 06, 2008

This looks great. However, I can't combine the destructor and verify, so `mockStore.Method(amop::Destructor()); mockStore.Verify();` doesn't fail although it should. (since I can't use Wills() or Expects() on the destructor)

Comment by rdescartes, Mar 06, 2008

You can use the Count function:

mock.Method(Destructor<Interface>()).Count(1);

CHECK_THROW((delete ((Interface*)mock)), TCallCountException);
Comment by onfreund, Mar 06, 2008

Great. Thanks!

Comment by onfreund, Mar 08, 2008

Thinking about it, you can also add the option to use a policy object with Expect, not just Set.

Comment by rdescartes, Mar 09, 2008

That's good idea too. :)

For the coming version v0.23.1 (if not other bugs fix):

  1. The Set function with policy.
  2. The Expect function with policy.

Any other idea ?

Currently i have 2 open source projects:

  1. amop
  2. ysql , I am currently working on it right now ( i need to use that in my team project)

I plan to work on other one, which is about GUI acceptance test for C++, do you have any idea that, or wanna co-operate with me?

Comment by onfreund, Mar 09, 2008

Hi, I would love to co-operate in the future, but currently I do not have the time. In the meanwhile I'm happy to contribute from a product management standpoint :)

I think you should stick with the existing projects (maybe adding just one more), and make them best of breed. I really think amop has a great future.

Comment by rdescartes, Mar 09, 2008

So what is next about amop?

I totally have no idea about the big improvement of it except porting to another platform like linux.

Comment by onfreund, Mar 09, 2008

hopefully, I'll have a new idea by the time v0.23.1 is out (most of my suggestion up to now were simply things that I noticed I need while using amop. I'm pretty certain there will be more). It might also be helpful to look at mocking frameworks for other platforms (maybe record-plaback, a mocking repository with a VerifyAll? method that verifies all mocks, etc...).

Another thing that will probably turn up is memory issues (I'm not sure what is the effect of calling delete on the vtable, or what stack corruptions can occur due to the fact that the method signatures in the vtable do not match the original ones). Memory problems are much easier to debug when they reproduce in unit tests, but if amop will have memory issues itself, it will be harder to differentiate.

And of course, don't forget to make sure that it compiles with warning level 4 :) (I think turning static methods into inline will solve it).

Comment by rdescartes, Mar 09, 2008
  1. It should complies with no warning in level4 in the v0.22.1. isn't it?
  2. There should be no any memory issues right now. Calling the delete on the vtable wont get any issue(i tested it.) On the other hand, the method signatures in the vtable will not mis-match the original ones except giving a other class's method in the Method() call. Even you input a non-virtual method, i have set a static assert(by using c++ template) to check.
  3. I have not so much idea about the mock object of other platforms, I just know a little but about Python and Lua (Mainly in the C++ binding of these), so may be you can help me a lot on that part.
Comment by onfreund, Mar 09, 2008
  1. This is true for the project itself, but not for the code using it. For example, you need to comment out the name of the parameter (offset) in CallHandler?.h line 38, since it's not being used. There are a couple of more places with the exact same problem.
  2. That's good to know. I was only speculating. Glad to hear I was wrong :)
  3. I will. As I said in my previous post, there are at least two features I can think of importing - one is the record-replay mechanism that is nice for simple tests, but I wouldn't rank it high in my requested features, and the other is a mock repository, which you can use to get mocks from, instead of constructing them, and let's you call VerifyAll?, which calls Verify on all of the mocks that were generated by calls to is. This is also just a nice to have rather than a really needed feature.
Comment by rdescartes, Mar 09, 2008
  1. Let me fix it in the next version.
  2. About #3, is there any reference for other platform which have a mock repository or play back feature i can know more ?
Comment by onfreund, Mar 09, 2008

http://www.ayende.com/Wiki/(S(4h0bmy551hvxvl45ygwv0fie))/Default.aspx?Page=Rhino+Mocks+Documentation

You can find a demonstration of both features in the introduction.

Comment by rdescartes, Mar 09, 2008

Oh thanks, let me take a look first:)

Comment by onfreund, Mar 10, 2008

Another suggestions: the ability to customize the default function so it will do something else instead of throwing a TNotImplementedException.

Comment by rdescartes, Mar 11, 2008

Um.. for examples?? what is it used for?

Comment by onfreund, Mar 11, 2008

Well, for example, I use CXXTEST, so I might be interested in replacing the default method with TS_FAIL("Method not implemented").

Comment by rdescartes, Mar 11, 2008

ok, how about this one

TEST(NotImplementRedirect)
{
    struct Local
    {
        static void NotImplement()
        {
            std::cout << "Not Implement" << std::endl;
        }
    }

    TMockObject<Interface> mock;
    mock.NotImplemented(&Local::NotImplement);

    mock.SimpleFunction();
}

But.. in fact, if the simple function called, the stack winding will be have some problems...

it need to be tested...

Comment by onfreund, Mar 12, 2008

The example looks just what I wanted. As for the stack winding, I don't see the difference between this and calling the original not implemented function, but in any way, this is not an issue since I expect these functions to be called only when tests are failing, whereas memory issues will be investigated when the tests are passing.

Comment by onfreund, Mar 17, 2008

Hey. Any news?

Comment by rdescartes, Mar 17, 2008

Oh, i just ported the source code to support GCC. (it is tough..), And for your feature request, i will add some in the coming weekends... :)

Comment by kirk_kor...@hotmail.com, Mar 19, 2008

In config.h I see the line #define MAX_NUM_VIRTUAL_FUNCTIONS 25

Looking further, it appears this framework will behave strangely if you happen to have an interface with more than 25 functions. 25 seems to be way too small. If you have GUI objects or others, hundreds of functions in an interface is not unusual.

Is there any real harm changing this variable to 1024?

Comment by kirk_kor...@hotmail.com, Mar 19, 2008

Do you plan to support the interface keyword? Interfaces are all pure-virtual and cannot have destructor. I know this is MSVC specific.

E.g.

__interface IInterface
{
	virtual void SimpleFunction() = 0;
	virtual void SimpleFunctionWithParams(float, std::string, const char*) = 0;

    virtual int SimpleFunctionWithParamsAndReturn(float, std::string, const char*) = 0;
	
	virtual void SimpleFunctionWithAlotParams(float, int, float, int, std::string) = 0;

	virtual int SimpleFunctionWithReturn() = 0;
	virtual int& SimpleFunctionWithRefReturn() = 0;

	virtual void SimpleConstFunction() const = 0;

	virtual std::string ComplexFunction(const std::string& crs,
		std::string& rs, std::string s) = 0;

	virtual std::string ComplexConstFunction(const std::string& crs,
		std::string& rs, std::string s) const = 0;
};
Comment by rdescartes, Mar 19, 2008
  1. if you really need, you can set the MAX_NUM_VIRTUAL_FUNCTIONS bigger, but it is used for a template recursion, so that if u set that larger, the compile time will be longer.
  1. I just tested it for the interface keyword, it seem to work, except the destructor test. If u dun need to test the destructor, just use it :).
Comment by onfreund, Mar 21, 2008

Hey, I just downloaded the last version, and I'm getting errors in my project:

  1. Unreferenced formal parameter in MockObject?.h(35) - can be solved by removing the name (d) of the parameter.
  2. Unreferenced formal parameter in CallHandler?.h(56) - same solution
  3. Unreferenced formal parameter in FastDelegate?.h(1039) - same solution
  4. l-value specifies const object in Comparable.h(63) - I don't know how to get around it. The method I'm trying to mock is virtual int GetInt(const std::wstring & configItem, const int defaultValue) = 0; and the line setting the mock is mockConfiguration.Method(&Configuration::GetInt).Expect<0>(std::wstring("Item")).Expect<1>(10).Will(15);
Comment by rdescartes, Mar 21, 2008

I fixed the first 3 warning... i will put it in the current dev. build later.

But i tried the #4:

// Bug Test
//------------------------------------------------------------------
TEST(MockObjectMethodExpectConstInt)
{
    TMockObject<IInterface> mock;

    mock.Method(&IInterface::GetInt).Expect<0>(std::wstring(L"Item")).Expect<1>(10).Will(15);

    CHECK_EQUAL(15, ((IInterface*)mock)->GetInt(std::wstring(L"Item"), 10));
}

But i get no warning and error.. can you give me more information??

Comment by onfreund, Mar 22, 2008

Hmm, the only thing I can think of is that I'm using VS2003 (I believe you're using 2005).

Comment by onfreund, Mar 23, 2008

If it helps, here is the full error from VS:

Comparable.h(63) : error C2166: l-value specifies const object
        Comparable.h(62) : while compiling class-template member function 'void amop::Detail::TComparableImp<From,To>::Assign(const amop::any &)'
        with
        [
            From=int,
            To=const ToType
        ]
        Comparable.h(106) : see reference to class template instantiation 'amop::Detail::TComparableImp<From,To>' being compiled
        with
        [
            From=int,
            To=const ToType
        ]
        ReturnMatchBuilder.h(74) : see reference to function template instantiation 'amop::Detail::TComparable amop::Detail::TComparable::MakeCompare<const ToType,T>(From)' being compiled
        with
        [
            T=int,
            From=int
        ]
        ReturnMatchBuilder.h(100) : see reference to function template instantiation 'amop::Detail::TComparable amop::TReturnMatchBuilder<F>::SetNormal::Set<1,T>(T)' being compiled
        with
        [
            F=int (__thiscall Configuration::* )(const std::wstring &,const int),
            T=int
        ]
        Test.h(41) : see reference to function template instantiation 'amop::TReturnMatchBuilder<F> amop::TReturnMatchBuilder<F>::Expect<1,int>(T)' being compiled
        with
        [
            F=int (__thiscall Configuration::* )(const std::wstring &,const int),
            T=int
        ]
Comment by rdescartes, Mar 23, 2008

Hi onfreund, i dont have the 2003 in my hand now, but i guess i know the problem, Please try to download the newer 0.31.2 , and test it ...

And may i have your email??

Comment by pcwood, Mar 25, 2008

Hi, really like what I'm seeing. Great idea to use the ABI. Obvious and genius at the same time.

Could you add an operator to select the functions rather than needing a cast. i.e.

template <class T>
class TMockObject : public Detail::TMockObjectBase {
public:
    //...
    T* operator->() { 
        return (T*)GetVptr();
    }
    //...

This will enable calls to be made without the cast. e.g.

//------------------------------------------------------------------
TEST(MockObjectMethodMultiWithReturn)
{
    TMockObject<IInterface> mock;

    mock.Method(&IInterface::SimpleFunctionWithReturn)
        .Wills(22).Wills(11);

    CHECK_EQUAL(22, mock->SimpleFunctionWithReturn());
    CHECK_EQUAL(11, mock->SimpleFunctionWithReturn());
}
Comment by rdescartes, Mar 25, 2008

Hi pcwood, i am very happy that someone think this library good.

For the -> operator, i thought of it before, but i finally decided that i wont add that. Because the "Real" usage of the TMockObject wont call the method directly. Normally you will call the method by setting the TMockObject inside it and then test it behavior. Like this:

class IPersist
{
    virtual ~IPersist() = 0;
    virtual bool Write(TFile* file) = 0;
};

//------------------------------------------------------------------
TEST(FileWriterTest)
{
    TMockObject<IPersist> mock;
    File file;

    mock.Method(&IPersist::Write).Expect<0>(&file).Will(true);
   
    file.Write();    
}

In this test case, the method wont be called directly. So that's why i haven't added the ->operator.

Comment by pcwood, Mar 25, 2008

Yes I thought that after I'd posted. It's not needed, only in the test cases for the mock itself. I'll leave the comment in case anyone else thinks the same. Thanks for getting back.

Comment by onfreund, Mar 29, 2008

Hmm, I tried and the new version and now I have this: ` Test.cpp(0) : fatal error C1001: INTERNAL COMPILER ERROR

(compiler file 'msc1.cpp', line 2701)
Please choose the Technical Support command on the Visual C++ Help menu, or open the Technical Support help file for more information
Test.cpp `

Comment by rdescartes, Mar 30, 2008

ICE??...anyway thanks onfreund, i will install a VS2003 and then test it..thanks.

Comment by onfreund, Apr 01, 2008

Hi. Did you get a chance to test it?

Comment by rdescartes, Apr 01, 2008

Sorry, this week i am very busy, i will test it later.

Comment by our.spare.account, Apr 02, 2008

Hi - Any news on a Linux version?

Comment by rdescartes, Apr 02, 2008

Oh, currently it is supported, please go to BuildInstructions for more details

Comment by our.spare.account, Apr 02, 2008

Thanks. Progressing, but get this error on GCC: "enumerator value for `Result' not integer constant" - as far as I can see on this line: enum { Result = ((sizeof (Convertor(MakeFrom??())) == sizeof(Yes))) };

Comment by rdescartes, Apr 02, 2008

Could u mind to give me more information about that?? What gcc version you are using ?? and how to that error occur?? from building the code or using AMOP in you own code?

Comment by our.spare.account, Apr 03, 2008

Sure.

gcc version 3.2.3 20030502 (Red Hat Linux 3.2.3-59)

The Code:

#include <stdio.h>
#include <string.h>
#include <fstream>
#include "UnitTest++.h"
#include "XmlTestReporter.h"
#include "MockObject.h"
 
using namespace amop;
class Interface
{
public:
    virtual void VoidMethod         () = 0;
    virtual int  ReturnIntNoParams  () = 0;
    virtual int  ReturnIntWithParams() = 0;
};
	
SUITE(TheTestSuite)
{
    TEST(TheTestCase)
    {
        TMockObject<Interface> mock;
        mock.Method(&Interface::ReturnIntNoParams)
            .Will(1);
    }
}
 
static void RunUnitTests()
{
    std::ofstream xmlFile("TestResult.xml");
    UnitTest::XmlTestReporter reporter(xmlFile); 
    UnitTest::RunAllTests(reporter, UnitTest::Test::GetTestList(), NULL, 0);
}
 
int main()
{
    RunUnitTests();
    return 0;
}
Comment by our.spare.account, Apr 03, 2008

Which produces:

../amop/include/ReturnMatchBuilder.h: In instantiation of 'amop::Detail::IsConvertible<int, int>':

../amop/include/ReturnMatchBuilder.h:34: instantiated from 'amop::TReturnMatchBuilder<F> amop::TReturnMatchBuilder<F>::Will(T) [with T = int, F = int (Interface::*)()]' main.cpp:35: instantiated from here

../amop/include/ReturnMatchBuilder.h:34: invalid use of undefined type 'struct amop::Detail::IsConvertible<int, int>'

../amop/include/Comparable.h:13: declaration of 'struct amop::Detail::IsConvertible<int, int>'

../amop/include/ReturnMatchBuilder.h:34: invalid use of undefined type 'struct amop::Detail::IsConvertible<int, int>'

../amop/include/Comparable.h:13: declaration of 'struct amop::Detail::IsConvertible<int, int>'

../amop/include/ReturnMatchBuilder.h:34: enumerator value for 'Result' not integer constant

../amop/include/ReturnMatchBuilder.h: In member function 'amop::TReturnMatchBuilder<F> amop::TReturnMatchBuilder<F>::Will(T) [with T = int, F = int (Interface::*)()]': main.cpp:35: instantiated from here

../amop/include/ReturnMatchBuilder.h:34: creating array with size zero ('-1')

Comment by our.spare.account, Apr 03, 2008

Just tested with gcc version 3.4.6 20060404 (Red Hat 3.4.6-3) and compiles ok.

Comment by rdescartes, Apr 03, 2008

I just test it in gcc 4.1.3, it is ok too..

Maybe there are some template related feature not supported in the older gcc version.

Comment by our.spare.account, Apr 03, 2008

I am stuck with v3.2.3 though so will have to find a workaround...

Comment by rdescartes, Apr 03, 2008

I am not a linux guy, so my knowledge of it is very limited, but is it possible install 2 different version in the same environment??

If not possible, is there any free site i can access for test it?

Comment by our.spare.account, Apr 03, 2008

Sorry, don't know of anywhere to test Linux.

Another error I'm getting with GCC 3.2.3:

../amop/source/Comparable.h:138: return-statement with a value, in function declared with a void return type

Comment by rdescartes, Apr 03, 2008

Thanks, i just fixed in SVN. i will make a release in coming days.

Comment by rdescartes, Apr 03, 2008

Hi onfreund, i just added an VS2003 project and Solution in the SVN Trunk, and i tested it, all tests can build.

Then could you mind write a simple unittest for the error and post it in here??

Comment by onfreund, Apr 03, 2008

Very strange, I can't seem to reproduce it either. It used to happen consistently, but after restarting my computer it's gone. Sorry you went through all this trouble.

Comment by onfreund, Apr 06, 2008

Another feature request, I would like to be able to mock throwing an exception, e.g. mockFile.Method(&File::Close).Throws(FileCloseException("message"));

Comment by rdescartes, Apr 07, 2008

Ok , I just added that in the issue page, i will add that later.

Comment by onfreund, Apr 07, 2008

Thanks, I added a comment there.

Comment by Vlad3D, Apr 10, 2008

Is there any way to output the expected and actual values when a comparison fails? It would be nice if the TNotEqualException had string expected and actual values instead of the "any" type so that when the exception is caught its values could be displayed.

Awesome framework by the way!

Comment by rdescartes, Apr 10, 2008

Hi Vlad3D, that's good idea of adding that strings in the exception, let me add in the issue page.

Comment by rdescartes, Apr 10, 2008

Oh , sorry, after a deeper thinking, although i can use template class to throw the exception, but you still know the type to catch it, right?

e.g:

try 
{
  ((IInterface*)mock)->Foo(1); // It will throw a TNotEqualException<int>
}
catch( ??? e)
{
  std::cout << e.Actual << std::endl;
}

The ??? is the type u catch, but if you know the type of the template, how about just casting the "any" to the that type, it is easier, right?

Comment by Vlad3D, Apr 11, 2008

Yeah, I also thought about just outputting the contents of the "any" type, but there could be a problem if the type is a pointer to something on the stack - it might have been destructed by the time the exception is caught. I guess you could make a copy, but that might not work with all types.

I figured that it might be easier to just output the values to a string stream with operator << and then the user could provide user defined operator << for custom types.

Comment by rdescartes, Apr 12, 2008

Could you mind to write a simple test case which i can follow?

Comment by Vlad3D, Apr 12, 2008

Sure. Here's a possible scenario:

class IBar
{
public:
   ~IBar();
   void bar(int* pi) = 0;
};

class Foo
{
public:
   Foo(IBar* pBar) : m_pBar(pBar) {}

   void foo(int a)
   {
      int b = a * a;
      m_pBar->bar(&b);
   }

private:
   IBar* m_pBar;
};

TEST(FooTest)
{
   TMockObject<IBar>   mockBar;
   Foo                 testFoo(mockBar);

   mockBar.Method(&IBar::bar).Expect<0>(Policy::Pointer(81));

   try
   {
      testFoo.foo(8);
   }
   catch (TNotEqualException e)
   {
      cout << *any_cast<int*>(e.actual());  // oops, actual value is now gone!
   }
}

The other thing is sometimes there are a lot of different parameter types so you'd have to try to cast the any to each of the possible types, but that's not a big issue.

Comment by rdescartes, Apr 14, 2008

thanks so much, i think i can change the policy for Pointer and other policy , before the exception is throwing, it will copy the value to the exception (not only the pointer ). But i think it cant solve all problems related on that.

On the other hand, i can use the ostream << operator to output the value to exception. But i think it cant solve the problem too, because it will change all value being checked (using the TMockObject::Expect) have to have the ostream << operator...

It is because there are no way to check a type whether is supporting that operator in compile time. (Maybe there are a way to check...i just know how to do it C++0x...:P )

So i have to think deeper on this issue first..Or do you have any other idea on that?

Comment by wangfeideyouxiang, May 11, 2008
//the base calss
class CB
{
public:
	CB();
	~CB();
protected:
	int error;
};
class CD:public CB
{
public:
	CD();
	~CD();
	//result is the real resualt, 
	//the return value is controled by CB::errCode
	virtual	int add(double a, double b, double &result);
	virtual	int mul(double a, double b, double &result);
};

//under test method,calculate (a+b)*b + b
int complexCal(CD *pCal,double a, double b,double &dResult)
{
	double result;
	pCal->add(a, b, &result);//a+b
	if(pCal->error)
	{
		return -1;//failed
	}

	pCal->mul(result, b, &result);//(a+b)*b
	if(pCal->error)
	{
		return -1;//failed
	}
	pCal->add(result, b, &result);//(a+b)*b + b
	if(pCal->error)
	{
		return -1;//failed
	}
	dResult = result;
	return 0;
}

TEST(testComplexCal)
{
	TMockObject<CD> mock; 
	CD *pCal = (CD*)mock;
	double a =1;
	double b = 2;
	double respectResult = 6;
	int	   respectReturn = 0;
	double realResult = 6;
	int	   realReturn = 0;
	//mock.Method(&calcProxy::add).Will(3);
	//mock.Method(&calcProxy::mul).Will(6);

}
My question:
	1.how to control the result value of method add in complexCal?
	2.invoke the method add two times, how to control the return value and the result every time?

Thank you~~~
Comment by rdescartes, May 11, 2008

Hi wangfeideyouxiang,

1. Currently amop only support pure abstract interface testing, which means the class used in TMockObject<T> have to be no data member. 2. Use Will method will return the default result value, if you want to control the specific values in each time, use Wills instead.

PS: i am Chinese in HK , so you can use chinese in here too:)

Comment by onfreund, May 12, 2008

1. This is actually a good thing. This could be a sign that you're using inheritance for the wrong reason. 2. I think that for the benefit of others (such as myself), we should stay with English :)

Comment by wangfeideyouxiang, May 13, 2008

Thanks a lot, now i counter the same problem as Vlad3D because I want to control the value of result in function add. PS:i am in shenzhen_.

Comment by rdescartes, May 14, 2008

the problem of Vlad3d is getting the value of the input argument. But what is your case ?? can you write me down a sample test case ??

Comment by wangfeideyouxiang, May 16, 2008
I have solve this problem, because I didn't understand the meaning of "Sets" and "Expects".  
If I want test this function:      
   virtual int add(double a, double b, double *result);  
   1. the following code will thow exception.        
	   mock.Method(&calcProxy::add)                
	   .Expects<0>(1)	   .Expects<0>(3)                
	   .Expects<1>(2)      .Expects<1>(2)                
	   .Expects<2>(Policy::Pointer(3.0))       .Expects<2>(Policy::Pointer(4))                
	   .Wills(0)                               .Wills(-1);  
   2. This following code will thow exception too, because lack i input.        
	   mock.Method(&calcProxy::add)                
	   .Sets<0>(1)	   .Sets<0>(3)                
	   .Sets<1>(2)     .Sets<1>(2)                
	   .Sets<2>(Policy::Pointer(3.0))  .Sets<2>(Policy::Pointer(4));    
   3. This following code is correct.        
	   mock.Method(&calcProxy::add)                
	   .Sets<0>(1)                                             .Sets<0>(3)                
	   .Sets<1>(2)                                             .Sets<1>(2)                
	   .Sets<2>(Policy::Pointer(3.0))  .Sets<2>(Policy::Pointer(4))                
	   .Wills(0)                                               .Wills(-1);   
   I suggest rdescartes to add note for every function and samples,this c++ mock frame is very simple for user, I think if the relative docs is good,more and more users will use~~~~
Comment by mgsram, May 19, 2008

Hi, I m having difficulty in using AMOP with COM style interfaces. Suppose the following is the interface

struct declspec(uuid("793DAC59-430B-4A44-AE80-36C2E0A0B507")) declspec(novtable) IView : public IDispatch { public:

virtual HRESULT STDMETHODCALLTYPE DisplayMessage?(BSTR message) = 0;
};

and when I create a mock like this -

TMockObject<IMyInterface> mock; mock.Method(&IMyInterface::DisplayMessage?);

I get compilation errors which goes like - error C2027: use of undefined type 'amop::Detail::Functor<T>' with [

T=long (stdcall IMyInterface:: )(BSTR)
]

and it goes on like that. I have been able to isolate the error to the calling convention. If I remove STDMETHODCALLTYPE, everything works fine. Is there any work around to this ? I tried compiling with /Gz to use the stdcall calling convention, but that did not work. So is there anyway to make this work?

Thanks for your help Sriram

Comment by mgsram, May 19, 2008

That did not show up properly. Here is the formatted post Hi, I m having difficulty in using AMOP with COM style interfaces. Suppose the following is the interface

struct __declspec(uuid("793DAC59-430B-4A44-AE80-36C2E0A0B507")) __declspec(novtable)
IMyInterface : public IDispatch
{
public:
   virtual HRESULT STDMETHODCALLTYPE DisplayMessage(BSTR message) = 0;
};

and when I create a mock like this -

      TMockObject<IMyInterface> mock;
      mock.Method(&IMyInterface::DisplayMessage);

I get compilation errors which goes like -

d:\arena\amop\source\FunctionHolder.h(18) : error C2027: use of undefined type 'amop::Detail::Functor<T>'
        with
        [
            T=long (__stdcall IMyInterface::* )(BSTR)
        ]
        d:\arena\amop\source\CallHandler.h(25) : see reference to class template instantiation 'amop::Detail::FunctionHolder<I,T>' being compiled
        with
        [
            I=0,
            T=long (__stdcall IMyInterface::* )(BSTR)
        ]
        d:\arena\amop\source\CallHandler.h(23) : while compiling class template member function 'amop::Detail::TFunctionAddress amop::Detail::CallHandler::SelectID<I>::Get<T>::Select(size_t)'
        with
        [
            I=0,
            T=long (__stdcall IMyInterface::* )(BSTR)
        ]
        d:\arena\amop\source\CallHandler.h(36) : see reference to class template instantiation 'amop::Detail::CallHandler::SelectID<I>::Get<T>' being compiled
        with
        [
            I=0,
            T=long (__stdcall IMyInterface::* )(BSTR)
        ]
        d:\arena\amop\source\CallHandler.h(42) : see reference to function template instantiation 'amop::Detail::TFunctionAddress amop::Detail::CallHandler::Select<T>(size_t)' being compiled
        with
        [
            T=HRESULT (__stdcall IMyInterface::* )(BSTR)
        ]
        d:\arena\amop\source\MockObjectBase.h(92) : see reference to function template instantiation 'amop::Detail::TFunctionAddress amop::Detail::CallHandler::Create<F>(size_t)' being compiled
        with
        [
            F=HRESULT (__stdcall IMyInterface::* )(BSTR)
        ]
        d:\arena\amop\source\MockObject.h(30) : see reference to function template instantiation 'amop::TReturnMatchBuilder<F> amop::Detail::TMockObjectBase::CreateMatchBuilder<F,T>(F)' being mpiled
        with
        [
            F=HRESULT (__stdcall IMyInterface::* )(BSTR),
            T=IMyInterface
        ]
        d:\arena\amop\source\Test.h(25) : see reference to function template instantiation 'amop::TReturnMatchBuilder<F> amop::TMockObject<T>::Method<HRESULT(__stdcall IMyInterface::* )(BSTR)>)' being compiled
        with
        [
            F=HRESULT (__stdcall IMyInterface::* )(BSTR),
            T=IMyInterface
        ]
d:\arena\amop\source\FunctionHolder.h(18) : error C2146: syntax error : missing ',' before identifier 'ReturnType'
d:\arena\amop\source\FunctionHolder.h(18) : error C2065: 'ReturnType' : undeclared identifier
d:\arena\amop\source\FunctionHolder.h(18) : error C2955: 'amop::Detail::IsEqual' : use of class template requires template argument list
        d:\arena\amop\source\Config.h(33) : see declaration of 'amop::Detail::IsEqual'
d:\arena\amop\source\FunctionHolder.h(19) : fatal error C1903: unable to recover from previous error(s); stopping compilation
Comment by rdescartes, May 19, 2008

Currently AMOP only support cdecl... Um.. let me try your test-case first, and see there are any solution about that..

Comment by mgsram, May 20, 2008

Let me know if I can help in any way. Thanks for your help.

Comment by rdescartes, May 20, 2008

I added your test-case and i try to find a workaround, but AMOP depend on cdel behavior (i.e. caller clean up the stack) to find out the offset of a function. (it is the main magic of AMOP no-interface implementation mechanism.) So it is hard to support stdcall...

But..here is a fast hack... just define this in the beginning of your test-case .cpp

#define __stdcall

It will work like a magic :)

Comment by mgsram, May 20, 2008

Perhaps, I should have provided a better example. I have already tried (#define STDMETHODCALLTYPE ) doing this. It works for the example that I provided. Not for the actual project as the interface definition in the actual project comes from a auto generated .h file from MIDL compiler. And not only that, since there are lots of ATL base classes involved, the #define has to preclude any header includes. I tried this just now once again and I am seeing lots of errors due to calling convention mismatch. In short, it is not easily possible for me to go this route.

I do understand the need for cdecl. So right now, i m using poor man's mock. Hopefully we can come up with some solution.

Comment by wangfeideyouxiang, May 30, 2008
Now, I want to mock function SimpleFunctionStuct, but whose parameter PC don't implement the == operator,
I cann't control the code of PC, because it is  generated automatically.
how I can test this case with AMOP,
thank you~~~

class PC
{
public:
	int i;
public:
	//bool operator==(const PC& oc)const
	//{
	//	return true;
	//}
};


virtual void SimpleFunctionStuct(PC *p) = 0;


//------------------------------------------------------------------
TEST(MockObjectMethodSimpleFunctionStuct)
{

    TMockObject<IInterface> mock;
	PC op;
	mock.Method(&IInterface::SimpleFunctionStuct)
		//.Set<0>(Policy::Pointer(op));//failed to compile, must implement ==
		.Set<0>(&op);//it will crash
		
	PC op2;
	((IInterface*)mock)->SimpleFunctionStuct(&op2) ;

}
Comment by rdescartes, May 30, 2008

How about a free function version of operator==??

   bool operator==(const PC& pc1, const PC& pc2)
   {
      return True??
   }
Comment by wangfeideyouxiang, May 30, 2008
It can complie sucessfully,
but it occurs the following error when running,
"Run-Time Check Failure #3 - The variable 'op' is being used without being defined."
Comment by wangfeideyouxiang, May 30, 2008
 I have solve the problem, because i didn't initialize the variable i;
        PC(){ i = 0;}; 

 now i have anther problem, whether it effect the test result if i implement operator== simplely?
Comment by rdescartes, May 30, 2008

the == operator is used for checking whether 2 objects are equal. In your case, you should check whether op2 are equal to op.

Comment by j...@gavia.dk, May 30, 2008

I will suggest a rewrite of the MockObjectMethodVerifyExpects? test

to

    mock.Method(&IInterface::SimpleFunctionWithParams)
        .Expects<0>(1.0f).Expect<1>("").Expect<2>("")
        .Expects<0>(2.0f)
        .Expects<0>(3.0f);

This places the arguments in the order they are later seen in the code, which makes the test more instructive as example.

Comment by j...@gavia.dk, May 30, 2008

I am trying to mock an call to operator(), but then it crashes.

I am extending the IInterface with

virtual int  operator()(int const & a, int const & b) const = 0;

I am testing this with:

TEST(MockObjectOperatorVerifyExpects)
{		
    TMockObject<IInterface> mock;

    mock.Method(&IInterface::operator())
      .Expects<0>(1).Expect<1>(2)
      .Expects<0>(10).Expect<1>(20)
      ;

    IInterface & callFace(*((IInterface*)mock));
    callFace(1, 2);
    callFace(10, 20);

    // It will not throw
    mock.Verify();

    callFace(100, 200);
    CHECK_THROW(mock.Verify(), TCallCountException);
}

This is on GCC 4.2.1

Comment by j...@gavia.dk, May 30, 2008

descartes, if you are looking for shell acconts to build/test on Linux, you should consider sourceforge, they have buildfarms for many platforms.

Comment by eaimounir, Jun 04, 2008

Hello, I'm starting with amop(Newb), but I have some difficults to understand. Can you add comments to explain the utility of each method? Is it possible to mock a class with implementation(no abstract class)? Is it possible to mock the class tested? Thank you!

Comment by rdescartes, Jun 06, 2008

hi j...@gavia.dk, it is not a bug in amop, the current test case should be like this:

//------------------------------------------------------------------
TEST(MockObjectOperatorVerifyExpects)
{               
    TMockObject<IInterface> mock;

    mock.Method(&IInterface::operator())
        .Expects<0>(1).Expects<1>(2).Will(0)
        .Expects<0>(10).Expects<1>(20).Will(0)
        ;

    IInterface & callFace(*((IInterface*)mock));
    callFace(1, 2);
    callFace(10, 20);

    // It will not throw
    mock.Verify();

    callFace(100, 200);
    CHECK_THROW(mock.Verify(), TCallCountException);
}

Because your operator have return value, you have to tell TMockObject to return some value by using will.

And TMockObject::Expects and TMockObject::Expect cannot be used at the same time.

Comment by rdescartes, Jun 06, 2008

hi eaimounir,

1. Basically, i want to make some tutorial of whole library, but there are some difficults for me, the main part is my english is bad and this project is my spare time project... but i will try to write some tutorial in coming. i promised. ;)

2. AMOP is only used for pure abstract class now. Although i am thinking of make it work in partially with implementation. But right now, the answer is no, sorry.

Comment by j...@gavia.dk, Jun 09, 2008

Thanks a lot descartes, now I have changed the test to

TEST(MockObjectOperatorVerifyExpects)
{		
    TMockObject<IInterface> mock;

    mock.Method(&IInterface::operator())
      .Expects<0>(1).Expects<1>(2).Will(142)
      .Expects<0>(10).Expects<1>(20).Will(242)
      ;

    IInterface & callFace(*((IInterface*)mock));
    CHECK_EQUAL(142, callFace(1, 2));
    CHECK_EQUAL(242, callFace(10, 20));

    // It will not throw
    mock.Verify();

    callFace(100, 200);
    CHECK_THROW(mock.Verify(), TCallCountException);
}

But this fails with Failure in MockObjectOperatorVerifyExpects: Expected 142 but was 242. It seems like it is only recording the last "Will" value. I have even tried to split the two expectations (including Will) into two statements, but that didn't change anything.

BTW: It would be nice if AMOP could give a decent error message, when Expect was used opposed to Expects+Will, in stead of crashing.

Jarl

Comment by j...@gavia.dk, Jun 09, 2008

I changed the mock statement from

    mock.Method(&IInterface::operator())
      .Expects<0>(1).Expects<1>(2).Will(142)
      .Expects<0>(10).Expects<1>(20).Will(242)
      ;

to

    mock.Method(&IInterface::operator())
      .Expects<0>(1).Expects<1>(2).Wills(142)
      .Expects<0>(10).Expects<1>(20).Will(242)
      ;

And that made it work. Could you add this test case to the file TestMockObject?.cpp? Primarily because it will serve as documentary example.

Again, if I change both Will to Wills, it crashes.

It is not very natural to use Expects versus Expect and Wills versus Will just because it is not the last mock call, and if you pick the wrong one it either does not behave as expected or it crashes.

Is there anyway that the API could be changed to only have one method in stead of two methods depending on whether something is comming after or not? I imagine that the introduction of two methods Expects and Expect and similar for Will(s) is due to some technical coding issues, not because it makes the API more intuitive.

What are the coding challenges in providing such an API with only one method named Expect and Will respectively?

Could you elaborate a bit on this descartes?

Jarl

Comment by rdescartes, Jun 09, 2008

Hi Jarl, 1. i totally agree that Expect & Expects and Will & Wills is mis-leading. My original ideal is something like this

// For all call
mock.Method(&IInterface::operator())
     .Expect<0>(1).Expect<1>(2).Will(142);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(142, callFace(1, 2) );

mock.Clear();

// For each calls
mock.Method(&IInterface::operator())
     .Expect<0>(1,2,3).Expect<1>(2,4,6).Will(142, 143,144);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(143, callFace(2, 4) );
CHECK_EQUAL(144, callFace(3, 6) );

It make all things clear, right? but when i try following test-case:

// Just for one time!!!
mock.Method(&IInterface::operator())
     .Expect<0>(1).Expect<1>(2).Will(142);

CHECK_EQUAL(142, callFace(1, 2) );

 // It will not throw
mock.Verify();

callFace(100, 200);
// I want it will throw, but this test will fail..
CHECK_THROW(mock.Verify(), TCallCountException);

The problem here is , i dunno what i should use for when the "each call" version only for one time. So i add the Expects and Wills version. The other method for this problem in other Mock Library is doing something like:

// For all call
mock.Method(&IInterface::operator())
     .Expect<0>(Default(1)).Expect<1>(Default(2)).Will(Default(142));

But i think the "for all call" version is more often then the "for each all" one. So i chose current form. But if you have any better idea, pls tell me.

Comment by rdescartes, Jun 09, 2008

2. >>> BTW: It would be nice if AMOP could give a decent error message, when Expect was used opposed to Expects+Will, in stead of crashing. >>>

Um... In fact, it is not crashing... it just throw an exception. It is better to make the exception inherit from std::exception to let UnitTest?++ to show that.

3. The exception thrown when you use both wills, is because the Wills only handle each calls, means when you call callFace(100, 200), there are no suitable return value for that call, so it throw a TCallCountException.

Comment by j...@gavia.dk, Jun 09, 2008

It make all things clear, right?

I understand what you mean. In the first case ("for all call") it shall mock and verify arguments an infinite number of times, and shall not care about number of calls, whereas in the other case ("for each call") the number of calls must match exactly, and each call has distinct arguments that it shall verify against, and distinct return values it shall mock.

However, the syntax for multiple "for each call" as you show

// For each calls
mock.Method(&IInterface::operator())
     .Expect<0>(1,2,3).Expect<1>(2,4,6).Will(142, 143,144);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(143, callFace(2, 4) );
CHECK_EQUAL(144, callFace(3, 6) );

is not at all natural/intuitive. First of all the numbers are read in different order than they will show up in client code calling the mock object, this is also mentioned in my comment May 30, 2008.

I will suggest that both the "for all call" situation and the "for each call" situation all takes exactly one argument (no more), this will of course force the use of "for each call" into code like this:

// For each calls
mock.Method(&IInterface::operator())
     .Expect<0>(1).Expect<1>(2).Will(142)
     .Expect<0>(2).Expect<1>(4).Will(143)
     .Expect<0>(3).Expect<1>(6).Will(144);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(143, callFace(2, 4) );
CHECK_EQUAL(144, callFace(3, 6) );

but that is a huge readability advantage, each line of mocking correspond to each call done later on the mock object.

This makes the mock code much more clear. Now every use of "for each call" mocking is now in the problematic category as you mention: i dunno what i should use for when the "each call" version only for one time

And to that I will suggest the following differentiation. In the case "for each call" I will keep a syntax similar to the one just illustrated. But to mock "for all call" I will suggest the following syntax:

// For all call
mock.All(&IInterface::operator())
     .Expect<0>(1).Expect<1>(2).Will(142);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(142, callFace(1, 2) );

The differentiation is now made very clear very early in the code.

Regards implementaiton. There are several options for implementing this. Either the return type TReturnMatchBuilder<F> could be extended with an extra bool template parameter differentiating the two ways to mock, providing a compile time different implementation (fastests), or TReturnMatchBuilder<F> could contain a bool member indicating the difference. This will make the distinction at runtime.

Back to useage syntax, now that I think about it, I may even prefer the following even more verbose distinction between the two ways to mock:

// For all call
mock.All(&IInterface::operator())
     .Expect<0>(1).Expect<1>(2).Will(142);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(142, callFace(1, 2) );

mock.Clear();

// For each calls
mock.Single(&IInterface::operator())
     .Expect<0>(1).Expect<1>(2).Will(142)
     .Expect<0>(2).Expect<1>(4).Will(143)
     .Expect<0>(3).Expect<1>(6).Will(144);

CHECK_EQUAL(142, callFace(1, 2) );
CHECK_EQUAL(143, callFace(2, 4) );
CHECK_EQUAL(144, callFace(3, 6) );

BTW: In other literature the "for all call" mock object is called a stub, where as the "for each call" mock object is called a mock object.

Jarl

Comment by j...@gavia.dk, Jun 09, 2008

descartes writes:

It is better to make the exception inherit from std::exception to let UnitTest++ to show that.

Yes, that will definitely be an improvement.

Comment by j...@gavia.dk, Jun 09, 2008

If you plan to change the API as suggested, I will further encourage you to rename Will/Wills to Return for two reasons:

  1. It actually states the return value of the mocked method call
  2. It will align this mock framework method naming with other mock frameworks

Jarl

Comment by rdescartes, Jun 09, 2008

It is what i want!!

I will changed the API as your suggestion in 4.0. And the old one will be abandoned.

On the other hand, i think the documentation of AMOP need to be have a big improvement too...

Comment by j...@gavia.dk, Jun 10, 2008

I even think that if you add the extra bool template parameter to TReturnMatchBuilder<F> to diffenrentiate the return types of All() and Single(), it must be possible to make compile-time errors for invalid constructs such as

mock.All(&IInterface::operator())
     .Expects<0>(1).Expects<1>(2).Returns(142)
     .Expects<0>(10).Expects<1>(20).Returns(143)
     ;

or (Count should only be valid on type returned by All, not Single)

mock.Single(&IInterface::operator())
     .Count(3);

And even enforce correct parameterorder such that the following construct becomes invalid (and gives compile error):

mock.All(&IInterface::operator())
     .Expects<1>(2).Expects<0>(1).Returns(142)
     ;

I would even like to see the framework support the following syntax:

mock.All(&IInterface::operator())
     .Expects(1,2).Returns(142);

in stead of

mock.All(&IInterface::operator())
     .Expects<0>(1).Expects<1>(2).Returns(142);

because that is how other mock frameworks work, and recognition makes the learning much easier.

Further, what do you think of changing the API for querying information on the mocked methods? That is, changing

    CHECK_EQUAL(3, mock.Method(&IInterface::ComplexFunction).Count());

to

    CHECK_EQUAL(3, mock.Query(&IInterface::ComplexFunction).Count());

So there will then be three main methods on MockObject?:

  1. Single
  2. All
  3. Query

>>> I will changed the API as your suggestion in 4.0

You probably mean version 0.4

>>> On the other hand, i think the documentation of AMOP need to be have a big improvement too...

You should really consider Doxygen for making a API reference, anyway I can maybe help writing a tutorial similar to TestMockObject.cpp but more verbose for version 0.4

Jarl

Comment by j...@gavia.dk, Jun 10, 2008

Amop produces some minor warnings when compiling with all warnings on GCC. I would like to contribute a patch to fix this. How is this best done?

Jarl

Comment by rdescartes, Jun 10, 2008

1. Yes, version 0.4 :) 2. I like the Query one 3. You can post the patch in google group, or email me. thanks a lot.

Comment by onfreund, Jun 25, 2008

Hi,

I can't seem to get the exception mocking to work. I'm trying both Throw() and Throws but none of them causes the mock to throw any exception (the scenario is equivalent to the simple test case and I'm using VS2003).

Comment by rdescartes, Jun 26, 2008

But i tried that in my VS2003 and it works (at least it works in the test case in testMockObject)

Comment by wangfeideyouxiang, Jun 26, 2008
 Hi, I want to test a inferface that contains overload fuction as follows, it cannot compile, thank you.
  
 	
	virtual int f1(int iTypeId, int a)
	{
		return 0;		 
	}
	virtual int f1(int iTypeId, int a, int b)
	{
		return 0;	 
	}
	
	
	\mytest.cpp(227) : error C2228: “.Set”的左侧必须有类/结构/联合类型
	\mytest.cpp(228) : error C2228: “.Set”的左侧必须有类/结构/联合类型       
	\mytest.cpp(229) : error C2228: “.Will”的左侧必须有类/结构/联合类型
        
Comment by rdescartes, Jun 26, 2008

Hi wangfeideyouxiang, could you mind to write down your whole test case ?

Comment by wangfeideyouxiang, Jun 26, 2008
class MyInterface
{
public:
	virtual int f1(int iTypeId, int a);
	virtual int f1(int iTypeId, int a,int c);
	virtual ~MyInterface(){}
};
TEST(MyInterface_F1)
{ 
	TMockObject<MyInterface>   mock;   
	MyInterface *p =  (MyInterface *)mock;
	mock.Method(&MyInterface::f1)
		.Set<0>(1)
		.Set<1>(2)
		.Will(3);
	int iTypeId = 22,a=33,iRet=0;
	iRet =p->f1(iTypeId,a);	
}
Comment by onfreund, Jun 27, 2008

I'm failing this simple test:

void testSimpleThrow( void )
{
	amop::TMockObject<File> mock;
	mock.Method(&File::Delete).Throws(std::bad_exception("test"));
	TS_ASSERT_THROWS(((File *)mock)->Delete(), std::bad_exception);
}

What am I doing wrong?

Comment by rdescartes, Jun 27, 2008

oic, i find that part has a bug when your function (i.e. File::Delete) return type is void, the exception will not be thrown. We should fixed that in later release.

Comment by onfreund, Jun 27, 2008

OK. Thanks.


Sign in to add a comment