git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/netmap/yml_test.go (about) 1 package netmap 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "os" 8 "path/filepath" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 "gopkg.in/yaml.v3" 13 ) 14 15 // TestCase represents collection of placement policy tests for a single node set. 16 type TestCase struct { 17 Name string `json:"name" yaml:"name"` 18 Nodes []NodeInfo `json:"nodes" yaml:"nodes"` 19 Tests map[string]struct { 20 Policy PlacementPolicy 21 Pivot Base64 22 Result [][]int `json:"result,omitempty" yaml:"result,omitempty"` 23 Error string `json:"error,omitempty" yaml:"error,omitempty"` 24 Placement struct { 25 Pivot Base64 `json:"pivot" yaml:"pivot"` 26 Result [][]int `json:"result,omitempty" yaml:"result,omitempty"` 27 } `json:"placement,omitempty" yaml:"placement,omitempty"` 28 } 29 } 30 31 // Base64 is a type that will hold the decoded Base64 data. 32 type Base64 []byte 33 34 func (b *Base64) UnmarshalYAML(unmarshal func(interface{}) error) error { 35 var base64Str string 36 if err := unmarshal(&base64Str); err != nil { 37 return err 38 } 39 decodedBytes, err := base64.StdEncoding.DecodeString(base64Str) 40 if err != nil { 41 return err 42 } 43 *b = decodedBytes 44 return nil 45 } 46 47 var _, _ json.Unmarshaler = new(NodeInfo), new(PlacementPolicy) 48 49 func compareNodes(t testing.TB, expected [][]int, nodes nodes, actual [][]NodeInfo) { 50 require.Equal(t, len(expected), len(actual)) 51 for i := range expected { 52 require.Equal(t, len(expected[i]), len(actual[i])) 53 for j, index := range expected[i] { 54 require.Equal(t, nodes[index], actual[i][j]) 55 } 56 } 57 } 58 59 func TestPlacementPolicy_Interopability(t *testing.T) { 60 const testsDir = "./yml_tests" 61 62 f, err := os.Open(testsDir) 63 require.NoError(t, err) 64 65 ds, err := f.ReadDir(0) 66 require.NoError(t, err) 67 68 for i := range ds { 69 filename := filepath.Join(testsDir, ds[i].Name()) 70 bs, err := os.ReadFile(filename) 71 require.NoError(t, err) 72 73 var tc TestCase 74 require.NoError(t, yaml.Unmarshal(bs, &tc), "cannot unmarshal %s", ds[i].Name()) 75 76 srcNodes := make([]NodeInfo, len(tc.Nodes)) 77 copy(srcNodes, tc.Nodes) 78 79 t.Run(fmt.Sprintf("%s:%s", filename, tc.Name), func(t *testing.T) { 80 var nm NetMap 81 nm.SetNodes(tc.Nodes) 82 83 for name, tt := range tc.Tests { 84 t.Run(name, func(t *testing.T) { 85 v, err := nm.ContainerNodes(tt.Policy, tt.Pivot) 86 if tt.Result == nil { 87 require.Error(t, err) 88 require.Contains(t, err.Error(), tt.Error) 89 } else { 90 require.NoError(t, err) 91 require.Equal(t, srcNodes, tc.Nodes) 92 93 compareNodes(t, tt.Result, tc.Nodes, v) 94 95 if tt.Placement.Result != nil { 96 res, err := nm.PlacementVectors(v, tt.Placement.Pivot) 97 require.NoError(t, err) 98 compareNodes(t, tt.Placement.Result, tc.Nodes, res) 99 require.Equal(t, srcNodes, tc.Nodes) 100 } 101 } 102 }) 103 } 104 }) 105 } 106 } 107 108 func BenchmarkPlacementPolicyInteropability(b *testing.B) { 109 const testsDir = "./yml_tests" 110 111 f, err := os.Open(testsDir) 112 require.NoError(b, err) 113 114 ds, err := f.ReadDir(0) 115 require.NoError(b, err) 116 117 for i := range ds { 118 bs, err := os.ReadFile(filepath.Join(testsDir, ds[i].Name())) 119 require.NoError(b, err) 120 121 var tc TestCase 122 require.NoError(b, yaml.Unmarshal(bs, &tc), "cannot unmarshal %s", ds[i].Name()) 123 124 b.Run(tc.Name, func(b *testing.B) { 125 var nm NetMap 126 nm.SetNodes(tc.Nodes) 127 require.NoError(b, err) 128 129 for name, tt := range tc.Tests { 130 b.Run(name, func(b *testing.B) { 131 b.ReportAllocs() 132 b.ResetTimer() 133 for range b.N { 134 b.StartTimer() 135 v, err := nm.ContainerNodes(tt.Policy, tt.Pivot) 136 b.StopTimer() 137 if tt.Result == nil { 138 require.Error(b, err) 139 require.Contains(b, err.Error(), tt.Error) 140 } else { 141 require.NoError(b, err) 142 143 compareNodes(b, tt.Result, tc.Nodes, v) 144 145 if tt.Placement.Result != nil { 146 b.StartTimer() 147 res, err := nm.PlacementVectors(v, tt.Placement.Pivot) 148 b.StopTimer() 149 require.NoError(b, err) 150 compareNodes(b, tt.Placement.Result, tc.Nodes, res) 151 } 152 } 153 } 154 }) 155 } 156 }) 157 } 158 } 159 160 func BenchmarkManySelects(b *testing.B) { 161 testsFile := filepath.Join("yml_tests", "many_selects.yml") 162 bs, err := os.ReadFile(testsFile) 163 require.NoError(b, err) 164 165 var tc TestCase 166 require.NoError(b, yaml.Unmarshal(bs, &tc)) 167 tt, ok := tc.Tests["Select"] 168 require.True(b, ok) 169 170 var nm NetMap 171 nm.SetNodes(tc.Nodes) 172 173 b.ResetTimer() 174 b.ReportAllocs() 175 176 for range b.N { 177 _, err = nm.ContainerNodes(tt.Policy, tt.Pivot) 178 if err != nil { 179 b.FailNow() 180 } 181 } 182 }