|
- // Copyright 2013 The Go Authors. All rights reserved.
- //
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file or at
- // https://developers.google.com/open-source/licenses/bsd.
-
- package database
-
- import (
- "math"
- "reflect"
- "strconv"
- "testing"
- "time"
-
- "github.com/garyburd/redigo/redis"
- "golang.org/x/net/context"
- "google.golang.org/appengine/aetest"
-
- "github.com/golang/gddo/doc"
- )
-
- func newDB(t *testing.T) *Database {
- p := redis.NewPool(func() (redis.Conn, error) {
- c, err := redis.DialTimeout("tcp", ":6379", 0, 1*time.Second, 1*time.Second)
- if err != nil {
- return nil, err
- }
- _, err = c.Do("SELECT", "9")
- if err != nil {
- c.Close()
- return nil, err
- }
- return c, nil
- }, 1)
-
- c := p.Get()
- defer c.Close()
- n, err := redis.Int(c.Do("DBSIZE"))
- if n != 0 || err != nil {
- t.Errorf("DBSIZE returned %d, %v", n, err)
- }
- return &Database{Pool: p}
- }
-
- func closeDB(db *Database) {
- c := db.Pool.Get()
- c.Do("FLUSHDB")
- c.Close()
- }
-
- func TestPutGet(t *testing.T) {
- var nextCrawl = time.Unix(time.Now().Add(time.Hour).Unix(), 0).UTC()
- ctx, done, err := aetest.NewContext()
- if err != nil {
- t.Fatal(err)
- }
- defer done()
- bgCtx = func() context.Context {
- return ctx
- }
-
- db := newDB(t)
- defer closeDB(db)
- pdoc := &doc.Package{
- ImportPath: "github.com/user/repo/foo/bar",
- Name: "bar",
- Synopsis: "hello",
- ProjectRoot: "github.com/user/repo",
- ProjectName: "foo",
- Updated: time.Now().Add(-time.Hour),
- Imports: []string{"C", "errors", "github.com/user/repo/foo/bar"}, // self import for testing convenience.
- }
- if err := db.Put(pdoc, nextCrawl, false); err != nil {
- t.Errorf("db.Put() returned error %v", err)
- }
- if err := db.Put(pdoc, time.Time{}, false); err != nil {
- t.Errorf("second db.Put() returned error %v", err)
- }
-
- actualPdoc, actualSubdirs, actualCrawl, err := db.Get("github.com/user/repo/foo/bar")
- if err != nil {
- t.Fatalf("db.Get(.../foo/bar) returned %v", err)
- }
- if len(actualSubdirs) != 0 {
- t.Errorf("db.Get(.../foo/bar) returned subdirs %v, want none", actualSubdirs)
- }
- if !reflect.DeepEqual(actualPdoc, pdoc) {
- t.Errorf("db.Get(.../foo/bar) returned doc %v, want %v", actualPdoc, pdoc)
- }
- if !nextCrawl.Equal(actualCrawl) {
- t.Errorf("db.Get(.../foo/bar) returned crawl %v, want %v", actualCrawl, nextCrawl)
- }
-
- before := time.Now().Unix()
- if err := db.BumpCrawl(pdoc.ProjectRoot); err != nil {
- t.Errorf("db.BumpCrawl() returned %v", err)
- }
- after := time.Now().Unix()
-
- _, _, actualCrawl, _ = db.Get("github.com/user/repo/foo/bar")
- if actualCrawl.Unix() < before || after < actualCrawl.Unix() {
- t.Errorf("actualCrawl=%v, expect value between %v and %v", actualCrawl.Unix(), before, after)
- }
-
- // Popular
-
- if err := db.IncrementPopularScore(pdoc.ImportPath); err != nil {
- t.Errorf("db.IncrementPopularScore() returned %v", err)
- }
-
- // Get "-"
-
- actualPdoc, _, _, err = db.Get("-")
- if err != nil {
- t.Fatalf("db.Get(-) returned %v", err)
- }
- if !reflect.DeepEqual(actualPdoc, pdoc) {
- t.Errorf("db.Get(-) returned doc %v, want %v", actualPdoc, pdoc)
- }
-
- actualPdoc, actualSubdirs, _, err = db.Get("github.com/user/repo/foo")
- if err != nil {
- t.Fatalf("db.Get(.../foo) returned %v", err)
- }
- if actualPdoc != nil {
- t.Errorf("db.Get(.../foo) returned doc %v, want %v", actualPdoc, nil)
- }
- expectedSubdirs := []Package{{Path: "github.com/user/repo/foo/bar", Synopsis: "hello"}}
- if !reflect.DeepEqual(actualSubdirs, expectedSubdirs) {
- t.Errorf("db.Get(.../foo) returned subdirs %v, want %v", actualSubdirs, expectedSubdirs)
- }
- actualImporters, err := db.Importers("github.com/user/repo/foo/bar")
- if err != nil {
- t.Fatalf("db.Importers() returned error %v", err)
- }
- expectedImporters := []Package{{Path: "github.com/user/repo/foo/bar", Synopsis: "hello"}}
- if !reflect.DeepEqual(actualImporters, expectedImporters) {
- t.Errorf("db.Importers() = %v, want %v", actualImporters, expectedImporters)
- }
- actualImports, err := db.Packages(pdoc.Imports)
- if err != nil {
- t.Fatalf("db.Imports() returned error %v", err)
- }
- for i := range actualImports {
- if actualImports[i].Path == "C" {
- actualImports[i].Synopsis = ""
- }
- }
- expectedImports := []Package{
- {Path: "C", Synopsis: ""},
- {Path: "errors", Synopsis: ""},
- {Path: "github.com/user/repo/foo/bar", Synopsis: "hello"},
- }
- if !reflect.DeepEqual(actualImports, expectedImports) {
- t.Errorf("db.Imports() = %v, want %v", actualImports, expectedImports)
- }
- importerCount, _ := db.ImporterCount("github.com/user/repo/foo/bar")
- if importerCount != 1 {
- t.Errorf("db.ImporterCount() = %d, want %d", importerCount, 1)
- }
- if err := db.Delete("github.com/user/repo/foo/bar"); err != nil {
- t.Errorf("db.Delete() returned error %v", err)
- }
-
- db.Query("bar")
-
- if err := db.Put(pdoc, time.Time{}, false); err != nil {
- t.Errorf("db.Put() returned error %v", err)
- }
-
- if err := db.Block("github.com/user/repo"); err != nil {
- t.Errorf("db.Block() returned error %v", err)
- }
-
- blocked, err := db.IsBlocked("github.com/user/repo/foo/bar")
- if !blocked || err != nil {
- t.Errorf("db.IsBlocked(github.com/user/repo/foo/bar) returned %v, %v, want true, nil", blocked, err)
- }
-
- blocked, err = db.IsBlocked("github.com/foo/bar")
- if blocked || err != nil {
- t.Errorf("db.IsBlocked(github.com/foo/bar) returned %v, %v, want false, nil", blocked, err)
- }
-
- c := db.Pool.Get()
- defer c.Close()
- c.Send("DEL", "maxQueryId")
- c.Send("DEL", "maxPackageId")
- c.Send("DEL", "block")
- c.Send("DEL", "popular:0")
- c.Send("DEL", "newCrawl")
- keys, err := redis.Values(c.Do("HKEYS", "ids"))
- for _, key := range keys {
- t.Errorf("unexpected id %s", key)
- }
- keys, err = redis.Values(c.Do("KEYS", "*"))
- for _, key := range keys {
- t.Errorf("unexpected key %s", key)
- }
- }
-
- const epsilon = 0.000001
-
- func TestPopular(t *testing.T) {
- db := newDB(t)
- defer closeDB(db)
- c := db.Pool.Get()
- defer c.Close()
-
- // Add scores for packages. On each iteration, add half-life to time and
- // divide the score by two. All packages should have the same score.
-
- now := time.Now()
- score := float64(4048)
- for id := 12; id >= 0; id-- {
- path := "github.com/user/repo/p" + strconv.Itoa(id)
- c.Do("HSET", "ids", path, id)
- err := db.incrementPopularScoreInternal(path, score, now)
- if err != nil {
- t.Fatal(err)
- }
- now = now.Add(popularHalfLife)
- score /= 2
- }
-
- values, _ := redis.Values(c.Do("ZRANGE", "popular", "0", "100000", "WITHSCORES"))
- if len(values) != 26 {
- t.Fatalf("Expected 26 values, got %d", len(values))
- }
-
- // Check for equal scores.
- score, err := redis.Float64(values[1], nil)
- if err != nil {
- t.Fatal(err)
- }
- for i := 3; i < len(values); i += 2 {
- s, _ := redis.Float64(values[i], nil)
- if math.Abs(score-s)/score > epsilon {
- t.Errorf("Bad score, score[1]=%g, score[%d]=%g", score, i, s)
- }
- }
- }
-
- func TestCounter(t *testing.T) {
- db := newDB(t)
- defer closeDB(db)
-
- const key = "127.0.0.1"
-
- now := time.Now()
- n, err := db.incrementCounterInternal(key, 1, now)
- if err != nil {
- t.Fatal(err)
- }
- if math.Abs(n-1.0) > epsilon {
- t.Errorf("1: got n=%g, want 1", n)
- }
- n, err = db.incrementCounterInternal(key, 1, now)
- if err != nil {
- t.Fatal(err)
- }
- if math.Abs(n-2.0)/2.0 > epsilon {
- t.Errorf("2: got n=%g, want 2", n)
- }
- now = now.Add(counterHalflife)
- n, err = db.incrementCounterInternal(key, 1, now)
- if err != nil {
- t.Fatal(err)
- }
- if math.Abs(n-2.0)/2.0 > epsilon {
- t.Errorf("3: got n=%g, want 2", n)
- }
- }
|