-
-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix RHT.Remove and add test code (#752)
Changes: - Fixed nil pointer dereference error in RHT.Remove. - Fixed RHT.Len to exclude the removed elements. - Fixed RHT.Nodes to exclude the removed elements. - Fixed an error in RHT.Set when modifying an already deleted key. --------- Co-authored-by: Youngteac Hong <susukang98@gmail.com>
- Loading branch information
1 parent
6936d2b
commit 3876b8f
Showing
2 changed files
with
268 additions
and
43 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,246 @@ | ||
package crdt | ||
package crdt_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/yorkie-team/yorkie/pkg/document/crdt" | ||
"github.com/yorkie-team/yorkie/test/helper" | ||
) | ||
|
||
func TestMarshal(t *testing.T) { | ||
t.Run("marshal test", func(t *testing.T) { | ||
key1 := `hello\\\t` | ||
value1 := "world\"\f\b" | ||
key2 := "hi" | ||
value2 := `test\r` | ||
expected := `{"hello\\\\\\t":"world\"\f\b","hi":"test\\r"}` | ||
|
||
rht := NewRHT() | ||
rht.Set(key1, value1, nil) | ||
rht.Set(key2, value2, nil) | ||
actual := rht.Marshal() | ||
assert.Equal(t, expected, actual) | ||
}) | ||
func TestRHT_Marshal(t *testing.T) { | ||
tests := []struct { | ||
desc string | ||
insertKey string | ||
insertVal string | ||
expectStr string | ||
}{ | ||
{ | ||
desc: `1. empty hash table`, | ||
insertKey: ``, | ||
insertVal: ``, | ||
expectStr: `{}`, | ||
}, | ||
{ | ||
desc: `2. only one element`, | ||
insertKey: "hello\\\\\\t", | ||
insertVal: "world\"\f\b", | ||
expectStr: `{"hello\\\\\\t":"world\"\f\b"}`, | ||
}, | ||
{ | ||
desc: `3. non-empty hash table`, | ||
insertKey: "hi", | ||
insertVal: `test\r`, | ||
expectStr: `{"hello\\\\\\t":"world\"\f\b","hi":"test\\r"}`, | ||
}, | ||
} | ||
|
||
root := helper.TestRoot() | ||
ctx := helper.TextChangeContext(root) | ||
|
||
rht := crdt.NewRHT() | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.desc, func(t *testing.T) { | ||
if len(tt.insertKey) > 0 { | ||
rht.Set(tt.insertKey, tt.insertVal, ctx.IssueTimeTicket()) | ||
} | ||
assert.Equal(t, tt.expectStr, rht.Marshal()) | ||
}) | ||
} | ||
} | ||
|
||
func TestRHT_ToXML(t *testing.T) { | ||
tests := []struct { | ||
desc string | ||
insertKey string | ||
insertVal string | ||
expectStr string | ||
}{ | ||
{ | ||
desc: `1. empty hash table`, | ||
insertKey: ``, | ||
insertVal: ``, | ||
expectStr: ``, | ||
}, | ||
{ | ||
desc: `2. only one element`, | ||
insertKey: "hello\\\\\\t", | ||
insertVal: "world\"\f\b", | ||
expectStr: `hello\\\t="world\"\f\b"`, | ||
}, | ||
{ | ||
desc: `3. non-empty hash table`, | ||
insertKey: "hi", | ||
insertVal: `test\r`, | ||
expectStr: `hello\\\t="world\"\f\b" hi="test\\r"`, | ||
}, | ||
} | ||
|
||
root := helper.TestRoot() | ||
ctx := helper.TextChangeContext(root) | ||
|
||
rht := crdt.NewRHT() | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.desc, func(t *testing.T) { | ||
if len(tt.insertKey) > 0 { | ||
rht.Set(tt.insertKey, tt.insertVal, ctx.IssueTimeTicket()) | ||
} | ||
assert.Equal(t, tt.expectStr, rht.ToXML()) | ||
}) | ||
} | ||
} | ||
|
||
func TestRHT_Set(t *testing.T) { | ||
key1, val1 := `key1`, `value1` | ||
key2, val2 := `key2`, `value2` | ||
|
||
tests := []struct { | ||
desc string | ||
insertKey []string | ||
insertVal []string | ||
expectStr string | ||
expectSize int | ||
}{ | ||
{ | ||
desc: `1. set elements`, | ||
insertKey: []string{key1, key2}, | ||
insertVal: []string{val1, val2}, | ||
expectStr: `{"key1":"value1","key2":"value2"}`, | ||
expectSize: 2, | ||
}, | ||
{ | ||
desc: `2. change elements`, | ||
insertKey: []string{key1, key2}, | ||
insertVal: []string{val2, val1}, | ||
expectStr: `{"key1":"value2","key2":"value1"}`, | ||
expectSize: 2, | ||
}, | ||
} | ||
|
||
root := helper.TestRoot() | ||
ctx := helper.TextChangeContext(root) | ||
|
||
rht := crdt.NewRHT() | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.desc, func(t *testing.T) { | ||
for i, key := range tt.insertKey { | ||
rht.Set(key, tt.insertVal[i], ctx.IssueTimeTicket()) | ||
} | ||
assert.Equal(t, tt.expectStr, rht.Marshal()) | ||
assert.Equal(t, tt.expectSize, rht.Len()) | ||
}) | ||
} | ||
} | ||
|
||
func TestRHT_Remove(t *testing.T) { | ||
key1, val1, val11 := `key1`, `value1`, `value11` | ||
key2, val2, val22 := `key2`, `value2`, `value22` | ||
|
||
tests := []struct { | ||
desc string | ||
insertKey []string | ||
insertVal []string | ||
deleteKey []string | ||
deleteVal []string | ||
expectXML string | ||
expectJSON string | ||
expectSize int | ||
}{ | ||
{ | ||
desc: `1. set elements`, | ||
insertKey: []string{key1, key2}, | ||
insertVal: []string{val1, val2}, | ||
deleteKey: []string{}, | ||
deleteVal: []string{}, | ||
expectXML: `key1="value1" key2="value2"`, | ||
expectJSON: `{"key1":"value1","key2":"value2"}`, | ||
expectSize: 2, | ||
}, | ||
{ | ||
desc: `2. remove element`, | ||
insertKey: []string{}, | ||
insertVal: []string{}, | ||
deleteKey: []string{key1}, | ||
deleteVal: []string{val1}, | ||
expectXML: `key2="value2"`, | ||
expectJSON: `{"key2":"value2"}`, | ||
expectSize: 1, | ||
}, | ||
{ | ||
desc: `3. set after remove`, | ||
insertKey: []string{key1}, | ||
insertVal: []string{val11}, | ||
deleteKey: []string{}, | ||
deleteVal: []string{}, | ||
expectXML: `key1="value11" key2="value2"`, | ||
expectJSON: `{"key1":"value11","key2":"value2"}`, | ||
expectSize: 2, | ||
}, | ||
{ | ||
desc: `4. remove element`, | ||
insertKey: []string{key2}, | ||
insertVal: []string{val22}, | ||
deleteKey: []string{key1}, | ||
deleteVal: []string{val11}, | ||
expectXML: `key2="value22"`, | ||
expectJSON: `{"key2":"value22"}`, | ||
expectSize: 1, | ||
}, | ||
{ | ||
desc: `5. remove element again`, | ||
insertKey: []string{}, | ||
insertVal: []string{}, | ||
deleteKey: []string{key1}, | ||
deleteVal: []string{``}, | ||
expectXML: `key2="value22"`, | ||
expectJSON: `{"key2":"value22"}`, | ||
expectSize: 1, | ||
}, | ||
{ | ||
desc: `6. remove element(cleared)`, | ||
insertKey: []string{}, | ||
insertVal: []string{}, | ||
deleteKey: []string{key2}, | ||
deleteVal: []string{val22}, | ||
expectXML: ``, | ||
expectJSON: `{}`, | ||
expectSize: 0, | ||
}, | ||
{ | ||
desc: `7. remove not exist key`, | ||
insertKey: []string{}, | ||
insertVal: []string{}, | ||
deleteKey: []string{`not-exist-key`}, | ||
deleteVal: []string{``}, | ||
expectXML: ``, | ||
expectJSON: `{}`, | ||
expectSize: 0, | ||
}, | ||
} | ||
|
||
root := helper.TestRoot() | ||
ctx := helper.TextChangeContext(root) | ||
|
||
rht := crdt.NewRHT() | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.desc, func(t *testing.T) { | ||
for i, key := range tt.insertKey { | ||
rht.Set(key, tt.insertVal[i], ctx.IssueTimeTicket()) | ||
} | ||
for i, key := range tt.deleteKey { | ||
removedElement := rht.Remove(key, ctx.IssueTimeTicket()) | ||
assert.Equal(t, tt.deleteVal[i], removedElement) | ||
} | ||
assert.Equal(t, tt.expectXML, rht.ToXML()) | ||
assert.Equal(t, tt.expectJSON, rht.Marshal()) | ||
assert.Equal(t, tt.expectSize, rht.Len()) | ||
assert.Equal(t, tt.expectSize, len(rht.Nodes())) | ||
assert.Equal(t, tt.expectSize, len(rht.Elements())) | ||
}) | ||
} | ||
} |