mirror of
https://github.com/ScuroNeko/mtg.git
synced 2026-09-01 12:44:01 +03:00
Add doppel and tls packages
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
package doppel
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type StatsTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) GenWeibull(k, lambda float64, n int, seed uint64) []time.Duration {
|
||||
rng := rand.New(rand.NewPCG(seed, 0))
|
||||
samples := make([]time.Duration, n)
|
||||
|
||||
for i := range samples {
|
||||
u := 1.0 - rng.Float64()
|
||||
ms := lambda * math.Pow(-math.Log(u), 1.0/k)
|
||||
d := time.Duration(ms * float64(time.Millisecond))
|
||||
|
||||
if d < time.Microsecond {
|
||||
time.Sleep(time.Microsecond)
|
||||
d = time.Microsecond
|
||||
}
|
||||
|
||||
samples[i] = d
|
||||
}
|
||||
|
||||
return samples
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestNewStatsRecoverParameters() {
|
||||
knownK := 1.5
|
||||
knownLambda := 100.0
|
||||
|
||||
samples := suite.GenWeibull(knownK, knownLambda, 5000, 42)
|
||||
stats := NewStats(samples)
|
||||
|
||||
suite.InDelta(knownK, stats.k, 0.1)
|
||||
suite.InDelta(knownLambda, stats.lambda, 5.0)
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestNewStatsExponentialCase() {
|
||||
// When k=1, Weibull reduces to exponential distribution.
|
||||
knownK := 1.0
|
||||
knownLambda := 50.0
|
||||
|
||||
samples := suite.GenWeibull(knownK, knownLambda, 5000, 123)
|
||||
stats := NewStats(samples)
|
||||
|
||||
suite.InDelta(knownK, stats.k, 0.1)
|
||||
suite.InDelta(knownLambda, stats.lambda, 5.0)
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestNewStatsSmallK() {
|
||||
// k < 1 produces a heavy-tailed distribution typical for network delays.
|
||||
// Lambda must be large enough so samples stay above microsecond precision
|
||||
// after time.Duration round-trip.
|
||||
knownK := 0.6
|
||||
knownLambda := 100.0
|
||||
|
||||
samples := suite.GenWeibull(knownK, knownLambda, 10000, 99)
|
||||
stats := NewStats(samples)
|
||||
|
||||
suite.InDelta(knownK, stats.k, 0.05)
|
||||
suite.InDelta(knownLambda, stats.lambda, 5.0)
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestNewStatsLargeK() {
|
||||
// k > 1: light tail, concentrated around the mode.
|
||||
knownK := 5.0
|
||||
knownLambda := 200.0
|
||||
|
||||
samples := suite.GenWeibull(knownK, knownLambda, 5000, 77)
|
||||
stats := NewStats(samples)
|
||||
|
||||
suite.InDelta(knownK, stats.k, 0.3)
|
||||
suite.InDelta(knownLambda, stats.lambda, 5.0)
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestDelayNonNegative() {
|
||||
stats := &Stats{
|
||||
k: 1.5,
|
||||
lambda: 100.0,
|
||||
}
|
||||
|
||||
for range 200 {
|
||||
dur := stats.Delay()
|
||||
suite.GreaterOrEqual(dur, time.Duration(0))
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestDelayDistributionMean() {
|
||||
// Weibull mean = λ · Γ(1 + 1/k)
|
||||
k := 2.0
|
||||
lambda := 50.0
|
||||
stats := &Stats{k: k, lambda: lambda}
|
||||
|
||||
n := 50000
|
||||
sum := 0.0
|
||||
|
||||
for range n {
|
||||
dur := stats.Delay()
|
||||
sum += float64(dur) / float64(time.Millisecond)
|
||||
}
|
||||
|
||||
sampleMean := sum / float64(n)
|
||||
expectedMean := lambda * math.Gamma(1.0+1.0/k)
|
||||
|
||||
suite.InDelta(expectedMean, sampleMean, expectedMean*0.05)
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestNewStatsRoundTrip() {
|
||||
// Estimate parameters from data, then verify that Delay samples
|
||||
// from the fitted distribution have approximately the same mean.
|
||||
knownK := 1.2
|
||||
knownLambda := 80.0
|
||||
|
||||
samples := suite.GenWeibull(knownK, knownLambda, 5000, 555)
|
||||
stats := NewStats(samples)
|
||||
|
||||
n := 50000
|
||||
sum := 0.0
|
||||
|
||||
for range n {
|
||||
dur := stats.Delay()
|
||||
sum += float64(dur) / float64(time.Millisecond)
|
||||
}
|
||||
|
||||
sampleMean := sum / float64(n)
|
||||
expectedMean := knownLambda * math.Gamma(1.0+1.0/knownK)
|
||||
|
||||
suite.InDelta(expectedMean, sampleMean, expectedMean*0.05)
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestSizeStartPhase() {
|
||||
stats := &Stats{k: 1.0, lambda: 1.0}
|
||||
|
||||
for range TLSCounterAccelAfter {
|
||||
size := stats.Size()
|
||||
suite.Equal(TLSRecordSizeStart, size)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestSizeAccelPhase() {
|
||||
stats := &Stats{k: 1.0, lambda: 1.0}
|
||||
|
||||
for range TLSCounterAccelAfter {
|
||||
stats.Size()
|
||||
}
|
||||
|
||||
for range TLSCounterMaxAfter - TLSCounterAccelAfter {
|
||||
size := stats.Size()
|
||||
suite.Equal(TLSRecordSizeAccel, size)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestSizeMaxPhase() {
|
||||
stats := &Stats{k: 1.0, lambda: 1.0}
|
||||
|
||||
for range TLSCounterMaxAfter {
|
||||
stats.Size()
|
||||
}
|
||||
|
||||
for range 20 {
|
||||
size := stats.Size()
|
||||
suite.Equal(TLSRecordSizeMax, size)
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *StatsTestSuite) TestSizeResetsAfterInactivity() {
|
||||
stats := &Stats{k: 1.0, lambda: 1.0}
|
||||
|
||||
// Advance past start phase.
|
||||
for range TLSCounterMaxAfter {
|
||||
stats.Size()
|
||||
}
|
||||
|
||||
suite.Equal(TLSRecordSizeMax, stats.Size())
|
||||
|
||||
// Simulate inactivity by backdating sizeLastRequested.
|
||||
stats.sizeLastRequested = time.Now().Add(-TLSRecordSizeResetAfter - time.Millisecond)
|
||||
|
||||
suite.Equal(TLSRecordSizeStart, stats.Size())
|
||||
}
|
||||
|
||||
func TestStats(t *testing.T) {
|
||||
t.Parallel()
|
||||
suite.Run(t, &StatsTestSuite{})
|
||||
}
|
||||
Reference in New Issue
Block a user