mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-02 07:52:05 +01:00
217 lines
5.0 KiB
Go
217 lines
5.0 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"tailscale.com/cmd/cloner/clonerex"
|
|
)
|
|
|
|
func TestSliceContainer(t *testing.T) {
|
|
num := 5
|
|
examples := []struct {
|
|
name string
|
|
in *clonerex.SliceContainer
|
|
}{
|
|
{
|
|
name: "nil",
|
|
in: nil,
|
|
},
|
|
{
|
|
name: "zero",
|
|
in: &clonerex.SliceContainer{},
|
|
},
|
|
{
|
|
name: "empty",
|
|
in: &clonerex.SliceContainer{
|
|
Slice: []*int{},
|
|
},
|
|
},
|
|
{
|
|
name: "nils",
|
|
in: &clonerex.SliceContainer{
|
|
Slice: []*int{nil, nil, nil, nil, nil},
|
|
},
|
|
},
|
|
{
|
|
name: "one",
|
|
in: &clonerex.SliceContainer{
|
|
Slice: []*int{&num},
|
|
},
|
|
},
|
|
{
|
|
name: "several",
|
|
in: &clonerex.SliceContainer{
|
|
Slice: []*int{&num, &num, &num, &num, &num},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, ex := range examples {
|
|
t.Run(ex.name, func(t *testing.T) {
|
|
out := ex.in.Clone()
|
|
if !reflect.DeepEqual(ex.in, out) {
|
|
t.Errorf("Clone() = %v, want %v", out, ex.in)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInterfaceContainer(t *testing.T) {
|
|
examples := []struct {
|
|
name string
|
|
in *clonerex.InterfaceContainer
|
|
}{
|
|
{
|
|
name: "nil",
|
|
in: nil,
|
|
},
|
|
{
|
|
name: "zero",
|
|
in: &clonerex.InterfaceContainer{},
|
|
},
|
|
{
|
|
name: "with_interface",
|
|
in: &clonerex.InterfaceContainer{
|
|
Interface: &clonerex.CloneableImpl{Value: 42},
|
|
},
|
|
},
|
|
{
|
|
name: "with_nil_interface",
|
|
in: &clonerex.InterfaceContainer{
|
|
Interface: nil,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, ex := range examples {
|
|
t.Run(ex.name, func(t *testing.T) {
|
|
out := ex.in.Clone()
|
|
if !reflect.DeepEqual(ex.in, out) {
|
|
t.Errorf("Clone() = %v, want %v", out, ex.in)
|
|
}
|
|
|
|
// Verify no aliasing: modifying the clone should not affect the original
|
|
if ex.in != nil && ex.in.Interface != nil {
|
|
if impl, ok := out.Interface.(*clonerex.CloneableImpl); ok {
|
|
impl.Value = 999
|
|
if origImpl, ok := ex.in.Interface.(*clonerex.CloneableImpl); ok {
|
|
if origImpl.Value == 999 {
|
|
t.Errorf("Clone() aliased memory with original")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMapWithPointers(t *testing.T) {
|
|
num1, num2 := 42, 100
|
|
orig := &clonerex.MapWithPointers{
|
|
Nested: map[string]*int{
|
|
"foo": &num1,
|
|
"bar": &num2,
|
|
},
|
|
WithCloneMethod: map[string]*clonerex.SliceContainer{
|
|
"container1": {Slice: []*int{&num1, &num2}},
|
|
"container2": {Slice: []*int{&num1}},
|
|
},
|
|
CloneInterface: map[string]clonerex.Cloneable{
|
|
"impl1": &clonerex.CloneableImpl{Value: 123},
|
|
"impl2": &clonerex.CloneableImpl{Value: 456},
|
|
},
|
|
}
|
|
|
|
cloned := orig.Clone()
|
|
if !reflect.DeepEqual(orig, cloned) {
|
|
t.Errorf("Clone() = %v, want %v", cloned, orig)
|
|
}
|
|
|
|
// Mutate cloned.Nested pointer values
|
|
*cloned.Nested["foo"] = 999
|
|
if *orig.Nested["foo"] == 999 {
|
|
t.Errorf("Clone() aliased memory in Nested: original was modified")
|
|
}
|
|
|
|
// Mutate cloned.WithCloneMethod slice values
|
|
*cloned.WithCloneMethod["container1"].Slice[0] = 888
|
|
if *orig.WithCloneMethod["container1"].Slice[0] == 888 {
|
|
t.Errorf("Clone() aliased memory in WithCloneMethod: original was modified")
|
|
}
|
|
|
|
// Mutate cloned.CloneInterface values
|
|
if impl, ok := cloned.CloneInterface["impl1"].(*clonerex.CloneableImpl); ok {
|
|
impl.Value = 777
|
|
if origImpl, ok := orig.CloneInterface["impl1"].(*clonerex.CloneableImpl); ok {
|
|
if origImpl.Value == 777 {
|
|
t.Errorf("Clone() aliased memory in CloneInterface: original was modified")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDeeplyNestedMap(t *testing.T) {
|
|
num := 123
|
|
orig := &clonerex.DeeplyNestedMap{
|
|
ThreeLevels: map[string]map[string]map[string]int{
|
|
"a": {
|
|
"b": {"c": 1, "d": 2},
|
|
"e": {"f": 3},
|
|
},
|
|
"g": {
|
|
"h": {"i": 4},
|
|
},
|
|
},
|
|
FourLevels: map[string]map[string]map[string]map[string]*clonerex.SliceContainer{
|
|
"l1a": {
|
|
"l2a": {
|
|
"l3a": {
|
|
"l4a": {Slice: []*int{&num}},
|
|
"l4b": {Slice: []*int{&num, &num}},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
cloned := orig.Clone()
|
|
if !reflect.DeepEqual(orig, cloned) {
|
|
t.Errorf("Clone() = %v, want %v", cloned, orig)
|
|
}
|
|
|
|
// Mutate the clone's ThreeLevels map
|
|
cloned.ThreeLevels["a"]["b"]["c"] = 777
|
|
if orig.ThreeLevels["a"]["b"]["c"] == 777 {
|
|
t.Errorf("Clone() aliased memory in ThreeLevels: original was modified")
|
|
}
|
|
|
|
// Mutate the clone's FourLevels map at the deepest pointer level
|
|
*cloned.FourLevels["l1a"]["l2a"]["l3a"]["l4a"].Slice[0] = 666
|
|
if *orig.FourLevels["l1a"]["l2a"]["l3a"]["l4a"].Slice[0] == 666 {
|
|
t.Errorf("Clone() aliased memory in FourLevels: original was modified")
|
|
}
|
|
|
|
// Add a new top-level key to the clone's FourLevels map
|
|
newNum := 999
|
|
cloned.FourLevels["l1b"] = map[string]map[string]map[string]*clonerex.SliceContainer{
|
|
"l2b": {
|
|
"l3b": {
|
|
"l4c": {Slice: []*int{&newNum}},
|
|
},
|
|
},
|
|
}
|
|
if _, exists := orig.FourLevels["l1b"]; exists {
|
|
t.Errorf("Clone() aliased FourLevels map: new top-level key appeared in original")
|
|
}
|
|
|
|
// Add a new nested key to the clone's FourLevels map
|
|
cloned.FourLevels["l1a"]["l2a"]["l3a"]["l4c"] = &clonerex.SliceContainer{Slice: []*int{&newNum}}
|
|
if _, exists := orig.FourLevels["l1a"]["l2a"]["l3a"]["l4c"]; exists {
|
|
t.Errorf("Clone() aliased FourLevels map: new nested key appeared in original")
|
|
}
|
|
}
|