Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)
| ... | ... |
@@ -31,6 +31,7 @@ import ( |
| 31 | 31 |
"github.com/dotcloud/docker/pkg/namesgenerator" |
| 32 | 32 |
"github.com/dotcloud/docker/pkg/networkfs/resolvconf" |
| 33 | 33 |
"github.com/dotcloud/docker/pkg/sysinfo" |
| 34 |
+ "github.com/dotcloud/docker/pkg/truncindex" |
|
| 34 | 35 |
"github.com/dotcloud/docker/runconfig" |
| 35 | 36 |
"github.com/dotcloud/docker/utils" |
| 36 | 37 |
) |
| ... | ... |
@@ -87,7 +88,7 @@ type Daemon struct {
|
| 87 | 87 |
containers *contStore |
| 88 | 88 |
graph *graph.Graph |
| 89 | 89 |
repositories *graph.TagStore |
| 90 |
- idIndex *utils.TruncIndex |
|
| 90 |
+ idIndex *truncindex.TruncIndex |
|
| 91 | 91 |
sysInfo *sysinfo.SysInfo |
| 92 | 92 |
volumes *graph.Graph |
| 93 | 93 |
srv Server |
| ... | ... |
@@ -869,7 +870,7 @@ func NewDaemonFromDirectory(config *daemonconfig.Config, eng *engine.Engine) (*D |
| 869 | 869 |
containers: &contStore{s: make(map[string]*Container)},
|
| 870 | 870 |
graph: g, |
| 871 | 871 |
repositories: repositories, |
| 872 |
- idIndex: utils.NewTruncIndex([]string{}),
|
|
| 872 |
+ idIndex: truncindex.NewTruncIndex([]string{}),
|
|
| 873 | 873 |
sysInfo: sysInfo, |
| 874 | 874 |
volumes: volumes, |
| 875 | 875 |
config: config, |
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
"github.com/dotcloud/docker/daemon/graphdriver" |
| 17 | 17 |
"github.com/dotcloud/docker/dockerversion" |
| 18 | 18 |
"github.com/dotcloud/docker/image" |
| 19 |
+ "github.com/dotcloud/docker/pkg/truncindex" |
|
| 19 | 20 |
"github.com/dotcloud/docker/runconfig" |
| 20 | 21 |
"github.com/dotcloud/docker/utils" |
| 21 | 22 |
) |
| ... | ... |
@@ -23,7 +24,7 @@ import ( |
| 23 | 23 |
// A Graph is a store for versioned filesystem images and the relationship between them. |
| 24 | 24 |
type Graph struct {
|
| 25 | 25 |
Root string |
| 26 |
- idIndex *utils.TruncIndex |
|
| 26 |
+ idIndex *truncindex.TruncIndex |
|
| 27 | 27 |
driver graphdriver.Driver |
| 28 | 28 |
} |
| 29 | 29 |
|
| ... | ... |
@@ -41,7 +42,7 @@ func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
|
| 41 | 41 |
|
| 42 | 42 |
graph := &Graph{
|
| 43 | 43 |
Root: abspath, |
| 44 |
- idIndex: utils.NewTruncIndex([]string{}),
|
|
| 44 |
+ idIndex: truncindex.NewTruncIndex([]string{}),
|
|
| 45 | 45 |
driver: driver, |
| 46 | 46 |
} |
| 47 | 47 |
if err := graph.restore(); err != nil {
|
| ... | ... |
@@ -62,7 +63,7 @@ func (graph *Graph) restore() error {
|
| 62 | 62 |
ids = append(ids, id) |
| 63 | 63 |
} |
| 64 | 64 |
} |
| 65 |
- graph.idIndex = utils.NewTruncIndex(ids) |
|
| 65 |
+ graph.idIndex = truncindex.NewTruncIndex(ids) |
|
| 66 | 66 |
utils.Debugf("Restored %d elements", len(dir))
|
| 67 | 67 |
return nil |
| 68 | 68 |
} |
| 69 | 69 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,102 @@ |
| 0 |
+package truncindex |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "index/suffixarray" |
|
| 5 |
+ "strings" |
|
| 6 |
+ "sync" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. |
|
| 10 |
+// This is used to retrieve image and container IDs by more convenient shorthand prefixes. |
|
| 11 |
+type TruncIndex struct {
|
|
| 12 |
+ sync.RWMutex |
|
| 13 |
+ index *suffixarray.Index |
|
| 14 |
+ ids map[string]bool |
|
| 15 |
+ bytes []byte |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+func NewTruncIndex(ids []string) (idx *TruncIndex) {
|
|
| 19 |
+ idx = &TruncIndex{
|
|
| 20 |
+ ids: make(map[string]bool), |
|
| 21 |
+ bytes: []byte{' '},
|
|
| 22 |
+ } |
|
| 23 |
+ for _, id := range ids {
|
|
| 24 |
+ idx.ids[id] = true |
|
| 25 |
+ idx.bytes = append(idx.bytes, []byte(id+" ")...) |
|
| 26 |
+ } |
|
| 27 |
+ idx.index = suffixarray.New(idx.bytes) |
|
| 28 |
+ return |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+func (idx *TruncIndex) addId(id string) error {
|
|
| 32 |
+ if strings.Contains(id, " ") {
|
|
| 33 |
+ return fmt.Errorf("Illegal character: ' '")
|
|
| 34 |
+ } |
|
| 35 |
+ if _, exists := idx.ids[id]; exists {
|
|
| 36 |
+ return fmt.Errorf("Id already exists: %s", id)
|
|
| 37 |
+ } |
|
| 38 |
+ idx.ids[id] = true |
|
| 39 |
+ idx.bytes = append(idx.bytes, []byte(id+" ")...) |
|
| 40 |
+ return nil |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 43 |
+func (idx *TruncIndex) Add(id string) error {
|
|
| 44 |
+ idx.Lock() |
|
| 45 |
+ defer idx.Unlock() |
|
| 46 |
+ if err := idx.addId(id); err != nil {
|
|
| 47 |
+ return err |
|
| 48 |
+ } |
|
| 49 |
+ idx.index = suffixarray.New(idx.bytes) |
|
| 50 |
+ return nil |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func (idx *TruncIndex) AddWithoutSuffixarrayUpdate(id string) error {
|
|
| 54 |
+ idx.Lock() |
|
| 55 |
+ defer idx.Unlock() |
|
| 56 |
+ return idx.addId(id) |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func (idx *TruncIndex) UpdateSuffixarray() {
|
|
| 60 |
+ idx.Lock() |
|
| 61 |
+ defer idx.Unlock() |
|
| 62 |
+ idx.index = suffixarray.New(idx.bytes) |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+func (idx *TruncIndex) Delete(id string) error {
|
|
| 66 |
+ idx.Lock() |
|
| 67 |
+ defer idx.Unlock() |
|
| 68 |
+ if _, exists := idx.ids[id]; !exists {
|
|
| 69 |
+ return fmt.Errorf("No such id: %s", id)
|
|
| 70 |
+ } |
|
| 71 |
+ before, after, err := idx.lookup(id) |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ return err |
|
| 74 |
+ } |
|
| 75 |
+ delete(idx.ids, id) |
|
| 76 |
+ idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...) |
|
| 77 |
+ idx.index = suffixarray.New(idx.bytes) |
|
| 78 |
+ return nil |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func (idx *TruncIndex) lookup(s string) (int, int, error) {
|
|
| 82 |
+ offsets := idx.index.Lookup([]byte(" "+s), -1)
|
|
| 83 |
+ //log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
|
|
| 84 |
+ if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
|
|
| 85 |
+ return -1, -1, fmt.Errorf("No such id: %s", s)
|
|
| 86 |
+ } |
|
| 87 |
+ offsetBefore := offsets[0] + 1 |
|
| 88 |
+ offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ") |
|
| 89 |
+ return offsetBefore, offsetAfter, nil |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (idx *TruncIndex) Get(s string) (string, error) {
|
|
| 93 |
+ idx.RLock() |
|
| 94 |
+ defer idx.RUnlock() |
|
| 95 |
+ before, after, err := idx.lookup(s) |
|
| 96 |
+ //log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
|
|
| 97 |
+ if err != nil {
|
|
| 98 |
+ return "", err |
|
| 99 |
+ } |
|
| 100 |
+ return string(idx.bytes[before:after]), err |
|
| 101 |
+} |
| 0 | 102 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,105 @@ |
| 0 |
+package truncindex |
|
| 1 |
+ |
|
| 2 |
+import "testing" |
|
| 3 |
+ |
|
| 4 |
+// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix. |
|
| 5 |
+func TestTruncIndex(t *testing.T) {
|
|
| 6 |
+ ids := []string{}
|
|
| 7 |
+ index := NewTruncIndex(ids) |
|
| 8 |
+ // Get on an empty index |
|
| 9 |
+ if _, err := index.Get("foobar"); err == nil {
|
|
| 10 |
+ t.Fatal("Get on an empty index should return an error")
|
|
| 11 |
+ } |
|
| 12 |
+ |
|
| 13 |
+ // Spaces should be illegal in an id |
|
| 14 |
+ if err := index.Add("I have a space"); err == nil {
|
|
| 15 |
+ t.Fatalf("Adding an id with ' ' should return an error")
|
|
| 16 |
+ } |
|
| 17 |
+ |
|
| 18 |
+ id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96" |
|
| 19 |
+ // Add an id |
|
| 20 |
+ if err := index.Add(id); err != nil {
|
|
| 21 |
+ t.Fatal(err) |
|
| 22 |
+ } |
|
| 23 |
+ // Get a non-existing id |
|
| 24 |
+ assertIndexGet(t, index, "abracadabra", "", true) |
|
| 25 |
+ // Get the exact id |
|
| 26 |
+ assertIndexGet(t, index, id, id, false) |
|
| 27 |
+ // The first letter should match |
|
| 28 |
+ assertIndexGet(t, index, id[:1], id, false) |
|
| 29 |
+ // The first half should match |
|
| 30 |
+ assertIndexGet(t, index, id[:len(id)/2], id, false) |
|
| 31 |
+ // The second half should NOT match |
|
| 32 |
+ assertIndexGet(t, index, id[len(id)/2:], "", true) |
|
| 33 |
+ |
|
| 34 |
+ id2 := id[:6] + "blabla" |
|
| 35 |
+ // Add an id |
|
| 36 |
+ if err := index.Add(id2); err != nil {
|
|
| 37 |
+ t.Fatal(err) |
|
| 38 |
+ } |
|
| 39 |
+ // Both exact IDs should work |
|
| 40 |
+ assertIndexGet(t, index, id, id, false) |
|
| 41 |
+ assertIndexGet(t, index, id2, id2, false) |
|
| 42 |
+ |
|
| 43 |
+ // 6 characters or less should conflict |
|
| 44 |
+ assertIndexGet(t, index, id[:6], "", true) |
|
| 45 |
+ assertIndexGet(t, index, id[:4], "", true) |
|
| 46 |
+ assertIndexGet(t, index, id[:1], "", true) |
|
| 47 |
+ |
|
| 48 |
+ // 7 characters should NOT conflict |
|
| 49 |
+ assertIndexGet(t, index, id[:7], id, false) |
|
| 50 |
+ assertIndexGet(t, index, id2[:7], id2, false) |
|
| 51 |
+ |
|
| 52 |
+ // Deleting a non-existing id should return an error |
|
| 53 |
+ if err := index.Delete("non-existing"); err == nil {
|
|
| 54 |
+ t.Fatalf("Deleting a non-existing id should return an error")
|
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ // Deleting id2 should remove conflicts |
|
| 58 |
+ if err := index.Delete(id2); err != nil {
|
|
| 59 |
+ t.Fatal(err) |
|
| 60 |
+ } |
|
| 61 |
+ // id2 should no longer work |
|
| 62 |
+ assertIndexGet(t, index, id2, "", true) |
|
| 63 |
+ assertIndexGet(t, index, id2[:7], "", true) |
|
| 64 |
+ assertIndexGet(t, index, id2[:11], "", true) |
|
| 65 |
+ |
|
| 66 |
+ // conflicts between id and id2 should be gone |
|
| 67 |
+ assertIndexGet(t, index, id[:6], id, false) |
|
| 68 |
+ assertIndexGet(t, index, id[:4], id, false) |
|
| 69 |
+ assertIndexGet(t, index, id[:1], id, false) |
|
| 70 |
+ |
|
| 71 |
+ // non-conflicting substrings should still not conflict |
|
| 72 |
+ assertIndexGet(t, index, id[:7], id, false) |
|
| 73 |
+ assertIndexGet(t, index, id[:15], id, false) |
|
| 74 |
+ assertIndexGet(t, index, id, id, false) |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) {
|
|
| 78 |
+ if result, err := index.Get(input); err != nil && !expectError {
|
|
| 79 |
+ t.Fatalf("Unexpected error getting '%s': %s", input, err)
|
|
| 80 |
+ } else if err == nil && expectError {
|
|
| 81 |
+ t.Fatalf("Getting '%s' should return an error", input)
|
|
| 82 |
+ } else if result != expectedResult {
|
|
| 83 |
+ t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
|
|
| 84 |
+ } |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+func BenchmarkTruncIndexAdd(b *testing.B) {
|
|
| 88 |
+ ids := []string{"banana", "bananaa", "bananab"}
|
|
| 89 |
+ b.ResetTimer() |
|
| 90 |
+ for i := 0; i < b.N; i++ {
|
|
| 91 |
+ index := NewTruncIndex([]string{})
|
|
| 92 |
+ for _, id := range ids {
|
|
| 93 |
+ index.Add(id) |
|
| 94 |
+ } |
|
| 95 |
+ } |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+func BenchmarkTruncIndexNew(b *testing.B) {
|
|
| 99 |
+ ids := []string{"banana", "bananaa", "bananab"}
|
|
| 100 |
+ b.ResetTimer() |
|
| 101 |
+ for i := 0; i < b.N; i++ {
|
|
| 102 |
+ NewTruncIndex(ids) |
|
| 103 |
+ } |
|
| 104 |
+} |
| ... | ... |
@@ -9,7 +9,6 @@ import ( |
| 9 | 9 |
"encoding/json" |
| 10 | 10 |
"errors" |
| 11 | 11 |
"fmt" |
| 12 |
- "index/suffixarray" |
|
| 13 | 12 |
"io" |
| 14 | 13 |
"io/ioutil" |
| 15 | 14 |
"net/http" |
| ... | ... |
@@ -397,100 +396,6 @@ func GetTotalUsedFds() int {
|
| 397 | 397 |
return -1 |
| 398 | 398 |
} |
| 399 | 399 |
|
| 400 |
-// TruncIndex allows the retrieval of string identifiers by any of their unique prefixes. |
|
| 401 |
-// This is used to retrieve image and container IDs by more convenient shorthand prefixes. |
|
| 402 |
-type TruncIndex struct {
|
|
| 403 |
- sync.RWMutex |
|
| 404 |
- index *suffixarray.Index |
|
| 405 |
- ids map[string]bool |
|
| 406 |
- bytes []byte |
|
| 407 |
-} |
|
| 408 |
- |
|
| 409 |
-func NewTruncIndex(ids []string) (idx *TruncIndex) {
|
|
| 410 |
- idx = &TruncIndex{
|
|
| 411 |
- ids: make(map[string]bool), |
|
| 412 |
- bytes: []byte{' '},
|
|
| 413 |
- } |
|
| 414 |
- for _, id := range ids {
|
|
| 415 |
- idx.ids[id] = true |
|
| 416 |
- idx.bytes = append(idx.bytes, []byte(id+" ")...) |
|
| 417 |
- } |
|
| 418 |
- idx.index = suffixarray.New(idx.bytes) |
|
| 419 |
- return |
|
| 420 |
-} |
|
| 421 |
- |
|
| 422 |
-func (idx *TruncIndex) addId(id string) error {
|
|
| 423 |
- if strings.Contains(id, " ") {
|
|
| 424 |
- return fmt.Errorf("Illegal character: ' '")
|
|
| 425 |
- } |
|
| 426 |
- if _, exists := idx.ids[id]; exists {
|
|
| 427 |
- return fmt.Errorf("Id already exists: %s", id)
|
|
| 428 |
- } |
|
| 429 |
- idx.ids[id] = true |
|
| 430 |
- idx.bytes = append(idx.bytes, []byte(id+" ")...) |
|
| 431 |
- return nil |
|
| 432 |
-} |
|
| 433 |
- |
|
| 434 |
-func (idx *TruncIndex) Add(id string) error {
|
|
| 435 |
- idx.Lock() |
|
| 436 |
- defer idx.Unlock() |
|
| 437 |
- if err := idx.addId(id); err != nil {
|
|
| 438 |
- return err |
|
| 439 |
- } |
|
| 440 |
- idx.index = suffixarray.New(idx.bytes) |
|
| 441 |
- return nil |
|
| 442 |
-} |
|
| 443 |
- |
|
| 444 |
-func (idx *TruncIndex) AddWithoutSuffixarrayUpdate(id string) error {
|
|
| 445 |
- idx.Lock() |
|
| 446 |
- defer idx.Unlock() |
|
| 447 |
- return idx.addId(id) |
|
| 448 |
-} |
|
| 449 |
- |
|
| 450 |
-func (idx *TruncIndex) UpdateSuffixarray() {
|
|
| 451 |
- idx.Lock() |
|
| 452 |
- defer idx.Unlock() |
|
| 453 |
- idx.index = suffixarray.New(idx.bytes) |
|
| 454 |
-} |
|
| 455 |
- |
|
| 456 |
-func (idx *TruncIndex) Delete(id string) error {
|
|
| 457 |
- idx.Lock() |
|
| 458 |
- defer idx.Unlock() |
|
| 459 |
- if _, exists := idx.ids[id]; !exists {
|
|
| 460 |
- return fmt.Errorf("No such id: %s", id)
|
|
| 461 |
- } |
|
| 462 |
- before, after, err := idx.lookup(id) |
|
| 463 |
- if err != nil {
|
|
| 464 |
- return err |
|
| 465 |
- } |
|
| 466 |
- delete(idx.ids, id) |
|
| 467 |
- idx.bytes = append(idx.bytes[:before], idx.bytes[after:]...) |
|
| 468 |
- idx.index = suffixarray.New(idx.bytes) |
|
| 469 |
- return nil |
|
| 470 |
-} |
|
| 471 |
- |
|
| 472 |
-func (idx *TruncIndex) lookup(s string) (int, int, error) {
|
|
| 473 |
- offsets := idx.index.Lookup([]byte(" "+s), -1)
|
|
| 474 |
- //log.Printf("lookup(%s): %v (index bytes: '%s')\n", s, offsets, idx.index.Bytes())
|
|
| 475 |
- if offsets == nil || len(offsets) == 0 || len(offsets) > 1 {
|
|
| 476 |
- return -1, -1, fmt.Errorf("No such id: %s", s)
|
|
| 477 |
- } |
|
| 478 |
- offsetBefore := offsets[0] + 1 |
|
| 479 |
- offsetAfter := offsetBefore + strings.Index(string(idx.bytes[offsetBefore:]), " ") |
|
| 480 |
- return offsetBefore, offsetAfter, nil |
|
| 481 |
-} |
|
| 482 |
- |
|
| 483 |
-func (idx *TruncIndex) Get(s string) (string, error) {
|
|
| 484 |
- idx.RLock() |
|
| 485 |
- defer idx.RUnlock() |
|
| 486 |
- before, after, err := idx.lookup(s) |
|
| 487 |
- //log.Printf("Get(%s) bytes=|%s| before=|%d| after=|%d|\n", s, idx.bytes, before, after)
|
|
| 488 |
- if err != nil {
|
|
| 489 |
- return "", err |
|
| 490 |
- } |
|
| 491 |
- return string(idx.bytes[before:after]), err |
|
| 492 |
-} |
|
| 493 |
- |
|
| 494 | 400 |
// TruncateID returns a shorthand version of a string identifier for convenience. |
| 495 | 401 |
// A collision with other shorthands is very unlikely, but possible. |
| 496 | 402 |
// In case of a collision a lookup with TruncIndex.Get() will fail, and the caller |
| ... | ... |
@@ -135,108 +135,6 @@ func TestRaceWriteBroadcaster(t *testing.T) {
|
| 135 | 135 |
<-c |
| 136 | 136 |
} |
| 137 | 137 |
|
| 138 |
-// Test the behavior of TruncIndex, an index for querying IDs from a non-conflicting prefix. |
|
| 139 |
-func TestTruncIndex(t *testing.T) {
|
|
| 140 |
- ids := []string{}
|
|
| 141 |
- index := NewTruncIndex(ids) |
|
| 142 |
- // Get on an empty index |
|
| 143 |
- if _, err := index.Get("foobar"); err == nil {
|
|
| 144 |
- t.Fatal("Get on an empty index should return an error")
|
|
| 145 |
- } |
|
| 146 |
- |
|
| 147 |
- // Spaces should be illegal in an id |
|
| 148 |
- if err := index.Add("I have a space"); err == nil {
|
|
| 149 |
- t.Fatalf("Adding an id with ' ' should return an error")
|
|
| 150 |
- } |
|
| 151 |
- |
|
| 152 |
- id := "99b36c2c326ccc11e726eee6ee78a0baf166ef96" |
|
| 153 |
- // Add an id |
|
| 154 |
- if err := index.Add(id); err != nil {
|
|
| 155 |
- t.Fatal(err) |
|
| 156 |
- } |
|
| 157 |
- // Get a non-existing id |
|
| 158 |
- assertIndexGet(t, index, "abracadabra", "", true) |
|
| 159 |
- // Get the exact id |
|
| 160 |
- assertIndexGet(t, index, id, id, false) |
|
| 161 |
- // The first letter should match |
|
| 162 |
- assertIndexGet(t, index, id[:1], id, false) |
|
| 163 |
- // The first half should match |
|
| 164 |
- assertIndexGet(t, index, id[:len(id)/2], id, false) |
|
| 165 |
- // The second half should NOT match |
|
| 166 |
- assertIndexGet(t, index, id[len(id)/2:], "", true) |
|
| 167 |
- |
|
| 168 |
- id2 := id[:6] + "blabla" |
|
| 169 |
- // Add an id |
|
| 170 |
- if err := index.Add(id2); err != nil {
|
|
| 171 |
- t.Fatal(err) |
|
| 172 |
- } |
|
| 173 |
- // Both exact IDs should work |
|
| 174 |
- assertIndexGet(t, index, id, id, false) |
|
| 175 |
- assertIndexGet(t, index, id2, id2, false) |
|
| 176 |
- |
|
| 177 |
- // 6 characters or less should conflict |
|
| 178 |
- assertIndexGet(t, index, id[:6], "", true) |
|
| 179 |
- assertIndexGet(t, index, id[:4], "", true) |
|
| 180 |
- assertIndexGet(t, index, id[:1], "", true) |
|
| 181 |
- |
|
| 182 |
- // 7 characters should NOT conflict |
|
| 183 |
- assertIndexGet(t, index, id[:7], id, false) |
|
| 184 |
- assertIndexGet(t, index, id2[:7], id2, false) |
|
| 185 |
- |
|
| 186 |
- // Deleting a non-existing id should return an error |
|
| 187 |
- if err := index.Delete("non-existing"); err == nil {
|
|
| 188 |
- t.Fatalf("Deleting a non-existing id should return an error")
|
|
| 189 |
- } |
|
| 190 |
- |
|
| 191 |
- // Deleting id2 should remove conflicts |
|
| 192 |
- if err := index.Delete(id2); err != nil {
|
|
| 193 |
- t.Fatal(err) |
|
| 194 |
- } |
|
| 195 |
- // id2 should no longer work |
|
| 196 |
- assertIndexGet(t, index, id2, "", true) |
|
| 197 |
- assertIndexGet(t, index, id2[:7], "", true) |
|
| 198 |
- assertIndexGet(t, index, id2[:11], "", true) |
|
| 199 |
- |
|
| 200 |
- // conflicts between id and id2 should be gone |
|
| 201 |
- assertIndexGet(t, index, id[:6], id, false) |
|
| 202 |
- assertIndexGet(t, index, id[:4], id, false) |
|
| 203 |
- assertIndexGet(t, index, id[:1], id, false) |
|
| 204 |
- |
|
| 205 |
- // non-conflicting substrings should still not conflict |
|
| 206 |
- assertIndexGet(t, index, id[:7], id, false) |
|
| 207 |
- assertIndexGet(t, index, id[:15], id, false) |
|
| 208 |
- assertIndexGet(t, index, id, id, false) |
|
| 209 |
-} |
|
| 210 |
- |
|
| 211 |
-func assertIndexGet(t *testing.T, index *TruncIndex, input, expectedResult string, expectError bool) {
|
|
| 212 |
- if result, err := index.Get(input); err != nil && !expectError {
|
|
| 213 |
- t.Fatalf("Unexpected error getting '%s': %s", input, err)
|
|
| 214 |
- } else if err == nil && expectError {
|
|
| 215 |
- t.Fatalf("Getting '%s' should return an error", input)
|
|
| 216 |
- } else if result != expectedResult {
|
|
| 217 |
- t.Fatalf("Getting '%s' returned '%s' instead of '%s'", input, result, expectedResult)
|
|
| 218 |
- } |
|
| 219 |
-} |
|
| 220 |
- |
|
| 221 |
-func BenchmarkTruncIndexAdd(b *testing.B) {
|
|
| 222 |
- ids := []string{"banana", "bananaa", "bananab"}
|
|
| 223 |
- b.ResetTimer() |
|
| 224 |
- for i := 0; i < b.N; i++ {
|
|
| 225 |
- index := NewTruncIndex([]string{})
|
|
| 226 |
- for _, id := range ids {
|
|
| 227 |
- index.Add(id) |
|
| 228 |
- } |
|
| 229 |
- } |
|
| 230 |
-} |
|
| 231 |
- |
|
| 232 |
-func BenchmarkTruncIndexNew(b *testing.B) {
|
|
| 233 |
- ids := []string{"banana", "bananaa", "bananab"}
|
|
| 234 |
- b.ResetTimer() |
|
| 235 |
- for i := 0; i < b.N; i++ {
|
|
| 236 |
- NewTruncIndex(ids) |
|
| 237 |
- } |
|
| 238 |
-} |
|
| 239 |
- |
|
| 240 | 138 |
func assertKernelVersion(t *testing.T, a, b *KernelVersionInfo, result int) {
|
| 241 | 139 |
if r := CompareKernelVersion(a, b); r != result {
|
| 242 | 140 |
t.Fatalf("Unexpected kernel version comparison result. Found %d, expected %d", r, result)
|