mirror of
https://github.com/syncthing/syncthing.git
synced 2024-11-16 10:28:49 -07:00
744ef0d8ac
This makes version vector values clock based instead of just incremented from zero. The effect is that a vector that is created from scratch (after database reset) will have a higher value for the local device than what it could have been previously, causing a conflict. That is, if we are A and we had {A: 42, B: 12} in the old scheme, a reset and rescan would give us {A: 1} which is a strict ancestor of the older file (this might be wrong). With the new scheme we would instead have {A: someClockTime, b: otherClockTime} and the new version after reset would become {A: someClockTime+delta} which is in conflict with the previous entry (better). In case the clocks are wrong (current time is less than the value in the vector) we fall back to just simple increment like today. This scheme is ineffective if we suffer a database reset while at the same time setting the clock back far into the past. It's however no worse than what we already do. This loses the ability to emit the "added" event, as we can't look for the magic 1 entry any more. That event was however already broken (#5541). Another place where we infer meaning from the vector itself is in receive only folders, but there the only criteria is that the vector is one item long and includes just ourselves, which remains the case with this change. * wip
387 lines
11 KiB
Go
387 lines
11 KiB
Go
// Copyright (C) 2015 The Protocol Authors.
|
|
|
|
package protocol
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
var v Vector
|
|
|
|
// Append
|
|
|
|
v = v.updateWithNow(42, 5)
|
|
expected := Vector{Counters: []Counter{{ID: 42, Value: 5}}}
|
|
|
|
if v.Compare(expected) != Equal {
|
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
|
}
|
|
|
|
// Insert at front
|
|
|
|
v = v.updateWithNow(36, 6)
|
|
expected = Vector{Counters: []Counter{{ID: 36, Value: 6}, {ID: 42, Value: 5}}}
|
|
|
|
if v.Compare(expected) != Equal {
|
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
|
}
|
|
|
|
// Insert in middle
|
|
|
|
v = v.updateWithNow(37, 7)
|
|
expected = Vector{Counters: []Counter{{ID: 36, Value: 6}, {ID: 37, Value: 7}, {ID: 42, Value: 5}}}
|
|
|
|
if v.Compare(expected) != Equal {
|
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
|
}
|
|
|
|
// Update existing
|
|
|
|
v = v.updateWithNow(37, 1)
|
|
expected = Vector{Counters: []Counter{{ID: 36, Value: 6}, {ID: 37, Value: 8}, {ID: 42, Value: 5}}}
|
|
|
|
if v.Compare(expected) != Equal {
|
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
|
}
|
|
|
|
// Update existing with higher current time
|
|
|
|
v = v.updateWithNow(37, 100)
|
|
expected = Vector{Counters: []Counter{{ID: 36, Value: 6}, {ID: 37, Value: 100}, {ID: 42, Value: 5}}}
|
|
|
|
if v.Compare(expected) != Equal {
|
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
|
}
|
|
|
|
// Update existing with lower current time
|
|
|
|
v = v.updateWithNow(37, 50)
|
|
expected = Vector{Counters: []Counter{{ID: 36, Value: 6}, {ID: 37, Value: 101}, {ID: 42, Value: 5}}}
|
|
|
|
if v.Compare(expected) != Equal {
|
|
t.Errorf("Update error, %+v != %+v", v, expected)
|
|
}
|
|
}
|
|
|
|
func TestCopy(t *testing.T) {
|
|
v0 := Vector{Counters: []Counter{{ID: 42, Value: 1}}}
|
|
v1 := v0.Copy()
|
|
v1.Update(42)
|
|
if v0.Compare(v1) != Lesser {
|
|
t.Errorf("Copy error, %+v should be ancestor of %+v", v0, v1)
|
|
}
|
|
}
|
|
|
|
func TestMerge(t *testing.T) {
|
|
testcases := []struct {
|
|
a, b, m Vector
|
|
}{
|
|
// No-ops
|
|
{
|
|
Vector{},
|
|
Vector{},
|
|
Vector{},
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
},
|
|
|
|
// Appends
|
|
{
|
|
Vector{},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
},
|
|
|
|
// Insert
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 23, Value: 2}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 23, Value: 2}, {ID: 42, Value: 1}}},
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 1}}},
|
|
},
|
|
|
|
// Update
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 1}, {ID: 42, Value: 2}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 2}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 2}, {ID: 42, Value: 2}}},
|
|
},
|
|
|
|
// All of the above
|
|
{
|
|
Vector{Counters: []Counter{{ID: 10, Value: 1}, {ID: 20, Value: 2}, {ID: 30, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 5, Value: 1}, {ID: 10, Value: 2}, {ID: 15, Value: 1}, {ID: 20, Value: 1}, {ID: 25, Value: 1}, {ID: 35, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 5, Value: 1}, {ID: 10, Value: 2}, {ID: 15, Value: 1}, {ID: 20, Value: 2}, {ID: 25, Value: 1}, {ID: 30, Value: 1}, {ID: 35, Value: 1}}},
|
|
},
|
|
}
|
|
|
|
for i, tc := range testcases {
|
|
if m := tc.a.Merge(tc.b); m.Compare(tc.m) != Equal {
|
|
t.Errorf("%d: %+v.Merge(%+v) == %+v (expected %+v)", i, tc.a, tc.b, m, tc.m)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCounterValue(t *testing.T) {
|
|
v0 := Vector{Counters: []Counter{{ID: 42, Value: 1}, {ID: 64, Value: 5}}}
|
|
if v0.Counter(42) != 1 {
|
|
t.Errorf("Counter error, %d != %d", v0.Counter(42), 1)
|
|
}
|
|
if v0.Counter(64) != 5 {
|
|
t.Errorf("Counter error, %d != %d", v0.Counter(64), 5)
|
|
}
|
|
if v0.Counter(72) != 0 {
|
|
t.Errorf("Counter error, %d != %d", v0.Counter(72), 0)
|
|
}
|
|
}
|
|
|
|
func TestCompare(t *testing.T) {
|
|
testcases := []struct {
|
|
a, b Vector
|
|
r Ordering
|
|
}{
|
|
// Empty vectors are identical
|
|
{Vector{}, Vector{}, Equal},
|
|
{Vector{}, Vector{Counters: []Counter{{ID: 42, Value: 0}}}, Equal},
|
|
{Vector{Counters: []Counter{{ID: 42, Value: 0}}}, Vector{}, Equal},
|
|
|
|
// Zero is the implied value for a missing Counter
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 0}}},
|
|
Vector{Counters: []Counter{{ID: 77, Value: 0}}},
|
|
Equal,
|
|
},
|
|
|
|
// Equal vectors are equal
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 33}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 33}}},
|
|
Equal,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 33}, {ID: 77, Value: 24}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 33}, {ID: 77, Value: 24}}},
|
|
Equal,
|
|
},
|
|
|
|
// These a-vectors are all greater than the b-vector
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Vector{},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 0, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 0, Value: 0}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 0}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: math.MaxUint64, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: math.MaxUint64, Value: 0}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 0, Value: math.MaxUint64}}},
|
|
Vector{Counters: []Counter{{ID: 0, Value: 0}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: math.MaxUint64}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 0}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: math.MaxUint64, Value: math.MaxUint64}}},
|
|
Vector{Counters: []Counter{{ID: math.MaxUint64, Value: 0}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 0, Value: math.MaxUint64}}},
|
|
Vector{Counters: []Counter{{ID: 0, Value: math.MaxUint64 - 1}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: math.MaxUint64}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: math.MaxUint64 - 1}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: math.MaxUint64, Value: math.MaxUint64}}},
|
|
Vector{Counters: []Counter{{ID: math.MaxUint64, Value: math.MaxUint64 - 1}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 2}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 2}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 2}, {ID: 77, Value: 3}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}, {ID: 77, Value: 3}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 2}, {ID: 77, Value: 3}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}, {ID: 77, Value: 3}}},
|
|
Greater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 23}, {ID: 42, Value: 2}, {ID: 77, Value: 4}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}, {ID: 77, Value: 3}}},
|
|
Greater,
|
|
},
|
|
|
|
// These a-vectors are all lesser than the b-vector
|
|
{Vector{}, Vector{Counters: []Counter{{ID: 42, Value: 1}}}, Lesser},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 0}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Lesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 2}}},
|
|
Lesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 2}}},
|
|
Lesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 1}, {ID: 77, Value: 3}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 2}, {ID: 77, Value: 3}}},
|
|
Lesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}, {ID: 77, Value: 3}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 2}, {ID: 77, Value: 3}}},
|
|
Lesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}, {ID: 77, Value: 3}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 23}, {ID: 42, Value: 2}, {ID: 77, Value: 4}}},
|
|
Lesser,
|
|
},
|
|
|
|
// These are all in conflict
|
|
{
|
|
Vector{Counters: []Counter{{ID: 42, Value: 2}}},
|
|
Vector{Counters: []Counter{{ID: 43, Value: 1}}},
|
|
ConcurrentGreater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 43, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 42, Value: 2}}},
|
|
ConcurrentLesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 23}, {ID: 42, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 2}}},
|
|
ConcurrentGreater,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 21}, {ID: 42, Value: 2}}},
|
|
Vector{Counters: []Counter{{ID: 22, Value: 22}, {ID: 42, Value: 1}}},
|
|
ConcurrentLesser,
|
|
},
|
|
{
|
|
Vector{Counters: []Counter{{ID: 22, Value: 21}, {ID: 42, Value: 2}, {ID: 43, Value: 1}}},
|
|
Vector{Counters: []Counter{{ID: 20, Value: 1}, {ID: 22, Value: 22}, {ID: 42, Value: 1}}},
|
|
ConcurrentLesser,
|
|
},
|
|
}
|
|
|
|
for i, tc := range testcases {
|
|
// Test real Compare
|
|
if r := tc.a.Compare(tc.b); r != tc.r {
|
|
t.Errorf("%d: %+v.Compare(%+v) == %v (expected %v)", i, tc.a, tc.b, r, tc.r)
|
|
}
|
|
|
|
// Test convenience functions
|
|
switch tc.r {
|
|
case Greater:
|
|
if tc.a.Equal(tc.b) {
|
|
t.Errorf("%+v == %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.Concurrent(tc.b) {
|
|
t.Errorf("%+v concurrent %+v", tc.a, tc.b)
|
|
}
|
|
if !tc.a.GreaterEqual(tc.b) {
|
|
t.Errorf("%+v not >= %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.LesserEqual(tc.b) {
|
|
t.Errorf("%+v <= %+v", tc.a, tc.b)
|
|
}
|
|
case Lesser:
|
|
if tc.a.Concurrent(tc.b) {
|
|
t.Errorf("%+v concurrent %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.Equal(tc.b) {
|
|
t.Errorf("%+v == %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.GreaterEqual(tc.b) {
|
|
t.Errorf("%+v >= %+v", tc.a, tc.b)
|
|
}
|
|
if !tc.a.LesserEqual(tc.b) {
|
|
t.Errorf("%+v not <= %+v", tc.a, tc.b)
|
|
}
|
|
case Equal:
|
|
if tc.a.Concurrent(tc.b) {
|
|
t.Errorf("%+v concurrent %+v", tc.a, tc.b)
|
|
}
|
|
if !tc.a.Equal(tc.b) {
|
|
t.Errorf("%+v not == %+v", tc.a, tc.b)
|
|
}
|
|
if !tc.a.GreaterEqual(tc.b) {
|
|
t.Errorf("%+v not <= %+v", tc.a, tc.b)
|
|
}
|
|
if !tc.a.LesserEqual(tc.b) {
|
|
t.Errorf("%+v not <= %+v", tc.a, tc.b)
|
|
}
|
|
case ConcurrentLesser, ConcurrentGreater:
|
|
if !tc.a.Concurrent(tc.b) {
|
|
t.Errorf("%+v not concurrent %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.Equal(tc.b) {
|
|
t.Errorf("%+v == %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.GreaterEqual(tc.b) {
|
|
t.Errorf("%+v >= %+v", tc.a, tc.b)
|
|
}
|
|
if tc.a.LesserEqual(tc.b) {
|
|
t.Errorf("%+v <= %+v", tc.a, tc.b)
|
|
}
|
|
}
|
|
}
|
|
}
|