github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/index/index_test.go (about)

     1  /*
     2  Copyright 2011 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package index_test
    18  
    19  import (
    20  	"fmt"
    21  	"go/ast"
    22  	"go/parser"
    23  	"go/token"
    24  	"os"
    25  	"path/filepath"
    26  	"reflect"
    27  	"strings"
    28  	"testing"
    29  
    30  	"camlistore.org/pkg/blob"
    31  	"camlistore.org/pkg/blobserver"
    32  	"camlistore.org/pkg/index"
    33  	"camlistore.org/pkg/index/indextest"
    34  	"camlistore.org/pkg/sorted"
    35  	"camlistore.org/pkg/test"
    36  	"camlistore.org/pkg/types/camtypes"
    37  )
    38  
    39  func TestReverseTimeString(t *testing.T) {
    40  	in := "2011-11-27T01:23:45Z"
    41  	got := index.ExpReverseTimeString(in)
    42  	want := "rt7988-88-72T98:76:54Z"
    43  	if got != want {
    44  		t.Fatalf("reverseTimeString = %q, want %q", got, want)
    45  	}
    46  	back := index.ExpUnreverseTimeString(got)
    47  	if back != in {
    48  		t.Fatalf("unreverseTimeString = %q, want %q", back, in)
    49  	}
    50  }
    51  
    52  func TestIndex_Memory(t *testing.T) {
    53  	indextest.Index(t, index.NewMemoryIndex)
    54  }
    55  
    56  func TestPathsOfSignerTarget_Memory(t *testing.T) {
    57  	indextest.PathsOfSignerTarget(t, index.NewMemoryIndex)
    58  }
    59  
    60  func TestFiles_Memory(t *testing.T) {
    61  	indextest.Files(t, index.NewMemoryIndex)
    62  }
    63  
    64  func TestEdgesTo_Memory(t *testing.T) {
    65  	indextest.EdgesTo(t, index.NewMemoryIndex)
    66  }
    67  
    68  func TestDelete_Memory(t *testing.T) {
    69  	indextest.Delete(t, index.NewMemoryIndex)
    70  }
    71  
    72  var (
    73  	// those test files are not specific to an indexer implementation
    74  	// hence we do not want to check them.
    75  	notAnIndexer = []string{
    76  		"corpus_bench_test.go",
    77  		"corpus_test.go",
    78  		"export_test.go",
    79  		"index_test.go",
    80  		"keys_test.go",
    81  	}
    82  	// A map is used in hasAllRequiredTests to note which required
    83  	// tests have been found in a package, by setting the corresponding
    84  	// booleans to true. Those are the keys for this map.
    85  	requiredTests = []string{"TestIndex_", "TestPathsOfSignerTarget_", "TestFiles_", "TestEdgesTo_"}
    86  )
    87  
    88  // This function checks that all the functions using the tests
    89  // defined in indextest, namely:
    90  // TestIndex_, TestPathOfSignerTarget_, TestFiles_
    91  // do exist in the provided test file.
    92  func hasAllRequiredTests(name string, t *testing.T) error {
    93  	tests := make(map[string]bool)
    94  	for _, v := range requiredTests {
    95  		tests[v] = false
    96  	}
    97  
    98  	if !strings.HasSuffix(name, "_test.go") || skipFromList(name) {
    99  		return nil
   100  	}
   101  	fset := token.NewFileSet()
   102  	f, err := parser.ParseFile(fset, name, nil, 0)
   103  	if err != nil {
   104  		t.Fatalf("%v: %v", name, err)
   105  	}
   106  	ast.Inspect(f, func(n ast.Node) bool {
   107  		switch x := n.(type) {
   108  		case *ast.FuncDecl:
   109  			name := x.Name.Name
   110  			for k, _ := range tests {
   111  				if strings.HasPrefix(name, k) {
   112  					tests[k] = true
   113  				}
   114  			}
   115  		}
   116  		return true
   117  	})
   118  
   119  	for k, v := range tests {
   120  		if !v {
   121  			return fmt.Errorf("%v not implemented in %v", k, name)
   122  		}
   123  	}
   124  	return nil
   125  }
   126  
   127  // For each test file dedicated to an indexer implementation, this checks that
   128  // all the required tests are present in its test suite.
   129  func TestIndexerTestsCompleteness(t *testing.T) {
   130  	cwd, err := os.Open(".")
   131  	if err != nil {
   132  		t.Fatal(err)
   133  	}
   134  	defer cwd.Close()
   135  	files, err := cwd.Readdir(-1)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  
   140  	for _, file := range files {
   141  		name := file.Name()
   142  		if file.IsDir() || strings.HasPrefix(name, ".") {
   143  			continue
   144  		}
   145  		if err := hasAllRequiredTests(name, t); err != nil {
   146  			t.Error(err)
   147  		}
   148  	}
   149  	// special case for sqlite as it is the only one left in its own package
   150  	if err := hasAllRequiredTests(filepath.FromSlash("sqlite/sqlite_test.go"), t); err != nil {
   151  		t.Error(err)
   152  	}
   153  }
   154  
   155  func skipFromList(name string) bool {
   156  	for _, v := range notAnIndexer {
   157  		if name == v {
   158  			return true
   159  		}
   160  	}
   161  	return false
   162  }
   163  
   164  func TestMergeFileInfoRow(t *testing.T) {
   165  	c := index.ExpNewCorpus()
   166  	c.Exp_mergeFileInfoRow("fileinfo|sha1-579f7f246bd420d486ddeb0dadbb256cfaf8bf6b",
   167  		"100|something%2egif|image%2Fgif")
   168  	fi := c.Exp_files(blob.MustParse("sha1-579f7f246bd420d486ddeb0dadbb256cfaf8bf6b"))
   169  	want := camtypes.FileInfo{
   170  		Size:     100,
   171  		MIMEType: "image/gif",
   172  		FileName: "something.gif",
   173  	}
   174  	if !reflect.DeepEqual(want, fi) {
   175  		t.Errorf("Got %+v; want %+v", fi, want)
   176  	}
   177  }
   178  
   179  var (
   180  	chunk1 = &test.Blob{Contents: "foo"}
   181  	chunk2 = &test.Blob{Contents: "bar"}
   182  	chunk3 = &test.Blob{Contents: "baz"}
   183  
   184  	chunk1ref = chunk1.BlobRef()
   185  	chunk2ref = chunk2.BlobRef()
   186  	chunk3ref = chunk3.BlobRef()
   187  
   188  	fileBlob = &test.Blob{fmt.Sprintf(`{"camliVersion": 1,
   189  "camliType": "file",
   190  "fileName": "stuff.txt",
   191  "parts": [
   192    {"blobRef": "%s", "size": 3},
   193    {"blobRef": "%s", "size": 3},
   194    {"blobRef": "%s", "size": 3}
   195  ]}`, chunk1ref, chunk2ref, chunk3ref)}
   196  	fileBlobRef = fileBlob.BlobRef()
   197  )
   198  
   199  func TestInitNeededMaps(t *testing.T) {
   200  	s := sorted.NewMemoryKeyValue()
   201  
   202  	// Start unknowning that the data chunks are all gone:
   203  	s.Set("schemaversion", fmt.Sprint(index.Exp_schemaVersion()))
   204  	s.Set(index.Exp_missingKey(fileBlobRef, chunk1ref), "1")
   205  	s.Set(index.Exp_missingKey(fileBlobRef, chunk2ref), "1")
   206  	s.Set(index.Exp_missingKey(fileBlobRef, chunk3ref), "1")
   207  	ix, err := index.New(s)
   208  	if err != nil {
   209  		t.Fatal(err)
   210  	}
   211  	{
   212  		needs, neededBy, _ := ix.NeededMapsForTest()
   213  		needsWant := map[blob.Ref][]blob.Ref{
   214  			fileBlobRef: []blob.Ref{chunk1ref, chunk2ref, chunk3ref},
   215  		}
   216  		neededByWant := map[blob.Ref][]blob.Ref{
   217  			chunk1ref: []blob.Ref{fileBlobRef},
   218  			chunk2ref: []blob.Ref{fileBlobRef},
   219  			chunk3ref: []blob.Ref{fileBlobRef},
   220  		}
   221  		if !reflect.DeepEqual(needs, needsWant) {
   222  			t.Errorf("needs = %v; want %v", needs, needsWant)
   223  		}
   224  		if !reflect.DeepEqual(neededBy, neededByWant) {
   225  			t.Errorf("neededBy = %v; want %v", neededBy, neededByWant)
   226  		}
   227  	}
   228  
   229  	ix.Exp_noteBlobIndexed(chunk2ref)
   230  
   231  	{
   232  		needs, neededBy, ready := ix.NeededMapsForTest()
   233  		needsWant := map[blob.Ref][]blob.Ref{
   234  			fileBlobRef: []blob.Ref{chunk1ref, chunk3ref},
   235  		}
   236  		neededByWant := map[blob.Ref][]blob.Ref{
   237  			chunk1ref: []blob.Ref{fileBlobRef},
   238  			chunk3ref: []blob.Ref{fileBlobRef},
   239  		}
   240  		if !reflect.DeepEqual(needs, needsWant) {
   241  			t.Errorf("needs = %v; want %v", needs, needsWant)
   242  		}
   243  		if !reflect.DeepEqual(neededBy, neededByWant) {
   244  			t.Errorf("neededBy = %v; want %v", neededBy, neededByWant)
   245  		}
   246  		if len(ready) != 0 {
   247  			t.Errorf("ready = %v; want nothing", ready)
   248  		}
   249  	}
   250  
   251  	ix.Exp_noteBlobIndexed(chunk1ref)
   252  
   253  	{
   254  		needs, neededBy, ready := ix.NeededMapsForTest()
   255  		needsWant := map[blob.Ref][]blob.Ref{
   256  			fileBlobRef: []blob.Ref{chunk3ref},
   257  		}
   258  		neededByWant := map[blob.Ref][]blob.Ref{
   259  			chunk3ref: []blob.Ref{fileBlobRef},
   260  		}
   261  		if !reflect.DeepEqual(needs, needsWant) {
   262  			t.Errorf("needs = %v; want %v", needs, needsWant)
   263  		}
   264  		if !reflect.DeepEqual(neededBy, neededByWant) {
   265  			t.Errorf("neededBy = %v; want %v", neededBy, neededByWant)
   266  		}
   267  		if len(ready) != 0 {
   268  			t.Errorf("ready = %v; want nothing", ready)
   269  		}
   270  	}
   271  
   272  	ix.Exp_noteBlobIndexed(chunk3ref)
   273  
   274  	{
   275  		needs, neededBy, ready := ix.NeededMapsForTest()
   276  		needsWant := map[blob.Ref][]blob.Ref{}
   277  		neededByWant := map[blob.Ref][]blob.Ref{}
   278  		if !reflect.DeepEqual(needs, needsWant) {
   279  			t.Errorf("needs = %v; want %v", needs, needsWant)
   280  		}
   281  		if !reflect.DeepEqual(neededBy, neededByWant) {
   282  			t.Errorf("neededBy = %v; want %v", neededBy, neededByWant)
   283  		}
   284  		if !ready[fileBlobRef] {
   285  			t.Error("fileBlobRef not ready")
   286  		}
   287  	}
   288  	dumpSorted(t, s)
   289  }
   290  
   291  func dumpSorted(t *testing.T, s sorted.KeyValue) {
   292  	foreachSorted(t, s, func(k, v string) {
   293  		t.Logf("index %q = %q", k, v)
   294  	})
   295  }
   296  
   297  func foreachSorted(t *testing.T, s sorted.KeyValue, fn func(string, string)) {
   298  	it := s.Find("", "")
   299  	for it.Next() {
   300  		fn(it.Key(), it.Value())
   301  	}
   302  	if err := it.Close(); err != nil {
   303  		t.Fatal(err)
   304  	}
   305  }
   306  
   307  func TestOutOfOrderIndexing(t *testing.T) {
   308  	tf := new(test.Fetcher)
   309  	s := sorted.NewMemoryKeyValue()
   310  
   311  	ix, err := index.New(s)
   312  	if err != nil {
   313  		t.Fatal(err)
   314  	}
   315  	ix.BlobSource = tf
   316  
   317  	t.Logf("file ref = %v", fileBlobRef)
   318  	t.Logf("missing data chunks = %v, %v, %v", chunk1ref, chunk2ref, chunk3ref)
   319  
   320  	add := func(b *test.Blob) {
   321  		tf.AddBlob(b)
   322  		if _, err := ix.ReceiveBlob(b.BlobRef(), b.Reader()); err != nil {
   323  			t.Fatalf("ReceiveBlob(%v): %v", b.BlobRef(), err)
   324  		}
   325  	}
   326  
   327  	add(fileBlob)
   328  
   329  	{
   330  		key := fmt.Sprintf("missing|%s|%s", fileBlobRef, chunk1ref)
   331  		if got, err := s.Get(key); got == "" || err != nil {
   332  			t.Errorf("key %q missing (err: %v); want 1", key, err)
   333  		}
   334  	}
   335  
   336  	add(chunk1)
   337  	add(chunk2)
   338  
   339  	ix.Exp_AwaitReindexing(t)
   340  
   341  	{
   342  		key := fmt.Sprintf("missing|%s|%s", fileBlobRef, chunk3ref)
   343  		if got, err := s.Get(key); got == "" || err != nil {
   344  			t.Errorf("key %q missing (err: %v); want 1", key, err)
   345  		}
   346  	}
   347  
   348  	add(chunk3)
   349  
   350  	ix.Exp_AwaitReindexing(t)
   351  
   352  	foreachSorted(t, s, func(k, v string) {
   353  		if strings.HasPrefix(k, "missing|") {
   354  			t.Errorf("Shouldn't have missing key: %q", k)
   355  		}
   356  	})
   357  }
   358  
   359  func TestIndexingClaimMissingPubkey(t *testing.T) {
   360  	s := sorted.NewMemoryKeyValue()
   361  	idx, err := index.New(s)
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	id := indextest.NewIndexDeps(idx)
   367  	id.Fataler = t
   368  
   369  	goodKeyFetcher := id.Index.KeyFetcher
   370  	emptyFetcher := new(test.Fetcher)
   371  
   372  	pn := id.NewPermanode()
   373  
   374  	// Prevent the index from being able to find the public key:
   375  	idx.KeyFetcher = emptyFetcher
   376  
   377  	// This previous failed to upload, since the signer's public key was
   378  	// unavailable.
   379  	claimRef := id.SetAttribute(pn, "tag", "foo")
   380  
   381  	t.Logf(" Claim is %v", claimRef)
   382  	t.Logf("Signer is %v", id.SignerBlobRef)
   383  
   384  	// Verify that populateClaim noted the missing public key blob:
   385  	{
   386  		key := fmt.Sprintf("missing|%s|%s", claimRef, id.SignerBlobRef)
   387  		if got, err := s.Get(key); got == "" || err != nil {
   388  			t.Errorf("key %q missing (err: %v); want 1", key, err)
   389  		}
   390  	}
   391  
   392  	// Now make it available again:
   393  	idx.KeyFetcher = idx.BlobSource
   394  
   395  	if err := copyBlob(id.SignerBlobRef, idx.BlobSource.(*test.Fetcher), goodKeyFetcher); err != nil {
   396  		t.Errorf("Error copying public key to BlobSource: %v", err)
   397  	}
   398  	if err := copyBlob(id.SignerBlobRef, idx, goodKeyFetcher); err != nil {
   399  		t.Errorf("Error uploading public key to indexer: %v", err)
   400  	}
   401  
   402  	idx.Exp_AwaitReindexing(t)
   403  
   404  	// Verify that populateClaim noted the missing public key blob:
   405  	{
   406  		key := fmt.Sprintf("missing|%s|%s", claimRef, id.SignerBlobRef)
   407  		if got, err := s.Get(key); got != "" || err == nil {
   408  			t.Errorf("row %q still exists", key)
   409  		}
   410  	}
   411  }
   412  
   413  func copyBlob(br blob.Ref, dst blobserver.BlobReceiver, src blob.Fetcher) error {
   414  	rc, _, err := src.Fetch(br)
   415  	if err != nil {
   416  		return err
   417  	}
   418  	defer rc.Close()
   419  	_, err = dst.ReceiveBlob(br, rc)
   420  	return err
   421  }