GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

cache_test.go 3.65 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"os"
	"testing"
	"time"
)

func TestCacheFunctions(t *testing.T) {

	cache := make(TestCache)
	bridgeLine := "obfs4 127.0.0.1:1 cert=foo iat-mode=0"

	e := cache.IsCached(bridgeLine)
	if e != nil {
		t.Errorf("Cache is empty but marks bridge line as existing.")
	}

24
	cache.AddEntry(bridgeLine, nil, time.Now().UTC())
25 26 27 28 29 30
	e = cache.IsCached(bridgeLine)
	if e == nil {
		t.Errorf("Could not retrieve existing element from cache.")
	}

	testError := fmt.Errorf("bridge is on fire")
31
	cache.AddEntry(bridgeLine, testError, time.Now().UTC())
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
	e = cache.IsCached(bridgeLine)
	if e.Error != testError.Error() {
		t.Errorf("Got test result %q but expected %q.", e.Error, testError)
	}
}

func TestCacheExpiration(t *testing.T) {

	cache := make(TestCache)

	const shortForm = "2006-Jan-02"
	expiry, _ := time.Parse(shortForm, "2000-Jan-01")
	bridgeLine1 := "1.1.1.1:1111"
	cache[bridgeLine1] = &CacheEntry{"", expiry}

	bridgeLine2 := "2.2.2.2:2222"
48
	cache[bridgeLine2] = &CacheEntry{"", time.Now().UTC()}
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

	e := cache.IsCached(bridgeLine1)
	if e != nil {
		t.Errorf("Expired cache entry was not successfully pruned.")
	}

	e = cache.IsCached(bridgeLine2)
	if e == nil {
		t.Errorf("Valid cache entry was incorrectly pruned.")
	}
}

func BenchmarkIsCached(b *testing.B) {

	getRandAddrPort := func() string {
		return fmt.Sprintf("%d.%d.%d.%d:%d",
			rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(65536))
	}
	getRandError := func() error {
		errors := []error{nil, errors.New("censorship"), errors.New("no censorship")}
		return errors[rand.Intn(len(errors))]
	}

	numCacheEntries := 10000
	cache := make(TestCache)
	for i := 0; i < numCacheEntries; i++ {
75
		cache.AddEntry(getRandAddrPort(), getRandError(), time.Now().UTC())
76 77 78 79 80 81 82 83 84 85 86 87 88
	}

	// How long does it take to iterate over numCacheEntries cache entries?
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		cache.IsCached("invalid bridge line")
	}
}

func TestCacheSerialisation(t *testing.T) {

	cache := make(TestCache)
	testError := fmt.Errorf("foo")
89 90
	cache.AddEntry("1.1.1.1:1", testError, time.Now().UTC())
	cache.AddEntry("2.2.2.2:2", fmt.Errorf("bar"), time.Now().UTC())
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

	tmpFh, err := ioutil.TempFile(os.TempDir(), "cache-file-")
	if err != nil {
		t.Errorf("Could not create temporary file for test: %s", err)
	}
	defer os.Remove(tmpFh.Name())

	err = cache.WriteToDisk(tmpFh.Name())
	if err != nil {
		t.Errorf("Failed to write cache to disk: %s", err)
	}
	err = cache.ReadFromDisk(tmpFh.Name())
	if err != nil {
		t.Errorf("Failed to read cache from disk: %s", err)
	}

	if len(cache) != 2 {
		t.Errorf("Cache supposed to contain but two elements but has %d.", len(cache))
	}

	e1 := cache.IsCached("1.1.1.1:1")
	if e1 == nil {
		t.Errorf("Cache element supposed to exist but doesn't.")
	}
	if e1.Error != testError.Error() {
		t.Errorf("Error string expected to be %q but is %q.", testError, e1.Error)
	}
}

func TestCacheConcurrency(t *testing.T) {

	cache := make(TestCache)
	max := 10000
	doneReading := make(chan bool)
	doneWriting := make(chan bool)

	// Trigger many concurrent reads and writes, to verify that there are no
	// synchronisation issues.
	go func() {
		for i := 0; i < max; i++ {
			ipAddr := net.IPv4(byte((i>>24)&0xff),
				byte((i>>16)&0xff),
				byte((i>>8)&0xff),
				byte(i&0xff))
135
			cache.AddEntry(fmt.Sprintf("%s:1234", ipAddr.String()), nil, time.Now().UTC())
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
		}
		doneWriting <- true
	}()

	go func() {
		for i := 0; i < max; i++ {
			ipAddr := net.IPv4(byte((i>>24)&0xff),
				byte((i>>16)&0xff),
				byte((i>>8)&0xff),
				byte(i&0xff))
			cache.IsCached(fmt.Sprintf("%s:1234", ipAddr.String()))
		}
		doneReading <- true
	}()

	<-doneReading
	<-doneWriting
}