From 3ab9fe706dc92a1ee9fb94301d2eaf3e2017f592 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 23 Oct 2007 16:29:37 +0000 Subject: [PATCH] 2007-10-24 Tatsuhiro Tsujikawa * src/Piece.{h, cc}: Added SubPiece infrastructure to track down the data smaller than block length. A block length can be specified by constructor's argument. * src/DefaultPieceStorage.{h, cc} (getMissingPiece): Get a missing piece in the range of given FileEntry. This function is not used in the program yet. * src/Util.h: Added some macros. --- ChangeLog | 10 +++ TODO | 9 ++- src/DefaultBtInteractive.cc | 2 +- src/DefaultPieceStorage.cc | 34 +++++++++- src/DefaultPieceStorage.h | 3 + src/Piece.cc | 104 ++++++++++++++++++++++++++++-- src/Piece.h | 25 +++++++- src/PiecedSegment.cc | 4 +- src/Segment.cc | 2 +- src/Util.h | 5 +- test/DefaultPieceStorageTest.cc | 110 ++++++++++++++++++++++++++++++++ test/Makefile.am | 3 +- test/Makefile.in | 34 +++++----- test/PieceTest.cc | 106 ++++++++++++++++++++++++++++++ 14 files changed, 419 insertions(+), 32 deletions(-) create mode 100644 test/PieceTest.cc diff --git a/ChangeLog b/ChangeLog index 5e14bd00..23c2d505 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-10-24 Tatsuhiro Tsujikawa + + * src/Piece.{h, cc}: Added SubPiece infrastructure to track down + the data smaller than block length. + A block length can be specified by constructor's argument. + * src/DefaultPieceStorage.{h, cc} (getMissingPiece): + Get a missing piece in the range of given FileEntry. This function is + not used in the program yet. + * src/Util.h: Added some macros. + 2007-10-18 Tatsuhiro Tsujikawa Added HTTP/1.1 keep alive and pipelining support. diff --git a/TODO b/TODO index 3227fe43..31c1118e 100644 --- a/TODO +++ b/TODO @@ -49,8 +49,13 @@ * Rewrite MetaFileUtil * Reconsider the use of RecoverableException and FatalException * Limit the number of opening file to,say,100 in MultiDiskAdaptor. - -* Implement duplicate download checking in Bt * Implement the feature to treat http/ftp as auxuality download method for BitTorrent + +-- remaining features to be implemented for 0.12.0 release +* Reimplement ChecksumCommand(validation using 1 checksum for 1 file) +* Implement duplicate download checking in Bt * Add PeerListenCommand to DownloadEngine only when it is really necessary. +* ftp://USER:PASSWD@Servername automatic parsing +* improve --metalink-location field * Use content-type for PostDownloadHandler +* Torrent information diff --git a/src/DefaultBtInteractive.cc b/src/DefaultBtInteractive.cc index c3ab0676..33ad725d 100644 --- a/src/DefaultBtInteractive.cc +++ b/src/DefaultBtInteractive.cc @@ -236,7 +236,7 @@ void DefaultBtInteractive::addRequests() { if(pieceStorage->isEndGame()) { pieceNum = 1; } else { - int32_t blocks = DIV_FLOOR(btContext->getPieceLength(), BLOCK_LENGTH); + int32_t blocks = DIV_FLOOR(btContext->getPieceLength(), Piece::BLOCK_LENGTH); pieceNum = DIV_FLOOR(MAX_PENDING_REQUEST, blocks); } fillPiece(pieceNum); diff --git a/src/DefaultPieceStorage.cc b/src/DefaultPieceStorage.cc index 7435fbce..e75570f6 100644 --- a/src/DefaultPieceStorage.cc +++ b/src/DefaultPieceStorage.cc @@ -46,6 +46,7 @@ #include "message.h" #include "DefaultDiskWriterFactory.h" #include "DlAbortEx.h" +#include "Util.h" DefaultPieceStorage::DefaultPieceStorage(const DownloadContextHandle& downloadContext, const Option* option): downloadContext(downloadContext), @@ -194,6 +195,36 @@ PieceHandle DefaultPieceStorage::getMissingPiece() return checkOutPiece(bitfieldMan->getSparseMissingUnusedIndex()); } +PieceHandle DefaultPieceStorage::getMissingPiece(const FileEntryHandle& fileEntry) +{ + BitfieldMan temp(*bitfieldMan); + temp.clearFilter(); + temp.addFilter(fileEntry->getOffset(), fileEntry->getLength()); + temp.enableFilter(); + + int32_t firstPieceIndex = START_INDEX(fileEntry->getOffset(), downloadContext->getPieceLength()); + int32_t endPieceIndex = END_INDEX(fileEntry->getOffset(), fileEntry->getLength(), downloadContext->getPieceLength()); + if(!temp.isBitSet(firstPieceIndex) && !temp.isUseBitSet(firstPieceIndex)) { + PieceHandle piece = findUsedPiece(firstPieceIndex); + if(!piece.isNull()) { + if(piece->isRangeComplete(fileEntry->getOffset()-firstPieceIndex*downloadContext->getPieceLength(), + fileEntry->getLength() > downloadContext->getPieceLength() ? + downloadContext->getPieceLength():fileEntry->getLength())) { + temp.setBit(firstPieceIndex); + } + } + } + if(firstPieceIndex != endPieceIndex && !temp.isBitSet(endPieceIndex) && !temp.isUseBitSet(endPieceIndex)) { + PieceHandle piece = findUsedPiece(endPieceIndex); + if(!piece.isNull()) { + if(piece->isRangeComplete(0, fileEntry->getOffset()+fileEntry->getLength()-endPieceIndex*downloadContext->getPieceLength())) { + temp.setBit(endPieceIndex); + } + } + } + return checkOutPiece(temp.getSparseMissingUnusedIndex()); +} + PieceHandle DefaultPieceStorage::getMissingPiece(int32_t index) { if(hasPiece(index) || isPieceUsed(index)) { @@ -299,7 +330,7 @@ void DefaultPieceStorage::cancelPiece(const PieceHandle& piece) } bitfieldMan->unsetUseBit(piece->getIndex()); if(!isEndGame()) { - if(piece->countCompleteBlock() == 0) { + if(piece->getCompletedLength() == 0) { deleteUsedPiece(piece); } } @@ -376,6 +407,7 @@ void DefaultPieceStorage::clearFileFilter() // not unittested bool DefaultPieceStorage::downloadFinished() { + // TODO iterate all requested FileEntry and Call bitfieldMan->isBitSetOffsetRange() return bitfieldMan->isFilteredAllBitSet(); } diff --git a/src/DefaultPieceStorage.h b/src/DefaultPieceStorage.h index 9da473c2..9ef68a3e 100644 --- a/src/DefaultPieceStorage.h +++ b/src/DefaultPieceStorage.h @@ -45,6 +45,8 @@ class Option; extern typedef deque Pieces; class DiskWriterFactory; extern typedef SharedHandle DiskWriterFactoryHandle; +class FileEntry; +extern typedef SharedHandle FileEntryHandle; #define END_GAME_PIECE_NUM 20 @@ -97,6 +99,7 @@ public: virtual PieceHandle getMissingFastPiece(const PeerHandle& peer); virtual PieceHandle getMissingPiece(); + virtual PieceHandle getMissingPiece(const FileEntryHandle& fileEntry); virtual PieceHandle getMissingPiece(int32_t index); diff --git a/src/Piece.cc b/src/Piece.cc index 6aec598e..cabaa8db 100644 --- a/src/Piece.cc +++ b/src/Piece.cc @@ -36,16 +36,17 @@ #include "Util.h" #include "BitfieldManFactory.h" -Piece::Piece():index(0), length(0), bitfield(0) {} +Piece::Piece():index(0), length(0), _blockLength(BLOCK_LENGTH), bitfield(0) {} -Piece::Piece(int32_t index, int32_t length):index(index), length(length) { +Piece::Piece(int32_t index, int32_t length, int32_t blockLength):index(index), length(length), _blockLength(blockLength) { bitfield = - BitfieldManFactory::getFactoryInstance()->createBitfieldMan(BLOCK_LENGTH, length); + BitfieldManFactory::getFactoryInstance()->createBitfieldMan(_blockLength, length); } Piece::Piece(const Piece& piece) { index = piece.index; length = piece.length; + _blockLength = piece._blockLength; if(piece.bitfield == 0) { bitfield = 0; } else { @@ -53,10 +54,10 @@ Piece::Piece(const Piece& piece) { } } - void Piece::completeBlock(int32_t blockIndex) { bitfield->setBit(blockIndex); bitfield->unsetUseBit(blockIndex); + removeSubPiece(blockIndex); } void Piece::clearAllBlock() { @@ -106,7 +107,7 @@ void Piece::reconfigure(int32_t length) { this->length = length; bitfield = - BitfieldManFactory::getFactoryInstance()->createBitfieldMan(BLOCK_LENGTH, length); + BitfieldManFactory::getFactoryInstance()->createBitfieldMan(_blockLength, length); } void Piece::setBitfield(const unsigned char* bitfield, int32_t len) @@ -114,3 +115,96 @@ void Piece::setBitfield(const unsigned char* bitfield, int32_t len) this->bitfield->setBitfield(bitfield, len); } +void Piece::addSubPiece(const PieceHandle& subPiece) +{ + _subPieces.push_back(subPiece); +} + +PieceHandle Piece::getSubPiece(int32_t blockIndex) +{ + Pieces::iterator itr = getSubPieceIterator(blockIndex); + if(itr == _subPieces.end()) { + return 0; + } else { + return *itr; + } +} + +void Piece::removeSubPiece(int32_t blockIndex) +{ + Pieces::iterator itr = getSubPieceIterator(blockIndex); + if(itr != _subPieces.end()) { + _subPieces.erase(itr); + } +} + +Pieces::iterator Piece::getSubPieceIterator(int32_t blockIndex) +{ + for(Pieces::iterator itr = _subPieces.begin(); itr != _subPieces.end(); ++itr) { + if((*itr)->getIndex() == blockIndex) { + return itr; + } + } + return _subPieces.end(); +} + +bool Piece::isRangeComplete(int32_t offset, int32_t length) +{ + int32_t startIndex = START_INDEX(offset, _blockLength); + int32_t endIndex = END_INDEX(offset, length, _blockLength); + if(countBlock() <= endIndex) { + endIndex = countBlock()-1; + } + if(startIndex+1 < endIndex) { + if(!bitfield->isBitRangeSet(startIndex+1, endIndex-1)) { + return false; + } + } + if(startIndex == endIndex) { + if(hasBlock(startIndex)) { + return true; + } + PieceHandle subPiece = getSubPiece(startIndex); + if(subPiece.isNull()) { + return false; + } + return subPiece->isRangeComplete(offset, length); + } else { + if(!hasBlock(startIndex)) { + PieceHandle subPiece = getSubPiece(startIndex); + if(subPiece.isNull()) { + return false; + } + if(!subPiece->isRangeComplete(offset-startIndex*_blockLength, length)) { + return false; + } + } + if(!hasBlock(endIndex)) { + PieceHandle subPiece = getSubPiece(endIndex); + if(subPiece.isNull()) { + return false; + } + if(!subPiece->isRangeComplete(0, offset+length-endIndex*_blockLength)) { + return false; + } + } + return true; + } +} + +int32_t Piece::getCompletedLength() +{ + int32_t length = 0; + for(int32_t i = 0; i < countBlock(); ++i) { + if(hasBlock(i)) { + length += getBlockLength(i); + } else { + PieceHandle subPiece = getSubPiece(i); + if(!subPiece.isNull()) { + length += subPiece->getCompletedLength(); + } + } + } + return length; +} + diff --git a/src/Piece.h b/src/Piece.h index f9f04788..d1428709 100644 --- a/src/Piece.h +++ b/src/Piece.h @@ -38,17 +38,25 @@ #include "BitfieldMan.h" #include "common.h" -#define BLOCK_LENGTH (16*1024) +class Piece; +typedef SharedHandle PieceHandle; +typedef deque Pieces; class Piece { private: int32_t index; int32_t length; + int32_t _blockLength; BitfieldMan* bitfield; + + Pieces _subPieces; public: + + static const int32_t BLOCK_LENGTH = 16*1024; + Piece(); - Piece(int32_t index, int32_t length); + Piece(int32_t index, int32_t length, int32_t blockLength = BLOCK_LENGTH); Piece(const Piece& piece); @@ -118,6 +126,19 @@ public: return bitfield->isUseBitSet(index); } + void addSubPiece(const PieceHandle& subPiece); + + PieceHandle getSubPiece(int32_t blockIndex); + + void removeSubPiece(int32_t blockIndex); + + Pieces::iterator getSubPieceIterator(int32_t blockIndex); + + bool isRangeComplete(int32_t offset, int32_t length); + + // Calculates completed length, taking into account SubPieces + int32_t getCompletedLength(); + /** * Loses current bitfield state. */ diff --git a/src/PiecedSegment.cc b/src/PiecedSegment.cc index c958a7be..c7de5c92 100644 --- a/src/PiecedSegment.cc +++ b/src/PiecedSegment.cc @@ -38,7 +38,7 @@ PiecedSegment::PiecedSegment(int32_t pieceLength, const PieceHandle& piece): _pieceLength(pieceLength), _overflowLength(0), _piece(piece) { - _writtenLength = _piece->getAllMissingBlockIndexes().front()*BLOCK_LENGTH; + _writtenLength = _piece->getAllMissingBlockIndexes().front()*_piece->getBlockLength(); } PiecedSegment::~PiecedSegment() {} @@ -75,7 +75,7 @@ void PiecedSegment::updateWrittenLength(int32_t bytes) _overflowLength = newWrittenLength-_piece->getLength(); newWrittenLength = _piece->getLength(); } - for(int32_t i = _writtenLength/BLOCK_LENGTH; i < newWrittenLength/BLOCK_LENGTH; ++i) { + for(int32_t i = _writtenLength/_piece->getBlockLength(); i < newWrittenLength/_piece->getBlockLength(); ++i) { _piece->completeBlock(i); } if(newWrittenLength == _piece->getLength()) { diff --git a/src/Segment.cc b/src/Segment.cc index 79f8ce20..9c8127b3 100644 --- a/src/Segment.cc +++ b/src/Segment.cc @@ -42,7 +42,7 @@ ostream& operator<<(ostream& o, const Segment& segment) { if(segment.complete()) { o << "writtenLength = " << segment._piece->getLength(); } else { - o << "writtenLength = " << (segment._piece->getMissingUnusedBlockIndex()-1)*BLOCK_LENGTH; + o << "writtenLength = " << (segment._piece->getMissingUnusedBlockIndex()-1)*segment._piece->getBlockLength(); } return o; } diff --git a/src/Util.h b/src/Util.h index 7f85d699..f57c228b 100644 --- a/src/Util.h +++ b/src/Util.h @@ -43,7 +43,10 @@ #include #include -#define STRTOLL(X) strtoll(X, (char**)NULL, 10); +#define STRTOLL(X) strtoll(X, (char**)NULL, 10) + +#define START_INDEX(OFFSET, PIECE_LENGTH) ((OFFSET)/(PIECE_LENGTH)) +#define END_INDEX(OFFSET, LENGTH, PIECE_LENGTH) (((OFFSET)+(LENGTH)-1)/(PIECE_LENGTH)) class Util { public: diff --git a/test/DefaultPieceStorageTest.cc b/test/DefaultPieceStorageTest.cc index 237fd2b1..0a529788 100644 --- a/test/DefaultPieceStorageTest.cc +++ b/test/DefaultPieceStorageTest.cc @@ -7,6 +7,7 @@ #include "Piece.h" #include "Peer.h" #include "Option.h" +#include "MockBtContext.h" #include using namespace std; @@ -22,6 +23,8 @@ class DefaultPieceStorageTest:public CppUnit::TestFixture { CPPUNIT_TEST(testGetPiece); CPPUNIT_TEST(testGetPieceInUsedPieces); CPPUNIT_TEST(testGetPieceCompletedPiece); + CPPUNIT_TEST(testGetMissingPiece_fileEntry); + CPPUNIT_TEST(testCancelPiece); CPPUNIT_TEST_SUITE_END(); private: BtContextHandle btContext; @@ -43,6 +46,12 @@ public: option = new Option(); } + void tearDown() + { + delete option; + option = 0; + } + void testGetTotalLength(); void testGetMissingPiece(); void testGetMissingFastPiece(); @@ -51,6 +60,8 @@ public: void testGetPiece(); void testGetPieceInUsedPieces(); void testGetPieceCompletedPiece(); + void testGetMissingPiece_fileEntry(); + void testCancelPiece(); }; @@ -153,3 +164,102 @@ void DefaultPieceStorageTest::testGetPieceCompletedPiece() { CPPUNIT_ASSERT_EQUAL((int32_t)128, pieceGot->getLength()); CPPUNIT_ASSERT_EQUAL(true, pieceGot->pieceComplete()); } + +void DefaultPieceStorageTest::testGetMissingPiece_fileEntry() +{ + // - 32KB + // +--------+ + // |11111222| + int32_t pieceLength = 256*1024; + int64_t totalLength = 1*pieceLength; + int32_t blockLength = 16*1024; + Strings uris1; + uris1.push_back("http://localhost/src/file1.txt"); + Strings uris2; + uris2.push_back("http://localhost/src/file2.txt"); + FileEntryHandle file1 = new FileEntry("src/file1.txt", 150*1024, 0/*, uris1*/); + FileEntryHandle file2 = new FileEntry("src/file2.txt", 106*1024, file1->getLength() /*, uris2*/); + + MockBtContextHandle dctx = new MockBtContext(); + dctx->setPieceLength(pieceLength); + dctx->setTotalLength(totalLength); + dctx->addFileEntry(file1); + dctx->addFileEntry(file2); + + DefaultPieceStorageHandle ps = new DefaultPieceStorage(dctx, option); + + PieceHandle p = ps->getMissingPiece(file1); + CPPUNIT_ASSERT(!p.isNull()); + CPPUNIT_ASSERT_EQUAL((int32_t)0, p->getIndex()); + + for(int32_t i = 0; i < 9; ++i) { + p->completeBlock(i); + } + PieceHandle subPiece = new Piece(9, blockLength, 1); + p->addSubPiece(subPiece); + + ps->cancelPiece(p); + + // Piece index = 0 should be retrieved again because the part of file1 is + // not complete + PieceHandle p2 = ps->getMissingPiece(file1); + CPPUNIT_ASSERT(!p2.isNull()); + CPPUNIT_ASSERT_EQUAL((int32_t)0, p2->getIndex()); + + // Make the part of file1 complete + for(int32_t i = 0; i < 6*1024; ++i) { + p2->getSubPiece(9)->completeBlock(i); + } + ps->cancelPiece(p2); + + // Null Piece should be retrieved + CPPUNIT_ASSERT(ps->getMissingPiece(file1).isNull()); + + // Next, I retrive the piece giving file2 + PieceHandle p3 = ps->getMissingPiece(file2); + CPPUNIT_ASSERT(!p3.isNull()); + CPPUNIT_ASSERT_EQUAL((int32_t)0, p3->getIndex()); + + // Make the part of file2 complete + for(int32_t i = 6*1024; i < 16*1024; ++i) { + p3->getSubPiece(9)->completeBlock(i); + } + for(int32_t i = 10; i < 16; ++i) { + p3->completeBlock(i); + } + ps->cancelPiece(p3); + + // Null Piece should be retrieved + CPPUNIT_ASSERT(ps->getMissingPiece(file2).isNull()); +} + +void DefaultPieceStorageTest::testCancelPiece() +{ + int32_t pieceLength = 256*1024; + int64_t totalLength = 32*pieceLength; // <-- make the number of piece greater than END_GAME_PIECE_NUM + int32_t blockLength = 16*1024; + Strings uris1; + uris1.push_back("http://localhost/src/file1.txt"); + FileEntryHandle file1 = new FileEntry("src/file1.txt", totalLength, 0 /*, uris1*/); + + MockBtContextHandle dctx = new MockBtContext(); + dctx->setPieceLength(pieceLength); + dctx->setTotalLength(totalLength); + dctx->addFileEntry(file1); + + DefaultPieceStorageHandle ps = new DefaultPieceStorage(dctx, option); + + PieceHandle p = ps->getMissingPiece(file1); + + PieceHandle subPiece = new Piece(0, blockLength, 1); + subPiece->completeBlock(0); + p->addSubPiece(subPiece); + + ps->cancelPiece(p); + + // See the sub piece is also hibernated... + PieceHandle p2 = ps->getMissingPiece(file1); + + CPPUNIT_ASSERT(!p2->getSubPiece(0).isNull()); + +} diff --git a/test/Makefile.am b/test/Makefile.am index 547f46c0..e99d16d4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,8 @@ TESTS = aria2c check_PROGRAMS = $(TESTS) aria2c_SOURCES = AllTest.cc\ + PieceTest.cc\ + DefaultPieceStorageTest.cc\ SegmentTest.cc\ GrowSegmentTest.cc\ SingleFileAllocationIteratorTest.cc\ @@ -67,7 +69,6 @@ aria2c_SOURCES += BtAllowedFastMessageTest.cc\ DefaultBtContextTest.cc\ DefaultBtMessageDispatcherTest.cc\ DefaultBtRequestFactoryTest.cc\ - DefaultPieceStorageTest.cc\ MockBtMessage.h\ MockBtMessageDispatcher.h\ MockBtMessageFactory.h\ diff --git a/test/Makefile.in b/test/Makefile.in index 36855d64..047dafae 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -61,7 +61,6 @@ check_PROGRAMS = $(am__EXEEXT_1) @ENABLE_BITTORRENT_TRUE@ DefaultBtContextTest.cc\ @ENABLE_BITTORRENT_TRUE@ DefaultBtMessageDispatcherTest.cc\ @ENABLE_BITTORRENT_TRUE@ DefaultBtRequestFactoryTest.cc\ -@ENABLE_BITTORRENT_TRUE@ DefaultPieceStorageTest.cc\ @ENABLE_BITTORRENT_TRUE@ MockBtMessage.h\ @ENABLE_BITTORRENT_TRUE@ MockBtMessageDispatcher.h\ @ENABLE_BITTORRENT_TRUE@ MockBtMessageFactory.h\ @@ -112,7 +111,8 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = am__EXEEXT_1 = aria2c$(EXEEXT) -am__aria2c_SOURCES_DIST = AllTest.cc SegmentTest.cc GrowSegmentTest.cc \ +am__aria2c_SOURCES_DIST = AllTest.cc PieceTest.cc \ + DefaultPieceStorageTest.cc SegmentTest.cc GrowSegmentTest.cc \ SingleFileAllocationIteratorTest.cc \ DefaultBtProgressInfoFileTest.cc \ SingleFileDownloadContextTest.cc RequestGroupTest.cc \ @@ -140,17 +140,17 @@ am__aria2c_SOURCES_DIST = AllTest.cc SegmentTest.cc GrowSegmentTest.cc \ BtRequestMessageTest.cc BtSuggestPieceMessageTest.cc \ BtUnchokeMessageTest.cc DefaultBtAnnounceTest.cc \ DefaultBtContextTest.cc DefaultBtMessageDispatcherTest.cc \ - DefaultBtRequestFactoryTest.cc DefaultPieceStorageTest.cc \ - MockBtMessage.h MockBtMessageDispatcher.h \ - MockBtMessageFactory.h ShaVisitorTest.cc \ - DefaultPeerListProcessorTest.cc AnnounceListTest.cc \ - DefaultPeerStorageTest.cc MockPeerStorage.h DataTest.cc \ - DictionaryTest.cc ListTest.cc MetaFileUtilTest.cc \ - MultiDiskAdaptorTest.cc ByteArrayDiskWriterTest.cc PeerTest.cc \ - PeerMessageUtilTest.cc ShareRatioSeedCriteriaTest.cc \ - BtRegistryTest.cc MultiFileAllocationIteratorTest.cc \ - BtDependencyTest.cc BtPostDownloadHandlerTest.cc \ - TimeSeedCriteriaTest.cc MetalinkerTest.cc MetalinkEntryTest.cc \ + DefaultBtRequestFactoryTest.cc MockBtMessage.h \ + MockBtMessageDispatcher.h MockBtMessageFactory.h \ + ShaVisitorTest.cc DefaultPeerListProcessorTest.cc \ + AnnounceListTest.cc DefaultPeerStorageTest.cc \ + MockPeerStorage.h DataTest.cc DictionaryTest.cc ListTest.cc \ + MetaFileUtilTest.cc MultiDiskAdaptorTest.cc \ + ByteArrayDiskWriterTest.cc PeerTest.cc PeerMessageUtilTest.cc \ + ShareRatioSeedCriteriaTest.cc BtRegistryTest.cc \ + MultiFileAllocationIteratorTest.cc BtDependencyTest.cc \ + BtPostDownloadHandlerTest.cc TimeSeedCriteriaTest.cc \ + MetalinkerTest.cc MetalinkEntryTest.cc \ Xml2MetalinkProcessorTest.cc Metalink2RequestGroupTest.cc \ MetalinkPostDownloadHandlerTest.cc @ENABLE_MESSAGE_DIGEST_TRUE@am__objects_1 = \ @@ -178,7 +178,6 @@ am__aria2c_SOURCES_DIST = AllTest.cc SegmentTest.cc GrowSegmentTest.cc \ @ENABLE_BITTORRENT_TRUE@ DefaultBtContextTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DefaultBtMessageDispatcherTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DefaultBtRequestFactoryTest.$(OBJEXT) \ -@ENABLE_BITTORRENT_TRUE@ DefaultPieceStorageTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ ShaVisitorTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ DefaultPeerListProcessorTest.$(OBJEXT) \ @ENABLE_BITTORRENT_TRUE@ AnnounceListTest.$(OBJEXT) \ @@ -202,7 +201,8 @@ am__aria2c_SOURCES_DIST = AllTest.cc SegmentTest.cc GrowSegmentTest.cc \ @ENABLE_METALINK_TRUE@ Xml2MetalinkProcessorTest.$(OBJEXT) \ @ENABLE_METALINK_TRUE@ Metalink2RequestGroupTest.$(OBJEXT) \ @ENABLE_METALINK_TRUE@ MetalinkPostDownloadHandlerTest.$(OBJEXT) -am_aria2c_OBJECTS = AllTest.$(OBJEXT) SegmentTest.$(OBJEXT) \ +am_aria2c_OBJECTS = AllTest.$(OBJEXT) PieceTest.$(OBJEXT) \ + DefaultPieceStorageTest.$(OBJEXT) SegmentTest.$(OBJEXT) \ GrowSegmentTest.$(OBJEXT) \ SingleFileAllocationIteratorTest.$(OBJEXT) \ DefaultBtProgressInfoFileTest.$(OBJEXT) \ @@ -412,7 +412,8 @@ target_cpu = @target_cpu@ target_os = @target_os@ target_vendor = @target_vendor@ TESTS = aria2c -aria2c_SOURCES = AllTest.cc SegmentTest.cc GrowSegmentTest.cc \ +aria2c_SOURCES = AllTest.cc PieceTest.cc DefaultPieceStorageTest.cc \ + SegmentTest.cc GrowSegmentTest.cc \ SingleFileAllocationIteratorTest.cc \ DefaultBtProgressInfoFileTest.cc \ SingleFileDownloadContextTest.cc RequestGroupTest.cc \ @@ -561,6 +562,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParameterizedStringParserTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerMessageUtilTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PeerTest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PieceTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestFactoryTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestGroupManTest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RequestGroupTest.Po@am__quote@ diff --git a/test/PieceTest.cc b/test/PieceTest.cc new file mode 100644 index 00000000..0c554b8d --- /dev/null +++ b/test/PieceTest.cc @@ -0,0 +1,106 @@ +#include "Piece.h" +#include +#include + +using namespace std; + +class PieceTest:public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE(PieceTest); + CPPUNIT_TEST(testCompleteBlock); + CPPUNIT_TEST(testIsRangeComplete); + CPPUNIT_TEST(testIsRangeComplete_subPiece); + CPPUNIT_TEST(testGetCompletedLength); + CPPUNIT_TEST_SUITE_END(); +private: + +public: + void setUp() {} + + void testCompleteBlock(); + void testIsRangeComplete(); + void testIsRangeComplete_subPiece(); + void testGetCompletedLength(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION( PieceTest ); + +void PieceTest::testCompleteBlock() +{ + int32_t blockLength = 32*1024; + Piece p(0, blockLength*10, blockLength); + + // 5 is a block index inside the Piece p. + PieceHandle subPiece = new Piece(5, blockLength, 1); + p.addSubPiece(subPiece); + + CPPUNIT_ASSERT(!p.getSubPiece(5).isNull()); + + // When block is complete, then its associated sub piece must be deleted. + p.completeBlock(5); + + CPPUNIT_ASSERT(p.getSubPiece(5).isNull()); +} + +void PieceTest::testIsRangeComplete() +{ + int32_t blockLength = 16*1024; + Piece p(0, blockLength*10, blockLength); + + CPPUNIT_ASSERT(!p.isRangeComplete(8*1024, 16*1024)); + + p.completeBlock(0); + CPPUNIT_ASSERT(!p.isRangeComplete(8*1024, 16*1024)); + CPPUNIT_ASSERT(p.isRangeComplete(8*1024, 8*1024)); + + p.completeBlock(1); + CPPUNIT_ASSERT(p.isRangeComplete(8*1024, 16*1024)); +} + +void PieceTest::testIsRangeComplete_subPiece() +{ + int32_t blockLength = 16*1024; + Piece p(0, blockLength*10, blockLength); + + CPPUNIT_ASSERT(!p.isRangeComplete(8*1024, 32*1024)); + + PieceHandle startSubPiece = new Piece(0, blockLength, 1); + p.addSubPiece(startSubPiece); + + PieceHandle endSubPiece = new Piece(2, blockLength, 1); + p.addSubPiece(endSubPiece); + + p.completeBlock(1); + + CPPUNIT_ASSERT(!p.isRangeComplete(8*1024, 32*1024)); + + for(int32_t i = 8*1024; i < blockLength; ++i) { + startSubPiece->completeBlock(i); + } + + CPPUNIT_ASSERT(!p.isRangeComplete(8*1024, 32*1024)); + + for(int32_t i = 0; i < 8*1024; ++i) { + endSubPiece->completeBlock(i); + } + CPPUNIT_ASSERT(p.isRangeComplete(8*1024, 32*1024)); +} + +void PieceTest::testGetCompletedLength() +{ + int32_t blockLength = 16*1024; + Piece p(0, blockLength*10, blockLength); + + PieceHandle subPiece = new Piece(0, blockLength, 1); + for(int32_t i = 0; i < blockLength-1; ++i) { + subPiece->completeBlock(i); + } + p.addSubPiece(subPiece); + + p.completeBlock(1); + p.completeBlock(2); + p.completeBlock(9); + + CPPUNIT_ASSERT_EQUAL(blockLength*3+blockLength-1, p.getCompletedLength()); +}