diff --git a/doorman.go b/doorman.go index b3a241c..2bd178a 100644 --- a/doorman.go +++ b/doorman.go @@ -3,7 +3,6 @@ package doorman import ( "encoding/json" "errors" - "fmt" "log" "math" "math/big" @@ -17,8 +16,6 @@ import ( "gopkg.in/mgo.v2/bson" ) -var _ = fmt.Print - var ONE *big.Rat = big.NewRat(1, 1) var rand *mathrand.Rand @@ -43,7 +40,7 @@ type Doorman struct { wg sync.WaitGroup // waitgroup for goroutine safety } -func NewDoorman(id bson.ObjectId, Probabilities []*big.Rat) *Doorman { +func New(id bson.ObjectId, Probabilities []*big.Rat) *Doorman { wab := &Doorman{} wab.Id = id wab.Probabilities = Probabilities @@ -143,6 +140,10 @@ func (w *Doorman) GetCaseFromData(data ...[]byte) uint { return w.GetCase(random) } +func (w *Doorman) GetCaseFromString(data string) uint { + return w.GetCaseFromData([]byte(data)) +} + func (w *Doorman) GetRandomCase() uint { r := rand.Float64() return w.GetCase(new(big.Rat).SetFloat64(r)) diff --git a/doorman_test.go b/doorman_test.go index 4512b6c..5061d41 100644 --- a/doorman_test.go +++ b/doorman_test.go @@ -18,22 +18,16 @@ import ( var epsilon = 0.0 var ZERO = big.NewRat(0, 1) -func assertIsAbout(t *testing.T, expected, received *big.Rat) { +func assertIsEqual(t *testing.T, expected, received *big.Rat) { if expected.Cmp(received) != 0 { t.Error("received", received, "but expected", expected) } } -func assertIsNotAbout(t *testing.T, expected, received *big.Rat) { - if expected.Cmp(received) != 0 { - t.Error("received", received, "but expected something different than", expected) - } -} - func getProbs(probs ...float64) []*big.Rat { ret := make([]*big.Rat, len(probs)) for i, p := range probs { - ret[i], _ = new(big.Rat).SetString(fmt.Sprint(p)) + ret[i] = new(big.Rat).SetFloat64(p) } return ret } @@ -72,7 +66,7 @@ func TestValidate(t *testing.T) { } func TestGetCase(t *testing.T) { - w := NewDoorman(bson.NewObjectId(), getProbs(0.25, 0.5, 0.25)) + w := New(bson.NewObjectId(), getProbs(0.25, 0.5, 0.25)) if c := w.GetCase(ZERO); c != 0 { t.Error("expected 0 but received", c) } @@ -83,7 +77,7 @@ func TestGetCase(t *testing.T) { } func TestGetCaseCoroutineSafety(t *testing.T) { - w := NewDoorman(bson.NewObjectId(), getProbs(0.25, 0.5, 0.25)) + w := New(bson.NewObjectId(), getProbs(0.25, 0.5, 0.25)) i := 0 w.wg.Add(1) go func() { @@ -98,11 +92,11 @@ func TestGetCaseCoroutineSafety(t *testing.T) { func TestGenerateRandomProbabilityFromBitSlice(t *testing.T) { w := new(Doorman) - assertIsAbout(t, big.NewRat(1, 2), w.GenerateRandomProbabilityFromInteger(1)) + assertIsEqual(t, big.NewRat(1, 2), w.GenerateRandomProbabilityFromInteger(1)) - // assertIsAbout(t, 0.5+0*0.25+1*0.125, w.GenerateRandomProbabilityFromInteger(5)) + assertIsEqual(t, new(big.Rat).SetFloat64(0.5+0*0.25+1*0.125), w.GenerateRandomProbabilityFromInteger(5)) - assertIsAbout(t, ONE, w.GenerateRandomProbabilityFromInteger(math.MaxUint64)) + assertIsEqual(t, ONE, w.GenerateRandomProbabilityFromInteger(math.MaxUint64)) } func TestHash(t *testing.T) { @@ -166,7 +160,7 @@ func randomBytes(n int) []byte { func TestUpdateHard(t *testing.T) { - wab := NewDoorman(bson.ObjectIdHex(oid), getProbs()) + wab := New(bson.ObjectIdHex(oid), getProbs()) createMessage := func() string { msg := &shared.DoormanUpdater{Timestamp: 2, Probabilities: getProbs(0.5, 0.25, 0.25), Id: oid} if ret, err := json.Marshal(msg); err != nil { @@ -209,7 +203,7 @@ func IsExtremeBinomialResult(x int, n, p float64) error { func TestGetRandomCase(t *testing.T) { p := 0.5 n := 10000 - wab := NewDoorman(bson.NewObjectId(), getProbs(p, 1-p)) + wab := New(bson.NewObjectId(), getProbs(p, 1-p)) var sum = 0 for i := 0; i < n; i++ { sum += int(wab.GetRandomCase()) @@ -220,9 +214,22 @@ func TestGetRandomCase(t *testing.T) { } } +func TestGetCaseFromString(t *testing.T) { + w := New(bson.NewObjectId(), getProbs(0.25, 0.5, 0.25)) + expts := map[string]uint{ + "j'aime la ratoutille": 2, + } + for data, expt := range expts { + if res := w.GetCaseFromString(data); res != expt { + t.Error("incorrect results for: ", data, res) + } + } + +} + func BenchmarkGetCaseFromData(b *testing.B) { var data = randomBytes(1024 * 1024) - w := NewDoorman(bson.NewObjectId(), getProbs(0.1, 0.4, 0.4, 0.05, 0.05)) + w := New(bson.NewObjectId(), getProbs(0.1, 0.4, 0.4, 0.05, 0.05)) for i := 0; i < b.N; i++ { w.GetCaseFromData(data) } diff --git a/example/integration_test.go b/example/integration_test.go deleted file mode 100644 index ed4ea2d..0000000 --- a/example/integration_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "testing" - - "github.com/didiercrunch/doorman" - "gopkg.in/mgo.v2/bson" -) - -func TestDoormanDistribution(t *testing.T) { - wab = doorman.NewDoorman(bson.NewObjectId(), []float64{0.25, 0.5, 0.10, 0.05, 0.1}) - n := 10000 - cases := make([]float64, wab.Length()) - for i := 0; i <= n; i++ { - cases[wab.GetRandomCase()] += 1 - } - expt := make([]float64, wab.Length()) - for i, p := range wab.Probabilities { - expt[i] = p * float64(n) - } - // Todo: use chi square test to assert the results are not absurde - //if err := AssertTwoDistributionAreProbabliNotDifferent(expt, cases, 0.001); err != nil { - // t.Error(err) - //} - -} diff --git a/example/main.go b/example/main.go index 17b2365..b42f1b5 100644 --- a/example/main.go +++ b/example/main.go @@ -3,17 +3,28 @@ package main import ( "html/template" "log" + "math/big" + "math/rand" "net/http" "os" + "strconv" "github.com/didiercrunch/doorman" "gopkg.in/mgo.v2/bson" ) -var wab = doorman.NewDoorman(bson.ObjectIdHex("55048bfff6fb5e213d000001"), []float64{0.25, 0.5, 0.10, 0.05, 0.1}) +func getProbs(probs ...float64) []*big.Rat { + ret := make([]*big.Rat, len(probs)) + for i, p := range probs { + ret[i] = new(big.Rat).SetFloat64(p) + } + return ret +} + +var wab = doorman.New(bson.ObjectIdHex("558701a2f6fb5e29a4000003"), getProbs(1, 0, 0)) func init() { - if err := wab.NanoMsgSubscriber("tcp://127.0.0.1:40899"); err != nil { + if err := wab.Subscriber("http://localhost:1999"); err != nil { panic(err) } } @@ -21,15 +32,11 @@ func init() { func GetDoormanValue(r *http.Request) (string, int) { switch wab.GetCaseFromData([]byte(r.URL.String())) { case 0: - return "blue", 0 + return "red", 0 case 1: - return "red", 1 + return "green", 1 case 2: - return "pink", 2 - case 3: - return "yellow", 3 - case 4: - return "magenta", 4 + return "blue", 2 } log.Panic("impossible value returned by doorman") return "", 0 @@ -43,8 +50,10 @@ var tmpl = `
this doorman depend solely on the url. If you hit different url you - will have different doorman results. + will have different doorman results. For example, here is a random + url: {{ .nextUrl }}
+