Pārlūkot izejas kodu

2007-10-24 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	* 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.
Tatsuhiro Tsujikawa 18 gadi atpakaļ
vecāks
revīzija
3ab9fe706d

+ 10 - 0
ChangeLog

@@ -1,3 +1,13 @@
+2007-10-24  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	* 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  <tujikawa at rednoah dot com>
 
 	Added HTTP/1.1 keep alive and pipelining support.

+ 6 - 1
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 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
-* Implement the feature to treat http/ftp as auxuality download method for BitTorrent
 * 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

+ 1 - 1
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);

+ 33 - 1
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();
 }
 

+ 3 - 0
src/DefaultPieceStorage.h

@@ -45,6 +45,8 @@ class Option;
 extern typedef deque<PieceHandle> Pieces;
 class DiskWriterFactory;
 extern typedef SharedHandle<DiskWriterFactory> DiskWriterFactoryHandle;
+class FileEntry;
+extern typedef SharedHandle<FileEntry> 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);
 

+ 99 - 5
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;
+}
+

+ 23 - 2
src/Piece.h

@@ -38,17 +38,25 @@
 #include "BitfieldMan.h"
 #include "common.h"
 
-#define BLOCK_LENGTH (16*1024)
+class Piece;
+typedef SharedHandle<Piece> PieceHandle;
+typedef deque<PieceHandle> 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.
    */

+ 2 - 2
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()) {

+ 1 - 1
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;
 }

+ 4 - 1
src/Util.h

@@ -43,7 +43,10 @@
 #include <deque>
 #include <ostream>
 
-#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:

+ 110 - 0
test/DefaultPieceStorageTest.cc

@@ -7,6 +7,7 @@
 #include "Piece.h"
 #include "Peer.h"
 #include "Option.h"
+#include "MockBtContext.h"
 #include <cppunit/extensions/HelperMacros.h>
 
 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());
+  
+}

+ 2 - 1
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\

+ 18 - 16
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@

+ 106 - 0
test/PieceTest.cc

@@ -0,0 +1,106 @@
+#include "Piece.h"
+#include <string>
+#include <cppunit/extensions/HelperMacros.h>
+
+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());
+}