Highly-opinionated (ex-bullshit-free) MTPROTO proxy for Telegram. If you use v1.0 or upgrade broke you proxy, please read the chapter Version 2
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

stats_test.go 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package doppel
  2. import (
  3. "math"
  4. "math/rand/v2"
  5. "testing"
  6. "time"
  7. "github.com/stretchr/testify/suite"
  8. )
  9. type StatsTestSuite struct {
  10. suite.Suite
  11. }
  12. func (suite *StatsTestSuite) GenWeibull(k, lambda float64, n int, seed uint64) []time.Duration {
  13. rng := rand.New(rand.NewPCG(seed, 0))
  14. samples := make([]time.Duration, n)
  15. for i := range samples {
  16. u := 1.0 - rng.Float64()
  17. ms := lambda * math.Pow(-math.Log(u), 1.0/k)
  18. d := time.Duration(ms * float64(time.Millisecond))
  19. if d < time.Microsecond {
  20. time.Sleep(time.Microsecond)
  21. d = time.Microsecond
  22. }
  23. samples[i] = d
  24. }
  25. return samples
  26. }
  27. func (suite *StatsTestSuite) TestNewStatsRecoverParameters() {
  28. knownK := 1.5
  29. knownLambda := 100.0
  30. samples := suite.GenWeibull(knownK, knownLambda, 5000, 42)
  31. stats := NewStats(samples, true)
  32. suite.InDelta(knownK, stats.k, 0.1)
  33. suite.InDelta(knownLambda, stats.lambda, 5.0)
  34. }
  35. func (suite *StatsTestSuite) TestNewStatsExponentialCase() {
  36. // When k=1, Weibull reduces to exponential distribution.
  37. knownK := 1.0
  38. knownLambda := 50.0
  39. samples := suite.GenWeibull(knownK, knownLambda, 5000, 123)
  40. stats := NewStats(samples, true)
  41. suite.InDelta(knownK, stats.k, 0.1)
  42. suite.InDelta(knownLambda, stats.lambda, 5.0)
  43. }
  44. func (suite *StatsTestSuite) TestNewStatsSmallK() {
  45. // k < 1 produces a heavy-tailed distribution typical for network delays.
  46. // Lambda must be large enough so samples stay above microsecond precision
  47. // after time.Duration round-trip.
  48. knownK := 0.6
  49. knownLambda := 100.0
  50. samples := suite.GenWeibull(knownK, knownLambda, 10000, 99)
  51. stats := NewStats(samples, true)
  52. suite.InDelta(knownK, stats.k, 0.05)
  53. suite.InDelta(knownLambda, stats.lambda, 5.0)
  54. }
  55. func (suite *StatsTestSuite) TestNewStatsLargeK() {
  56. // k > 1: light tail, concentrated around the mode.
  57. knownK := 5.0
  58. knownLambda := 200.0
  59. samples := suite.GenWeibull(knownK, knownLambda, 5000, 77)
  60. stats := NewStats(samples, true)
  61. suite.InDelta(knownK, stats.k, 0.3)
  62. suite.InDelta(knownLambda, stats.lambda, 5.0)
  63. }
  64. func (suite *StatsTestSuite) TestDelayNonNegative() {
  65. stats := &Stats{
  66. k: 1.5,
  67. lambda: 100.0,
  68. }
  69. for range 200 {
  70. dur := stats.Delay()
  71. suite.GreaterOrEqual(dur, time.Duration(0))
  72. }
  73. }
  74. func (suite *StatsTestSuite) TestDelayDistributionMean() {
  75. // Weibull mean = λ · Γ(1 + 1/k)
  76. k := 2.0
  77. lambda := 50.0
  78. stats := &Stats{k: k, lambda: lambda}
  79. n := 50000
  80. sum := 0.0
  81. for range n {
  82. dur := stats.Delay()
  83. sum += float64(dur) / float64(time.Millisecond)
  84. }
  85. sampleMean := sum / float64(n)
  86. expectedMean := lambda * math.Gamma(1.0+1.0/k)
  87. suite.InDelta(expectedMean, sampleMean, expectedMean*0.05)
  88. }
  89. func (suite *StatsTestSuite) TestNewStatsRoundTrip() {
  90. // Estimate parameters from data, then verify that Delay samples
  91. // from the fitted distribution have approximately the same mean.
  92. knownK := 1.2
  93. knownLambda := 80.0
  94. samples := suite.GenWeibull(knownK, knownLambda, 5000, 555)
  95. stats := NewStats(samples, true)
  96. n := 50000
  97. sum := 0.0
  98. for range n {
  99. dur := stats.Delay()
  100. sum += float64(dur) / float64(time.Millisecond)
  101. }
  102. sampleMean := sum / float64(n)
  103. expectedMean := knownLambda * math.Gamma(1.0+1.0/knownK)
  104. suite.InDelta(expectedMean, sampleMean, expectedMean*0.05)
  105. }
  106. func (suite *StatsTestSuite) TestSizeStartPhase() {
  107. stats := &Stats{k: 1.0, lambda: 1.0, drs: true}
  108. for range TLSCounterAccelAfter {
  109. size := stats.Size()
  110. suite.GreaterOrEqual(size, TLSRecordSizeStart-DRSNoise)
  111. suite.LessOrEqual(size, TLSRecordSizeStart)
  112. }
  113. }
  114. func (suite *StatsTestSuite) TestSizeAccelPhase() {
  115. stats := &Stats{k: 1.0, lambda: 1.0, drs: true}
  116. for range TLSCounterAccelAfter {
  117. stats.Size()
  118. }
  119. for range TLSCounterMaxAfter - TLSCounterAccelAfter {
  120. size := stats.Size()
  121. suite.GreaterOrEqual(size, TLSRecordSizeAccel-DRSNoise)
  122. suite.LessOrEqual(size, TLSRecordSizeAccel)
  123. }
  124. }
  125. func (suite *StatsTestSuite) TestSizeMaxPhase() {
  126. stats := &Stats{k: 1.0, lambda: 1.0, drs: true}
  127. for range TLSCounterMaxAfter {
  128. stats.Size()
  129. }
  130. for range 20 {
  131. size := stats.Size()
  132. suite.Equal(TLSRecordSizeMax, size)
  133. }
  134. }
  135. func (suite *StatsTestSuite) TestSizeResetsAfterInactivity() {
  136. stats := &Stats{k: 1.0, lambda: 1.0, drs: true}
  137. // Advance past start phase.
  138. for range TLSCounterMaxAfter {
  139. stats.Size()
  140. }
  141. suite.Equal(TLSRecordSizeMax, stats.Size())
  142. // Simulate inactivity by backdating sizeLastRequested.
  143. stats.sizeLastRequested = time.Now().Add(-TLSRecordSizeResetAfter - time.Millisecond)
  144. size := stats.Size()
  145. suite.GreaterOrEqual(size, TLSRecordSizeStart-DRSNoise)
  146. suite.LessOrEqual(size, TLSRecordSizeStart)
  147. }
  148. func (suite *StatsTestSuite) TestSizeNoDRSAlwaysMax() {
  149. stats := &Stats{k: 1.0, lambda: 1.0, drs: false}
  150. for range TLSCounterMaxAfter + 20 {
  151. suite.Equal(TLSRecordSizeMax, stats.Size())
  152. }
  153. }
  154. func (suite *StatsTestSuite) TestSizeNoDRSIgnoresCounter() {
  155. stats := &Stats{k: 1.0, lambda: 1.0, drs: false}
  156. // Even after many calls, always returns max.
  157. for range 200 {
  158. suite.Equal(TLSRecordSizeMax, stats.Size())
  159. }
  160. // Inactivity has no effect either.
  161. stats.sizeLastRequested = time.Now().Add(-TLSRecordSizeResetAfter - time.Millisecond)
  162. suite.Equal(TLSRecordSizeMax, stats.Size())
  163. }
  164. func TestStats(t *testing.T) {
  165. t.Parallel()
  166. suite.Run(t, &StatsTestSuite{})
  167. }