diff options
Diffstat (limited to 'Swiften/FileTransfer/UnitTest')
7 files changed, 1225 insertions, 60 deletions
diff --git a/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h new file mode 100644 index 0000000..ae06cd3 --- /dev/null +++ b/Swiften/FileTransfer/UnitTest/DummyFileTransferManager.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <string> +#include <boost/filesystem.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <Swiften/FileTransfer/FileTransferManager.h> + +namespace Swift { + class DummyFileTransferManager : public FileTransferManager { + public: + DummyFileTransferManager() : FileTransferManager() { + } + + virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const boost::filesystem::path&, const std::string&, boost::shared_ptr<ReadBytestream>) { + return OutgoingFileTransfer::ref(); + } + + virtual OutgoingFileTransfer::ref createOutgoingFileTransfer(const JID&, const std::string&, const std::string&, const boost::uintmax_t, const boost::posix_time::ptime&, boost::shared_ptr<ReadBytestream>) { + return OutgoingFileTransfer::ref(); + } + + virtual void startListeningOnPort(int) { + } + + virtual void addS5BProxy(boost::shared_ptr<S5BProxyRequest>) { + } + + }; +} diff --git a/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp new file mode 100644 index 0000000..c62636d --- /dev/null +++ b/Swiften/FileTransfer/UnitTest/IBBReceiveSessionTest.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <vector> +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Base/ByteArray.h> +#include <Swiften/FileTransfer/IBBReceiveSession.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Client/DummyStanzaChannel.h> + +using namespace Swift; + +class IBBReceiveSessionTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(IBBReceiveSessionTest); + CPPUNIT_TEST(testOpen); + CPPUNIT_TEST(testReceiveData); + CPPUNIT_TEST(testReceiveMultipleData); + CPPUNIT_TEST(testReceiveDataForOtherSession); + CPPUNIT_TEST(testReceiveDataOutOfOrder); + CPPUNIT_TEST(testReceiveLastData); + CPPUNIT_TEST(testReceiveClose); + CPPUNIT_TEST(testStopWhileActive); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + stanzaChannel = new DummyStanzaChannel(); + iqRouter = new IQRouter(stanzaChannel); + finished = false; + } + + void tearDown() { + delete iqRouter; + delete stanzaChannel; + } + + void testOpen() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(0, "id-open")); + CPPUNIT_ASSERT(!finished); + + testling->stop(); + } + + void testReceiveData() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + + CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(1, "id-a")); + CPPUNIT_ASSERT(createByteArray("abc") == receivedData); + CPPUNIT_ASSERT(!finished); + + testling->stop(); + } + + void testReceiveMultipleData() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, createByteArray("def")), "foo@bar.com/baz", "id-b")); + + CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b")); + CPPUNIT_ASSERT(createByteArray("abcdef") == receivedData); + CPPUNIT_ASSERT(!finished); + + testling->stop(); + } + + void testReceiveDataForOtherSession() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("othersession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + + CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(1, "id-a")); + + testling->stop(); + } + + void testReceiveDataOutOfOrder() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("def")), "foo@bar.com/baz", "id-b")); + + CPPUNIT_ASSERT(stanzaChannel->isErrorAtIndex(2, "id-b")); + CPPUNIT_ASSERT(finished); + CPPUNIT_ASSERT(error); + + testling->stop(); + } + + void testReceiveLastData() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession", 6)); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 1, createByteArray("def")), "foo@bar.com/baz", "id-b")); + + CPPUNIT_ASSERT(stanzaChannel->isResultAtIndex(2, "id-b")); + CPPUNIT_ASSERT(createByteArray("abcdef") == receivedData); + CPPUNIT_ASSERT(finished); + CPPUNIT_ASSERT(!error); + + testling->stop(); + } + + void testReceiveClose() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBClose("mysession"), "foo@bar.com/baz", "id-close")); + + CPPUNIT_ASSERT(finished); + CPPUNIT_ASSERT(error); + + testling->stop(); + } + + void testStopWhileActive() { + boost::shared_ptr<IBBReceiveSession> testling(createSession("foo@bar.com/baz", "mysession")); + testling->start(); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + + testling->stop(); + + CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set)); + IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>(); + CPPUNIT_ASSERT_EQUAL(IBB::Close, ibb->getAction()); + CPPUNIT_ASSERT_EQUAL(std::string("mysession"), ibb->getStreamID()); + CPPUNIT_ASSERT(finished); + CPPUNIT_ASSERT(!error); + } + + private: + IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { + IQ::ref request = IQ::createRequest(IQ::Set, JID("baz@fum.com/dum"), id, ibb); + request->setFrom(from); + return request; + } + + IBBReceiveSession* createSession(const std::string& from, const std::string& id, size_t size = 0x1000) { + IBBReceiveSession* session = new IBBReceiveSession(id, JID(from), JID(), size, iqRouter); + session->onDataReceived.connect(boost::bind(&IBBReceiveSessionTest::handleDataReceived, this, _1)); + session->onFinished.connect(boost::bind(&IBBReceiveSessionTest::handleFinished, this, _1)); + return session; + } + + + void handleFinished(boost::optional<FileTransferError> error) { + finished = true; + this->error = error; + } + + void handleDataReceived(const std::vector<unsigned char>& data) { + receivedData.insert(receivedData.end(), data.begin(), data.end()); + } + + private: + DummyStanzaChannel* stanzaChannel; + IQRouter* iqRouter; + bool finished; + boost::optional<FileTransferError> error; + std::vector<unsigned char> receivedData; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(IBBReceiveSessionTest); diff --git a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp index e89ef93..d12f99e 100644 --- a/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp +++ b/Swiften/FileTransfer/UnitTest/IBBSendSessionTest.cpp @@ -4,17 +4,16 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Base/ByteArray.h" - #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <vector> #include <boost/bind.hpp> -#include "Swiften/FileTransfer/IBBSendSession.h" -#include "Swiften/FileTransfer/ByteArrayReadBytestream.h" -#include "Swiften/Queries/IQRouter.h" -#include "Swiften/Client/DummyStanzaChannel.h" +#include <Swiften/Base/ByteArray.h> +#include <Swiften/FileTransfer/IBBSendSession.h> +#include <Swiften/FileTransfer/ByteArrayReadBytestream.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Client/DummyStanzaChannel.h> using namespace Swift; @@ -27,13 +26,19 @@ class IBBSendSessionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testErrorResponseFinishesWithError); CPPUNIT_TEST(testStopDuringSessionCloses); CPPUNIT_TEST(testStopAfterFinishedDoesNotClose); + CPPUNIT_TEST(testDataStreamPauseStopsSendingData); + CPPUNIT_TEST(testDataStreamResumeAfterPauseSendsData); + CPPUNIT_TEST(testDataStreamResumeBeforePauseDoesNotSendData); + CPPUNIT_TEST(testDataStreamResumeAfterResumeDoesNotSendData); + CPPUNIT_TEST_SUITE_END(); public: void setUp() { stanzaChannel = new DummyStanzaChannel(); iqRouter = new IQRouter(stanzaChannel); - bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg"))); + bytestream = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg"))); + finished = false; } void tearDown() { @@ -42,7 +47,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture { } void testStart() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(1234); testling->start(); @@ -56,7 +61,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture { } void testStart_ResponseStartsSending() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); @@ -66,13 +71,13 @@ class IBBSendSessionTest : public CppUnit::TestFixture { CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(1, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[1]->getPayload<IBB>(); CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction()); - CPPUNIT_ASSERT_EQUAL(ByteArray("abc"), ibb->getData()); + CPPUNIT_ASSERT(createByteArray("abc") == ibb->getData()); CPPUNIT_ASSERT_EQUAL(0, ibb->getSequenceNumber()); CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID()); } void testResponseContinuesSending() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); @@ -82,13 +87,13 @@ class IBBSendSessionTest : public CppUnit::TestFixture { CPPUNIT_ASSERT(stanzaChannel->isRequestAtIndex<IBB>(2, JID("foo@bar.com/baz"), IQ::Set)); IBB::ref ibb = stanzaChannel->sentStanzas[2]->getPayload<IBB>(); CPPUNIT_ASSERT_EQUAL(IBB::Data, ibb->getAction()); - CPPUNIT_ASSERT_EQUAL(ByteArray("def"), ibb->getData()); + CPPUNIT_ASSERT(createByteArray("def") == ibb->getData()); CPPUNIT_ASSERT_EQUAL(1, ibb->getSequenceNumber()); CPPUNIT_ASSERT_EQUAL(std::string("myid"), ibb->getStreamID()); } void testRespondToAllFinishes() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); @@ -101,7 +106,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture { } void testErrorResponseFinishesWithError() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); stanzaChannel->onIQReceived(IQ::createError(JID("baz@fum.com/foo"), stanzaChannel->sentStanzas[0]->getTo(), stanzaChannel->sentStanzas[0]->getID())); @@ -111,7 +116,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture { } void testStopDuringSessionCloses() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(3); testling->start(); testling->stop(); @@ -126,7 +131,7 @@ class IBBSendSessionTest : public CppUnit::TestFixture { } void testStopAfterFinishedDoesNotClose() { - std::auto_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); testling->setBlockSize(16); testling->start(); stanzaChannel->onIQReceived(createIBBResult()); @@ -137,15 +142,74 @@ class IBBSendSessionTest : public CppUnit::TestFixture { CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size())); } - + + void testDataStreamPauseStopsSendingData() { + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + bytestream->setDataComplete(false); + testling->setBlockSize(3); + testling->start(); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + + CPPUNIT_ASSERT(!finished); + CPPUNIT_ASSERT(!error); + + CPPUNIT_ASSERT_EQUAL(4, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testDataStreamResumeAfterPauseSendsData() { + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + bytestream->setDataComplete(false); + testling->setBlockSize(3); + testling->start(); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + + bytestream->addData(createByteArray("xyz")); + + CPPUNIT_ASSERT_EQUAL(5, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testDataStreamResumeBeforePauseDoesNotSendData() { + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + bytestream->setDataComplete(false); + testling->setBlockSize(3); + testling->start(); + stanzaChannel->onIQReceived(createIBBResult()); + + bytestream->addData(createByteArray("xyz")); + + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + + void testDataStreamResumeAfterResumeDoesNotSendData() { + boost::shared_ptr<IBBSendSession> testling = createSession("foo@bar.com/baz"); + bytestream->setDataComplete(false); + testling->setBlockSize(3); + testling->start(); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + stanzaChannel->onIQReceived(createIBBResult()); + + bytestream->addData(createByteArray("xyz")); + bytestream->addData(createByteArray("xuv")); + + CPPUNIT_ASSERT_EQUAL(5, static_cast<int>(stanzaChannel->sentStanzas.size())); + } + private: IQ::ref createIBBResult() { return IQ::createResult(JID("baz@fum.com/dum"), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getTo(), stanzaChannel->sentStanzas[stanzaChannel->sentStanzas.size()-1]->getID(), boost::shared_ptr<IBB>()); } private: - std::auto_ptr<IBBSendSession> createSession(const std::string& to) { - std::auto_ptr<IBBSendSession> session(new IBBSendSession("myid", JID(to), bytestream, iqRouter)); + boost::shared_ptr<IBBSendSession> createSession(const std::string& to) { + boost::shared_ptr<IBBSendSession> session(new IBBSendSession("myid", JID(), JID(to), bytestream, iqRouter)); session->onFinished.connect(boost::bind(&IBBSendSessionTest::handleFinished, this, _1)); return session; } diff --git a/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp new file mode 100644 index 0000000..4c6ae72 --- /dev/null +++ b/Swiften/FileTransfer/UnitTest/IncomingJingleFileTransferTest.cpp @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/Log.h> +#include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/Elements/IBB.h> +#include <Swiften/Elements/JingleIBBTransportPayload.h> +#include <Swiften/Elements/JingleS5BTransportPayload.h> +#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h> +#include <Swiften/FileTransfer/IncomingJingleFileTransfer.h> +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h> +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h> +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h> +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h> +#include <Swiften/Jingle/FakeJingleSession.h> +#include <Swiften/Network/DummyTimerFactory.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/Network/DummyConnectionFactory.h> +#include <Swiften/Network/PlatformNATTraversalWorker.h> +#include <Swiften/Queries/IQRouter.h> + +#include <iostream> + +using namespace Swift; +using namespace boost; + +class FakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector { + void addRemoteTransportCandidates(JingleTransportPayload::ref cand) { + candidate = cand; + } + + void selectCandidate() { + boost::shared_ptr<JingleS5BTransportPayload> payload = make_shared<JingleS5BTransportPayload>(); + payload->setCandidateError(true); + payload->setSessionID(candidate->getSessionID()); + onRemoteTransportCandidateSelectFinished(payload); + } + + void setMinimumPriority(int) { + + } + + bool isActualCandidate(JingleTransportPayload::ref) { + return false; + } + + int getPriority(JingleTransportPayload::ref) { + return 0; + } + + JingleTransport::ref selectTransport(JingleTransportPayload::ref) { + return JingleTransport::ref(); + } + +private: + JingleTransportPayload::ref candidate; +}; + +class FakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory { +public: + virtual ~FakeRemoteJingleTransportCandidateSelectorFactory() { + + } + + virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() { + return new FakeRemoteJingleTransportCandidateSelector(); + } +}; + +class FakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator { +public: + virtual void generateLocalTransportCandidates(JingleTransportPayload::ref payload) { + JingleS5BTransportPayload::ref payL = make_shared<JingleS5BTransportPayload>(); + payL->setSessionID(payload->getSessionID()); + onLocalTransportCandidatesGenerated(payL); + } + + void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) { + onLocalTransportCandidatesGenerated(payload); + } + + virtual bool isActualCandidate(JingleTransportPayload::ref) { + return false; + } + + virtual int getPriority(JingleTransportPayload::ref) { + return 0; + } + + virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) { + return JingleTransport::ref(); + } +}; + +class FakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory { +public: + virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() { + return new FakeLocalJingleTransportCandidateGenerator(); + } +}; + +class IncomingJingleFileTransferTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(IncomingJingleFileTransferTest); + CPPUNIT_TEST(test_AcceptOnyIBBSendsSessionAccept); + CPPUNIT_TEST(test_OnlyIBBTransferReceiveWorks); + CPPUNIT_TEST(test_AcceptFailingS5BFallsBackToIBB); + CPPUNIT_TEST_SUITE_END(); +public: + shared_ptr<IncomingJingleFileTransfer> createTestling() { + JID ourJID("our@jid.org/full"); + return make_shared<IncomingJingleFileTransfer>(ourJID, shared_ptr<JingleSession>(fakeJingleSession), jingleContentPayload, fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, bytestreamRegistry, bytestreamProxy, timerFactory); + } + + IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { + IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb); + request->setFrom(from); + return request; + } + + void setUp() { + eventLoop = new DummyEventLoop(); + fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession"); + jingleContentPayload = make_shared<JingleContentPayload>(); + fakeRJTCSF = make_shared<FakeRemoteJingleTransportCandidateSelectorFactory>(); + fakeLJTCF = make_shared<FakeLocalJingleTransportCandidateGeneratorFactory>(); + stanzaChannel = new DummyStanzaChannel(); + iqRouter = new IQRouter(stanzaChannel); + bytestreamRegistry = new SOCKS5BytestreamRegistry(); + timerFactory = new DummyTimerFactory(); + connectionFactory = new DummyConnectionFactory(eventLoop); + bytestreamProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory); + } + + void tearDown() { + delete bytestreamProxy; + delete connectionFactory; + delete timerFactory; + delete bytestreamRegistry; + delete iqRouter; + delete stanzaChannel; + delete eventLoop; + } + + // Tests whether IncomingJingleFileTransfer would accept a IBB only file transfer. + void test_AcceptOnyIBBSendsSessionAccept() { + //1. create your test incoming file transfer + shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>(); + desc->addOffer(StreamInitiationFileInfo("foo.txt", "", 10)); + jingleContentPayload->addDescription(desc); + JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>(); + tpRef->setSessionID("mysession"); + jingleContentPayload->addTransport(tpRef); + + shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling(); + + //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one) + shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>(); + fileTransfer->accept(byteStream); + + // check whether accept has been called + getCall<FakeJingleSession::AcceptCall>(0); + } + + void test_OnlyIBBTransferReceiveWorks() { + //1. create your test incoming file transfer + shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>(); + desc->addOffer(StreamInitiationFileInfo("file.txt", "", 10)); + jingleContentPayload->addDescription(desc); + JingleIBBTransportPayload::ref tpRef = make_shared<JingleIBBTransportPayload>(); + tpRef->setSessionID("mysession"); + jingleContentPayload->addTransport(tpRef); + + shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling(); + + //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one) + shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>(); + fileTransfer->accept(byteStream); + + // check whether accept has been called + getCall<FakeJingleSession::AcceptCall>(0); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData()); + } + + void test_AcceptFailingS5BFallsBackToIBB() { + //1. create your test incoming file transfer + addFileTransferDescription(); + + // add SOCKS5BytestreamTransportPayload + JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload(); + + shared_ptr<IncomingJingleFileTransfer> fileTransfer = createTestling(); + + //2. do 'accept' on a dummy writebytestream (you'll have to look if there already is one) + shared_ptr<ByteArrayWriteBytestream> byteStream = make_shared<ByteArrayWriteBytestream>(); + fileTransfer->accept(byteStream); + + // check whether accept has been called + FakeJingleSession::AcceptCall acceptCall = getCall<FakeJingleSession::AcceptCall>(0); + CPPUNIT_ASSERT_EQUAL(payLoad->getSessionID(), acceptCall.payload->getSessionID()); + + // check for candidate error + FakeJingleSession::InfoTransportCall infoTransportCall = getCall<FakeJingleSession::InfoTransportCall>(1); + JingleS5BTransportPayload::ref s5bPayload = dynamic_pointer_cast<JingleS5BTransportPayload>(infoTransportCall.payload); + CPPUNIT_ASSERT(s5bPayload->hasCandidateError()); + + // indicate transport replace (Romeo) + fakeJingleSession->onTransportReplaceReceived(getContentID(), addJingleIBBPayload()); + + FakeJingleSession::AcceptTransportCall acceptTransportCall = getCall<FakeJingleSession::AcceptTransportCall>(2); + + // send a bit of data + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBOpen("mysession", 0x10), "foo@bar.com/baz", "id-open")); + stanzaChannel->onIQReceived(createIBBRequest(IBB::createIBBData("mysession", 0, createByteArray("abc")), "foo@bar.com/baz", "id-a")); + CPPUNIT_ASSERT(createByteArray("abc") == byteStream->getData()); + } + + void test_S5BTransferReceiveTest() { + addFileTransferDescription(); + JingleS5BTransportPayload::ref payLoad = addJingleS5BPayload(); + } + +private: + void addFileTransferDescription() { + shared_ptr<JingleFileTransferDescription> desc = make_shared<JingleFileTransferDescription>(); + desc->addOffer(StreamInitiationFileInfo("file.txt", "", 10)); + jingleContentPayload->addDescription(desc); + } + + shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() { + JingleS5BTransportPayload::ref payLoad = make_shared<JingleS5BTransportPayload>(); + payLoad->setSessionID("mysession"); + jingleContentPayload->addTransport(payLoad); + return payLoad; + } + + shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() { + JingleIBBTransportPayload::ref payLoad = make_shared<JingleIBBTransportPayload>(); + payLoad->setSessionID("mysession"); + jingleContentPayload->addTransport(payLoad); + return payLoad; + } + + JingleContentID getContentID() const { + return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator()); + } + + template <typename T> T getCall(int i) const { + size_t index = static_cast<size_t>(i); + CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size()); + T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]); + CPPUNIT_ASSERT(cmd); + return *cmd; + } + +private: + EventLoop* eventLoop; + FakeJingleSession* fakeJingleSession; + shared_ptr<JingleContentPayload> jingleContentPayload; + shared_ptr<FakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF; + shared_ptr<FakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF; + DummyStanzaChannel* stanzaChannel; + IQRouter* iqRouter; + SOCKS5BytestreamRegistry* bytestreamRegistry; + DummyConnectionFactory* connectionFactory; + SOCKS5BytestreamProxy* bytestreamProxy; + DummyTimerFactory* timerFactory; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(IncomingJingleFileTransferTest); diff --git a/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp new file mode 100644 index 0000000..0c324bf --- /dev/null +++ b/Swiften/FileTransfer/UnitTest/OutgoingJingleFileTransferTest.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/bind.hpp> +#include <boost/optional.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/FileTransfer/OutgoingJingleFileTransfer.h> +#include <Swiften/Jingle/FakeJingleSession.h> +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelectorFactory.h> +#include <Swiften/FileTransfer/RemoteJingleTransportCandidateSelector.h> +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGeneratorFactory.h> +#include <Swiften/FileTransfer/LocalJingleTransportCandidateGenerator.h> +#include <Swiften/Queries/IQRouter.h> +#include <Swiften/Client/DummyStanzaChannel.h> +#include <Swiften/FileTransfer/ByteArrayReadBytestream.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamProxy.h> + +#include <Swiften/Elements/JingleIBBTransportPayload.h> +#include <Swiften/Elements/JingleS5BTransportPayload.h> +#include <Swiften/Elements/JingleFileTransferDescription.h> +#include <Swiften/Elements/IBB.h> +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/IDGenerator.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/Network/PlatformNATTraversalWorker.h> +#include <Swiften/Network/DummyTimerFactory.h> +#include <Swiften/Network/DummyConnection.h> +#include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/DummyConnectionFactory.h> + +#include <Swiften/Base/Log.h> + +#include <iostream> + +using namespace Swift; + +class OFakeRemoteJingleTransportCandidateSelector : public RemoteJingleTransportCandidateSelector { + void addRemoteTransportCandidates(JingleTransportPayload::ref cand) { + candidate = cand; + } + + void selectCandidate() { + JingleS5BTransportPayload::ref payload = boost::make_shared<JingleS5BTransportPayload>(); + payload->setCandidateError(true); + payload->setSessionID(candidate->getSessionID()); + onRemoteTransportCandidateSelectFinished(payload); + } + + void setMinimumPriority(int) { + + } + + bool isActualCandidate(JingleTransportPayload::ref) { + return false; + } + + int getPriority(JingleTransportPayload::ref) { + return 0; + } + + JingleTransport::ref selectTransport(JingleTransportPayload::ref) { + return JingleTransport::ref(); + } + +private: + JingleTransportPayload::ref candidate; +}; + +class OFakeRemoteJingleTransportCandidateSelectorFactory : public RemoteJingleTransportCandidateSelectorFactory { +public: + virtual ~OFakeRemoteJingleTransportCandidateSelectorFactory() { + + } + + virtual RemoteJingleTransportCandidateSelector* createCandidateSelector() { + return new OFakeRemoteJingleTransportCandidateSelector(); + } +}; + +class OFakeLocalJingleTransportCandidateGenerator : public LocalJingleTransportCandidateGenerator { +public: + virtual void generateLocalTransportCandidates(JingleTransportPayload::ref /* payload */) { + //JingleTransportPayload::ref payL = make_shared<JingleTransportPayload>(); + //payL->setSessionID(payload->getSessionID()); + JingleS5BTransportPayload::ref payL = boost::make_shared<JingleS5BTransportPayload>(); + + onLocalTransportCandidatesGenerated(payL); + } + + void emitonLocalTransportCandidatesGenerated(JingleTransportPayload::ref payload) { + onLocalTransportCandidatesGenerated(payload); + } + + virtual bool isActualCandidate(JingleTransportPayload::ref) { + return false; + } + + virtual int getPriority(JingleTransportPayload::ref) { + return 0; + } + + virtual JingleTransport::ref selectTransport(JingleTransportPayload::ref) { + return JingleTransport::ref(); + } +}; + +class OFakeLocalJingleTransportCandidateGeneratorFactory : public LocalJingleTransportCandidateGeneratorFactory { +public: + virtual LocalJingleTransportCandidateGenerator* createCandidateGenerator() { + return new OFakeLocalJingleTransportCandidateGenerator(); + } +}; + +class OutgoingJingleFileTransferTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(OutgoingJingleFileTransferTest); + CPPUNIT_TEST(test_SendSessionInitiateOnStart); + CPPUNIT_TEST(test_IBBStartsAfterSendingSessionAccept); + CPPUNIT_TEST(test_ReceiveSessionTerminateAfterSessionInitiate); + CPPUNIT_TEST_SUITE_END(); + + class FTStatusHelper { + public: + bool finishedCalled; + FileTransferError::Type error; + void handleFileTransferFinished(boost::optional<FileTransferError> error) { + finishedCalled = true; + if (error.is_initialized()) this->error = error.get().getType(); + } + }; +public: + + boost::shared_ptr<OutgoingJingleFileTransfer> createTestling() { + JID to("test@foo.com/bla"); + StreamInitiationFileInfo fileInfo; + fileInfo.setDescription("some file"); + fileInfo.setName("test.bin"); + fileInfo.setHash("asdjasdas"); + fileInfo.setSize(1024 * 1024); + return boost::shared_ptr<OutgoingJingleFileTransfer>(new OutgoingJingleFileTransfer(boost::shared_ptr<JingleSession>(fakeJingleSession), fakeRJTCSF.get(), fakeLJTCF.get(), iqRouter, idGen, JID(), to, stream, fileInfo, s5bRegistry, s5bProxy)); + } + + IQ::ref createIBBRequest(IBB::ref ibb, const JID& from, const std::string& id) { + IQ::ref request = IQ::createRequest(IQ::Set, JID("foo@bar.com/baz"), id, ibb); + request->setFrom(from); + return request; + } + + void setUp() { + fakeJingleSession = new FakeJingleSession("foo@bar.com/baz", "mysession"); + jingleContentPayload = boost::make_shared<JingleContentPayload>(); + fakeRJTCSF = boost::make_shared<OFakeRemoteJingleTransportCandidateSelectorFactory>(); + fakeLJTCF = boost::make_shared<OFakeLocalJingleTransportCandidateGeneratorFactory>(); + stanzaChannel = new DummyStanzaChannel(); + iqRouter = new IQRouter(stanzaChannel); + eventLoop = new DummyEventLoop(); + timerFactory = new DummyTimerFactory(); + connectionFactory = new DummyConnectionFactory(eventLoop); + s5bRegistry = new SOCKS5BytestreamRegistry(); + s5bProxy = new SOCKS5BytestreamProxy(connectionFactory, timerFactory); + + data.clear(); + for (int n=0; n < 1024 * 1024; ++n) { + data.push_back(34); + } + + stream = boost::make_shared<ByteArrayReadBytestream>(data); + + idGen = new IDGenerator(); + } + + void tearDown() { + delete idGen; + delete s5bRegistry; + delete connectionFactory; + delete timerFactory; + delete eventLoop; + delete iqRouter; + delete stanzaChannel; + } + + + void test_SendSessionInitiateOnStart() { + boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling(); + transfer->start(); + FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0); + JingleFileTransferDescription::ref description = boost::dynamic_pointer_cast<JingleFileTransferDescription>(call.description); + CPPUNIT_ASSERT(description); + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), description->getOffers().size()); + CPPUNIT_ASSERT(static_cast<size_t>(1048576) == description->getOffers()[0].getSize()); + + JingleS5BTransportPayload::ref transport = boost::dynamic_pointer_cast<JingleS5BTransportPayload>(call.payload); + CPPUNIT_ASSERT(transport); + } + + void test_IBBStartsAfterSendingSessionAccept() { + boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling(); + transfer->start(); + + FakeJingleSession::InitiateCall call = getCall<FakeJingleSession::InitiateCall>(0); + // FIXME: we initiate with SOCSK5 now and not IBB, needs to be fixed. + /* + fakeJingleSession->onSessionAcceptReceived(call.id, call.description, call.payload); + IQ::ref iqOpenStanza = stanzaChannel->getStanzaAtIndex<IQ>(0); + CPPUNIT_ASSERT(iqOpenStanza); + */ + } + + void test_ReceiveSessionTerminateAfterSessionInitiate() { + boost::shared_ptr<OutgoingJingleFileTransfer> transfer = createTestling(); + transfer->start(); + + getCall<FakeJingleSession::InitiateCall>(0); + + FTStatusHelper helper; + helper.finishedCalled = false; + transfer->onFinished.connect(bind(&FTStatusHelper::handleFileTransferFinished, &helper, _1)); + fakeJingleSession->onSessionTerminateReceived(JinglePayload::Reason(JinglePayload::Reason::Busy)); + CPPUNIT_ASSERT_EQUAL(true, helper.finishedCalled); + CPPUNIT_ASSERT(FileTransferError::PeerError == helper.error); + } + + +//TODO: some more testcases + +private: + void addFileTransferDescription() { + boost::shared_ptr<JingleFileTransferDescription> desc = boost::make_shared<JingleFileTransferDescription>(); + desc->addOffer(StreamInitiationFileInfo()); + jingleContentPayload->addDescription(desc); + } + + boost::shared_ptr<JingleS5BTransportPayload> addJingleS5BPayload() { + JingleS5BTransportPayload::ref payLoad = boost::make_shared<JingleS5BTransportPayload>(); + payLoad->setSessionID("mysession"); + jingleContentPayload->addTransport(payLoad); + return payLoad; + } + + boost::shared_ptr<JingleIBBTransportPayload> addJingleIBBPayload() { + JingleIBBTransportPayload::ref payLoad = boost::make_shared<JingleIBBTransportPayload>(); + payLoad->setSessionID("mysession"); + jingleContentPayload->addTransport(payLoad); + return payLoad; + } + + JingleContentID getContentID() const { + return JingleContentID(jingleContentPayload->getName(), jingleContentPayload->getCreator()); + } + + template <typename T> T getCall(int i) const { + size_t index = static_cast<size_t>(i); + CPPUNIT_ASSERT(index < fakeJingleSession->calledCommands.size()); + T* cmd = boost::get<T>(&fakeJingleSession->calledCommands[index]); + CPPUNIT_ASSERT(cmd); + return *cmd; + } + +private: + std::vector<unsigned char> data; + boost::shared_ptr<ByteArrayReadBytestream> stream; + FakeJingleSession* fakeJingleSession; + boost::shared_ptr<JingleContentPayload> jingleContentPayload; + boost::shared_ptr<OFakeRemoteJingleTransportCandidateSelectorFactory> fakeRJTCSF; + boost::shared_ptr<OFakeLocalJingleTransportCandidateGeneratorFactory> fakeLJTCF; + DummyStanzaChannel* stanzaChannel; + IQRouter* iqRouter; + IDGenerator* idGen; + EventLoop *eventLoop; + SOCKS5BytestreamRegistry* s5bRegistry; + SOCKS5BytestreamProxy* s5bProxy; + DummyTimerFactory* timerFactory; + DummyConnectionFactory* connectionFactory; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(OutgoingJingleFileTransferTest); diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp new file mode 100644 index 0000000..527e0ca --- /dev/null +++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamClientSessionTest.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2011 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <Swiften/Base/ByteArray.h> +#include <QA/Checker/IO.h> + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/bind.hpp> +#include <boost/random/mersenne_twister.hpp> +#include <boost/random/uniform_int.hpp> +#include <boost/random/variate_generator.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/ByteArray.h> +#include <Swiften/Base/Concat.h> +#include <Swiften/Base/Log.h> +#include <Swiften/Base/StartStopper.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/FileTransfer/ByteArrayReadBytestream.h> +#include <Swiften/FileTransfer/ByteArrayWriteBytestream.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamClientSession.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> +#include <Swiften/JID/JID.h> +#include <Swiften/Network/DummyConnection.h> +#include <Swiften/Network/DummyTimerFactory.h> +#include <Swiften/StringCodecs/Hexify.h> + +using namespace Swift; + +boost::mt19937 randomGen; + +class SOCKS5BytestreamClientSessionTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(SOCKS5BytestreamClientSessionTest); + CPPUNIT_TEST(testForSessionReady); + CPPUNIT_TEST(testErrorHandlingHello); + CPPUNIT_TEST(testErrorHandlingRequest); + CPPUNIT_TEST(testWriteBytestream); + CPPUNIT_TEST(testReadBytestream); + CPPUNIT_TEST_SUITE_END(); + + const HostAddressPort destinationAddressPort; + const std::string destination; + +public: + SOCKS5BytestreamClientSessionTest() : destinationAddressPort(HostAddressPort(HostAddress("127.0.0.1"), 8888)), + destination(SOCKS5BytestreamRegistry::getHostname("foo", JID("requester@example.com/test"), JID("target@example.com/test"))), eventLoop(NULL), timerFactory(NULL) { } + + void setUp() { + randomGen.seed(time(NULL)); + eventLoop = new DummyEventLoop(); + timerFactory = new DummyTimerFactory(); + connection = boost::make_shared<MockeryConnection>(failingPorts, true, eventLoop); + //connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1)); + //stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg"))); +// connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamClientSessionTest::handleDataRead, this, _1)); + } + + void tearDown() { + //connection.reset(); + delete timerFactory; + delete eventLoop; + } + + void testForSessionReady() { + TestHelper helper; + connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); + + SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory); + clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); + + clientSession->start(); + eventLoop->processEvents(); + CPPUNIT_ASSERT(createByteArray("\x05\x01\x00", 3) == helper.unprocessedInput); + + helper.unprocessedInput.clear(); + serverRespondHelloOK(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4)); + CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4])); + CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size())); + CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1)); + + helper.unprocessedInput.clear(); + serverRespondRequestOK(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); + CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError); + } + + void testErrorHandlingHello() { + TestHelper helper; + connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); + + SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory); + clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); + + clientSession->start(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput); + + helper.unprocessedInput.clear(); + serverRespondHelloAuthFail(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError); + CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled); + } + + void testErrorHandlingRequest() { + TestHelper helper; + connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); + + SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory); + clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); + + clientSession->start(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00", 3), helper.unprocessedInput); + + helper.unprocessedInput.clear(); + serverRespondHelloOK(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(createByteArray("\x05\x01\x00\x03", 4), createByteArray(&helper.unprocessedInput[0], 4)); + CPPUNIT_ASSERT_EQUAL(createByteArray(destination.size()), createByteArray(helper.unprocessedInput[4])); + CPPUNIT_ASSERT_EQUAL(createByteArray(destination), createByteArray(&helper.unprocessedInput[5], destination.size())); + CPPUNIT_ASSERT_EQUAL(createByteArray("\x00", 1), createByteArray(&helper.unprocessedInput[5 + destination.size()], 1)); + + helper.unprocessedInput.clear(); + serverRespondRequestFail(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyError); + CPPUNIT_ASSERT_EQUAL(true, connection->disconnectCalled); + } + + void testWriteBytestream() { + TestHelper helper; + connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); + + SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory); + clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); + + clientSession->start(); + eventLoop->processEvents(); + + helper.unprocessedInput.clear(); + serverRespondHelloOK(); + eventLoop->processEvents(); + + helper.unprocessedInput.clear(); + serverRespondRequestOK(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); + CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError); + + boost::shared_ptr<ByteArrayWriteBytestream> output = boost::make_shared<ByteArrayWriteBytestream>(); + clientSession->startReceiving(output); + + ByteArray transferData = generateRandomByteArray(1024); + connection->onDataRead(createSafeByteArrayRef(transferData.data(), transferData.size())); + CPPUNIT_ASSERT_EQUAL(transferData, output->getData()); + } + + void testReadBytestream() { + TestHelper helper; + connection->onDataSent.connect(boost::bind(&TestHelper::handleConnectionDataWritten, &helper, _1)); + + SOCKS5BytestreamClientSession::ref clientSession = boost::make_shared<SOCKS5BytestreamClientSession>(connection, destinationAddressPort, destination, timerFactory); + clientSession->onSessionReady.connect(boost::bind(&TestHelper::handleSessionReady, &helper, _1)); + + clientSession->start(); + eventLoop->processEvents(); + + helper.unprocessedInput.clear(); + serverRespondHelloOK(); + eventLoop->processEvents(); + + helper.unprocessedInput.clear(); + serverRespondRequestOK(); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(true, helper.sessionReadyCalled); + CPPUNIT_ASSERT_EQUAL(false, helper.sessionReadyError); + + helper.unprocessedInput.clear(); + ByteArray transferData = generateRandomByteArray(1024); + boost::shared_ptr<ByteArrayReadBytestream> input = boost::make_shared<ByteArrayReadBytestream>(transferData); + clientSession->startSending(input); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(createByteArray(transferData.data(), transferData.size()), helper.unprocessedInput); + } + + + +private: + static ByteArray generateRandomByteArray(size_t len) { + boost::uniform_int<> dist(0, 255); + boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomByte(randomGen, dist); + ByteArray result(len); + for (size_t i=0; i < len; ++i ) { + result[i] = randomByte(); + } + return result; + } + + // Server responses + void serverRespondHelloOK() { + connection->onDataRead(createSafeByteArrayRef("\x05\00", 2)); + } + + void serverRespondHelloAuthFail() { + connection->onDataRead(createSafeByteArrayRef("\x05\xFF", 2)); + } + + void serverRespondRequestOK() { + boost::shared_ptr<SafeByteArray> dataToSend = createSafeByteArrayRef("\x05\x00\x00\x03", 4); + append(*dataToSend, createSafeByteArray(destination.size())); + append(*dataToSend, createSafeByteArray(destination)); + append(*dataToSend, createSafeByteArray("\x00", 1)); + connection->onDataRead(dataToSend); + } + + void serverRespondRequestFail() { + boost::shared_ptr<SafeByteArray> correctData = createSafeByteArrayRef("\x05\x00\x00\x03", 4); + append(*correctData, createSafeByteArray(destination.size())); + append(*correctData, createSafeByteArray(destination)); + append(*correctData, createSafeByteArray("\x00", 1)); + + boost::shared_ptr<SafeByteArray> dataToSend; + //ByteArray failingData = Hexify::unhexify("8417947d1d305c72c11520ea7d2c6e787396705e72c312c6ccc3f66613d7cae1b91b7ab48e8b59a17d559c15fb51"); + //append(dataToSend, failingData); + //SWIFT_LOG(debug) << "hexed: " << Hexify::hexify(failingData) << std::endl; + do { + ByteArray rndArray = generateRandomByteArray(correctData->size()); + dataToSend = createSafeByteArrayRef(rndArray.data(), rndArray.size()); + } while (*dataToSend == *correctData); + connection->onDataRead(dataToSend); + } + +private: + struct TestHelper { + TestHelper() : sessionReadyCalled(false), sessionReadyError(false) {} + ByteArray unprocessedInput; + bool sessionReadyCalled; + bool sessionReadyError; + + void handleConnectionDataWritten(const SafeByteArray& data) { + append(unprocessedInput, data); + //SWIFT_LOG(debug) << "unprocessedInput (" << unprocessedInput.size() << "): " << Hexify::hexify(unprocessedInput) << std::endl; + } + + void handleSessionReady(bool error) { + sessionReadyCalled = true; + sessionReadyError = error; + } + }; + + +private: + struct MockeryConnection : public Connection, public EventOwner, public boost::enable_shared_from_this<MockeryConnection> { + public: + MockeryConnection(const std::vector<HostAddressPort>& failingPorts, bool isResponsive, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), isResponsive(isResponsive), disconnectCalled(false) {} + + void listen() { assert(false); } + void connect(const HostAddressPort& address) { + hostAddressPort = address; + if (isResponsive) { + bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); + eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); + } + } + + HostAddressPort getLocalAddress() const { return HostAddressPort(); } + void disconnect() { + disconnectCalled = true; + } + + void write(const SafeByteArray& data) { + eventLoop->postEvent(boost::ref(onDataWritten), shared_from_this()); + onDataSent(data); + } + + boost::signal<void (const SafeByteArray&)> onDataSent; + + EventLoop* eventLoop; + boost::optional<HostAddressPort> hostAddressPort; + std::vector<HostAddressPort> failingPorts; + bool isResponsive; + bool disconnectCalled; + }; + +private: + DummyEventLoop* eventLoop; + DummyTimerFactory* timerFactory; + boost::shared_ptr<MockeryConnection> connection; + const std::vector<HostAddressPort> failingPorts; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5BytestreamClientSessionTest); diff --git a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp index c6d246d..4fe72c0 100644 --- a/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp +++ b/Swiften/FileTransfer/UnitTest/SOCKS5BytestreamServerSessionTest.cpp @@ -4,18 +4,19 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Swiften/Base/ByteArray.h" +#include <Swiften/Base/ByteArray.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <boost/bind.hpp> -#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h" -#include "Swiften/FileTransfer/ByteArrayReadBytestream.h" -#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h" -#include "Swiften/Network/DummyConnection.h" -#include "Swiften/EventLoop/DummyEventLoop.h" -#include "Swiften/Base/StartStopper.h" +#include <Swiften/Base/Concat.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamServerSession.h> +#include <Swiften/FileTransfer/ByteArrayReadBytestream.h> +#include <Swiften/FileTransfer/SOCKS5BytestreamRegistry.h> +#include <Swiften/Network/DummyConnection.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/Base/StartStopper.h> using namespace Swift; @@ -33,122 +34,127 @@ class SOCKS5BytestreamServerSessionTest : public CppUnit::TestFixture { void setUp() { receivedDataChunks = 0; eventLoop = new DummyEventLoop(); + bytestreams = new SOCKS5BytestreamRegistry(); connection = boost::shared_ptr<DummyConnection>(new DummyConnection(eventLoop)); connection->onDataSent.connect(boost::bind(&SOCKS5BytestreamServerSessionTest::handleDataWritten, this, _1)); - stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(ByteArray("abcdefg"))); + stream1 = boost::shared_ptr<ByteArrayReadBytestream>(new ByteArrayReadBytestream(createByteArray("abcdefg"))); } void tearDown() { connection.reset(); + delete bytestreams; delete eventLoop; } void testAuthenticate() { - std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession()); + boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession()); StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); - receive(ByteArray("\x05\x02\x01\x02")); + receive(createSafeByteArray("\x05\x02\x01\x02")); - CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData); + CPPUNIT_ASSERT(createByteArray("\x05\x00", 2) == receivedData); } void testAuthenticate_Chunked() { - std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession()); + boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession()); StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); - receive(ByteArray("\x05\x02\x01")); + receive(createSafeByteArray("\x05\x02\x01")); - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.getSize())); - receive(ByteArray("\x01")); - CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00", 2), receivedData); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedData.size())); + receive(createSafeByteArray("\x01")); + CPPUNIT_ASSERT(createByteArray("\x05\x00", 2) == receivedData); } void testRequest() { - std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession()); + boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession()); StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); - bytestreams.addBytestream("abcdef", stream1); + bytestreams->addReadBytestream("abcdef", stream1); authenticate(); - ByteArray hostname("abcdef"); - receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2)); - CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), ByteArray(receivedData.getData(), 13)); + ByteArray hostname(createByteArray("abcdef")); + receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2))); + CPPUNIT_ASSERT(createByteArray("\x05\x00\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == createByteArray(&receivedData[0], 13)); } void testRequest_UnknownBytestream() { - std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession()); + boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession()); StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); authenticate(); - ByteArray hostname("abcdef"); - receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.getSize() + hostname + ByteArray("\x00\x00", 2)); - CPPUNIT_ASSERT_EQUAL(ByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13), receivedData); + ByteArray hostname(createByteArray("abcdef")); + receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2))); + CPPUNIT_ASSERT(createByteArray("\x05\x04\x00\x03\x06\x61\x62\x63\x64\x65\x66\x00\x00", 13) == receivedData); } void testReceiveData() { - std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession()); + boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession()); StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); - bytestreams.addBytestream("abcdef", stream1); + bytestreams->addReadBytestream("abcdef", stream1); authenticate(); request("abcdef"); eventLoop->processEvents(); + testling->startTransfer(); skipHeader("abcdef"); + eventLoop->processEvents(); - CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData); + CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData); CPPUNIT_ASSERT_EQUAL(2, receivedDataChunks); } void testReceiveData_Chunked() { - std::auto_ptr<SOCKS5BytestreamServerSession> testling(createSession()); + boost::shared_ptr<SOCKS5BytestreamServerSession> testling(createSession()); testling->setChunkSize(3); StartStopper<SOCKS5BytestreamServerSession> stopper(testling.get()); - bytestreams.addBytestream("abcdef", stream1); + bytestreams->addReadBytestream("abcdef", stream1); authenticate(); request("abcdef"); eventLoop->processEvents(); - + testling->startTransfer(); + eventLoop->processEvents(); skipHeader("abcdef"); - CPPUNIT_ASSERT_EQUAL(ByteArray("abcdefg"), receivedData); + CPPUNIT_ASSERT(createByteArray("abcdefg") == receivedData); CPPUNIT_ASSERT_EQUAL(4, receivedDataChunks); } private: - void receive(const ByteArray& data) { + void receive(const SafeByteArray& data) { connection->receive(data); eventLoop->processEvents(); } void authenticate() { - receive(ByteArray("\x05\x02\x01\x02")); + receive(createSafeByteArray("\x05\x02\x01\x02")); receivedData.clear(); receivedDataChunks = 0; } void request(const std::string& hostname) { - receive(ByteArray("\x05\x01\x00\x03", 4) + hostname.size() + hostname + ByteArray("\x00\x00", 2)); + receive(concat(createSafeByteArray("\x05\x01\x00\x03", 4), createSafeByteArray(hostname.size()), createSafeByteArray(hostname), createSafeByteArray("\x00\x00", 2))); } void skipHeader(const std::string& hostname) { int headerSize = 7 + hostname.size(); - receivedData = ByteArray(receivedData.getData() + headerSize, receivedData.getSize() - headerSize); + receivedData = createByteArray(&receivedData[headerSize], receivedData.size() - headerSize); } - void handleDataWritten(const ByteArray& data) { - receivedData += data; + void handleDataWritten(const SafeByteArray& data) { + receivedData.insert(receivedData.end(), data.begin(), data.end()); receivedDataChunks++; } private: SOCKS5BytestreamServerSession* createSession() { - SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, &bytestreams); + SOCKS5BytestreamServerSession* session = new SOCKS5BytestreamServerSession(connection, bytestreams); return session; } private: DummyEventLoop* eventLoop; - SOCKS5BytestreamRegistry bytestreams; + SOCKS5BytestreamRegistry* bytestreams; boost::shared_ptr<DummyConnection> connection; - ByteArray receivedData; + std::vector<unsigned char> receivedData; int receivedDataChunks; boost::shared_ptr<ByteArrayReadBytestream> stream1; }; |