2017-04-30 00:15:37 +03:00
|
|
|
// Copyright 2017 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.
|
|
|
|
|
|
|
|
package maintner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestSumSegSize(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
in []fileSeg
|
|
|
|
want int64
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
in: []fileSeg{fileSeg{size: 1}},
|
|
|
|
want: 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: []fileSeg{fileSeg{size: 1}, fileSeg{size: 100}},
|
|
|
|
want: 101,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: nil,
|
|
|
|
want: 0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
|
|
got := sumSegSize(tt.in)
|
|
|
|
if got != tt.want {
|
|
|
|
t.Errorf("%d. sumSegSize = %v; want %v", i, got, tt.want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSumCommonPrefixSize(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
a, b []fileSeg
|
|
|
|
summer func(file string, n int64) string
|
|
|
|
want int64
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
a: []fileSeg{fileSeg{size: 1, sha224: "abab"}},
|
|
|
|
b: []fileSeg{fileSeg{size: 1, sha224: "abab"}},
|
|
|
|
want: 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
a: []fileSeg{fileSeg{size: 1, sha224: "abab"}},
|
|
|
|
b: []fileSeg{fileSeg{size: 1, sha224: "eeee"}},
|
|
|
|
want: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
a: []fileSeg{
|
|
|
|
fileSeg{size: 100, sha224: "abab"},
|
|
|
|
fileSeg{size: 100, sha224: "abab", file: "a.mutlog"},
|
|
|
|
},
|
|
|
|
b: []fileSeg{
|
|
|
|
fileSeg{size: 100, sha224: "abab"},
|
|
|
|
fileSeg{size: 50, sha224: "cccc"},
|
|
|
|
},
|
|
|
|
summer: func(file string, n int64) string {
|
|
|
|
if file == "a.mutlog" && n == 50 {
|
|
|
|
return "cccc"
|
|
|
|
}
|
|
|
|
return "xxx"
|
|
|
|
},
|
|
|
|
want: 150,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
a: []fileSeg{
|
|
|
|
fileSeg{size: 100, sha224: "abab"},
|
|
|
|
fileSeg{size: 50, sha224: "cccc"},
|
|
|
|
},
|
|
|
|
b: []fileSeg{
|
|
|
|
fileSeg{size: 100, sha224: "abab"},
|
|
|
|
fileSeg{size: 100, sha224: "abab", file: "b.mutlog"},
|
|
|
|
},
|
|
|
|
summer: func(file string, n int64) string {
|
|
|
|
if file == "b.mutlog" && n == 50 {
|
|
|
|
return "cccc"
|
|
|
|
}
|
|
|
|
return "xxx"
|
|
|
|
},
|
|
|
|
want: 150,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
|
|
summer := tt.summer
|
|
|
|
if summer == nil {
|
|
|
|
summer = func(file string, n int64) string {
|
|
|
|
t.Errorf("%d. unexpected call to prefix summer for file=%q, n=%v", i, file, n)
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ns := &netMutSource{
|
|
|
|
testHookFilePrefixSum224: summer,
|
|
|
|
}
|
|
|
|
got := ns.sumCommonPrefixSize(tt.a, tt.b)
|
|
|
|
if got != tt.want {
|
|
|
|
t.Errorf("%d. sumCommonPrefixSize = %v; want %v", i, got, tt.want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTrimLeadingSegBytes(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
in []fileSeg
|
|
|
|
trim int64
|
|
|
|
want []fileSeg
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
in: []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
|
|
|
|
trim: 0,
|
|
|
|
want: []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
|
|
|
|
trim: 150,
|
|
|
|
want: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
|
|
|
|
trim: 100,
|
|
|
|
want: []fileSeg{fileSeg{size: 50}},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
in: []fileSeg{fileSeg{size: 100}, fileSeg{size: 50}},
|
|
|
|
trim: 25,
|
|
|
|
want: []fileSeg{fileSeg{size: 100, skip: 25}, fileSeg{size: 50}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
|
|
copyIn := append([]fileSeg(nil), tt.in...)
|
|
|
|
got := trimLeadingSegBytes(tt.in, tt.trim)
|
|
|
|
if !reflect.DeepEqual(tt.in, copyIn) {
|
|
|
|
t.Fatalf("%d. trimLeadingSegBytes modified its input", i)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
|
|
t.Fatalf("%d. trim = %+v; want %+v", i, got, tt.want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetNewSegments(t *testing.T) {
|
|
|
|
type testCase struct {
|
|
|
|
name string
|
|
|
|
lastSegs []fileSeg
|
|
|
|
serverSegs [][]LogSegmentJSON
|
|
|
|
|
|
|
|
// prefixSum is the prefix sum to use if called.
|
|
|
|
// If empty, prefixSum calls are errors.
|
|
|
|
prefixSum string
|
|
|
|
|
|
|
|
want []fileSeg
|
|
|
|
wantSplit bool
|
|
|
|
}
|
|
|
|
tests := []testCase{
|
|
|
|
{
|
|
|
|
name: "first_download",
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 100, SHA224: "abc"},
|
|
|
|
{Number: 2, Size: 200, SHA224: "def"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: []fileSeg{
|
|
|
|
{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "incremental_download_growseg", // from first_download, segment 2 grows a bit
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
prefixSum: "def",
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 100, SHA224: "abc"},
|
|
|
|
{Number: 2, Size: 205, SHA224: "defdef"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: []fileSeg{
|
|
|
|
{seg: 2, size: 205, sha224: "defdef", skip: 200, file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "incremental_download_growseg_and_newseg", // from first_download, segment 2 grows, and segment 3 appears.
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
prefixSum: "def",
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 100, SHA224: "abc"},
|
|
|
|
{Number: 2, Size: 250, SHA224: "defdef"},
|
|
|
|
{Number: 3, Size: 300, SHA224: "fff"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: []fileSeg{
|
|
|
|
{seg: 2, size: 250, sha224: "defdef", skip: 200, file: "/fake/0002.mutlog"},
|
|
|
|
{seg: 3, size: 300, sha224: "fff", skip: 0, file: "/fake/0003.mutlog"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "incremental_download_newseg", // from first_download, segment 3 appears.
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
{seg: 2, size: 200, sha224: "def", file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 100, SHA224: "abc"},
|
|
|
|
{Number: 2, Size: 200, SHA224: "def"},
|
|
|
|
{Number: 3, Size: 300, SHA224: "fff"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: []fileSeg{
|
|
|
|
{seg: 3, size: 300, sha224: "fff", skip: 0, file: "/fake/0003.mutlog"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "incremental_with_sleep",
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
},
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 101, SHA224: "abc"},
|
|
|
|
},
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 101, SHA224: "abc"},
|
|
|
|
{Number: 2, Size: 102, SHA224: "def"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
want: []fileSeg{
|
|
|
|
{seg: 2, size: 102, sha224: "def", skip: 0, file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "split_error_diff_first_seg_same_size",
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
},
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 101, SHA224: "def"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
wantSplit: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "split_error_diff_first_seg_and_longer",
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
},
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 102, SHA224: "def"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
prefixSum: "ffffffffff", // no match
|
|
|
|
wantSplit: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "split_error_diff_first_seg_and_shorter",
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 101, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
},
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 50, SHA224: "def"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
prefixSum: "ffffffffff", // no match
|
|
|
|
wantSplit: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "split_error_diff_final_seg",
|
|
|
|
lastSegs: []fileSeg{
|
|
|
|
{seg: 1, size: 100, sha224: "abc", file: "/fake/0001.mutlog"},
|
|
|
|
{seg: 2, size: 2, sha224: "def", file: "/fake/0002.mutlog"},
|
|
|
|
},
|
|
|
|
serverSegs: [][]LogSegmentJSON{
|
|
|
|
[]LogSegmentJSON{
|
|
|
|
{Number: 1, Size: 100, SHA224: "abc"},
|
|
|
|
{Number: 2, Size: 4, SHA224: "fff"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
prefixSum: "not_def",
|
|
|
|
wantSplit: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
serverSegCalls := 0
|
|
|
|
waits := 0
|
|
|
|
ns := &netMutSource{
|
|
|
|
last: tt.lastSegs,
|
2017-05-05 23:23:37 +03:00
|
|
|
testHookGetServerSegments: func(_ context.Context, waitSizeNot int64) (segs []LogSegmentJSON, err error) {
|
2017-04-30 00:15:37 +03:00
|
|
|
serverSegCalls++
|
|
|
|
if serverSegCalls > 10 {
|
|
|
|
t.Fatalf("infinite loop calling getServerSegments? num wait calls = %v", waits)
|
|
|
|
}
|
|
|
|
if len(tt.serverSegs) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
segs = tt.serverSegs[0]
|
|
|
|
if len(tt.serverSegs) > 1 {
|
|
|
|
tt.serverSegs = tt.serverSegs[1:]
|
|
|
|
}
|
|
|
|
return segs, nil
|
|
|
|
},
|
2019-12-04 20:53:04 +03:00
|
|
|
testHookSyncSeg: func(_ context.Context, seg LogSegmentJSON) (fileSeg, []byte, error) {
|
2017-04-30 00:15:37 +03:00
|
|
|
return fileSeg{
|
|
|
|
seg: seg.Number,
|
|
|
|
size: seg.Size,
|
|
|
|
sha224: seg.SHA224,
|
|
|
|
file: fmt.Sprintf("/fake/%04d.mutlog", seg.Number),
|
2019-12-04 20:53:04 +03:00
|
|
|
}, nil, nil
|
2017-04-30 00:15:37 +03:00
|
|
|
},
|
|
|
|
testHookFilePrefixSum224: func(file string, n int64) string {
|
|
|
|
if tt.prefixSum != "" {
|
|
|
|
return tt.prefixSum
|
|
|
|
}
|
|
|
|
t.Errorf("unexpected call to filePrefixSum224(%q, %d)", file, n)
|
|
|
|
return "XXXX"
|
|
|
|
},
|
|
|
|
}
|
|
|
|
got, err := ns.getNewSegments(context.Background())
|
|
|
|
if tt.wantSplit && err == ErrSplit {
|
|
|
|
// Success.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if tt.wantSplit {
|
|
|
|
t.Fatalf("wanted ErrSplit; got %+v, %v", got, err)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
|
|
t.Errorf("mismatch\n got: %+v\nwant: %+v\n", got, tt.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|