diff --git a/ChangeLog b/ChangeLog index 008b32e2..0cfef774 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2010-03-06 Tatsuhiro Tsujikawa + + Added changeUri XML-RPC method. This method removes/adds URIs + dynamically. + * doc/aria2c.1.txt + * src/AbstractCommand.cc + * src/DownloadContext.cc + * src/DownloadContext.h + * src/FileEntry.cc + * src/FileEntry.h + * src/Request.cc + * src/Request.h + * src/RequestGroup.cc + * src/RequestGroupMan.cc + * src/XmlRpcMethodFactory.cc + * src/XmlRpcMethodImpl.cc + * src/XmlRpcMethodImpl.h + * test/FileEntryTest.cc + * test/XmlRpcMethodTest.cc + 2010-03-06 Tatsuhiro Tsujikawa Rewritten copy ctor of RequestSlot to use initialization list. diff --git a/doc/aria2c.1 b/doc/aria2c.1 index 3fd17b7c..d8e756d6 100644 --- a/doc/aria2c.1 +++ b/doc/aria2c.1 @@ -2,12 +2,12 @@ .\" Title: aria2c .\" Author: [FIXME: author] [see http://docbook.sf.net/el/author] .\" Generator: DocBook XSL Stylesheets v1.75.2 -.\" Date: 02/25/2010 +.\" Date: 03/06/2010 .\" Manual: Aria2 Manual .\" Source: Aria2 1.9.0a .\" Language: English .\" -.TH "ARIA2C" "1" "02/25/2010" "Aria2 1\&.9\&.0a" "Aria2 Manual" +.TH "ARIA2C" "1" "03/06/2010" "Aria2 1\&.9\&.0a" "Aria2 Manual" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -2467,6 +2467,10 @@ This method changes the position of the download denoted by \fIgid\fR\&. \fIpos\ .sp For example, if GID#1 is placed in position 3, aria2\&.changePosition(1, \-1, POS_CUR) will change its position to 2\&. Additional aria2\&.changePosition(1, 0, POS_SET) will change its position to 0(the beginning of the queue)\&. .sp +\fBaria2\&.changeUri\fR \fIgid, fileIndex, delUris, addUris[, position]\fR +.sp +This method removes URIs in \fIdelUris\fR from and appends URIs in \fIaddUris\fR to download denoted by \fIgid\fR\&. \fIdelUris\fR and \fIaddUris\fR are list of string\&. A download can contain multiple files and URIs are attached to each file\&. \fIfileIndex\fR is used to select which file to remove/attach given URIs\&. \fIposition\fR is used to specify where URIs are inserted in the existing waiting URI list\&. \fIposition\fR is 0\-based\&. When \fIposition\fR is omitted, URIs are appended to the back of the list\&. This method first execute removal and then addition\&. \fIposition\fR is the position after URIs are removed, not the position when this method is called\&. When removing URI, if same URIs exist in download, only one of them is removed for each URI in \fIdelUris\fR\&. In other words, there are three URIs "http://example\&.org/aria2" and you want remove them all, you have to specify (at least) 3 "http://example\&.org/aria2" in \fIdelUris\fR\&. This method returns a list which contains 2 integers\&. The first integer is the number of URIs deleted\&. The second integer is the number of URIs added\&. +.sp \fBaria2\&.getOption\fR \fIgid\fR .sp This method returns options of the download denoted by \fIgid\fR\&. The response is of type struct\&. Its key is the name of option\&. The value type is string\&. diff --git a/doc/aria2c.1.html b/doc/aria2c.1.html index 16480e7e..c47ddc13 100644 --- a/doc/aria2c.1.html +++ b/doc/aria2c.1.html @@ -3176,6 +3176,24 @@ destination position.

-1, POS_CUR) will change its position to 2. Additional aria2.changePosition(1, 0, POS_SET) will change its position to 0(the beginning of the queue).

+

aria2.changeUri gid, fileIndex, delUris, addUris[, position]

+

This method removes URIs in delUris from and appends URIs in +addUris to download denoted by gid. delUris and addUris are +list of string. A download can contain multiple files and URIs are +attached to each file. fileIndex is used to select which file to +remove/attach given URIs. position is used to specify where URIs +are inserted in the existing waiting URI list. position is +0-based. When position is omitted, URIs are appended to the back of +the list. This method first execute removal and then +addition. position is the position after URIs are removed, not the +position when this method is called. When removing URI, if same URIs +exist in download, only one of them is removed for each URI in +delUris. In other words, there are three URIs +"http://example.org/aria2" and you want remove them all, you have to +specify (at least) 3 "http://example.org/aria2" in delUris. This +method returns a list which contains 2 integers. The first integer is +the number of URIs deleted. The second integer is the number of URIs +added.

aria2.getOption gid

This method returns options of the download denoted by gid. The response is of type struct. Its key is the name of option. The value type @@ -3722,7 +3740,7 @@ files in the program, then also delete it here.


diff --git a/doc/aria2c.1.txt b/doc/aria2c.1.txt index 6e945d96..8da9a3ec 100644 --- a/doc/aria2c.1.txt +++ b/doc/aria2c.1.txt @@ -1465,6 +1465,26 @@ For example, if GID#1 is placed in position 3, aria2.changePosition(1, aria2.changePosition(1, 0, POS_SET) will change its position to 0(the beginning of the queue). +*aria2.changeUri* 'gid, fileIndex, delUris, addUris[, position]' + +This method removes URIs in 'delUris' from and appends URIs in +'addUris' to download denoted by 'gid'. 'delUris' and 'addUris' are +list of string. A download can contain multiple files and URIs are +attached to each file. 'fileIndex' is used to select which file to +remove/attach given URIs. 'position' is used to specify where URIs +are inserted in the existing waiting URI list. 'position' is +0-based. When 'position' is omitted, URIs are appended to the back of +the list. This method first execute removal and then +addition. 'position' is the position after URIs are removed, not the +position when this method is called. When removing URI, if same URIs +exist in download, only one of them is removed for each URI in +'delUris'. In other words, there are three URIs +"http://example.org/aria2" and you want remove them all, you have to +specify (at least) 3 "http://example.org/aria2" in 'delUris'. This +method returns a list which contains 2 integers. The first integer is +the number of URIs deleted. The second integer is the number of URIs +added. + *aria2.getOption* 'gid' This method returns options of the download denoted by 'gid'. The diff --git a/src/AbstractCommand.cc b/src/AbstractCommand.cc index e57f35af..801190b8 100644 --- a/src/AbstractCommand.cc +++ b/src/AbstractCommand.cc @@ -107,6 +107,14 @@ bool AbstractCommand::execute() { //logger->debug("CUID#%d - finished.", cuid); return true; } + if(!req.isNull() && req->removalRequested()) { + if(logger->debug()) { + logger->debug + ("CUID#%d - Discard original URI=%s because it is requested.", + cuid, req->getUrl().c_str()); + } + return prepareForRetry(0); + } // TODO it is not needed to check other PeerStats every time. // Find faster Request when no segment is available. if(!req.isNull() && _fileEntry->countPooledRequest() > 0 && diff --git a/src/DownloadContext.cc b/src/DownloadContext.cc index ca027572..c5225eb4 100644 --- a/src/DownloadContext.cc +++ b/src/DownloadContext.cc @@ -39,6 +39,7 @@ #include "FileEntry.h" #include "StringFormat.h" #include "util.h" +#include "wallclock.h" namespace aria2 { @@ -58,7 +59,7 @@ DownloadContext::DownloadContext(size_t pieceLength, _knowsTotalLength(true), _ownerRequestGroup(0), _downloadStartTime(0), - _downloadStopTime(_downloadStartTime) + _downloadStopTime(0) { SharedHandle fileEntry (new FileEntry(util::escapePath(path), totalLength, 0)); @@ -67,12 +68,12 @@ DownloadContext::DownloadContext(size_t pieceLength, void DownloadContext::resetDownloadStartTime() { - _downloadStartTime.reset(); + _downloadStartTime = global::wallclock; } void DownloadContext::resetDownloadStopTime() { - _downloadStopTime.reset(); + _downloadStopTime = global::wallclock; } int64_t DownloadContext::calculateSessionTime() const diff --git a/src/DownloadContext.h b/src/DownloadContext.h index e2c5da42..adbe534b 100644 --- a/src/DownloadContext.h +++ b/src/DownloadContext.h @@ -233,6 +233,11 @@ public: void resetDownloadStopTime(); + const Time& getDownloadStopTime() const + { + return _downloadStopTime; + } + int64_t calculateSessionTime() const; // Returns FileEntry at given offset. SharedHandle() is diff --git a/src/FileEntry.cc b/src/FileEntry.cc index adc12da4..db0ff1b9 100644 --- a/src/FileEntry.cc +++ b/src/FileEntry.cc @@ -218,7 +218,9 @@ void FileEntry::storePool(const SharedHandle& request) void FileEntry::poolRequest(const SharedHandle& request) { removeRequest(request); - storePool(request); + if(!request->removalRequested()) { + storePool(request); + } } bool FileEntry::removeRequest(const SharedHandle& request) @@ -330,4 +332,49 @@ void FileEntry::releaseRuntimeResource() _uriResults.clear(); } +template +static InputIterator findRequestByUri +(InputIterator first, InputIterator last, const T& uri) +{ + for(; first != last; ++first) { + if(!(*first)->removalRequested() && (*first)->getUrl() == uri) { + return first; + } + } + return last; +} + +bool FileEntry::removeUri(const std::string& uri) +{ + std::deque::iterator itr = + std::find(_spentUris.begin(), _spentUris.end(), uri); + if(itr == _spentUris.end()) { + itr = std::find(_uris.begin(), _uris.end(), uri); + if(itr == _uris.end()) { + return false; + } else { + _uris.erase(itr); + return true; + } + } else { + _spentUris.erase(itr); + SharedHandle req; + std::deque >::iterator riter = + findRequestByUri(_inFlightRequests.begin(), _inFlightRequests.end(), uri); + if(riter == _inFlightRequests.end()) { + riter = findRequestByUri(_requestPool.begin(), _requestPool.end(), uri); + if(riter == _requestPool.end()) { + return true; + } else { + req = *riter; + _requestPool.erase(riter); + } + } else { + req = *riter; + } + req->requestRemoval(); + return true; + } +} + } // namespace aria2 diff --git a/src/FileEntry.h b/src/FileEntry.h index eac6980c..0017c925 100644 --- a/src/FileEntry.h +++ b/src/FileEntry.h @@ -129,15 +129,43 @@ public: return _spentUris; } - void setUris(const std::vector& uris) + size_t setUris(const std::vector& uris) { - _uris = std::deque(uris.begin(), uris.end()); + _uris.clear(); + return addUris(uris.begin(), uris.end()); } template - void addUris(InputIterator first, InputIterator last) + size_t addUris(InputIterator first, InputIterator last) { - _uris.insert(_uris.end(), first, last); + size_t count = 0; + for(; first != last; ++first) { + if(addUri(*first)) { + ++count; + } + } + return count; + } + + bool addUri(const std::string& uri) + { + if(Request().setUrl(uri)) { + _uris.push_back(uri); + return true; + } else { + return false; + } + } + + bool insertUri(const std::string& uri, size_t pos) + { + if(Request().setUrl(uri)) { + pos = std::min(pos, _uris.size()); + _uris.insert(_uris.begin()+pos, uri); + return true; + } else { + return false; + } } // Inserts _uris and _spentUris into uris. @@ -234,6 +262,8 @@ public: { return _originalName; } + + bool removeUri(const std::string& uri); }; // Returns the first FileEntry which isRequested() method returns diff --git a/src/Request.cc b/src/Request.cc index 76fffc5b..22b49ced 100644 --- a/src/Request.cc +++ b/src/Request.cc @@ -63,7 +63,8 @@ Request::Request(): _maxPipelinedRequest(1), _method(METHOD_GET), _hasPassword(false), - _ipv6LiteralAddress(false) + _ipv6LiteralAddress(false), + _removalRequested(false) {} static std::string removeFragment(const std::string& url) diff --git a/src/Request.h b/src/Request.h index 907f2b81..916237c4 100644 --- a/src/Request.h +++ b/src/Request.h @@ -88,6 +88,8 @@ private: SharedHandle _peerStat; + bool _removalRequested; + bool parseUrl(const std::string& url); public: Request(); @@ -204,6 +206,16 @@ public: const SharedHandle& initPeerStat(); + void requestRemoval() + { + _removalRequested = true; + } + + bool removalRequested() const + { + return _removalRequested; + } + static const std::string METHOD_GET; static const std::string METHOD_HEAD; diff --git a/src/RequestGroup.cc b/src/RequestGroup.cc index c2a0f092..42074b9e 100644 --- a/src/RequestGroup.cc +++ b/src/RequestGroup.cc @@ -991,6 +991,9 @@ bool RequestGroup::needsFileAllocation() const DownloadResultHandle RequestGroup::createDownloadResult() const { + if(_logger->debug()) { + _logger->debug("GID#%d - Creating DownloadResult.", _gid); + } uint64_t sessionDownloadLength = 0; #ifdef ENABLE_BITTORRENT diff --git a/src/RequestGroupMan.cc b/src/RequestGroupMan.cc index bf9a9206..17df2a23 100644 --- a/src/RequestGroupMan.cc +++ b/src/RequestGroupMan.cc @@ -336,6 +336,16 @@ public: void operator()(const SharedHandle& group) { if(group->getNumCommand() == 0) { + SharedHandle dctx = group->getDownloadContext(); + // DownloadContext::resetDownloadStopTime() is only called when + // download completed. If + // DownloadContext::getDownloadStopTime().isZero() is true, then + // there is a possibility that the download is error or + // in-progress and resetDownloadStopTime() is not called. So + // call it here. + if(dctx->getDownloadStopTime().isZero()) { + dctx->resetDownloadStopTime(); + } try { group->closeFile(); if(group->downloadFinished()) { diff --git a/src/XmlRpcMethodFactory.cc b/src/XmlRpcMethodFactory.cc index b0683420..e99ea72c 100644 --- a/src/XmlRpcMethodFactory.cc +++ b/src/XmlRpcMethodFactory.cc @@ -79,6 +79,8 @@ XmlRpcMethodFactory::create(const std::string& methodName) return SharedHandle(new TellStoppedXmlRpcMethod()); } else if(methodName == GetOptionXmlRpcMethod::getMethodName()) { return SharedHandle(new GetOptionXmlRpcMethod()); + } else if(methodName == ChangeUriXmlRpcMethod::getMethodName()) { + return SharedHandle(new ChangeUriXmlRpcMethod()); } else if(methodName == ChangeOptionXmlRpcMethod::getMethodName()) { return SharedHandle(new ChangeOptionXmlRpcMethod()); } else if(methodName == GetGlobalOptionXmlRpcMethod::getMethodName()) { diff --git a/src/XmlRpcMethodImpl.cc b/src/XmlRpcMethodImpl.cc index c6a12161..0213a761 100644 --- a/src/XmlRpcMethodImpl.cc +++ b/src/XmlRpcMethodImpl.cc @@ -851,6 +851,67 @@ BDE GetSessionInfoXmlRpcMethod::process return result; } +BDE ChangeUriXmlRpcMethod::process +(const XmlRpcRequest& req, DownloadEngine* e) +{ + const BDE& params = req._params; + assert(params.isList()); + + if(params.size() < 4 || + !params[0].isString() || !params[1].isInteger() || + !params[2].isList() || !params[3].isList() || + params[1].i() < 0) { + throw DL_ABORT_EX("Bad request"); + } + size_t pos = 0; + bool posGiven = false; + getPosParam(params, 4, posGiven, pos); + + int32_t gid = util::parseInt(params[0].s()); + size_t index = params[1].i(); + const BDE& deluris = params[2]; + const BDE& adduris = params[3]; + SharedHandle group = findRequestGroup(e->_requestGroupMan, gid); + if(group.isNull()) { + throw DL_ABORT_EX + (StringFormat("Cannot remove URIs from GID#%d", gid).str()); + } + SharedHandle dctx = group->getDownloadContext(); + const std::vector >& files = dctx->getFileEntries(); + if(files.size() <= index) { + throw DL_ABORT_EX(StringFormat("fileIndex is out of range").str()); + } + SharedHandle s = files[index]; + size_t delcount = 0; + for(BDE::List::const_iterator i = deluris.listBegin(), + eoi = deluris.listEnd(); i != eoi; ++i) { + if(s->removeUri((*i).s())) { + ++delcount; + } + } + size_t addcount = 0; + if(posGiven) { + for(BDE::List::const_iterator i = adduris.listBegin(), + eoi = adduris.listEnd(); i != eoi; ++i) { + if(s->insertUri((*i).s(), pos)) { + ++addcount; + ++pos; + } + } + } else { + for(BDE::List::const_iterator i = adduris.listBegin(), + eoi = adduris.listEnd(); i != eoi; ++i) { + if(s->addUri((*i).s())) { + ++addcount; + } + } + } + BDE res = BDE::list(); + res << delcount; + res << addcount; + return res; +} + BDE SystemMulticallXmlRpcMethod::process (const XmlRpcRequest& req, DownloadEngine* e) { diff --git a/src/XmlRpcMethodImpl.h b/src/XmlRpcMethodImpl.h index 5e187ee4..f69f0527 100644 --- a/src/XmlRpcMethodImpl.h +++ b/src/XmlRpcMethodImpl.h @@ -341,6 +341,17 @@ public: } }; +class ChangeUriXmlRpcMethod:public XmlRpcMethod { +protected: + virtual BDE process(const XmlRpcRequest& req, DownloadEngine* e); +public: + static const std::string& getMethodName() + { + static std::string methodName = "aria2.changeUri"; + return methodName; + } +}; + class GetSessionInfoXmlRpcMethod:public XmlRpcMethod { protected: virtual BDE process(const XmlRpcRequest& req, DownloadEngine* e); diff --git a/test/FileEntryTest.cc b/test/FileEntryTest.cc index ee082cd8..fb5077eb 100644 --- a/test/FileEntryTest.cc +++ b/test/FileEntryTest.cc @@ -15,6 +15,10 @@ class FileEntryTest : public CppUnit::TestFixture { CPPUNIT_TEST(testGetRequest); CPPUNIT_TEST(testGetRequest_disableSingleHostMultiConnection); CPPUNIT_TEST(testReuseUri); + CPPUNIT_TEST(testAddUri); + CPPUNIT_TEST(testAddUris); + CPPUNIT_TEST(testInsertUri); + CPPUNIT_TEST(testRemoveUri); CPPUNIT_TEST_SUITE_END(); public: void setUp() {} @@ -25,6 +29,10 @@ public: void testGetRequest(); void testGetRequest_disableSingleHostMultiConnection(); void testReuseUri(); + void testAddUri(); + void testAddUris(); + void testInsertUri(); + void testRemoveUri(); }; @@ -149,4 +157,63 @@ void FileEntryTest::testReuseUri() CPPUNIT_ASSERT_EQUAL(std::string("ftp://localhost/aria2.zip"), uris[2]); } +void FileEntryTest::testAddUri() +{ + FileEntry file; + CPPUNIT_ASSERT(file.addUri("http://good")); + CPPUNIT_ASSERT(!file.addUri("bad")); +} + +void FileEntryTest::testAddUris() +{ + FileEntry file; + std::string uris[] = {"bad", "http://good"}; + CPPUNIT_ASSERT_EQUAL((size_t)1, file.addUris(&uris[0], &uris[2])); +} + +void FileEntryTest::testInsertUri() +{ + FileEntry file; + CPPUNIT_ASSERT(file.insertUri("http://example.org/1", 0)); + CPPUNIT_ASSERT(file.insertUri("http://example.org/2", 0)); + CPPUNIT_ASSERT(file.insertUri("http://example.org/3", 1)); + CPPUNIT_ASSERT(file.insertUri("http://example.org/4", 5)); + std::deque uris = file.getRemainingUris(); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/2"), uris[0]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/3"), uris[1]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/1"), uris[2]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/4"), uris[3]); +} + +void FileEntryTest::testRemoveUri() +{ + SharedHandle selector(new InOrderURISelector()); + FileEntry file; + file.addUri("http://example.org/"); + CPPUNIT_ASSERT(file.removeUri("http://example.org/")); + CPPUNIT_ASSERT(file.getRemainingUris().empty()); + CPPUNIT_ASSERT(!file.removeUri("http://example.org/")); + + file.addUri("http://example.org/"); + SharedHandle exampleOrgReq = file.getRequest(selector); + CPPUNIT_ASSERT(!exampleOrgReq->removalRequested()); + CPPUNIT_ASSERT_EQUAL((size_t)1, file.getSpentUris().size()); + CPPUNIT_ASSERT(file.removeUri("http://example.org/")); + CPPUNIT_ASSERT(file.getSpentUris().empty()); + CPPUNIT_ASSERT(exampleOrgReq->removalRequested()); + file.poolRequest(exampleOrgReq); + CPPUNIT_ASSERT_EQUAL((size_t)0, file.countPooledRequest()); + + file.addUri("http://example.org/"); + exampleOrgReq = file.getRequest(selector); + file.poolRequest(exampleOrgReq); + CPPUNIT_ASSERT_EQUAL((size_t)1, file.countPooledRequest()); + CPPUNIT_ASSERT(file.removeUri("http://example.org/")); + CPPUNIT_ASSERT_EQUAL((size_t)0, file.countPooledRequest()); + CPPUNIT_ASSERT(file.getSpentUris().empty()); + + file.addUri("http://example.org/"); + CPPUNIT_ASSERT(!file.removeUri("http://example.net")); +} + } // namespace aria2 diff --git a/test/XmlRpcMethodTest.cc b/test/XmlRpcMethodTest.cc index 0c54e119..0f5027a3 100644 --- a/test/XmlRpcMethodTest.cc +++ b/test/XmlRpcMethodTest.cc @@ -75,6 +75,8 @@ class XmlRpcMethodTest:public CppUnit::TestFixture { CPPUNIT_TEST(testChangePosition); CPPUNIT_TEST(testChangePosition_fail); CPPUNIT_TEST(testGetSessionInfo); + CPPUNIT_TEST(testChangeUri); + CPPUNIT_TEST(testChangeUri_fail); CPPUNIT_TEST(testSystemMulticall); CPPUNIT_TEST(testSystemMulticall_fail); CPPUNIT_TEST_SUITE_END(); @@ -135,6 +137,8 @@ public: void testChangePosition(); void testChangePosition_fail(); void testGetSessionInfo(); + void testChangeUri(); + void testChangeUri_fail(); void testSystemMulticall(); void testSystemMulticall_fail(); }; @@ -779,6 +783,131 @@ void XmlRpcMethodTest::testChangePosition_fail() CPPUNIT_ASSERT_EQUAL(1, res._code); } +void XmlRpcMethodTest::testChangeUri() +{ + SharedHandle files[3]; + for(int i = 0; i < 3; ++i) { + files[i].reset(new FileEntry()); + } + files[1]->addUri("http://example.org/aria2.tar.bz2"); + files[1]->addUri("http://example.org/mustremove1"); + files[1]->addUri("http://example.org/mustremove2"); + SharedHandle dctx(new DownloadContext()); + dctx->setFileEntries(&files[0], &files[3]); + SharedHandle group(new RequestGroup(_option)); + group->setDownloadContext(dctx); + _e->_requestGroupMan->addReservedGroup(group); + + ChangeUriXmlRpcMethod m; + XmlRpcRequest req(ChangeUriXmlRpcMethod::getMethodName(), BDE::list()); + req._params << std::string("1"); // GID + req._params << 1; // index of FileEntry + BDE removeuris = BDE::list(); + removeuris << std::string("http://example.org/mustremove1"); + removeuris << std::string("http://example.org/mustremove2"); + removeuris << std::string("http://example.org/notexist"); + req._params << removeuris; + BDE adduris = BDE::list(); + adduris << std::string("http://example.org/added1"); + adduris << std::string("http://example.org/added2"); + adduris << std::string("baduri"); + adduris << std::string("http://example.org/added3"); + req._params << adduris; + XmlRpcResponse res = m.execute(req, _e.get()); + CPPUNIT_ASSERT_EQUAL(0, res._code); + CPPUNIT_ASSERT_EQUAL((int64_t)2, res._param[0].i()); + CPPUNIT_ASSERT_EQUAL((int64_t)3, res._param[1].i()); + CPPUNIT_ASSERT_EQUAL((size_t)0, files[0]->getRemainingUris().size()); + CPPUNIT_ASSERT_EQUAL((size_t)0, files[2]->getRemainingUris().size()); + std::deque uris = files[1]->getRemainingUris(); + CPPUNIT_ASSERT_EQUAL((size_t)4, uris.size()); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/aria2.tar.bz2"),uris[0]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added1"), uris[1]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added2"), uris[2]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added3"), uris[3]); + + // Change adduris + adduris = BDE::list(); + adduris << std::string("http://example.org/added1-1"); + adduris << std::string("http://example.org/added1-2"); + req._params[3] = adduris; + // Set position parameter + req._params << 2; + res = m.execute(req, _e.get()); + CPPUNIT_ASSERT_EQUAL(0, res._code); + CPPUNIT_ASSERT_EQUAL((int64_t)0, res._param[0].i()); + CPPUNIT_ASSERT_EQUAL((int64_t)2, res._param[1].i()); + uris = files[1]->getRemainingUris(); + CPPUNIT_ASSERT_EQUAL((size_t)6, uris.size()); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added1-1"), uris[2]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added1-2"), uris[3]); + + // Change index of FileEntry + req._params[1] = 0; + // Set position far beyond the size of uris in FileEntry. + req._params[4] = 1000; + res = m.execute(req, _e.get()); + CPPUNIT_ASSERT_EQUAL(0, res._code); + CPPUNIT_ASSERT_EQUAL((int64_t)0, res._param[0].i()); + CPPUNIT_ASSERT_EQUAL((int64_t)2, res._param[1].i()); + uris = files[0]->getRemainingUris(); + CPPUNIT_ASSERT_EQUAL((size_t)2, uris.size()); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added1-1"), uris[0]); + CPPUNIT_ASSERT_EQUAL(std::string("http://example.org/added1-2"), uris[1]); +} + +void XmlRpcMethodTest::testChangeUri_fail() +{ + SharedHandle files[3]; + for(int i = 0; i < 3; ++i) { + files[i].reset(new FileEntry()); + } + SharedHandle dctx(new DownloadContext()); + dctx->setFileEntries(&files[0], &files[3]); + SharedHandle group(new RequestGroup(_option)); + group->setDownloadContext(dctx); + _e->_requestGroupMan->addReservedGroup(group); + + ChangeUriXmlRpcMethod m; + XmlRpcRequest req(ChangeUriXmlRpcMethod::getMethodName(), BDE::list()); + req._params << std::string("1"); // GID + req._params << 0; // index of FileEntry + BDE removeuris = BDE::list(); + req._params << removeuris; + BDE adduris = BDE::list(); + req._params << adduris; + XmlRpcResponse res = m.execute(req, _e.get()); + CPPUNIT_ASSERT_EQUAL(0, res._code); + + req._params[0] = std::string("2"); + res = m.execute(req, _e.get()); + // RPC request fails because GID#2 does not exist. + CPPUNIT_ASSERT_EQUAL(1, res._code); + + req._params[0] = std::string("1"); + req._params[1] = 3; + res = m.execute(req, _e.get()); + // RPC request fails because FileEntry#3 does not exist. + CPPUNIT_ASSERT_EQUAL(1, res._code); + + req._params[1] = std::string("0"); + res = m.execute(req, _e.get()); + // RPC request fails because index of FileEntry is string. + CPPUNIT_ASSERT_EQUAL(1, res._code); + + req._params[1] = 0; + req._params[2] = std::string("http://url"); + res = m.execute(req, _e.get()); + // RPC request fails because 3rd param is not list. + CPPUNIT_ASSERT_EQUAL(1, res._code); + + req._params[2] = BDE::list(); + req._params[3] = std::string("http://url"); + res = m.execute(req, _e.get()); + // RPC request fails because 4th param is not list. + CPPUNIT_ASSERT_EQUAL(1, res._code); +} + void XmlRpcMethodTest::testGetSessionInfo() { GetSessionInfoXmlRpcMethod m;