github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/prng/prng_test.go (about)

     1  /*
     2   * Copyright (c) 2018, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package prng
    21  
    22  import (
    23  	"bytes"
    24  	crypto_rand "crypto/rand"
    25  	"fmt"
    26  	"math"
    27  	"math/big"
    28  	"sort"
    29  	"strings"
    30  	"testing"
    31  	"time"
    32  )
    33  
    34  func TestSeed(t *testing.T) {
    35  
    36  	seed, err := NewSeed()
    37  	if err != nil {
    38  		t.Fatalf("NewSeed failed: %s", err)
    39  	}
    40  
    41  	prng1 := NewPRNGWithSeed(seed)
    42  	prng2 := NewPRNGWithSeed(seed)
    43  
    44  	for i := 1; i < 10000; i++ {
    45  
    46  		bytes1 := make([]byte, i)
    47  		prng1.Read(bytes1)
    48  
    49  		bytes2 := make([]byte, i)
    50  		prng2.Read(bytes2)
    51  
    52  		zeroes := make([]byte, i)
    53  		if bytes.Equal(zeroes, bytes1) {
    54  			t.Fatalf("unexpected zero bytes")
    55  		}
    56  
    57  		if !bytes.Equal(bytes1, bytes2) {
    58  			t.Fatalf("unexpected different bytes")
    59  		}
    60  	}
    61  
    62  	prng1 = NewPRNGWithSeed(seed)
    63  
    64  	prng3, err := NewPRNGWithSaltedSeed(seed, "3")
    65  	if err != nil {
    66  		t.Fatalf("NewPRNGWithSaltedSeed failed: %s", err)
    67  	}
    68  
    69  	prng4, err := NewPRNGWithSaltedSeed(seed, "4")
    70  	if err != nil {
    71  		t.Fatalf("NewPRNGWithSaltedSeed failed: %s", err)
    72  	}
    73  
    74  	for i := 1; i < 10000; i++ {
    75  
    76  		bytes1 := make([]byte, i)
    77  		prng1.Read(bytes1)
    78  
    79  		bytes3 := make([]byte, i)
    80  		prng3.Read(bytes3)
    81  
    82  		bytes4 := make([]byte, i)
    83  		prng4.Read(bytes4)
    84  
    85  		if bytes.Equal(bytes1, bytes3) {
    86  			t.Fatalf("unexpected identical bytes")
    87  		}
    88  
    89  		if bytes.Equal(bytes3, bytes4) {
    90  			t.Fatalf("unexpected identical bytes")
    91  		}
    92  	}
    93  }
    94  
    95  func TestFlipWeightedCoin(t *testing.T) {
    96  
    97  	runs := 100000
    98  	tolerance := 1000
    99  
   100  	testCases := []struct {
   101  		weight        float64
   102  		expectedTrues int
   103  	}{
   104  		{0.333, runs / 3},
   105  		{0.5, runs / 2},
   106  		{1.0, runs},
   107  		{0.0, 0},
   108  	}
   109  
   110  	for _, testCase := range testCases {
   111  		t.Run(fmt.Sprintf("%f", testCase.weight), func(t *testing.T) {
   112  
   113  			p, err := NewPRNG()
   114  			if err != nil {
   115  				t.Fatalf("NewPRNG failed: %s", err)
   116  			}
   117  
   118  			trues := 0
   119  			for i := 0; i < runs; i++ {
   120  				if p.FlipWeightedCoin(testCase.weight) {
   121  					trues++
   122  				}
   123  			}
   124  
   125  			min := testCase.expectedTrues - tolerance
   126  			if min < 0 {
   127  				min = 0
   128  			}
   129  			max := testCase.expectedTrues + tolerance
   130  
   131  			if trues < min || trues > max {
   132  				t.Errorf("unexpected coin flip outcome: %f %d (+/-%d) %d",
   133  					testCase.weight, testCase.expectedTrues, tolerance, trues)
   134  			}
   135  		})
   136  	}
   137  }
   138  
   139  func TestPerm(t *testing.T) {
   140  
   141  	p, err := NewPRNG()
   142  	if err != nil {
   143  		t.Fatalf("NewPRNG failed: %s", err)
   144  	}
   145  
   146  	for n := 0; n < 1000; n++ {
   147  
   148  		perm := p.Perm(n)
   149  		if len(perm) != n {
   150  			t.Error("unexpected permutation size")
   151  		}
   152  
   153  		sum := 0
   154  		for i := 0; i < n; i++ {
   155  			sum += perm[i]
   156  		}
   157  
   158  		expectedSum := (n * (n - 1)) / 2
   159  		if sum != expectedSum {
   160  			t.Error("unexpected permutation")
   161  		}
   162  	}
   163  }
   164  
   165  func TestRange(t *testing.T) {
   166  
   167  	p, err := NewPRNG()
   168  	if err != nil {
   169  		t.Fatalf("NewPRNG failed: %s", err)
   170  	}
   171  
   172  	min := 1
   173  	max := 19
   174  	var gotMin, gotMax bool
   175  	for n := 0; n < 1000; n++ {
   176  
   177  		i := p.Range(min, max)
   178  
   179  		if i < min || i > max {
   180  			t.Error("out of range")
   181  		}
   182  
   183  		if i == min {
   184  			gotMin = true
   185  		}
   186  		if i == max {
   187  			gotMax = true
   188  		}
   189  	}
   190  
   191  	if !gotMin {
   192  		t.Error("missing min")
   193  	}
   194  	if !gotMax {
   195  		t.Error("missing max")
   196  	}
   197  }
   198  
   199  func TestPeriod(t *testing.T) {
   200  
   201  	p, err := NewPRNG()
   202  	if err != nil {
   203  		t.Fatalf("NewPRNG failed: %s", err)
   204  	}
   205  
   206  	min := 1 * time.Nanosecond
   207  	max := 10000 * time.Nanosecond
   208  
   209  	different := 0
   210  
   211  	for n := 0; n < 1000; n++ {
   212  
   213  		res1 := p.Period(min, max)
   214  
   215  		if res1 < min {
   216  			t.Error("duration should not be less than min")
   217  		}
   218  
   219  		if res1 > max {
   220  			t.Error("duration should not be more than max")
   221  		}
   222  
   223  		res2 := p.Period(min, max)
   224  
   225  		if res1 != res2 {
   226  			different += 1
   227  		}
   228  	}
   229  
   230  	// res1 and res2 should be different most of the time, but it's possible
   231  	// to get the same result twice in a row.
   232  	if different < 900 {
   233  		t.Error("duration insufficiently random")
   234  	}
   235  }
   236  
   237  func TestJitter(t *testing.T) {
   238  
   239  	testCases := []struct {
   240  		n           int64
   241  		factor      float64
   242  		expectedMin int64
   243  		expectedMax int64
   244  	}{
   245  		{100, 0.1, 90, 110},
   246  		{1000, 0.3, 700, 1300},
   247  	}
   248  
   249  	for _, testCase := range testCases {
   250  		t.Run(fmt.Sprintf("Jitter case: %+v", testCase), func(t *testing.T) {
   251  
   252  			p, err := NewPRNG()
   253  			if err != nil {
   254  				t.Fatalf("NewPRNG failed: %s", err)
   255  			}
   256  
   257  			min := int64(math.MaxInt64)
   258  			max := int64(0)
   259  
   260  			for i := 0; i < 100000; i++ {
   261  
   262  				x := p.Jitter(testCase.n, testCase.factor)
   263  				if x < min {
   264  					min = x
   265  				}
   266  				if x > max {
   267  					max = x
   268  				}
   269  			}
   270  
   271  			if min != testCase.expectedMin {
   272  				t.Errorf("unexpected minimum jittered value: %d", min)
   273  			}
   274  
   275  			if max != testCase.expectedMax {
   276  				t.Errorf("unexpected maximum jittered value: %d", max)
   277  			}
   278  		})
   279  	}
   280  }
   281  
   282  func TestIntn(t *testing.T) {
   283  
   284  	p, err := NewPRNG()
   285  	if err != nil {
   286  		t.Fatalf("NewPRNG failed: %s", err)
   287  	}
   288  
   289  	for max := 0; max <= 255; max++ {
   290  
   291  		counts := make(map[int]int)
   292  		repeats := 200000
   293  
   294  		for r := 0; r < repeats; r++ {
   295  			value := p.Intn(max)
   296  			if value < 0 || value > max {
   297  				t.Fatalf("unexpected value: max = %d, value = %d", max, value)
   298  			}
   299  			counts[value] += 1
   300  		}
   301  
   302  		expected := repeats / (max + 1)
   303  
   304  		for i := 0; i < max; i++ {
   305  			if counts[i] < (expected/10)*8 {
   306  				t.Logf("max = %d, counts = %+v", max, counts)
   307  				t.Fatalf("unexpected low count: max = %d, i = %d, count = %d", max, i, counts[i])
   308  			}
   309  		}
   310  	}
   311  }
   312  
   313  func TestExpFloat64Range(t *testing.T) {
   314  
   315  	testCases := []struct {
   316  		min, max, lambda float64
   317  		factor           int
   318  	}{
   319  		{1.0, 3.0, 2.0, 5},
   320  		{0.0, 1.0, 2.0, 5},
   321  		{-2.0, -1.0, 2.0, 5},
   322  	}
   323  
   324  	for _, testCase := range testCases {
   325  		t.Run(fmt.Sprintf("ExpFloat64Range case: %+v", testCase), func(t *testing.T) {
   326  
   327  			p, err := NewPRNG()
   328  			if err != nil {
   329  				t.Fatalf("NewPRNG failed: %s", err)
   330  			}
   331  
   332  			buckets := make(map[float64]int)
   333  
   334  			for i := 0; i < 100000; i++ {
   335  
   336  				value := p.ExpFloat64Range(testCase.min, testCase.max, testCase.lambda)
   337  
   338  				if value < testCase.min || value > testCase.max {
   339  					t.Fatalf(
   340  						"unexpected value: %f [%f, %f]", value, testCase.min, testCase.max)
   341  				}
   342  
   343  				buckets[float64(int(10.0*(value)))/10.0] += 1
   344  			}
   345  
   346  			keys := make([]float64, 0)
   347  			for k := range buckets {
   348  				keys = append(keys, k)
   349  			}
   350  
   351  			sort.Float64s(keys)
   352  
   353  			strs := make([]string, 0)
   354  			for _, k := range keys {
   355  				strs = append(strs, fmt.Sprintf("%0.2f: %d", k, buckets[k]))
   356  			}
   357  
   358  			t.Logf(strings.Join(strs, ","))
   359  
   360  			for i := 0; i < len(keys)-1; i++ {
   361  				if buckets[keys[i]] <= buckets[keys[i+1]] {
   362  					t.Fatalf("unexpected distribution")
   363  				}
   364  			}
   365  
   366  			// First bucket should have at least "factor" times more items than last
   367  			// bucket.
   368  			if buckets[keys[0]]/buckets[keys[len(keys)-1]] < testCase.factor {
   369  				t.Fatalf("unexpected distribution")
   370  			}
   371  
   372  		})
   373  	}
   374  }
   375  
   376  //lint:ignore U1000 intentionally unused
   377  func Disabled_TestRandomStreamLimit(t *testing.T) {
   378  
   379  	// This test takes up to ~2 minute to complete, so it's disabled by default.
   380  
   381  	p, err := NewPRNG()
   382  	if err != nil {
   383  		t.Fatalf("NewPRNG failed: %s", err)
   384  	}
   385  
   386  	// Use large blocks to get close to the key stream limit.
   387  
   388  	var b [2 * 1024 * 1024 * 1024]byte
   389  	n := int64(0)
   390  
   391  	for i := 0; i < 127; i++ {
   392  		p.Read(b[:])
   393  		n += int64(len(b))
   394  	}
   395  
   396  	// Stop using large blocks 64 bytes short of the limit, 2^38-64.
   397  
   398  	p.Read(b[0 : len(b)-128])
   399  	n += int64(len(b) - 128)
   400  
   401  	// Invoke byte at a time across the limit boundary to ensure we
   402  	// don't jump over the limit case.
   403  
   404  	for i := 0; i < 192; i++ {
   405  		p.Read(b[0:1])
   406  		n += int64(1)
   407  	}
   408  }
   409  
   410  func BenchmarkIntn(b *testing.B) {
   411  
   412  	p, err := NewPRNG()
   413  	if err != nil {
   414  		b.Fatalf("NewPRNG failed: %s", err)
   415  	}
   416  
   417  	max := 255
   418  
   419  	b.Run("PRNG", func(b *testing.B) {
   420  		for n := 0; n < b.N; n++ {
   421  			_ = p.Intn(n % max)
   422  		}
   423  	})
   424  
   425  	b.Run("getrandom()", func(b *testing.B) {
   426  		for n := 0; n < b.N; n++ {
   427  
   428  			_, err := crypto_rand.Int(crypto_rand.Reader, big.NewInt(int64(max)))
   429  			if err != nil {
   430  				b.Fatalf("crypto_rand.Int failed: %s", err)
   431  			}
   432  		}
   433  	})
   434  }