1/*
2Copyright 2011 The Perkeep Authors
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package localdisk
18
19import (
20	"context"
21	"fmt"
22	"os"
23	"strconv"
24	"sync"
25	"testing"
26
27	"perkeep.org/pkg/blob"
28	"perkeep.org/pkg/blobserver"
29	"perkeep.org/pkg/blobserver/storagetest"
30	"perkeep.org/pkg/test"
31)
32
33func cleanUp(ds *DiskStorage) {
34	os.RemoveAll(ds.root)
35}
36
37var (
38	epochLock sync.Mutex
39	rootEpoch = 0
40)
41
42func NewStorage(t *testing.T) *DiskStorage {
43	epochLock.Lock()
44	rootEpoch++
45	path := fmt.Sprintf("%s/camli-testroot-%d-%d", os.TempDir(), os.Getpid(), rootEpoch)
46	epochLock.Unlock()
47	if err := os.Mkdir(path, 0755); err != nil {
48		t.Fatalf("Failed to create temp directory %q: %v", path, err)
49	}
50	ds, err := New(path)
51	if err != nil {
52		t.Fatalf("Failed to run New: %v", err)
53	}
54	return ds
55}
56
57func TestUploadDup(t *testing.T) {
58	ds := NewStorage(t)
59	defer cleanUp(ds)
60	tb := &test.Blob{"Foo"}
61	tb.MustUpload(t, ds)
62	tb.MustUpload(t, ds)
63}
64
65func TestReceiveStat(t *testing.T) {
66	ds := NewStorage(t)
67	defer cleanUp(ds)
68
69	tb := &test.Blob{"Foo"}
70	tb.MustUpload(t, ds)
71
72	ctx := context.Background()
73	got, err := blobserver.StatBlobs(ctx, ds, tb.BlobRefSlice())
74	if err != nil {
75		t.Fatalf("StatBlobs: %v", err)
76	}
77	if len(got) != 1 {
78		t.Errorf("got %d stat blobs; expected 1", len(got))
79	}
80	sb, ok := got[tb.BlobRef()]
81	if !ok {
82		t.Fatalf("stat response lacked information for %v", tb.BlobRef())
83	}
84	tb.AssertMatches(t, sb)
85}
86
87func TestMultiStat(t *testing.T) {
88	ds := NewStorage(t)
89	defer cleanUp(ds)
90
91	blobfoo := &test.Blob{"foo"}
92	blobbar := &test.Blob{"bar!"}
93	blobfoo.MustUpload(t, ds)
94	blobbar.MustUpload(t, ds)
95
96	need := make(map[blob.Ref]bool)
97	need[blobfoo.BlobRef()] = true
98	need[blobbar.BlobRef()] = true
99
100	blobs := []blob.Ref{blobfoo.BlobRef(), blobbar.BlobRef()}
101
102	// In addition to the two "foo" and "bar" blobs, add
103	// maxParallelStats other dummy blobs, to exercise the stat
104	// rate-limiting (which had a deadlock once after a cleanup)
105	const maxParallelStats = 20
106	for i := 0; i < maxParallelStats; i++ {
107		blobs = append(blobs, blob.RefFromString(strconv.Itoa(i)))
108	}
109
110	ctx := context.Background()
111	gotStat, err := blobserver.StatBlobs(ctx, ds, blobs)
112	if err != nil {
113		t.Fatalf("StatBlobs: %v", err)
114	}
115	got := 0
116	for _, sb := range gotStat {
117		got++
118		if !need[sb.Ref] {
119			t.Errorf("didn't need %s", sb.Ref)
120		}
121		delete(need, sb.Ref)
122	}
123	if want := 2; got != want {
124		t.Errorf("number stats = %d; want %d", got, want)
125	}
126	if len(need) != 0 {
127		t.Errorf("Not all stat results returned; still need %d", len(need))
128	}
129}
130
131func TestMissingGetReturnsNoEnt(t *testing.T) {
132	ds := NewStorage(t)
133	defer cleanUp(ds)
134	foo := &test.Blob{"foo"}
135
136	blob, _, err := ds.Fetch(context.Background(), foo.BlobRef())
137	if err != os.ErrNotExist {
138		t.Errorf("expected ErrNotExist; got %v", err)
139	}
140	if blob != nil {
141		t.Errorf("expected nil blob; got a value")
142	}
143}
144
145type file struct {
146	name     string
147	contents string
148}
149
150func TestLocaldisk(t *testing.T) {
151	storagetest.Test(t, func(t *testing.T) (blobserver.Storage, func()) {
152		ds := NewStorage(t)
153		return ds, func() { cleanUp(ds) }
154	})
155}
156