github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/xact/xs/utils_test.go (about)

     1  // Package xs_test - basic list-concatenation unit tests.
     2  /*
     3   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package xs_test
     6  
     7  import (
     8  	"testing"
     9  
    10  	"github.com/NVIDIA/aistore/cmn"
    11  	"github.com/NVIDIA/aistore/tools/tassert"
    12  	"github.com/NVIDIA/aistore/tools/trand"
    13  )
    14  
    15  func TestConcatObjLists(t *testing.T) {
    16  	if testing.Short() {
    17  		t.Skipf("skipping %s in short mode", t.Name())
    18  	}
    19  	tests := []struct {
    20  		name      string
    21  		objCounts []int
    22  		maxSize   int
    23  		token     bool
    24  	}{
    25  		// * `st` stands for "single target"
    26  		{name: "st/all", objCounts: []int{10}, maxSize: 0, token: false},
    27  		{name: "st/half", objCounts: []int{10}, maxSize: 5, token: true},
    28  		{name: "st/all_with_marker", objCounts: []int{10}, maxSize: 10, token: true},
    29  		{name: "st/more_than_all", objCounts: []int{10}, maxSize: 11, token: false},
    30  
    31  		// * `mt` stands for "multiple targets"
    32  		// * `one` stands for "one target has objects"
    33  		{name: "mt/one/all", objCounts: []int{0, 0, 10}, maxSize: 0, token: false},
    34  		{name: "mt/one/half", objCounts: []int{0, 0, 10}, maxSize: 5, token: true},
    35  		{name: "mt/one/all_with_marker", objCounts: []int{0, 0, 10}, maxSize: 10, token: true},
    36  		{name: "mt/one/more_than_all", objCounts: []int{0, 0, 10}, maxSize: 11, token: false},
    37  
    38  		// * `mt` stands for "multiple targets"
    39  		// * `more` stands for "more than one target has objects"
    40  		{name: "mt/more/all", objCounts: []int{5, 1, 4, 10}, maxSize: 0, token: false},
    41  		{name: "mt/more/half", objCounts: []int{5, 1, 4, 10}, maxSize: 10, token: true},
    42  		{name: "mt/more/all_with_marker", objCounts: []int{5, 1, 4, 10}, maxSize: 20, token: true},
    43  		{name: "mt/more/more_than_all", objCounts: []int{5, 1, 4, 10}, maxSize: 21, token: false},
    44  	}
    45  
    46  	for _, test := range tests {
    47  		t.Run(test.name, func(t *testing.T) {
    48  			var (
    49  				lists          = make([]*cmn.LsoRes, 0, len(test.objCounts))
    50  				expectedObjCnt = 0
    51  			)
    52  			for _, objCount := range test.objCounts {
    53  				list := &cmn.LsoRes{}
    54  				for range objCount {
    55  					list.Entries = append(list.Entries, &cmn.LsoEnt{
    56  						Name: trand.String(5),
    57  					})
    58  				}
    59  				lists = append(lists, list)
    60  				expectedObjCnt += len(list.Entries)
    61  			}
    62  			expectedObjCnt = min(expectedObjCnt, test.maxSize)
    63  
    64  			objs := concatLso(lists, test.maxSize)
    65  			tassert.Errorf(
    66  				t, test.maxSize == 0 || len(objs.Entries) == expectedObjCnt,
    67  				"number of objects (%d) is different from expected (%d)", len(objs.Entries), expectedObjCnt,
    68  			)
    69  			tassert.Errorf(
    70  				t, (objs.ContinuationToken != "") == test.token,
    71  				"continuation token expected to be set=%t", test.token,
    72  			)
    73  		})
    74  	}
    75  }
    76  
    77  // concatLso takes a slice of object lists and concatenates them: all lists
    78  // are appended to the first one.
    79  // If maxSize is greater than 0, the resulting list is sorted and truncated. Zero
    80  // or negative maxSize means returning all objects.
    81  func concatLso(lists []*cmn.LsoRes, maxSize int) (objs *cmn.LsoRes) {
    82  	if len(lists) == 0 {
    83  		return &cmn.LsoRes{}
    84  	}
    85  
    86  	objs = &cmn.LsoRes{}
    87  	objs.Entries = make(cmn.LsoEntries, 0)
    88  
    89  	for _, l := range lists {
    90  		objs.Flags |= l.Flags
    91  		objs.Entries = append(objs.Entries, l.Entries...)
    92  	}
    93  
    94  	if len(objs.Entries) == 0 {
    95  		return objs
    96  	}
    97  
    98  	// For corner case: we have objects with replicas on page threshold
    99  	// we have to sort taking status into account. Otherwise wrong
   100  	// one(Status=moved) may get into the response
   101  	cmn.SortLso(objs.Entries)
   102  
   103  	// Remove duplicates
   104  	objs.Entries = cmn.DedupLso(objs.Entries, maxSize)
   105  	l := len(objs.Entries)
   106  	if maxSize > 0 && l >= maxSize {
   107  		objs.ContinuationToken = objs.Entries[l-1].Name
   108  	}
   109  	return
   110  }