TcpSocketBufferTest.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2006-2007 by Peter Dimov.
00004 
00005 This file is part of Calitko (http://www.calitko.org).
00006 
00007 Calitko is free software; you can redistribute it and/or modify
00008 it under the terms of the GNU General Public License as published by
00009 the Free Software Foundation; either version 2 of the License, or
00010 (at your option) any later version.
00011 
00012 Calitko is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Calitko; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021 */
00022 
00023 #include "Qt.h"
00024 #include "generated/TcpSocketBufferDriver.h"
00025 #include "generated/SocketMock.h"
00026 #include "Imports.h"
00027 
00028 namespace Protocols {
00029 namespace Generics {
00030 namespace Testing {
00031 
00033 class WriteCallChecker
00034 {
00035     QByteArray bytes;
00036 
00037 public:
00038     WriteCallChecker (const QByteArray &bytes_)
00039     : bytes (bytes_) {}
00040 
00041     typedef qint64 (Function) (const char *, qint64);
00042 
00043     bool check (const char *source, qint64 size)
00044     {
00045         QByteArray checkBytes = QByteArray::fromRawData (source, size);
00046         return checkBytes == bytes;
00047     }
00048 };
00049 
00051 class ReadCallChecker
00052 {
00053     QByteArray bytes;
00054 
00055 public:
00056     ReadCallChecker (const QByteArray &bytes_)
00057     : bytes (bytes_) {}
00058 
00059     typedef qint64 (Function) (char *, qint64);
00060 
00061     bool check (char *destination, qint64 size)
00062     {
00063         // We expect a buffer that would fit our bytes.
00064         if (size < bytes.size())
00065             return false;
00066         int count = std::min (static_cast <int> (size), bytes.size());
00067         memcpy (destination, bytes.constData(), count);
00068         return true;
00069     }
00070 };
00071 
00073 
00082 class TcpSocketBufferTest : public CppUnit::TestFixture
00083 {
00084     CPPUNIT_TEST_SUITE (TcpSocketBufferTest);
00085     CPPUNIT_TEST (testWritingZeroBytesAlwaysSucceeds);
00086     CPPUNIT_TEST (testWriteBufferSizeBytesAtOnce);
00087     CPPUNIT_TEST (testWriteBufferSizeBytesAtOnceTwoTimes);
00088     CPPUNIT_TEST (testWriteBufferSizeBytesAtTwoWrites);
00089     CPPUNIT_TEST (testWriteMoreThanBufferSize);
00090     CPPUNIT_TEST (testPartialWriteToSocket7of10);
00091     CPPUNIT_TEST (testPartialWriteToSocket0of10);
00092     CPPUNIT_TEST (testPartialWriteToSocket_1of10);
00093     CPPUNIT_TEST (testPartialWriteToSocketWithRotate3ofLast5);
00094     CPPUNIT_TEST (testPartialWriteToSocketWithRotate0ofLast5);
00095     CPPUNIT_TEST (testPartialWriteToSocketWithRotate_1ofLast5);
00096     CPPUNIT_TEST (testTryReadFromFillsBuffer);
00097     CPPUNIT_TEST (testTryReadFromReadsNothing);
00098     CPPUNIT_TEST (testReadingZeroBytesAlwaysSucceeds);
00099     CPPUNIT_TEST (testReadsWhatWasReadFrom5rotate5);
00100     CPPUNIT_TEST (testReadsWhatWasReadFrom5rotate0);
00101     CPPUNIT_TEST (testTryReadFromAtOnceSocketReadReturns_1NoDataLost);
00102     CPPUNIT_TEST (testTryReadFromAtTwoReadsSocketReadReturns_1NoDataLost);
00103     CPPUNIT_TEST (testTryReadFromOnceThemMultipleNormalReads);
00104     CPPUNIT_TEST (testReadAll);
00105     CPPUNIT_TEST (testPeekAndPeekAll);
00106     CPPUNIT_TEST (testPeekToAndReadTo);
00107     CPPUNIT_TEST (testPeekToAndReadToWhenSeparatorInTwoChunks);
00108     CPPUNIT_TEST (testCanRead);
00109     CPPUNIT_TEST (testCanWrite);
00110     CPPUNIT_TEST (testPeekAtMostThenReadAtMost);
00111     CPPUNIT_TEST_SUITE_END();
00112 
00113     static const int ReadBufferSize     = 20;
00114     static const int WriteBufferSize    = 20;
00115 
00116     auto_ptr <SocketMock>               socket;
00117     auto_ptr <TcpSocketBuffer>          buffer_real;
00118     auto_ptr <TcpSocketBufferDriver>    buffer;
00119 
00120 public:
00121     TcpSocketBufferTest() : socket(), buffer_real(), buffer()
00122     {
00123     }
00124 
00125     void setUp()
00126     {
00127         socket.reset (new SocketMock);
00128         buffer_real.reset (new TcpSocketBuffer (ReadBufferSize,
00129                                                 WriteBufferSize));
00130         buffer.reset (new TcpSocketBufferDriver (*buffer_real.get()));
00131     }
00132 
00133     void tearDown()
00134     {
00135         buffer.reset();
00136         buffer_real.reset();
00137         socket.reset();
00138     }
00139 
00141     void refWriteBytesFails (const QByteArray &bytes, bool flush = true)
00142     {
00143         call (buffer->write (bytes, flush))
00144         .willReturn (false);
00145     }
00146 
00148     void refWriteBytesSucceeds (const QByteArray &bytes, bool flush = true)
00149     {
00150         call (buffer->write (bytes, flush))
00151         .willReturn (true);
00152     }
00153 
00155     void testWritingZeroBytesAlwaysSucceeds()
00156     {
00157         refWriteBytesSucceeds (QByteArray(), true);
00158         refWriteBytesSucceeds (QByteArray(), false);
00159     }
00160 
00162     void testWriteBufferSizeBytesAtOnce()
00163     {
00164         refWriteBytesSucceeds (QByteArray (WriteBufferSize, '1'));
00165         refWriteBytesFails (QByteArray (1, '1')); // No more data should fit.
00166     }
00167 
00169     void testWriteBufferSizeBytesAtOnceTwoTimes()
00170     {
00171         testWriteBufferSizeBytesAtOnce();
00172         refWriteToSocketAtOnce (QByteArray (WriteBufferSize, '1'));
00173         // Try the same a second time.
00174         testWriteBufferSizeBytesAtOnce();
00175     }
00176 
00178     void testWriteBufferSizeBytesAtTwoWrites()
00179     {
00180         refWriteBytesSucceeds (QByteArray (1, '1'));
00181         refWriteBytesSucceeds (QByteArray (WriteBufferSize - 1, '1'));
00182         refWriteBytesFails (QByteArray (1, '1')); // No more data should fit.
00183     }
00184 
00186     void testWriteMoreThanBufferSize()
00187     {
00188         refWriteBytesFails (QByteArray (WriteBufferSize + 1, '1'));
00189     }
00190 
00192     void refWriteToSocketAtOnce (const QByteArray &bytes)
00193     {
00194         refWriteToSocketAtOncePartially (bytes, bytes.count());
00195     }
00196 
00198     void refWriteToSocketAtOncePartially (const QByteArray &bytes,
00199                                           int countWritten)
00200     {
00201         Q_ASSERT (countWritten <= bytes.size());
00202         call (buffer->tryWriteTo (*socket))
00203             .willCall (socket->write (checker (WriteCallChecker (bytes))))
00204             .willReturn (countWritten)
00205         .returns();
00206     }
00207 
00209 
00213     void refWriteToSocketAtTwoChunks (const QByteArray &bytes1,
00214                                       const QByteArray &bytes2)
00215     {
00216         // A special case of partially is writing all of the second array:
00217         refWriteToSocketAtTwoChunksSecondPartially (bytes1, bytes2,
00218                                                     bytes2.size());
00219     }
00220 
00223     void refWriteToSocketAtTwoChunksSecondPartially (const QByteArray &bytes1,
00224                                                      const QByteArray &bytes2,
00225                                                      int countWrittenBytes2)
00226     {
00227         Q_ASSERT (countWrittenBytes2 <= bytes2.size());
00228         call (buffer->tryWriteTo (*socket))
00229             .willCall (socket->write (checker (WriteCallChecker (bytes1))))
00230             .willReturn (bytes1.size())
00231             .willCall (socket->write (checker (WriteCallChecker (bytes2))))
00232             .willReturn (countWrittenBytes2)
00233         .returns();
00234     }
00235 
00237     void testRingBufferRotate()
00238     {
00239         refWriteBytesSucceeds (QByteArray (5, '1'));
00240         refWriteToSocketAtOnce (QByteArray (5, '1'));
00241         // This write should write the bytes in two chunks.
00242         refWriteBytesSucceeds (QByteArray (WriteBufferSize, '1'));
00243         refWriteToSocketAtTwoChunks (QByteArray (WriteBufferSize - 5, '1'),
00244                                      QByteArray (5, '1'));
00245     }
00246 
00248 
00252     void scenarioPartialWriteToSocketDoesNotLooseData (int countAll,
00253                                                        int countWritten)
00254     {
00255         QByteArray bytes (countAll, '1');
00256         refWriteBytesSucceeds (bytes);
00257         refWriteToSocketAtOncePartially (bytes, countWritten);
00258         // The subsequent write should only write the rest of the bytes.
00259         refWriteToSocketAtOnce (bytes.mid (countWritten));
00260     }
00261 
00263     void testPartialWriteToSocket7of10()
00264     {
00265         scenarioPartialWriteToSocketDoesNotLooseData (10, 7);
00266     }
00267 
00269     void testPartialWriteToSocket0of10()
00270     {
00271         scenarioPartialWriteToSocketDoesNotLooseData (10, 0);
00272     }
00273 
00275     void testPartialWriteToSocket_1of10()
00276     {
00277         scenarioPartialWriteToSocketDoesNotLooseData (10, -1);
00278     }
00279 
00281 
00292     void scenarioPartialWriteToSocketWithRotationDoesNotLoseData (int offset,
00293                                                                   int count)
00294     {
00295         Q_ASSERT (offset > 0);
00296         Q_ASSERT (offset <= WriteBufferSize);
00297         QByteArray bytes1 (offset, '1');
00298         QByteArray bytes2 (WriteBufferSize, '1');
00299         QByteArray bytes2chunk1 = bytes2.left (WriteBufferSize - offset);
00300         QByteArray bytes2chunk2 = bytes2.mid (WriteBufferSize - offset);
00301 
00302         refWriteBytesSucceeds (bytes1);
00303         refWriteToSocketAtOnce (bytes1);
00304         // This write should write the bytes in two chunks (second partially).
00305         refWriteBytesSucceeds (bytes2);
00306         refWriteToSocketAtTwoChunksSecondPartially (bytes2chunk1, bytes2chunk2,
00307                                                     count);
00308         // Subsequent tryWriteTo() should only try to write the rest.
00309         refWriteToSocketAtOnce (bytes2chunk2.mid (count));
00310     }
00311 
00313     void testPartialWriteToSocketWithRotate3ofLast5()
00314     {
00315         scenarioPartialWriteToSocketWithRotationDoesNotLoseData (5, 3);
00316     }
00317 
00319     void testPartialWriteToSocketWithRotate0ofLast5()
00320     {
00321         scenarioPartialWriteToSocketWithRotationDoesNotLoseData (5, 0);
00322     }
00323 
00325     void testPartialWriteToSocketWithRotate_1ofLast5()
00326     {
00327         scenarioPartialWriteToSocketWithRotationDoesNotLoseData (5, -1);
00328     }
00329 
00331     void refReadBytesSucceeds (const QByteArray &bytes)
00332     {
00333         call (buffer->read (bytes.count()))
00334         .willReturn (bytes);
00335     }
00336 
00338     void refReadBytesFails (int count)
00339     {
00340         call (buffer->read (count))
00341         .willReturn (QByteArray());
00342     }
00343 
00345     void refTryReadFromAtOnce (const QByteArray &bytes)
00346     {
00347         call (buffer->tryReadFrom (*socket))
00348             .willCall (socket->read (checker (ReadCallChecker (bytes))))
00349             .willReturn (bytes.size())
00350         .returns();
00351     }
00352 
00354 
00357     void refTryReadFromAtTwoReads (const QByteArray &bytes1,
00358                                    const QByteArray &bytes2)
00359     {
00360         call (buffer->tryReadFrom (*socket))
00361             .willCall (socket->read (checker (ReadCallChecker (bytes1))))
00362             .willReturn (bytes1.size())
00363             .willCall (socket->read (checker (ReadCallChecker (bytes2))))
00364             .willReturn (bytes2.size())
00365         .returns();
00366     }
00367 
00369     void testTryReadFromFillsBuffer()
00370     {
00371         refTryReadFromAtOnce (QByteArray (ReadBufferSize, '1'));
00372     }
00373 
00375     void testTryReadFromReadsNothing()
00376     {
00377         refTryReadFromAtOnce (QByteArray());
00378     }
00379 
00381     void testReadingZeroBytesAlwaysSucceeds()
00382     {
00383         // The two are equivalent. Furthermore the read buffer is empty.
00384         refReadBytesSucceeds (QByteArray());
00385         refReadBytesFails (0);
00386         // We should be allowed to read nothing even if there is something.
00387         testTryReadFromFillsBuffer();
00388         refReadBytesSucceeds (QByteArray());
00389     }
00390 
00392 
00398     void scenarioReadsWhatWasReadFrom (int offset, int secondSize)
00399     {
00400         Q_ASSERT (offset <= ReadBufferSize);
00401         QByteArray bytes1 (offset, '1');
00402         QByteArray bytes2 (ReadBufferSize - offset, '2');
00403         QByteArray bytes3 (secondSize, '2');
00404 
00405         refTryReadFromAtOnce (bytes1);
00406         refReadBytesSucceeds (bytes1);
00407         refTryReadFromAtTwoReads (bytes2, bytes3);
00408         refReadBytesSucceeds (bytes2 + bytes3);
00409     }
00410 
00412     void testReadsWhatWasReadFrom5rotate5()
00413     {
00414         scenarioReadsWhatWasReadFrom  (5, 5);
00415     }
00416 
00418     void testReadsWhatWasReadFrom5rotate0()
00419     {
00420         scenarioReadsWhatWasReadFrom  (5, 0);
00421     }
00422 
00424     void testTryReadFromAtOnceSocketReadReturns_1NoDataLost()
00425     {
00426         QByteArray bytes ("1234567890");
00427         refTryReadFromAtOnce (bytes);
00428 
00429         call (buffer->tryReadFrom (*socket))
00430             .willCall (socket->read (checker (ReadCallChecker (QByteArray()))))
00431             .willReturn (-1)
00432         .returns();
00433 
00434         refReadBytesSucceeds (bytes);
00435     }
00436 
00438     void testTryReadFromAtTwoReadsSocketReadReturns_1NoDataLost()
00439     {
00440         QByteArray bytes1 (5, '1');
00441         QByteArray bytes2 (ReadBufferSize - 5, '1');
00442 
00443         refTryReadFromAtOnce (bytes1);
00444         refReadBytesSucceeds (bytes1);
00445 
00446         call (buffer->tryReadFrom (*socket))
00447             .willCall (socket->read (checker (ReadCallChecker (bytes2))))
00448             .willReturn (bytes2.size())
00449             .willCall (socket->read (checker (ReadCallChecker (QByteArray()))))
00450             .willReturn (-1)
00451         .returns();
00452         refReadBytesSucceeds (bytes2);
00453     }
00454 
00456     void testTryReadFromOnceThemMultipleNormalReads()
00457     {
00458         QByteArray bytes ("1234567890");
00459         refTryReadFromAtOnce (bytes);
00460         refReadBytesSucceeds (bytes.left (3));
00461         refReadBytesSucceeds (bytes.mid (3, 3));
00462         refReadBytesSucceeds (bytes.mid (6));
00463     }
00464 
00466     void testReadAll()
00467     {
00468         // All bytes are stored as one chunk in the buffer.
00469         QByteArray bytes1 (10, '1');
00470         refTryReadFromAtOnce (bytes1);
00471 
00472         call (buffer->readAll())
00473         .willReturn (bytes1);
00474 
00475         // All bytes are stored as two chunks in the buffer.
00476         QByteArray bytes2 (ReadBufferSize - bytes1.size(), '2');
00477         refTryReadFromAtTwoReads (bytes2, bytes1);
00478 
00479         call (buffer->readAll())
00480         .willReturn (bytes2 + bytes1);
00481     }
00482 
00484     void testPeekAndPeekAll()
00485     {
00486         // Add some bytes to the read buffer:
00487         QByteArray bytes ("1234567890");
00488         refTryReadFromAtOnce (bytes);
00489         // test peek()
00490         call (buffer->peek (bytes.count()))
00491         .willReturn (bytes);
00492         // test peekAll()
00493         call (buffer->peekAll())
00494         .willReturn (bytes);
00495         // finally read the bytes:
00496         call (buffer->readAll())
00497         .willReturn (bytes);
00498     }
00499 
00501     void refPeekToAndReadTo (const QByteArray &delimiter,
00502                              const QByteArray &bytes)
00503     {
00504         call (buffer->peekTo (delimiter))
00505         .willReturn (bytes);
00506 
00507         call (buffer->readTo (delimiter))
00508         .willReturn (bytes);
00509     }
00510 
00512     void testPeekToAndReadTo()
00513     {
00514         QByteArray bytes ("1234567890cc12");
00515         refTryReadFromAtOnce (bytes);
00516         refPeekToAndReadTo ("cc", bytes.left (12));
00517         refReadBytesSucceeds (bytes.right (2)); // Just reads the rest.
00518     }
00519 
00521     void testPeekToAndReadToWhenSeparatorInTwoChunks()
00522     {
00523         QByteArray bytes1 (5, '1');
00524         refTryReadFromAtOnce (bytes1);
00525         refReadBytesSucceeds (bytes1);
00526         // Let us put in the buffer an array full of '2's separated by "cc",
00527         // which is itself split at the buffer end.
00528         QByteArray bytes2 (ReadBufferSize - bytes1.size() - 1, '2');
00529         QByteArray bytes3 (bytes1.size() - 1, '2');
00530         refTryReadFromAtTwoReads (bytes2 + "c", "c" + bytes3);
00531         // Now the actual test:
00532         refPeekToAndReadTo ("cc", bytes2 + "cc");
00533         refReadBytesSucceeds (bytes3); // Just reads the rest.
00534     }
00535 
00536     void refCanRead (int count, bool canRead)
00537     {
00538         call (buffer->canRead (count))
00539         .willReturn (canRead);
00540     }
00541 
00542     void testCanRead()
00543     {
00544         // The read buffer is initially empty:
00545         refCanRead (0, true);
00546         refCanRead (1, false);
00547         refCanRead (ReadBufferSize, false);
00548         // Fill the read buffer:
00549         refTryReadFromAtOnce (QByteArray (ReadBufferSize, '0'));
00550         refCanRead (0, true);
00551         refCanRead (1, true);
00552         refCanRead (ReadBufferSize, true);
00553         refCanRead (ReadBufferSize + 1, false);
00554     }
00555 
00556     void refCanWrite (int count, bool canWrite)
00557     {
00558         call (buffer->canWrite (count))
00559         .willReturn (canWrite);
00560     }
00561 
00562     void testCanWrite()
00563     {
00564         refCanWrite (0, true);
00565         refCanWrite (1, true);
00566         refCanWrite (10, true);
00567         refCanWrite (ReadBufferSize, true);
00568         refCanWrite (ReadBufferSize + 1, false);
00569         // Fill the write ead buffer but leave a single byte free.
00570         refWriteBytesSucceeds (QByteArray (ReadBufferSize - 1, '0'));
00571         refCanWrite (0, true);
00572         refCanWrite (1, true);
00573         refCanWrite (2, false);
00574         // Fill the write buffer completely.
00575         refWriteBytesSucceeds (QByteArray (1, '1'));
00576         refCanWrite (0, true);
00577         refCanWrite (1, false);
00578     }
00579 
00580     void testPeekAtMostThenReadAtMost()
00581     {
00582         QByteArray bytes (ReadBufferSize / 2, '1');
00583         refTryReadFromAtOnce (bytes);
00584 
00585         call (buffer->peekAtMost (ReadBufferSize))
00586         .willReturn (bytes);
00587 
00588         call (buffer->readAtMost (ReadBufferSize))
00589         .willReturn (bytes);
00590 
00591         refCanRead (0, true);
00592         refCanRead (1, false);
00593     }
00594 };
00595 
00596 CPPUNIT_TEST_SUITE_REGISTRATION (TcpSocketBufferTest);
00597 
00598 } // namespace Testing
00599 } // namespace Generics
00600 } // namespace Protocols