00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "Qt.h"
00024 #include "../Choker.h"
00025 #include "generated/ChokerDriver.h"
00026 #include "generated/ChokerMock.h"
00027 #include "generated/TransferSessionMock.h"
00028 #include "Imports.cpp"
00029
00030 namespace Protocols {
00031 namespace BitTorrent {
00032 namespace Transfers {
00033 namespace Testing {
00034
00036
00038 class ChokerTest : public CppUnit::TestFixture
00039 {
00040 CPPUNIT_TEST_SUITE (ChokerTest);
00041 CPPUNIT_TEST (testStartingChokerStartsTheTimer);
00042 CPPUNIT_TEST (testStoppingChokerStopsTheTimer);
00043 CPPUNIT_TEST (testAddOneSessionItGetsUnchokedNextChokingTime);
00044 CPPUNIT_TEST (testAddFourSessionsAllGetUnchokedNextChokingTime);
00045 CPPUNIT_TEST (testAddFiveSessionsFiveNotInterestedGetUnchokedNextChokingTime);
00046 CPPUNIT_TEST (testAddFiveSessionsFourInterestedGetUnchokedNextChokingTime);
00047 CPPUNIT_TEST (testFifthSessionUnchokedDuringFirstOptimisticUnchoking);
00048 CPPUNIT_TEST (testForthSessionUnchokedDuringSecondUnchoking);
00049 CPPUNIT_TEST (testOptimisticUnchokeDoesNotChangeFor2ChokingTimes);
00050 CPPUNIT_TEST (testUnchokePeersWeDownloadFastestFrom);
00051 CPPUNIT_TEST (testAntiSnubbingOneSnubbedSessionTwoOptimisticChokes);
00052 CPPUNIT_TEST (testAntiSnubbingHasNoEffectWhenNoConnectionsAreChoked);
00053 CPPUNIT_TEST (testClosedSessionsAreNeitherChokedNorUnchokedAnymore);
00054 CPPUNIT_TEST (testSeedersAreAlwaysKeptChoked);
00055 CPPUNIT_TEST (testSeedersAreNeverOptimisticlyUnchoked);
00056 CPPUNIT_TEST (testOptimisticUnchokingPicksRandomlyFromMultipleCandidates);
00057 CPPUNIT_TEST (testUnchokeSessionsWithHighestUploadRateWhenInSeedingMode);
00058 CPPUNIT_TEST (testWeIngnoreBeingSnubbedWhenWeAreInSeedingMode);
00059 CPPUNIT_TEST_SUITE_END();
00060
00061 struct SessionState
00062 {
00063 bool isPeerChoked;
00064 bool isPeerInterested;
00065 bool isPeerSeeder;
00066 bool areWeSnubbed;
00067 qint64 downloadSpeed;
00068 qint64 uploadSpeed;
00069
00070 SessionState() : isPeerChoked (true), isPeerInterested (false),
00071 isPeerSeeder (false), areWeSnubbed (false),
00072 downloadSpeed (0), uploadSpeed (0) {}
00073 };
00074
00075 typedef QList <TransferSessionMock *> Sessions;
00076 typedef QMap <TransferSessionMock *, SessionState> SessionsStateMap;
00077
00078 auto_ptr <TimerMock> timer;
00079 auto_ptr <RandomNumberGeneratorMock> randomNumberGenerator;
00080 auto_ptr <TransferManagerStateMock> transferManagerState;
00081 auto_ptr <Choker> chokerReal;
00082 auto_ptr <ChokerDriver> choker;
00083 Sessions sessions;
00084 SessionsStateMap sessionsState;
00085 Sessions openedSessions;
00086
00087 const Choke choke;
00088 const Unchoke unchoke;
00089 QList <int> randomIntsZero;
00090 QList <int> randomIntsZeroZero;
00091 bool isTestingSeedingMode;
00092 static const int SessionsCount = 10;
00093
00094 public:
00095 ChokerTest() : timer (), randomNumberGenerator(), transferManagerState(),
00096 chokerReal(), choker(), sessions(), sessionsState(),
00097 openedSessions(), choke(), unchoke(), randomIntsZero(),
00098 randomIntsZeroZero(), isTestingSeedingMode (false)
00099 {
00100
00101 randomIntsZero += 0;
00102
00103 randomIntsZeroZero += 0;
00104 randomIntsZeroZero += 0;
00105 }
00106
00107 void setUp()
00108 {
00109 timer.reset (new TimerMock());
00110 randomNumberGenerator.reset (new RandomNumberGeneratorMock());
00111 transferManagerState.reset (new TransferManagerStateMock());
00112 chokerReal.reset (new Choker (*timer,
00113 *randomNumberGenerator,
00114 *transferManagerState));
00115 choker.reset (new ChokerDriver (*chokerReal));
00116
00117 for (int i = 0; i < SessionsCount; ++i) {
00118 sessions += new TransferSessionMock();
00119 sessionsState.insert (sessions.last(), SessionState());
00120 }
00121 }
00122
00123 void tearDown()
00124 {
00125 choker.reset();
00126 chokerReal.reset();
00127 randomNumberGenerator.reset();
00128 transferManagerState.reset();
00129 timer.reset();
00130
00131 for (int i = 0; i < 10; ++i)
00132 delete sessions [i];
00133 sessions.clear();
00134 sessionsState.clear();
00135 openedSessions.clear();
00136 }
00137
00139 void sessionsStateSetPeerIsChoked (const Sessions &sessions, bool choked)
00140 {
00141 foreach (TransferSessionMock *session, sessions)
00142 sessionsState [session].isPeerChoked = choked;
00143 }
00144
00146 void sessionsStateSetPeerIsInterested (const Sessions &sessions,
00147 bool interested)
00148 {
00149 foreach (TransferSessionMock *session, sessions)
00150 sessionsState [session].isPeerInterested = interested;
00151 }
00152
00154 void sessionsStateSetPeerIsSeeder (const Sessions &sessions, bool seeder)
00155 {
00156 foreach (TransferSessionMock *session, sessions)
00157 sessionsState [session].isPeerSeeder = seeder;
00158 }
00159
00161 void sessionsStateSetWeAreSnubbed (const Sessions &sessions, bool snubbed)
00162 {
00163 foreach (TransferSessionMock *session, sessions)
00164 sessionsState [session].areWeSnubbed = snubbed;
00165 }
00166
00168 void refOpenSessions (const Sessions &sessions)
00169 {
00170 foreach (TransferSessionMock *session, sessions) {
00171 call (choker->openedSession (*session))
00172 .returns();
00173 }
00174 openedSessions += sessions;
00175 }
00176
00178 void refCloseSessions (const Sessions &sessions)
00179 {
00180 foreach (TransferSessionMock *session, sessions) {
00181 call (choker->closedSession (*session))
00182 .returns();
00183
00184 openedSessions.removeAll (session);
00185 }
00186 }
00187
00189
00200 void refChokingTimeUnchokeChokeSessions (const Sessions &unchokedSessions,
00201 const Sessions &chokedSessions,
00202 const QList <int> &randomInts
00203 = QList <int>())
00204 {
00205 CallDriver <BoundFunction <Choker, void()> > foo =
00206 timer->calls (choker->chokingTime());
00207 foo.willCall (transferManagerState->isDownloading())
00208 .numberOfTimes (1, -1)
00209 .willReturn (!isTestingSeedingMode);
00210
00211 foreach (TransferSessionMock *session, openedSessions)
00212 foo.willCall (session->isPeerChoked())
00213 .numberOfTimes (0, -1)
00214 .willReturn (sessionsState [session].isPeerChoked)
00215 .willCall (session->isPeerInterested())
00216 .numberOfTimes (0, -1)
00217 .willReturn (sessionsState [session].isPeerInterested)
00218 .willCall (session->areWeSnubbed())
00219 .numberOfTimes (0, -1)
00220 .willReturn (sessionsState [session].areWeSnubbed)
00221 .willCall (session->isPeerSeeder())
00222 .numberOfTimes (0, -1)
00223 .willReturn (sessionsState [session].isPeerSeeder)
00224 .willCall (session->currentDownloadSpeed())
00225 .numberOfTimes (0, -1)
00226 .willReturn (sessionsState [session].downloadSpeed)
00227 .willCall (session->currentUploadSpeed())
00228 .numberOfTimes (0, -1)
00229 .willReturn (sessionsState [session].uploadSpeed);
00230
00231 foreach (TransferSessionMock *session, unchokedSessions)
00232 foo.willCall (session->sendPacket (unchoke))
00233 .returns();
00234
00235 foreach (TransferSessionMock *session, chokedSessions)
00236 foo.willCall (session->sendPacket (choke))
00237 .returns();
00238
00239
00240
00241 foreach (int randomInt, randomInts)
00242 foo.willCall (randomNumberGenerator->random())
00243 .willReturn (randomInt);
00244 foo.returns();
00245
00246 sessionsStateSetPeerIsChoked (chokedSessions, true);
00247 sessionsStateSetPeerIsChoked (unchokedSessions, false);
00248 }
00249
00251
00255 void testStartingChokerStartsTheTimer()
00256 {
00257 Callable <void()> chokingTimeCallable (choker->operator Choker&(),
00258 &Choker::chokingTime);
00259
00260 call (choker->start())
00261 .willCall (timer->start (chokingTimeCallable, 10000))
00262 .returns()
00263 .returns();
00264 }
00265
00267 void testStoppingChokerStopsTheTimer()
00268 {
00269 call (choker->stop())
00270 .willCall (timer->stop())
00271 .returns()
00272 .returns();
00273 }
00274
00276 void testAddOneSessionItGetsUnchokedNextChokingTime()
00277 {
00278 refOpenSessions (sessions.mid (0, 1));
00279 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 1),
00280 sessions.mid (0, 0));
00281 }
00282
00284 void testAddFourSessionsAllGetUnchokedNextChokingTime()
00285 {
00286 refOpenSessions (sessions.mid (0, 4));
00287 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 4),
00288 sessions.mid (0, 0));
00289 }
00290
00292 void testAddFiveSessionsFiveNotInterestedGetUnchokedNextChokingTime()
00293 {
00294 refOpenSessions (sessions.mid (0, 5));
00295 sessionsStateSetPeerIsInterested (sessions.mid (0, 5), false);
00296 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 5),
00297 sessions.mid (0, 0));
00298 }
00299
00301 void testAddFiveSessionsFourInterestedGetUnchokedNextChokingTime()
00302 {
00303 refOpenSessions (sessions.mid (0, 5));
00304 sessionsStateSetPeerIsInterested (sessions.mid (0, 5), true);
00305 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 4),
00306 sessions.mid (0, 0));
00307 }
00308
00310
00323 void testFifthSessionUnchokedDuringFirstOptimisticUnchoking()
00324 {
00325 refOpenSessions (sessions.mid (0, 5));
00326 sessionsStateSetPeerIsInterested (sessions.mid (0, 5), true);
00327 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 4),
00328 sessions.mid (0, 0));
00329 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 0),
00330 sessions.mid (0, 0));
00331 refChokingTimeUnchokeChokeSessions (sessions.mid (4, 1),
00332 sessions.mid (3, 1),
00333 randomIntsZero);
00334 }
00335
00337
00345 void testForthSessionUnchokedDuringSecondUnchoking()
00346 {
00347 testFifthSessionUnchokedDuringFirstOptimisticUnchoking();
00348
00349 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00350 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00351 refChokingTimeUnchokeChokeSessions (sessions.mid (3, 1),
00352 sessions.mid (4, 1),
00353 randomIntsZero);
00354 }
00355
00357
00368 void testOptimisticUnchokeDoesNotChangeFor2ChokingTimes()
00369 {
00370 testFifthSessionUnchokedDuringFirstOptimisticUnchoking();
00371 sessionsState [sessions [0]].downloadSpeed = 15;
00372 sessionsState [sessions [1]].downloadSpeed = 10;
00373 sessionsState [sessions [2]].downloadSpeed = 5;
00374 sessionsState [sessions [3]].downloadSpeed = 5;
00375 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00376 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00377 refChokingTimeUnchokeChokeSessions (sessions.mid (3, 1),
00378 sessions.mid (4, 1),
00379 randomIntsZero);
00380 }
00381
00383
00387 void testUnchokePeersWeDownloadFastestFrom()
00388 {
00389 refOpenSessions (sessions.mid (0, SessionsCount));
00390
00391 Sessions unchokedSessions, chokedSessions;
00392
00393 sessionsState [sessions [0]].isPeerInterested = false;
00394 sessionsState [sessions [0]].downloadSpeed = 1;
00395 chokedSessions += sessions [0];
00396
00397 sessionsState [sessions [1]].isPeerInterested = true;
00398 sessionsState [sessions [1]].downloadSpeed = 0;
00399 chokedSessions += sessions [1];
00400
00401 sessionsState [sessions [2]].isPeerInterested = false;
00402 sessionsState [sessions [2]].downloadSpeed = 12;
00403 unchokedSessions += sessions [2];
00404
00405 sessionsState [sessions [3]].isPeerInterested = false;
00406 sessionsState [sessions [3]].downloadSpeed = 0;
00407 chokedSessions += sessions [3];
00408
00409 sessionsState [sessions [4]].isPeerInterested = true;
00410 sessionsState [sessions [4]].downloadSpeed = 12;
00411 unchokedSessions += sessions [4];
00412
00413 sessionsState [sessions [5]].isPeerInterested = true;
00414 sessionsState [sessions [5]].downloadSpeed = 48;
00415 unchokedSessions += sessions [5];
00416
00417 sessionsState [sessions [6]].isPeerInterested = true;
00418 sessionsState [sessions [6]].downloadSpeed = 0;
00419 chokedSessions += sessions [6];
00420
00421
00422 sessionsState [sessions [7]].isPeerInterested = true;
00423 sessionsState [sessions [7]].downloadSpeed = 1;
00424 unchokedSessions += sessions [7];
00425
00426 sessionsState [sessions [8]].isPeerInterested = true;
00427 sessionsState [sessions [8]].downloadSpeed = 12000000;
00428 unchokedSessions += sessions [8];
00429
00430 sessionsState [sessions [9]].isPeerInterested = false;
00431 sessionsState [sessions [9]].downloadSpeed = 0;
00432 chokedSessions += sessions [9];
00433
00434 refChokingTimeUnchokeChokeSessions (unchokedSessions, Sessions());
00435 }
00436
00438
00447 void testAntiSnubbingOneSnubbedSessionTwoOptimisticChokes()
00448 {
00449 refOpenSessions (sessions.mid (0, 6));
00450
00451 sessionsStateSetPeerIsChoked (sessions.mid (0, 4), false);
00452 sessionsStateSetPeerIsInterested (sessions.mid (0, 6), true);
00453 sessionsStateSetWeAreSnubbed (sessions.mid (2, 1), true);
00454
00455 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00456 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00457 refChokingTimeUnchokeChokeSessions (sessions.mid (4, 2),
00458 sessions.mid (2, 2),
00459 randomIntsZeroZero);
00460 }
00461
00463 void testAntiSnubbingHasNoEffectWhenNoConnectionsAreChoked()
00464 {
00465 refOpenSessions (sessions.mid (0, 4));
00466
00467 sessionsStateSetPeerIsChoked (sessions.mid (0, 4), false);
00468 sessionsStateSetWeAreSnubbed (sessions.mid (0, 4), true);
00469
00470 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00471 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00472 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00473 }
00474
00476
00481 void testClosedSessionsAreNeitherChokedNorUnchokedAnymore()
00482 {
00483 refOpenSessions (sessions.mid (0, 1));
00484 refCloseSessions (sessions.mid (0, 1));
00485 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00486 }
00487
00489
00494 void testSeedersAreAlwaysKeptChoked()
00495 {
00496 refOpenSessions (sessions.mid (0, 4));
00497
00498
00499 sessionsStateSetPeerIsSeeder (sessions.mid (0, 2), true);
00500 sessionsStateSetPeerIsChoked (sessions.mid (1, 1), false);
00501 refChokingTimeUnchokeChokeSessions (sessions.mid (2, 2),
00502 sessions.mid (1, 1));
00503 }
00504
00506 void testSeedersAreNeverOptimisticlyUnchoked()
00507 {
00508
00509 testSeedersAreAlwaysKeptChoked();
00510 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00511 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00512
00513 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00514 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00515 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00516 }
00517
00519
00533 void testOptimisticUnchokingPicksRandomlyFromMultipleCandidates()
00534 {
00535 refOpenSessions (sessions);
00536 sessionsStateSetPeerIsInterested (sessions, true);
00537
00538 refChokingTimeUnchokeChokeSessions (sessions.mid (0, 4), Sessions());
00539 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00540
00541 QList <int> randomIntsTwentyEight;
00542 randomIntsTwentyEight += 28;
00543 refChokingTimeUnchokeChokeSessions (sessions.mid (7, 1),
00544 sessions.mid (3, 1),
00545 randomIntsTwentyEight);
00546 }
00547
00548 void testUnchokeSessionsWithHighestUploadRateWhenInSeedingMode()
00549 {
00550 isTestingSeedingMode = true;
00551 refOpenSessions (sessions.mid (0, SessionsCount));
00552
00553 Sessions unchokedSessions, chokedSessions;
00554
00555 sessionsState [sessions [0]].isPeerInterested = false;
00556 sessionsState [sessions [0]].uploadSpeed = 1;
00557 chokedSessions += sessions [0];
00558
00559 sessionsState [sessions [1]].isPeerInterested = true;
00560 sessionsState [sessions [1]].uploadSpeed = 0;
00561 chokedSessions += sessions [1];
00562
00563 sessionsState [sessions [2]].isPeerInterested = false;
00564 sessionsState [sessions [2]].uploadSpeed = 12;
00565 unchokedSessions += sessions [2];
00566
00567 sessionsState [sessions [3]].isPeerInterested = false;
00568 sessionsState [sessions [3]].uploadSpeed = 0;
00569 chokedSessions += sessions [3];
00570
00571 sessionsState [sessions [4]].isPeerInterested = true;
00572 sessionsState [sessions [4]].uploadSpeed = 12;
00573 unchokedSessions += sessions [4];
00574
00575 sessionsState [sessions [5]].isPeerInterested = true;
00576 sessionsState [sessions [5]].uploadSpeed = 48;
00577 unchokedSessions += sessions [5];
00578
00579 sessionsState [sessions [6]].isPeerInterested = true;
00580 sessionsState [sessions [6]].uploadSpeed = 0;
00581 chokedSessions += sessions [6];
00582
00583
00584 sessionsState [sessions [7]].isPeerInterested = true;
00585 sessionsState [sessions [7]].uploadSpeed = 1;
00586 unchokedSessions += sessions [7];
00587
00588 sessionsState [sessions [8]].isPeerInterested = true;
00589 sessionsState [sessions [8]].uploadSpeed = 12000000;
00590 unchokedSessions += sessions [8];
00591
00592 sessionsState [sessions [9]].isPeerInterested = false;
00593 sessionsState [sessions [9]].uploadSpeed = 0;
00594 chokedSessions += sessions [9];
00595
00596 refChokingTimeUnchokeChokeSessions (unchokedSessions, Sessions());
00597 }
00598
00600 void testWeIngnoreBeingSnubbedWhenWeAreInSeedingMode()
00601 {
00602 isTestingSeedingMode = true;
00603 refOpenSessions (sessions.mid (0, 6));
00604
00605 sessionsStateSetPeerIsChoked (sessions.mid (0, 4), false);
00606 sessionsStateSetPeerIsInterested (sessions.mid (0, 6), true);
00607 sessionsStateSetWeAreSnubbed (sessions.mid (2, 1), true);
00608
00609 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00610 refChokingTimeUnchokeChokeSessions (Sessions(), Sessions());
00611 refChokingTimeUnchokeChokeSessions (sessions.mid (4, 1),
00612 sessions.mid (3, 1),
00613 randomIntsZero);
00614 }
00615 };
00616
00617 CPPUNIT_TEST_SUITE_REGISTRATION(ChokerTest);
00618
00619 }
00620 }
00621 }
00622 }