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  }