1///////////////////////////////////////////////////////////////////////////////
2// Name:        tests/streams/bstream.h
3// Purpose:     Template class for testing base stream functions.
4// Author:      Hans Van Leemputten
5// RCS-ID:      $Id: bstream.h 30731 2004-11-23 14:26:10Z RN $
6// Copyright:   (c) 2004 Hans Van Leemputten
7// Licence:     wxWidgets licence
8///////////////////////////////////////////////////////////////////////////////
9
10#ifndef _WX_TESTBSTREAM_H__
11#define _WX_TESTBSTREAM_H__
12
13#include "wx/cppunit.h"
14
15///////////////////////////////////////////////////////////////////////////////
16// Some macros preventing us from typing too much ;-)
17//
18
19#define STREAM_TEST_NAME "Streams"
20#define COMPOSE_TEST_NAME(Name) \
21    STREAM_TEST_NAME "." #Name
22#define STREAM_REGISTER_SUB_SUITE(Name) \
23    extern CppUnit::Test* Get##Name##Suite(); \
24    suite->addTest(Get##Name##Suite())
25#define STREAM_IMPLEMENT_SUB_REGISTRATION_ROUTINE(Name) \
26    CppUnit::Test* Get##Name##Suite() { return Name::suite(); }
27#define STREAM_TEST_SUBSUITE_NAMED_REGISTRATION(Name) \
28    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( Name, COMPOSE_TEST_NAME(Name) ); \
29    STREAM_IMPLEMENT_SUB_REGISTRATION_ROUTINE( Name )
30
31
32///////////////////////////////////////////////////////////////////////////////
33// Template class that implements a test for all base stream functions.
34//
35
36template <class TStreamIn, class TStreamOut> class BaseStreamTestCase : public CppUnit::TestCase
37{
38protected:
39    typedef BaseStreamTestCase<TStreamIn, TStreamOut> StreamTestCase;
40
41    class CleanupHelper
42    {
43    public:
44        CleanupHelper(StreamTestCase *value)
45            :m_pCleanup(value)
46        {}
47        ~CleanupHelper()
48        {
49            m_pCleanup->DeleteInStream();
50            m_pCleanup->DeleteOutStream();
51        }
52    private:
53        StreamTestCase   *m_pCleanup;
54    };
55    friend class CleanupHelper;
56
57public:
58    BaseStreamTestCase()
59        :m_bSimpleTellITest(false),
60         m_bSimpleTellOTest(false),
61         m_bSeekInvalidBeyondEnd(true),
62         m_bEofAtLastRead(true),
63         m_pCurrentIn(NULL),
64         m_pCurrentOut(NULL)
65    { /* Nothing extra */ }
66    virtual ~BaseStreamTestCase()
67    {
68        // Prevent mem leaks!
69        delete m_pCurrentIn;
70        delete m_pCurrentOut;
71    }
72
73protected:
74    /*
75     * Input stream tests.
76     */
77
78    // Just try to perform a GetSize() on the input stream.
79    void Input_GetSize()
80    {
81        CleanupHelper cleanup(this);
82        const TStreamIn &stream_in = CreateInStream();
83        CPPUNIT_ASSERT(!stream_in.Eof());
84
85        // Size should be greater then zero.
86        // Note: streams not supporting this should register this test
87        //       with CPPUNIT_TEST_FAIL instead of CPPUNIT_TEST.
88        CPPUNIT_ASSERT(stream_in.GetSize() != 0);
89    }
90
91    // Just try to perform a GetC() on the input stream.
92    void Input_GetC()
93    {
94        CleanupHelper cleanup(this);
95        TStreamIn &stream_in = CreateInStream();
96        CPPUNIT_ASSERT(!stream_in.Eof());
97
98        // If no exception occurs the test is successful.
99        (void)stream_in.GetC();
100    }
101
102    // Just try to perform a Read() on the input stream.
103    void Input_Read()
104    {
105        CleanupHelper cleanup(this);
106        TStreamIn &stream_in = CreateInStream();
107        CPPUNIT_ASSERT(!stream_in.Eof());
108
109        // Note: the input stream should at least be of min size +10!
110
111        char buf[10];
112        (void)stream_in.Read(buf, 10);
113
114        CPPUNIT_ASSERT(!stream_in.Eof());
115        CPPUNIT_ASSERT(stream_in.IsOk());
116
117        // Test the stream version aswell.
118        TStreamOut &stream_out = CreateOutStream();
119        (void)stream_in.Read(stream_out);
120
121        // The output stream should have read the input stream till the end.
122        CPPUNIT_ASSERT(stream_in.Eof());
123    }
124
125    // Test and see what happens to the EOF when we
126    // read after EOF was encountered.
127    void Input_Eof()
128    {
129        CleanupHelper cleanup(this);
130        TStreamIn &stream_in = CreateInStream();
131        CPPUNIT_ASSERT(!stream_in.Eof());
132        // Double check to see if Eof it self doesn't changes the Eof status.
133        CPPUNIT_ASSERT(!stream_in.Eof());
134
135        // Travel to the end of the stream.
136        while(!stream_in.Eof())
137        {
138            // Read, we move one byte along.
139            (void)stream_in.GetC();
140#if 0
141            // EOF behaviour is different in streams, disabled (for now?)
142
143            if (m_bEofAtLastRead)
144                // EOF should only occure after the last successful get.
145                CPPUNIT_ASSERT_MESSAGE("Eof is detected too late.", !(stream_in.LastRead() != 1 && stream_in.Eof()));
146            else
147                // EOF should only occure after a failed get.
148                CPPUNIT_ASSERT_MESSAGE("Eof is detected too soon.", !(stream_in.LastRead() == 1 && stream_in.Eof()));
149#endif
150        }
151
152        // Check EOF stream state.
153        CPPUNIT_ASSERT_MESSAGE("EOF is not EOF?", stream_in.Eof());
154
155        // Ok we found the end, lets see if we can go past it.
156        for (size_t i = 0; i < 100; i++)
157            (void)stream_in.GetC();
158
159        // Check for EOF correctness.
160        CPPUNIT_ASSERT_MESSAGE("EOF is wrong when we read past EOF!", stream_in.Eof());
161        CPPUNIT_ASSERT_MESSAGE("Last error is not EOF while stream_in.Eof() is true", stream_in.GetLastError() == wxSTREAM_EOF);
162    }
163
164    // Just try to perform a LastRead() on the input stream.
165    void Input_LastRead()
166    {
167        CleanupHelper cleanup(this);
168        TStreamIn &stream_in = CreateInStream();
169        CPPUNIT_ASSERT(!stream_in.Eof());
170
171        char buf[5];
172        (void)stream_in.Read(buf, 5);
173        CPPUNIT_ASSERT(stream_in.LastRead() == 5);
174        (void)stream_in.GetC();
175        CPPUNIT_ASSERT(stream_in.LastRead() == 1);
176    }
177
178    // Just try to perform a SeekI() on the input stream.
179    void Input_SeekI()
180    {
181        CleanupHelper cleanup(this);
182        TStreamIn &stream_in = CreateInStream();
183        CPPUNIT_ASSERT(!stream_in.Eof());
184
185        // Try to Seek in the stream...
186        // Note: streams not supporting this should register this test
187        //       with CPPUNIT_TEST_FAIL instead of CPPUNIT_TEST.
188        CPPUNIT_ASSERT(stream_in.SeekI(2, wxFromStart) == 2);
189        CPPUNIT_ASSERT(stream_in.SeekI(2, wxFromCurrent) == 4);
190        // Not sure the following line is correct, so test it differently.
191        //CPPUNIT_ASSERT(stream_in.SeekI(-2, wxFromEnd) == (off_t)stream_in.GetSize()-2);
192        CPPUNIT_ASSERT(stream_in.SeekI(-2, wxFromEnd) != wxInvalidOffset);
193        // Go beyond the stream size.
194        CPPUNIT_ASSERT((stream_in.SeekI(10, wxFromCurrent) == wxInvalidOffset) == m_bSeekInvalidBeyondEnd);
195    }
196
197    // Just try to perform a TellI() on the input stream.
198    void Input_TellI()
199    {
200        CleanupHelper cleanup(this);
201        TStreamIn &stream_in = CreateInStream();
202        CPPUNIT_ASSERT(!stream_in.Eof());
203
204        // Try to Get the location in the stream...
205        CPPUNIT_ASSERT(stream_in.TellI() == 0);
206        (void)stream_in.GetC();
207        CPPUNIT_ASSERT(stream_in.TellI() == 1);
208        if (!m_bSimpleTellITest)
209        {
210            wxFileOffset pos = stream_in.SeekI(5, wxFromStart);
211            CPPUNIT_ASSERT(stream_in.TellI() == pos);
212            (void)stream_in.GetC();
213            CPPUNIT_ASSERT(stream_in.TellI() == 6);
214            pos = stream_in.SeekI(2, wxFromCurrent);
215            CPPUNIT_ASSERT(stream_in.TellI() == pos);
216            pos = stream_in.SeekI(5, wxFromStart);
217            CPPUNIT_ASSERT(stream_in.TellI() == pos);
218        }
219    }
220
221    // Just try to perform a Peek() on the input stream.
222    void Input_Peek()
223    {
224        CleanupHelper cleanup(this);
225        TStreamIn &stream_in = CreateInStream();
226
227        // Test the full stream
228        while (stream_in.IsOk())
229        {
230            char peekChar = stream_in.Peek();
231            char getChar = stream_in.GetC();
232            if (stream_in.LastRead() == 1)
233                CPPUNIT_ASSERT(peekChar == getChar);
234        }
235    }
236
237    // Just try to perform a Ungetch() on the input stream.
238    void Input_Ungetch()
239    {
240        CleanupHelper cleanup(this);
241        TStreamIn &stream_in = CreateInStream();
242        CPPUNIT_ASSERT(!stream_in.Eof());
243
244        const char *ungetstr = "test";
245        size_t ungetsize = stream_in.Ungetch(ungetstr, strlen(ungetstr) + 1);
246        if (ungetsize != 0)
247        {
248            CPPUNIT_ASSERT(ungetsize == strlen(ungetstr) + 1);
249            char buf[10];
250            (void)stream_in.Read(buf, ungetsize);
251            CPPUNIT_ASSERT(strcmp(buf, ungetstr) == 0);
252        }
253
254        if (stream_in.Ungetch('a'))
255        {
256            CPPUNIT_ASSERT(stream_in.GetC() == 'a');
257        }
258    }
259
260    /*
261     * Output stream tests.
262     */
263
264    // Just try to perform a PutC() on the output stream.
265    void Output_PutC()
266    {
267        CleanupHelper cleanup(this);
268        TStreamOut &stream_out = CreateOutStream();
269
270        char *buf = "Some text";
271        int i;
272        int len = strlen(buf);
273        for (i = 0; i < len; i++)
274            stream_out.PutC(buf[i]);
275
276        CPPUNIT_ASSERT(i == stream_out.TellO());
277    }
278
279    // Just try to perform a Write() on the output stream.
280    void Output_Write()
281    {
282        CleanupHelper cleanup(this);
283        TStreamOut &stream_out = CreateOutStream();
284
285        // Do the buffer version.
286        char *buf = "Some text";
287        int len = strlen(buf);
288        (void)stream_out.Write(buf, len);
289        CPPUNIT_ASSERT(stream_out.TellO() == len);
290
291        // Do the Stream version.
292        TStreamIn &stream_in = CreateInStream();
293        (void)stream_out.Write(stream_in);
294        CPPUNIT_ASSERT(stream_out.TellO() > len);
295    }
296
297    // Just try to perform a LastWrite() on the output stream.
298    void Output_LastWrite()
299    {
300        CleanupHelper cleanup(this);
301        TStreamOut &stream_out = CreateOutStream();
302
303        char *buf = "12345";
304        (void)stream_out.Write(buf, 5);
305        CPPUNIT_ASSERT(stream_out.LastWrite() == 5);
306        (void)stream_out.PutC('1');
307        CPPUNIT_ASSERT(stream_out.LastWrite() == 1);
308    }
309
310    // Just try to perform a SeekO() on the output stream.
311    void Output_SeekO()
312    {
313        CleanupHelper cleanup(this);
314        TStreamOut &stream_out = CreateOutStream();
315
316        // First put some data in the stream, so it is not empty.
317        char *buf = "1234567890";
318        (void)stream_out.Write(buf, 10);
319
320        // Try to Seek in the stream...
321        // Note: streams not supporting this should register this test
322        //       with CPPUNIT_TEST_FAIL instead of CPPUNIT_TEST.
323        CPPUNIT_ASSERT(stream_out.SeekO(2, wxFromStart) == 2);
324        CPPUNIT_ASSERT(stream_out.SeekO(2, wxFromCurrent) == 4);
325        // Not sure the following line is correct, so test it differently.
326        //CPPUNIT_ASSERT(stream_out.SeekO(-2, wxFromEnd) == (off_t)stream_in.GetSize()-2);
327        CPPUNIT_ASSERT(stream_out.SeekO(-2, wxFromEnd) != wxInvalidOffset);
328        // Go beyond the stream size.
329        CPPUNIT_ASSERT((stream_out.SeekO(10, wxFromCurrent) == wxInvalidOffset) == m_bSeekInvalidBeyondEnd);
330    }
331
332    // Just try to perform a TellO() on the output stream.
333    void Output_TellO()
334    {
335        CleanupHelper cleanup(this);
336        TStreamOut &stream_out = CreateOutStream();
337
338        // Try to Get the location in the stream...
339        CPPUNIT_ASSERT(stream_out.TellO() == 0);
340        (void)stream_out.PutC('1');
341        CPPUNIT_ASSERT(stream_out.TellO() == 1);
342        if (!m_bSimpleTellOTest)
343        {
344            // First put some extra data in the stream, so it's not empty.
345            char *buf = "1234567890";
346            (void)stream_out.Write(buf, 10);
347
348            off_t pos = stream_out.SeekO(5, wxFromStart);
349            CPPUNIT_ASSERT(stream_out.TellO() == pos);
350            (void)stream_out.PutC('1');
351            CPPUNIT_ASSERT(stream_out.TellO() == 6);
352            pos = stream_out.SeekO(2, wxFromCurrent);
353            CPPUNIT_ASSERT(stream_out.TellO() == pos);
354            pos = stream_out.SeekO(5, wxFromStart);
355            CPPUNIT_ASSERT(stream_out.TellO() == pos);
356        }
357    }
358
359protected:
360    // Some tests can be configured... here you can find the config settings
361    bool m_bSimpleTellITest;    // if true, no SeekI will be used by the TellI test.
362                                // Default false.
363    bool m_bSimpleTellOTest;    // if true, no SeekO will be used by the TellI test.
364                                // Default false.
365    bool m_bSeekInvalidBeyondEnd; // if true a SeekI|O beyond the end of the stream should return wxInvalidOffset
366                                  // Default true.
367    bool m_bEofAtLastRead;      // Does EOF occure at the moment the last byte is read or when read past the last byte.
368                                // Default true.
369protected:
370    TStreamIn &CreateInStream()
371    {
372        if (m_pCurrentIn)
373        {
374            wxFAIL_MSG(_T("Error in test case, the previouse input stream needs to be delete first!"));
375        }
376
377        m_pCurrentIn = DoCreateInStream();
378        wxASSERT(m_pCurrentIn != NULL);
379        return *m_pCurrentIn;
380    }
381    TStreamOut &CreateOutStream()
382    {
383        if (m_pCurrentOut)
384        {
385            wxFAIL_MSG(_T("Error in test case, the previouse output stream needs to be delete first!"));
386        }
387
388        m_pCurrentOut = DoCreateOutStream();
389        wxASSERT(m_pCurrentOut != NULL);
390        return *m_pCurrentOut;
391    }
392
393    void DeleteInStream()
394    {
395        if (m_pCurrentIn == NULL)
396            return;
397        delete m_pCurrentIn;
398        m_pCurrentIn = NULL;
399        // Incase something extra needs to be done.
400        DoDeleteInStream();
401    }
402    void DeleteOutStream()
403    {
404        if (m_pCurrentOut == NULL)
405            return;
406
407        CPPUNIT_ASSERT(m_pCurrentOut->Close());
408
409        delete m_pCurrentOut;
410        m_pCurrentOut = NULL;
411        // Incase something extra needs to be done.
412        DoDeleteOutStream();
413    }
414
415protected:
416    // Items that need to be implemented by a derived class!
417    virtual TStreamIn  *DoCreateInStream() = 0;
418    virtual TStreamOut *DoCreateOutStream() = 0;
419    virtual void DoDeleteInStream()  { /* Depends on the base class */ }
420    virtual void DoDeleteOutStream() { /* Depends on the base class */ }
421
422private:
423    TStreamIn  *m_pCurrentIn;
424    TStreamOut *m_pCurrentOut;
425};
426
427#endif
428
429
430