@@ -25,7 +25,7 @@ import org.scalatest.concurrent.Eventually
25
25
import org .scalatest .flatspec .AnyFlatSpecLike
26
26
import org .scalatest .matchers .should .Matchers
27
27
import org .scalatestplus .scalacheck .ScalaCheckDrivenPropertyChecks
28
- import org .scalacheck .{Arbitrary , Gen }, Arbitrary .arbitrary
28
+ import org .scalacheck .{Arbitrary , Gen , Shrink }, Arbitrary .arbitrary
29
29
import scala .concurrent .duration ._
30
30
31
31
// scalastyle:off magic.number
@@ -260,6 +260,8 @@ class PeerManagerSpec
260
260
peerAsOutgoingProbe.expectMsg(PeerActor .DisconnectPeer (Disconnect .Reasons .AlreadyConnected ))
261
261
}
262
262
263
+ behavior of " outgoingConnectionDemand"
264
+
263
265
it should " try to connect to at least min-outgoing-peers but no longer than max-outgoing-peers" in new ConnectedPeersFixture {
264
266
forAll { (connectedPeers : ConnectedPeers ) =>
265
267
val demand = PeerManagerActor .outgoingConnectionDemand(connectedPeers, peerConfiguration)
@@ -272,6 +274,8 @@ class PeerManagerSpec
272
274
}
273
275
}
274
276
277
+ behavior of " numberOfIncomingConnectionsToPrune"
278
+
275
279
it should " try to prune incoming connections down to the minimum allowed number" in new ConnectedPeersFixture {
276
280
forAll { (connectedPeers : ConnectedPeers ) =>
277
281
val numPeersToPrune = PeerManagerActor .numberOfIncomingConnectionsToPrune(connectedPeers, peerConfiguration)
@@ -289,20 +293,23 @@ class PeerManagerSpec
289
293
}
290
294
}
291
295
296
+ behavior of " ConnectedPeers.prunePeers"
297
+
292
298
// The `ConnectedPeers` is quite slow to generate, so doing a few tests in one go.
293
- it should " prune peers which are old enough down to incoming number , protecting against repeated forced pruning" in new ConnectedPeersFixture {
299
+ it should " prune peers which are old enough, protecting against repeated forced pruning" in new ConnectedPeersFixture {
294
300
forAll { (connectedPeers : ConnectedPeers ) =>
295
301
val numPeersToPrune = PeerManagerActor .numberOfIncomingConnectionsToPrune(connectedPeers, peerConfiguration)
296
302
303
+ val now = System .currentTimeMillis
304
+
297
305
// Prune the requested number of peers.
298
306
{
299
307
// Pretend we are in the future so age doesn't count.
300
308
val (maxPrunedPeers, _) =
301
309
connectedPeers.prunePeers(
302
- incoming = true ,
303
310
peerConfiguration.minPruneAge,
304
311
numPeers = numPeersToPrune,
305
- currentTimeMillis = System .currentTimeMillis + peerConfiguration.minPruneAge.toMillis + 1
312
+ currentTimeMillis = now + peerConfiguration.minPruneAge.toMillis + 1
306
313
)
307
314
308
315
maxPrunedPeers.size shouldBe numPeersToPrune
@@ -311,61 +318,66 @@ class PeerManagerSpec
311
318
// Only prune peers which are old enough.
312
319
{
313
320
val (agedPrunedPeers, _) = connectedPeers.prunePeers(
314
- incoming = true ,
315
321
peerConfiguration.minPruneAge,
316
322
numPeers = numPeersToPrune
317
323
)
318
324
Inspectors .forAll(agedPrunedPeers) {
319
- _.createTimeMillis shouldBe <= (System .currentTimeMillis - peerConfiguration.minPruneAge.toMillis)
325
+ _.createTimeMillis shouldBe <= (now - peerConfiguration.minPruneAge.toMillis)
320
326
}
321
327
}
322
328
323
- // Not prune repeatedly .
329
+ // Not prune twice in a row within the prune cool-of time .
324
330
{
325
- val minAge = 1 .day // That should include all peers in the test data
326
-
327
- val (probe2, _) = connectedPeers.prunePeers(
328
- incoming = true ,
331
+ val now = System .currentTimeMillis
332
+ val minAge = 1 .minute
333
+ // Check that we have at least 2 peers to prune.
334
+ val (probe, _) = connectedPeers.prunePeers(
329
335
minAge,
330
- numPeers = 2
336
+ numPeers = Int .MaxValue ,
337
+ currentTimeMillis = now
331
338
)
332
- if (probe2.size == 2 ) {
333
- val (_, pruned1) = connectedPeers.prunePeers(
334
- incoming = true ,
335
- minAge,
336
- numPeers = 1
337
- )
338
- val (probe0, _) = pruned1.prunePeers(
339
- incoming = true ,
340
- minAge,
341
- numPeers = 1
342
- )
343
- probe0 shouldBe empty
344
-
345
- val (probe1, _) = pruned1.prunePeers(
346
- incoming = true ,
347
- minAge,
348
- numPeers = 1 ,
349
- currentTimeMillis = System .currentTimeMillis + minAge.toMillis
350
- )
351
- probe1 should not be empty
339
+ whenever(probe.size >= 2 ) {
340
+ val (_, pruned1) = connectedPeers.prunePeers(minAge, numPeers = 1 , currentTimeMillis = now)
341
+
342
+ pruned1.prunePeers(minAge, numPeers = 1 , currentTimeMillis = now + 1 )._1 shouldBe empty
343
+
344
+ pruned1
345
+ .prunePeers(
346
+ minAge,
347
+ numPeers = 1 ,
348
+ currentTimeMillis = now + minAge.toMillis
349
+ )
350
+ ._1 should not be empty
352
351
}
353
352
}
354
353
355
354
// Not prune the same peer repeatedly.
356
355
{
357
356
val (peers1, pruned) = connectedPeers.prunePeers(
358
- incoming = true ,
359
357
peerConfiguration.minPruneAge,
360
358
numPeers = numPeersToPrune
361
359
)
362
360
val (peers2, _) = pruned.prunePeers(
363
- incoming = true ,
364
361
peerConfiguration.minPruneAge,
365
- numPeers = numPeersToPrune
362
+ numPeers = numPeersToPrune,
363
+ currentTimeMillis = now + peerConfiguration.minPruneAge.toMillis
366
364
)
367
365
peers1.toSet intersect peers2.toSet shouldBe empty
368
366
}
367
+
368
+ // Prune peers with minimum priority first.
369
+ {
370
+ val (peers, _) = connectedPeers.prunePeers(
371
+ peerConfiguration.minPruneAge,
372
+ numPeers = numPeersToPrune,
373
+ priority = _.hashCode.toDouble // Dummy priority
374
+ )
375
+ whenever(peers.nonEmpty) {
376
+ Inspectors .forAll(peers.init zip peers.tail) { case (a, b) =>
377
+ a.id.hashCode shouldBe <= (b.id.hashCode)
378
+ }
379
+ }
380
+ }
369
381
}
370
382
}
371
383
@@ -385,7 +397,6 @@ class PeerManagerSpec
385
397
386
398
// Not prune again until the peers have been disconnected.
387
399
val (peers, pruning) = connectedPeers.prunePeers(
388
- incoming = true ,
389
400
peerConfiguration.minPruneAge,
390
401
numPeersToPrune0
391
402
)
@@ -395,22 +406,24 @@ class PeerManagerSpec
395
406
val pruned = peers.foldLeft(pruning) { case (ps, p) =>
396
407
ps.removeTerminatedPeer(p.ref)._2
397
408
}
409
+ // ignore should be at the minimum incoming peer count now.
398
410
PeerManagerActor .numberOfIncomingConnectionsToPrune(pruned, peerConfiguration) shouldBe 0
399
411
400
412
val replenished = newIncoming.foldLeft(pruned) { case (ps, p) =>
401
413
ps.addNewPendingPeer(p).promotePeerToHandshaked(p)
402
414
}
415
+ // ignore should be maxed out now, can prune again.
403
416
PeerManagerActor .numberOfIncomingConnectionsToPrune(replenished, peerConfiguration) shouldBe > (0 )
404
417
}
405
418
}
406
419
407
420
trait ConnectedPeersFixture {
408
421
case class TestConfig (
409
- minOutgoingPeers : Int = 20 ,
422
+ minOutgoingPeers : Int = 10 ,
410
423
maxOutgoingPeers : Int = 30 ,
411
424
maxIncomingPeers : Int = 30 ,
412
425
maxPendingPeers : Int = 20 ,
413
- pruneIncomingPeers : Int = 10 ,
426
+ pruneIncomingPeers : Int = 20 ,
414
427
minPruneAge : FiniteDuration = 30 .minutes
415
428
) extends PeerManagerActor .PeerConfiguration .ConnectionLimits
416
429
@@ -419,6 +432,9 @@ class PeerManagerSpec
419
432
implicit val arbConnectedPeers : Arbitrary [ConnectedPeers ] = Arbitrary {
420
433
genConnectedPeers(peerConfiguration.maxIncomingPeers, peerConfiguration.maxOutgoingPeers)
421
434
}
435
+
436
+ implicit val noShrinkConnectedPeers : Shrink [ConnectedPeers ] =
437
+ Shrink [ConnectedPeers ](_ => Stream .empty)
422
438
}
423
439
424
440
trait TestSetup {
@@ -508,7 +524,7 @@ class PeerManagerSpec
508
524
ip <- Gen .listOfN(4 , Gen .choose(0 , 255 )).map(_.mkString(" ." ))
509
525
port <- Gen .choose(10000 , 60000 )
510
526
incoming <- arbitrary[Boolean ]
511
- ageMillis <- Gen .choose(0 , 60 * 60 * 1000 )
527
+ ageMillis <- Gen .choose(0 , 24 * 60 * 60 * 1000 )
512
528
} yield Peer (
513
529
remoteAddress = new InetSocketAddress (ip, port),
514
530
ref = TestProbe ().ref,
@@ -531,7 +547,9 @@ class PeerManagerSpec
531
547
incoming <- Gen .listOfN(numIncoming, genIncomingPeer)
532
548
outgoing <- Gen .listOfN(numOutgoing, genOugoingPeer)
533
549
connections0 = (incoming ++ outgoing).foldLeft(ConnectedPeers .empty)(_ addNewPendingPeer _)
534
- handshaked <- Gen .someOf(incoming ++ outgoing)
550
+ ratioHandshaked <- Gen .choose(0.75 , 1.0 )
551
+ numHandshaked <- Gen .choose(0.75 , 1.0 ).map(_ * (numIncoming + numOutgoing)).map(_.toInt)
552
+ handshaked <- Gen .pick(numHandshaked, incoming ++ outgoing)
535
553
connections1 = handshaked.foldLeft(connections0)(_ promotePeerToHandshaked _)
536
554
} yield connections1
537
555
0 commit comments