Is CppUnit un-C++

Discussion in 'C++' started by Steven T. Hatton, Sep 4, 2004.

  1. I finally got this thing to build. There's something to be said for using
    the release of the cvs image sometimes. :-/

    I started reading the docs, and this example struck me as a fundamentally
    bad design for C++. Perhaps it's not bad design in the sense that it will
    fail, or that it can't be maintained. But there seems to be something
    fundamentally un-C++ about this. Does anybody else see what I'm talking
    about here?

    class ComplexNumberTest : public CppUnit::TestFixture {
    private:
    Complex *m_10_1, *m_1_1, *m_11_2;
    public:
    void setUp()
    {
    m_10_1 = new Complex( 10, 1 );
    m_1_1 = new Complex( 1, 1 );
    m_11_2 = new Complex( 11, 2 );
    }

    void tearDown()
    {
    delete m_10_1;
    delete m_1_1;
    delete m_11_2;
    }

    void testEquality()
    {
    CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
    CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
    }

    void testAddition()
    {
    CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
    }
    };
    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 4, 2004
    #1
    1. Advertising

  2. Steven T. Hatton

    David Hilsee Guest

    "Steven T. Hatton" <> wrote in message
    news:...
    > I finally got this thing to build. There's something to be said for using
    > the release of the cvs image sometimes. :-/
    >
    > I started reading the docs, and this example struck me as a fundamentally
    > bad design for C++. Perhaps it's not bad design in the sense that it will
    > fail, or that it can't be maintained. But there seems to be something
    > fundamentally un-C++ about this. Does anybody else see what I'm talking
    > about here?

    <snip>

    Yes, it looks like Java. The giveaway is the use of plain pointers and new
    instead of using local Complex objects or, at the very least, smart
    pointers. That's not surprising, because (IIRC) CppUnit was derived from
    JUnit. IMHO, it's not an indication of a bad design; it's just a bad
    example that isn't using some of the better features of C++. Is there
    something else that you don't like about it, besides the fact that it's
    overusing new and ignoring potential memory leaks?

    --
    David Hilsee
    David Hilsee, Sep 4, 2004
    #2
    1. Advertising

  3. David Hilsee wrote:

    > Yes, it looks like Java. The giveaway is the use of plain pointers and
    > new instead of using local Complex objects or, at the very least, smart
    > pointers.


    I agree that using local variables would make a lot of sense. There are
    conflicting objectives I've run into in this regard. If I do use local
    variables for class types, they require that I #include the headers
    containing their definitions in the header containing the class I'm
    defining. That is, instead of simply forward declaration. It also means I
    may introduce more compiler dependencies. I'd have to think a bit to give
    an example, but I know there can be problems caused by having class types
    as members, rather than pointers to them.

    OTOH, it makes resource management much simpler.

    > That's not surprising, because (IIRC) CppUnit was derived from
    > JUnit.


    That's what the documentation says.

    > IMHO, it's not an indication of a bad design; it's just a bad
    > example that isn't using some of the better features of C++. Is there
    > something else that you don't like about it, besides the fact that it's
    > overusing new and ignoring potential memory leaks?


    I was thinking about something that would not be an issue if the Complex
    objects were member variables. But if there were a compelling argument for
    using pointers, the way things are organized seems to neglect something
    fundamental in C++. It's a four letter acronym.

    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 4, 2004
    #3
  4. Steven T. Hatton wrote:
    > If I do use local variables for class types, they require that I #include
    > the headers containing their definitions in the header containing the class I'm
    > defining. That is, instead of simply forward declaration.


    Typically, nothing in a header file contains local variables anyway,
    except inline functions. And if you have an inline function using a
    class, presumedly you'd want to call methods on it - which would require
    the class's header anyway. This argument isn't very strong.

    > It also means I may introduce more compiler dependencies.


    Be happy if the compiler is keeping track of your dependencies instead
    of you. See the make manual for an example of code to automatically
    generate makefiles describing header dependencies. Other, more
    sophisticated build tools do this automatically.

    > ...but I know there can be problems caused by having class types
    > as members, rather than pointers to them.


    Actually I think it's preferable to include objects rather than pointers
    to them, where possible. It's often more efficient, since it avoids an
    indirection during calls, and it's less error-prone, since you don't
    have to allocate or deallocate the object (although using auto_ptr also
    suffices). The main problem is that without a pointer or reference you
    can't invoke polymorphic behaviour on data members, and every object can
    have only one place where it's stored (redundancy and its update issues
    aside).
    --
    Derrick Coetzee
    I grant this newsgroup posting into the public domain. I disclaim all
    express or implied warranty and all liability. I am not a professional.
    Derrick Coetzee, Sep 5, 2004
    #4
  5. Derrick Coetzee wrote:

    > Steven T. Hatton wrote:
    >> If I do use local variables for class types, they require that I #include
    >> the headers containing their definitions in the header containing the
    >> class I'm
    >> defining. That is, instead of simply forward declaration.

    >
    > Typically, nothing in a header file contains local variables anyway,
    > except inline functions.


    In this context it seems clear that /local/ was meant to signify 'class
    local'. That is to say class type used as members of the class being
    defined. I would not have introduced the word 'local' to describe such a
    member, but I believe it is technically correct. Add to the following that
    a class definition is a class specifier declaration:

    "So that several statements can be used where one is expected, the compound
    statement (also, and equiva-
    lently, called "block") is provided.
    compound-statement:
    { statement-seqopt }
    statement-seq:
    statement
    statement-seq statement
    A compound statement defines a local scope (3.3). [Note: a declaration is a
    statement (6.7). ]" - ISO/IEC 14882:2003§6.3

    >> It also means I may introduce more compiler dependencies.

    >
    > Be happy if the compiler is keeping track of your dependencies instead
    > of you. See the make manual for an example of code to automatically
    > generate makefiles describing header dependencies. Other, more
    > sophisticated build tools do this automatically.


    "C++ allows the programmer to expose the representation of a class as part
    of the interface. This representation may be hidden (using /private/
    or /protected/), but it is available to the compiler to allow allocation of
    automatic variables, to allow inline substitution of functions, etc. The
    negative effect of this is that the use of class types in the
    representation of a class may introduce undesirable dependencies" -
    TC++PL(SE)§24.4.2

    One example that comes to mind is when you have some kind of recursive
    reference. You end up with ODR violations.

    >> ...but I know there can be problems caused by having class types
    >> as members, rather than pointers to them.

    >
    > Actually I think it's preferable to include objects rather than pointers
    > to them, where possible. It's often more efficient, since it avoids an
    > indirection during calls, and it's less error-prone, since you don't
    > have to allocate or deallocate the object (although using auto_ptr also
    > suffices).


    Yes, that is what I meant in my previous post when I said it makes resource
    management much simpler.

    > The main problem is that without a pointer or reference you
    > can't invoke polymorphic behaviour on data members, and every object can
    > have only one place where it's stored (redundancy and its update issues
    > aside).


    You still have polymorphism as is demonstrated by the code below. I don't
    understand your comment about an object having only one location. That
    will always be true.

    #include <iostream>
    #include <string>

    class Printable{
    public:
    Printable()
    {}
    virtual ~Printable()
    {}
    virtual std::eek:stream& print(std::eek:stream& out) const = 0;
    };

    std::eek:stream& operator<<(std::eek:stream& out, const Printable& p)
    {
    return p.print(out);
    }

    class A : public Printable {

    public:
    A(const std::string& data="Class A")
    : m_data(data)
    {}

    virtual ~A()
    {}

    virtual std::eek:stream& print(std::eek:stream& out) const
    {
    out << m_data << ". Invoked on A.\n";
    }
    protected:
    std::string m_data;
    };

    class B :public A{
    public:
    B()
    : A("Class B")
    {}

    virtual ~B()
    {}

    virtual std::eek:stream& print(std::eek:stream& out) const
    {
    out << m_data << ". Invoked on B.\n";
    }
    };

    class C{
    A a;
    B b;
    public:
    A* getA_ptr()
    {
    return &a;
    }

    A* getB_ptr()
    {
    return &b;
    }
    };


    int main(){
    C c;
    A* a_ptr = c.getA_ptr();

    std::cout << "c.getA_ptr() " << *a_ptr;

    a_ptr = c.getB_ptr();
    std::cout << "c.getB_ptr() " << *a_ptr;
    }

    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 5, 2004
    #5
  6. Steven T. Hatton

    David Hilsee Guest

    "Steven T. Hatton" <> wrote in message
    news:...
    > Derrick Coetzee wrote:
    >
    > > Steven T. Hatton wrote:
    > >> If I do use local variables for class types, they require that I

    #include
    > >> the headers containing their definitions in the header containing the
    > >> class I'm
    > >> defining. That is, instead of simply forward declaration.

    > >
    > > Typically, nothing in a header file contains local variables anyway,
    > > except inline functions.

    >
    > In this context it seems clear that /local/ was meant to signify 'class
    > local'. That is to say class type used as members of the class being
    > defined. I would not have introduced the word 'local' to describe such a
    > member, but I believe it is technically correct. Add to the following

    that
    > a class definition is a class specifier declaration:

    <snip>

    In my post, I meant that they could be local to testEquality and
    testAddition. I think my wording was a bit confusing. At any rate, it
    doesn't matter much, because it would also make more sense to have them as
    members instead of pointers. IMHO, either way is better. While it is true
    that members can cause unnecessary recompilation in certain cases, I doubt
    that this will be a big problem for test code. When I want to avoid those
    problems, I usually just use the pimpl idiom.

    --
    David Hilsee
    David Hilsee, Sep 6, 2004
    #6
  7. Steven T. Hatton

    Daniel T. Guest

    In article <>,
    "Steven T. Hatton" <> wrote:

    > I finally got this thing to build. There's something to be said for using
    > the release of the cvs image sometimes. :-/
    >
    > I started reading the docs, and this example struck me as a fundamentally
    > bad design for C++. Perhaps it's not bad design in the sense that it will
    > fail, or that it can't be maintained. But there seems to be something
    > fundamentally un-C++ about this. Does anybody else see what I'm talking
    > about here?
    >
    > class ComplexNumberTest : public CppUnit::TestFixture {
    > private:
    > Complex *m_10_1, *m_1_1, *m_11_2;
    > public:
    > void setUp()
    > {
    > m_10_1 = new Complex( 10, 1 );
    > m_1_1 = new Complex( 1, 1 );
    > m_11_2 = new Complex( 11, 2 );
    > }
    >
    > void tearDown()
    > {
    > delete m_10_1;
    > delete m_1_1;
    > delete m_11_2;
    > }
    >
    > void testEquality()
    > {
    > CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
    > CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
    > }
    >
    > void testAddition()
    > {
    > CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
    > }
    > };


    Yes, this is poor C++ design. what happens if the second 'new Complex'
    throws? Will tearDown be called, and if it is the 'delete m_11_2' call
    will probably be undefined because nowhere are the pointers initialized.

    Would using members rather than pointers help? Not really, the whole
    point of the pattern is so testEquality and testAddition are isolated.

    The three variables are really ment to be local to the test functions. I
    would rather see something like:

    struct Numbers {
    Complex m_10_1, m_1_1, m_11_2;
    numbers(): m_10_1( 10, 1 ), m_1_1( 1, 1 ), m_11_2( 11, 2 ) { }
    };

    struct ComplexNumberTest: public CppUnit::TestFixture {
    void testEquality() {
    Numbers n;
    CPPUNIT_ASSERT( n.m_10_1 == n.m_10_1 );
    CPPUNIT_ASSERT( !(n.m_10_1 == n.m_11_2 ) );
    }

    void testAddition() {
    CPPUNIT_ASSERT( n.m_10_1 + n.m_1_1 == n.m_11_2 );
    }
    };
    Daniel T., Sep 6, 2004
    #7
  8. David Hilsee wrote:

    > In my post, I meant that they could be local to testEquality and
    > testAddition. I think my wording was a bit confusing. At any rate, it
    > doesn't matter much, because it would also make more sense to have them as
    > members instead of pointers. IMHO, either way is better. While it is
    > true that members can cause unnecessary recompilation in certain cases, I
    > doubt
    > that this will be a big problem for test code. When I want to avoid those
    > problems, I usually just use the pimpl idiom.


    I was certainly speaking in generalities regarding the problems of using
    class type members as opposed to using pointers to class types as members.
    Here is the case where I believe it is completely impossible to accomplish
    what is suggested by the code without introducing at least one pointer (or
    perhaps a reference):

    class B;
    class A{ B b; };

    class B{ A a; };

    The issue of header files is really irrelevant in this case. I just
    happened to encounter it while working on something involving headers, and
    found the obvious solution was to use pointers rather than classes as
    members. It was kind of two birds with one stone. I removed the compiler
    dependencies resulting from the definition of the class type member
    variables that applies to any header file, as well as the specific problem
    of indirect recursion. They both result from the same need for the
    compiler to know how to allocate the memory for the defined class type
    member.

    What I really found bothersome about the sample I presented was that setUp()
    should be a constructor and tearDown() should be a destructor. That is
    exactly what these special member functions are intended to do. That is the
    core concept behind RAII. A constructor establishes the class invariant or
    state of the class, and the destructor reverses that process, freeing any
    resources obtained in the construction of the class.

    In some Java subcultures constructors are considered evil because they don't
    work well in conjunction with serialization. It's more or less the same
    kind of restriction you meet when attempting to create a finite sized
    container of class type. You either have to feed the template a prototype
    constructed object, or you need to have a default constructor which takes
    no arguments.

    As for the pimpl idiom I haven't revisited that in a while, but I have to
    say, from what I recall about it, it supports my conviction that the single
    most important change C++ needs is a better way of accessing resources.
    One that doesn't require header files, or at least formalizes the notion of
    header files as declaration files and not simply #include paperclip and
    tape hacks.
    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 6, 2004
    #8
  9. Daniel T. wrote:

    > In article <>,
    > "Steven T. Hatton" <> wrote:
    >
    >> class ComplexNumberTest : public CppUnit::TestFixture {
    >> private:
    >> Complex *m_10_1, *m_1_1, *m_11_2;
    >> public:
    >> void setUp()
    >> {
    >> m_10_1 = new Complex( 10, 1 );
    >> m_1_1 = new Complex( 1, 1 );
    >> m_11_2 = new Complex( 11, 2 );
    >> }
    >>
    >> void tearDown()
    >> {
    >> delete m_10_1;
    >> delete m_1_1;
    >> delete m_11_2;
    >> }
    >>
    >> void testEquality()
    >> {
    >> CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
    >> CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
    >> }
    >>
    >> void testAddition()
    >> {
    >> CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
    >> }
    >> };

    >
    > Yes, this is poor C++ design. what happens if the second 'new Complex'
    > throws? Will tearDown be called, and if it is the 'delete m_11_2' call
    > will probably be undefined because nowhere are the pointers initialized.


    I didn't even think about the fact the pointers were un-initialize. Good
    observation.

    > Would using members rather than pointers help? Not really, the whole
    > point of the pattern is so testEquality and testAddition are isolated.
    >
    > The three variables are really ment to be local to the test functions. I
    > would rather see something like:
    >
    > struct Numbers {
    > Complex m_10_1, m_1_1, m_11_2;
    > numbers(): m_10_1( 10, 1 ), m_1_1( 1, 1 ), m_11_2( 11, 2 ) { }
    > };
    >
    > struct ComplexNumberTest: public CppUnit::TestFixture {
    > void testEquality() {
    > Numbers n;
    > CPPUNIT_ASSERT( n.m_10_1 == n.m_10_1 );
    > CPPUNIT_ASSERT( !(n.m_10_1 == n.m_11_2 ) );
    > }
    >
    > void testAddition() {
    > CPPUNIT_ASSERT( n.m_10_1 + n.m_1_1 == n.m_11_2 );
    > }
    > };

    Can I assume you intended

    void testAddition() {
    Numbers n;
    CPPUNIT_ASSERT( n.m_10_1 + n.m_1_1 == n.m_11_2 );
    }

    as the second function?

    That probably is a better approach for this case. I do believe there are
    instances where it makes sense to actually construct a test environment
    that exists for the duration of the TestFixture's lifetime. But I can't
    say I have an example of any. I've been trying to devise appropriate tests
    for a modestly complicated vector class I wrote with half a dozen operator
    overloads, some static member instances, and the obligatory access and
    mutate functions.

    I believe your approach makes more sense than creating instances to be used
    for every test. unless I have a compelling reason to want to test the
    side-effect on one test with another, I see no reason to allow a test to
    influence the data used for a subsequent test.

    One issue I'm unsure of is how detailed I need to be to satisfy 'generally
    accepted' expectations of thoroughness. As an example, suppose I have


    inline Vector3d& Vector3d::eek:perator+=(const Vector3d& v)
    {
    m_x += v.m_x;
    m_y += v.m_y;
    m_z += v.m_z;
    return *this;
    }

    inline Vector3d operator+(const Vector3d& v1, const Vector3d& v2)
    {
    Vector3d t(v1);
    return t += v2;
    }

    It seems to me, testing the second operator (+) implicitly verifies the
    first operator (+=).
    --
    "[M]y dislike for the preprocessor is well known. Cpp is essential in C
    programming, and still important in conventional C++ implementations, but
    it is a hack, and so are most of the techniques that rely on it. ...I think
    the time has come to be serious about macro-free C++ programming." - B. S.
    Steven T. Hatton, Sep 6, 2004
    #9
  10. Steven T. Hatton

    Daniel T. Guest

    In article <>,
    "Steven T. Hatton" <> wrote:

    > Daniel T. wrote:
    >
    > > In article <>,
    > > "Steven T. Hatton" <> wrote:
    > >
    > >> class ComplexNumberTest : public CppUnit::TestFixture {
    > >> private:
    > >> Complex *m_10_1, *m_1_1, *m_11_2;
    > >> public:
    > >> void setUp()
    > >> {
    > >> m_10_1 = new Complex( 10, 1 );
    > >> m_1_1 = new Complex( 1, 1 );
    > >> m_11_2 = new Complex( 11, 2 );
    > >> }
    > >>
    > >> void tearDown()
    > >> {
    > >> delete m_10_1;
    > >> delete m_1_1;
    > >> delete m_11_2;
    > >> }
    > >>
    > >> void testEquality()
    > >> {
    > >> CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
    > >> CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
    > >> }
    > >>
    > >> void testAddition()
    > >> {
    > >> CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
    > >> }
    > >> };

    > >
    > > Yes, this is poor C++ design. what happens if the second 'new Complex'
    > > throws? Will tearDown be called, and if it is the 'delete m_11_2' call
    > > will probably be undefined because nowhere are the pointers initialized.

    >
    > I didn't even think about the fact the pointers were un-initialize. Good
    > observation.
    >
    > > Would using members rather than pointers help? Not really, the whole
    > > point of the pattern is so testEquality and testAddition are isolated.
    > >
    > > The three variables are really ment to be local to the test functions. I
    > > would rather see something like:
    > >
    > > struct Numbers {
    > > Complex m_10_1, m_1_1, m_11_2;
    > > numbers(): m_10_1( 10, 1 ), m_1_1( 1, 1 ), m_11_2( 11, 2 ) { }
    > > };
    > >
    > > struct ComplexNumberTest: public CppUnit::TestFixture {
    > > void testEquality() {
    > > Numbers n;
    > > CPPUNIT_ASSERT( n.m_10_1 == n.m_10_1 );
    > > CPPUNIT_ASSERT( !(n.m_10_1 == n.m_11_2 ) );
    > > }
    > >
    > > void testAddition() {
    > > CPPUNIT_ASSERT( n.m_10_1 + n.m_1_1 == n.m_11_2 );
    > > }
    > > };

    > Can I assume you intended
    >
    > void testAddition() {
    > Numbers n;
    > CPPUNIT_ASSERT( n.m_10_1 + n.m_1_1 == n.m_11_2 );
    > }
    >
    > as the second function?


    Yes, sorry.


    > That probably is a better approach for this case. I do believe there are
    > instances where it makes sense to actually construct a test environment
    > that exists for the duration of the TestFixture's lifetime. But I can't
    > say I have an example of any. I've been trying to devise appropriate tests
    > for a modestly complicated vector class I wrote with half a dozen operator
    > overloads, some static member instances, and the obligatory access and
    > mutate functions.
    >
    > I believe your approach makes more sense than creating instances to be used
    > for every test. unless I have a compelling reason to want to test the
    > side-effect on one test with another, I see no reason to allow a test to
    > influence the data used for a subsequent test.


    The last time I created a testing framework, I didn't have setUp and
    tearDown methods. Instead, the c_tor was the 'setUp' and the d_tor was
    the 'tearDown'. Something like this:

    class TestFixture {
    public:
    virtual void run() = 0;
    };

    struct ComplexTest: TestFixture {
    Complex m_10_1, m_1_1, m_11_2;
    ComplexTest(): m_10_1( 10, 1 ), m_1_1( 1, 1 ), m_11_2( 11, 2 ) { }
    };

    struct ComplexEqualityTest: ComplexTest {
    void run() {
    ASSERT( m_10_1 == m_10_1 );
    }
    };

    struct ComplexAdditionTest: ComplexTest {
    void run() {
    ASSERT( m_10_1 + m_1_1 == m_11_2 );
    }
    };


    > One issue I'm unsure of is how detailed I need to be to satisfy 'generally
    > accepted' expectations of thoroughness. As an example, suppose I have
    >
    >
    > inline Vector3d& Vector3d::eek:perator+=(const Vector3d& v)
    > {
    > m_x += v.m_x;
    > m_y += v.m_y;
    > m_z += v.m_z;
    > return *this;
    > }
    >
    > inline Vector3d operator+(const Vector3d& v1, const Vector3d& v2)
    > {
    > Vector3d t(v1);
    > return t += v2;
    > }
    >
    > It seems to me, testing the second operator (+) implicitly verifies the
    > first operator (+=).


    In "test-first design", the idea is to write tests that will fail but
    should succeed, then write code to cause the test to succeed. So, if you
    can't conceve of a test that will fail, then you are done; and if you
    write a test that doesn't fail on the first try, you need to investigate
    what is going on because it should have failed...
    Daniel T., Sep 6, 2004
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roy Smith

    Forking sub-process in CppUnit?

    Roy Smith, Jul 4, 2003, in forum: C++
    Replies:
    0
    Views:
    383
    Roy Smith
    Jul 4, 2003
  2. Scott

    Xcode and cppunit

    Scott, Apr 27, 2004, in forum: C++
    Replies:
    2
    Views:
    1,025
    Scott
    Apr 27, 2004
  3. To Forum

    CPPUNIT

    To Forum, Jan 13, 2005, in forum: C++
    Replies:
    1
    Views:
    555
    Stephen Howe
    Jan 14, 2005
  4. Hooyoo
    Replies:
    59
    Views:
    4,953
    Gianni Mariani
    Nov 3, 2006
  5. Belebele

    Reusing tests in cppUnit

    Belebele, Jan 15, 2007, in forum: C++
    Replies:
    3
    Views:
    415
    Belebele
    Jan 22, 2007
Loading...

Share This Page