11db8185bc
Signed-off-by: Frederick F. Kautz IV <fkautz@alumni.cmu.edu>
340 lines
11 KiB
Go
340 lines
11 KiB
Go
package azure
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
azure "github.com/MSOpenTech/azure-sdk-for-go/storage"
|
|
)
|
|
|
|
func TestRandomWriter_writeChunkToBlocks(t *testing.T) {
|
|
s := NewStorageSimulator()
|
|
rw := newRandomBlobWriter(&s, 3)
|
|
rand := newBlockIDGenerator()
|
|
c := []byte("AAABBBCCCD")
|
|
|
|
if err := rw.bs.CreateBlockBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bw, nn, err := rw.writeChunkToBlocks("a", "b", bytes.NewReader(c), rand)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if expected := int64(len(c)); nn != expected {
|
|
t.Fatalf("wrong nn:%v, expected:%v", nn, expected)
|
|
}
|
|
if expected := 4; len(bw) != expected {
|
|
t.Fatal("unexpected written block count")
|
|
}
|
|
|
|
bx, err := s.GetBlockList("a", "b", azure.BlockListTypeAll)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if expected := 0; len(bx.CommittedBlocks) != expected {
|
|
t.Fatal("unexpected committed block count")
|
|
}
|
|
if expected := 4; len(bx.UncommittedBlocks) != expected {
|
|
t.Fatalf("unexpected uncommitted block count: %d -- %#v", len(bx.UncommittedBlocks), bx)
|
|
}
|
|
|
|
if err := rw.bs.PutBlockList("a", "b", bw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
r, err := rw.bs.GetBlob("a", "b")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
assertBlobContents(t, r, c)
|
|
}
|
|
|
|
func TestRandomWriter_blocksLeftSide(t *testing.T) {
|
|
blob := "AAAAABBBBBCCC"
|
|
cases := []struct {
|
|
offset int64
|
|
expectedBlob string
|
|
expectedPattern []azure.BlockStatus
|
|
}{
|
|
{0, "", []azure.BlockStatus{}}, // write to beginning, discard all
|
|
{13, blob, []azure.BlockStatus{azure.BlockStatusCommitted, azure.BlockStatusCommitted, azure.BlockStatusCommitted}}, // write to end, no change
|
|
{1, "A", []azure.BlockStatus{azure.BlockStatusUncommitted}}, // write at 1
|
|
{5, "AAAAA", []azure.BlockStatus{azure.BlockStatusCommitted}}, // write just after first block
|
|
{6, "AAAAAB", []azure.BlockStatus{azure.BlockStatusCommitted, azure.BlockStatusUncommitted}}, // split the second block
|
|
{9, "AAAAABBBB", []azure.BlockStatus{azure.BlockStatusCommitted, azure.BlockStatusUncommitted}}, // write just after first block
|
|
}
|
|
|
|
for _, c := range cases {
|
|
s := NewStorageSimulator()
|
|
rw := newRandomBlobWriter(&s, 5)
|
|
rand := newBlockIDGenerator()
|
|
|
|
if err := rw.bs.CreateBlockBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bw, _, err := rw.writeChunkToBlocks("a", "b", strings.NewReader(blob), rand)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := rw.bs.PutBlockList("a", "b", bw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bx, err := rw.blocksLeftSide("a", "b", c.offset, rand)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bs := []azure.BlockStatus{}
|
|
for _, v := range bx {
|
|
bs = append(bs, v.Status)
|
|
}
|
|
|
|
if !reflect.DeepEqual(bs, c.expectedPattern) {
|
|
t.Logf("Committed blocks %v", bw)
|
|
t.Fatalf("For offset %v: Expected pattern: %v, Got: %v\n(Returned: %v)", c.offset, c.expectedPattern, bs, bx)
|
|
}
|
|
if rw.bs.PutBlockList("a", "b", bx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, err := rw.bs.GetBlob("a", "b")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cout, err := ioutil.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
outBlob := string(cout)
|
|
if outBlob != c.expectedBlob {
|
|
t.Fatalf("wrong blob contents: %v, expected: %v", outBlob, c.expectedBlob)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRandomWriter_blocksRightSide(t *testing.T) {
|
|
blob := "AAAAABBBBBCCC"
|
|
cases := []struct {
|
|
offset int64
|
|
size int64
|
|
expectedBlob string
|
|
expectedPattern []azure.BlockStatus
|
|
}{
|
|
{0, 100, "", []azure.BlockStatus{}}, // overwrite the entire blob
|
|
{0, 3, "AABBBBBCCC", []azure.BlockStatus{azure.BlockStatusUncommitted, azure.BlockStatusCommitted, azure.BlockStatusCommitted}}, // split first block
|
|
{4, 1, "BBBBBCCC", []azure.BlockStatus{azure.BlockStatusCommitted, azure.BlockStatusCommitted}}, // write to last char of first block
|
|
{1, 6, "BBBCCC", []azure.BlockStatus{azure.BlockStatusUncommitted, azure.BlockStatusCommitted}}, // overwrite splits first and second block, last block remains
|
|
{3, 8, "CC", []azure.BlockStatus{azure.BlockStatusUncommitted}}, // overwrite a block in middle block, split end block
|
|
{10, 1, "CC", []azure.BlockStatus{azure.BlockStatusUncommitted}}, // overwrite first byte of rightmost block
|
|
{11, 2, "", []azure.BlockStatus{}}, // overwrite the rightmost index
|
|
{13, 20, "", []azure.BlockStatus{}}, // append to the end
|
|
}
|
|
|
|
for _, c := range cases {
|
|
s := NewStorageSimulator()
|
|
rw := newRandomBlobWriter(&s, 5)
|
|
rand := newBlockIDGenerator()
|
|
|
|
if err := rw.bs.CreateBlockBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bw, _, err := rw.writeChunkToBlocks("a", "b", strings.NewReader(blob), rand)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := rw.bs.PutBlockList("a", "b", bw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bx, err := rw.blocksRightSide("a", "b", c.offset, c.size, rand)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bs := []azure.BlockStatus{}
|
|
for _, v := range bx {
|
|
bs = append(bs, v.Status)
|
|
}
|
|
|
|
if !reflect.DeepEqual(bs, c.expectedPattern) {
|
|
t.Logf("Committed blocks %v", bw)
|
|
t.Fatalf("For offset %v-size:%v: Expected pattern: %v, Got: %v\n(Returned: %v)", c.offset, c.size, c.expectedPattern, bs, bx)
|
|
}
|
|
if rw.bs.PutBlockList("a", "b", bx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r, err := rw.bs.GetBlob("a", "b")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cout, err := ioutil.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
outBlob := string(cout)
|
|
if outBlob != c.expectedBlob {
|
|
t.Fatalf("For offset %v-size:%v: wrong blob contents: %v, expected: %v", c.offset, c.size, outBlob, c.expectedBlob)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRandomWriter_Write_NewBlob(t *testing.T) {
|
|
var (
|
|
s = NewStorageSimulator()
|
|
rw = newRandomBlobWriter(&s, 1024*3) // 3 KB blocks
|
|
blob = randomContents(1024 * 7) // 7 KB blob
|
|
)
|
|
if err := rw.bs.CreateBlockBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := rw.WriteBlobAt("a", "b", 10, bytes.NewReader(blob)); err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if _, err := rw.WriteBlobAt("a", "b", 100000, bytes.NewReader(blob)); err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if nn, err := rw.WriteBlobAt("a", "b", 0, bytes.NewReader(blob)); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := int64(len(blob)); expected != nn {
|
|
t.Fatalf("wrong written bytes count: %v, expected: %v", nn, expected)
|
|
}
|
|
if out, err := rw.bs.GetBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
assertBlobContents(t, out, blob)
|
|
}
|
|
if bx, err := rw.bs.GetBlockList("a", "b", azure.BlockListTypeCommitted); err != nil {
|
|
t.Fatal(err)
|
|
} else if len(bx.CommittedBlocks) != 3 {
|
|
t.Fatalf("got wrong number of committed blocks: %v", len(bx.CommittedBlocks))
|
|
}
|
|
|
|
// Replace first 512 bytes
|
|
leftChunk := randomContents(512)
|
|
blob = append(leftChunk, blob[512:]...)
|
|
if nn, err := rw.WriteBlobAt("a", "b", 0, bytes.NewReader(leftChunk)); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := int64(len(leftChunk)); expected != nn {
|
|
t.Fatalf("wrong written bytes count: %v, expected: %v", nn, expected)
|
|
}
|
|
if out, err := rw.bs.GetBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
assertBlobContents(t, out, blob)
|
|
}
|
|
if bx, err := rw.bs.GetBlockList("a", "b", azure.BlockListTypeCommitted); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := 4; len(bx.CommittedBlocks) != expected {
|
|
t.Fatalf("got wrong number of committed blocks: %v, expected: %v", len(bx.CommittedBlocks), expected)
|
|
}
|
|
|
|
// Replace last 512 bytes with 1024 bytes
|
|
rightChunk := randomContents(1024)
|
|
offset := int64(len(blob) - 512)
|
|
blob = append(blob[:offset], rightChunk...)
|
|
if nn, err := rw.WriteBlobAt("a", "b", offset, bytes.NewReader(rightChunk)); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := int64(len(rightChunk)); expected != nn {
|
|
t.Fatalf("wrong written bytes count: %v, expected: %v", nn, expected)
|
|
}
|
|
if out, err := rw.bs.GetBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
assertBlobContents(t, out, blob)
|
|
}
|
|
if bx, err := rw.bs.GetBlockList("a", "b", azure.BlockListTypeCommitted); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := 5; len(bx.CommittedBlocks) != expected {
|
|
t.Fatalf("got wrong number of committed blocks: %v, expected: %v", len(bx.CommittedBlocks), expected)
|
|
}
|
|
|
|
// Replace 2K-4K (overlaps 2 blocks from L/R)
|
|
newChunk := randomContents(1024 * 2)
|
|
offset = 1024 * 2
|
|
blob = append(append(blob[:offset], newChunk...), blob[offset+int64(len(newChunk)):]...)
|
|
if nn, err := rw.WriteBlobAt("a", "b", offset, bytes.NewReader(newChunk)); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := int64(len(newChunk)); expected != nn {
|
|
t.Fatalf("wrong written bytes count: %v, expected: %v", nn, expected)
|
|
}
|
|
if out, err := rw.bs.GetBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
assertBlobContents(t, out, blob)
|
|
}
|
|
if bx, err := rw.bs.GetBlockList("a", "b", azure.BlockListTypeCommitted); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := 6; len(bx.CommittedBlocks) != expected {
|
|
t.Fatalf("got wrong number of committed blocks: %v, expected: %v\n%v", len(bx.CommittedBlocks), expected, bx.CommittedBlocks)
|
|
}
|
|
|
|
// Replace the entire blob
|
|
newBlob := randomContents(1024 * 30)
|
|
if nn, err := rw.WriteBlobAt("a", "b", 0, bytes.NewReader(newBlob)); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := int64(len(newBlob)); expected != nn {
|
|
t.Fatalf("wrong written bytes count: %v, expected: %v", nn, expected)
|
|
}
|
|
if out, err := rw.bs.GetBlob("a", "b"); err != nil {
|
|
t.Fatal(err)
|
|
} else {
|
|
assertBlobContents(t, out, newBlob)
|
|
}
|
|
if bx, err := rw.bs.GetBlockList("a", "b", azure.BlockListTypeCommitted); err != nil {
|
|
t.Fatal(err)
|
|
} else if expected := 10; len(bx.CommittedBlocks) != expected {
|
|
t.Fatalf("got wrong number of committed blocks: %v, expected: %v\n%v", len(bx.CommittedBlocks), expected, bx.CommittedBlocks)
|
|
} else if expected, size := int64(1024*30), getBlobSize(bx); size != expected {
|
|
t.Fatalf("committed block size does not indicate blob size")
|
|
}
|
|
}
|
|
|
|
func Test_getBlobSize(t *testing.T) {
|
|
// with some committed blocks
|
|
if expected, size := int64(151), getBlobSize(azure.BlockListResponse{
|
|
CommittedBlocks: []azure.BlockResponse{
|
|
{"A", 100},
|
|
{"B", 50},
|
|
{"C", 1},
|
|
},
|
|
UncommittedBlocks: []azure.BlockResponse{
|
|
{"D", 200},
|
|
}}); expected != size {
|
|
t.Fatalf("wrong blob size: %v, expected: %v", size, expected)
|
|
}
|
|
|
|
// with no committed blocks
|
|
if expected, size := int64(0), getBlobSize(azure.BlockListResponse{
|
|
UncommittedBlocks: []azure.BlockResponse{
|
|
{"A", 100},
|
|
{"B", 50},
|
|
{"C", 1},
|
|
{"D", 200},
|
|
}}); expected != size {
|
|
t.Fatalf("wrong blob size: %v, expected: %v", size, expected)
|
|
}
|
|
}
|
|
|
|
func assertBlobContents(t *testing.T, r io.Reader, expected []byte) {
|
|
out, err := ioutil.ReadAll(r)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(out, expected) {
|
|
t.Fatalf("wrong blob contents. size: %v, expected: %v", len(out), len(expected))
|
|
}
|
|
}
|
|
|
|
func randomContents(length int64) []byte {
|
|
b := make([]byte, length)
|
|
for i := range b {
|
|
b[i] = byte(rand.Intn(2 << 8))
|
|
}
|
|
return b
|
|
}
|