From 84c7c67c5a4a1711f0b5d96e99c134635eb8d1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20S=C5=82apek?= <28485371+mslapek@users.noreply.github.com> Date: Sat, 20 May 2023 20:13:47 +0100 Subject: [PATCH] Add tests for stringCollectionsDeepCopy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Słapek <28485371+mslapek@users.noreply.github.com> --- pkg/chartutil/coalesce.go | 27 ------- pkg/chartutil/coalesce_test.go | 5 +- pkg/chartutil/copy.go | 44 +++++++++++ pkg/chartutil/copy_test.go | 129 +++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 31 deletions(-) create mode 100644 pkg/chartutil/copy.go create mode 100644 pkg/chartutil/copy_test.go diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go index a1cfb8ef0..c6f16585c 100644 --- a/pkg/chartutil/coalesce.go +++ b/pkg/chartutil/coalesce.go @@ -145,33 +145,6 @@ func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix st dest[GlobalKey] = dg } -func copyMap(src map[string]interface{}) map[string]interface{} { - m := make(map[string]interface{}, len(src)) - for k, v := range src { - m[k] = v - } - return m -} - -// stringCollectionsDeepCopy makes deep copy for string maps and lists. -// For other types performs shallow copy. -func stringCollectionsDeepCopy(src any) any { - switch t := src.(type) { - case map[string]any: - r := make(map[string]interface{}, len(t)) - for k, v := range t { - r[k] = stringCollectionsDeepCopy(v) - } - return r - case []string: - r := make([]string, len(t)) - copy(r, t) - return r - default: - return t - } -} - // coalesceValues builds up a values map for a particular chart. // // Values in v will override the values in the chart. diff --git a/pkg/chartutil/coalesce_test.go b/pkg/chartutil/coalesce_test.go index b1d634a5a..96eb21661 100644 --- a/pkg/chartutil/coalesce_test.go +++ b/pkg/chartutil/coalesce_test.go @@ -17,7 +17,6 @@ limitations under the License. package chartutil import ( - "bytes" "encoding/json" "fmt" "testing" @@ -69,9 +68,7 @@ func assertIsEqualToJSON(t *testing.T, name string, val any, expectedJson []byte if err != nil { t.Fatalf("JSON marshal failed: %v", err) } - if !bytes.Equal(j, expectedJson) { - t.Errorf("%s contents changed, got: %v", name, string(j)) - } + assert.Equal(t, expectedJson, j) } func TestCoalesceValues(t *testing.T) { diff --git a/pkg/chartutil/copy.go b/pkg/chartutil/copy.go new file mode 100644 index 000000000..07571744e --- /dev/null +++ b/pkg/chartutil/copy.go @@ -0,0 +1,44 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package chartutil + +func copyMap(src map[string]interface{}) map[string]interface{} { + m := make(map[string]interface{}, len(src)) + for k, v := range src { + m[k] = v + } + return m +} + +// stringCollectionsDeepCopy makes deep copy for string maps and lists. +// For other types performs shallow copy. +func stringCollectionsDeepCopy(src any) any { + switch t := src.(type) { + case map[string]any: + r := make(map[string]interface{}, len(t)) + for k, v := range t { + r[k] = stringCollectionsDeepCopy(v) + } + return r + case []string: + r := make([]string, len(t)) + copy(r, t) + return r + default: + return t + } +} diff --git a/pkg/chartutil/copy_test.go b/pkg/chartutil/copy_test.go new file mode 100644 index 000000000..9a3a49743 --- /dev/null +++ b/pkg/chartutil/copy_test.go @@ -0,0 +1,129 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package chartutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type Foo struct { + Bar int + Ipsum []string +} + +func makeExample() any { + return map[string]any{ + "ab": []string{"hello", "world"}, + "bc": "qwerty", + "cd": &Foo{1000, []string{"blue", "red"}}, + "nested": map[string]any{ + "alpha": 5, + }, + "nums": []int{3, 5, 7}, + "nums map": map[string]int{ + "a": 2, + "b": 4, + }, + } +} + +func TestStringCollectionsDeepCopy(t *testing.T) { + tests := []struct { + name string + factory func() any + }{ + { + "nil", + func() any { return nil }, + }, + { + "[]string", + func() any { return []string{"hello", "world"} }, + }, + { + "[]int", + func() any { return []int{2, 4, 6} }, + }, + { + "map[string]any", + func() any { + return map[string]any{ + "ab": []string{"hello", "world"}, + "bc": "qwerty", + } + }, + }, + { + "custom type", + func() any { + return Foo{1000, []string{"blue", "red"}} + }, + }, + { + "map[string]any with custom type", + func() any { + return map[string]any{ + "ab": []string{"hello", "world"}, + "bc": "qwerty", + "cd": Foo{1000, []string{"blue", "red"}}, + } + }, + }, + { + "complex example", makeExample, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + src := tt.factory() + got := stringCollectionsDeepCopy(src) + + assert.Equal(t, tt.factory(), got) + }) + } +} + +func TestStringCollectionsDeepCopyAliasing(t *testing.T) { + is := assert.New(t) + + src := makeExample().(map[string]any) + got := stringCollectionsDeepCopy(src).(map[string]any) + + got["ab"].([]string)[1] = "globe" + is.Equal("world", src["ab"].([]string)[1]) + + got["bc"] = "quartz" + is.Equal("qwerty", src["bc"]) + + got["nested"].(map[string]any)["alpha"] = "9" + is.Equal(5, src["nested"].(map[string]any)["alpha"]) + + // notice, that Foo is not deep copied + got["cd"].(*Foo).Bar = 500 + got["cd"].(*Foo).Ipsum[0] = "green" + is.Equal(500, src["cd"].(*Foo).Bar) + is.Equal("green", src["cd"].(*Foo).Ipsum[0]) + + // non-any collections are not deep copied + got["nums"].([]int)[1] = 10 + is.Equal(10, src["nums"].([]int)[1]) + + got["nums map"].(map[string]int)["b"] = 10 + is.Equal(10, src["nums map"].(map[string]int)["b"]) +}