github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/ext/dload/diff_resolver_test.go (about) 1 // Package dloader_test is a unit test 2 /* 3 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package dload_test 6 7 import ( 8 "testing" 9 10 "github.com/NVIDIA/aistore/cmn/debug" 11 "github.com/NVIDIA/aistore/core" 12 "github.com/NVIDIA/aistore/ext/dload" 13 "github.com/NVIDIA/aistore/tools/tassert" 14 ) 15 16 const ( 17 fromRemoteFQN = "remote" 18 ) 19 20 type ( 21 mockDiffResolverCtx struct{} 22 23 obj struct { 24 name string 25 remote bool 26 } 27 28 testCase struct { 29 name string 30 src []obj 31 dst []obj 32 expected []dload.DiffResolverResult 33 } 34 ) 35 36 func (*mockDiffResolverCtx) CompareObjects(*core.LOM, *dload.DstElement) (bool, error) { 37 return true, nil 38 } 39 40 func (*mockDiffResolverCtx) IsObjFromRemote(lom *core.LOM) (bool, error) { 41 return lom.FQN == fromRemoteFQN, nil 42 } 43 44 func TestDiffResolver(t *testing.T) { 45 tests := []testCase{ 46 { 47 name: "empty", 48 src: []obj{}, 49 dst: []obj{}, 50 expected: []dload.DiffResolverResult{{Action: dload.DiffResolverEOF}}, 51 }, 52 { 53 name: "all_send", 54 src: []obj{{name: "a"}, {name: "b"}, {name: "c"}}, 55 dst: []obj{}, 56 expected: []dload.DiffResolverResult{ 57 {Action: dload.DiffResolverSend}, 58 {Action: dload.DiffResolverSend}, 59 {Action: dload.DiffResolverSend}, 60 {Action: dload.DiffResolverEOF}, 61 }, 62 }, 63 { 64 name: "all_recv", 65 src: []obj{}, 66 dst: []obj{{name: "a"}, {name: "b"}, {name: "c"}}, 67 expected: []dload.DiffResolverResult{ 68 {Action: dload.DiffResolverRecv}, 69 {Action: dload.DiffResolverRecv}, 70 {Action: dload.DiffResolverRecv}, 71 {Action: dload.DiffResolverEOF}, 72 }, 73 }, 74 { 75 name: "mixed_send_recv", 76 src: []obj{{name: "a"}, {name: "c"}}, 77 dst: []obj{{name: "b"}, {name: "d"}}, 78 expected: []dload.DiffResolverResult{ 79 {Action: dload.DiffResolverSend}, 80 {Action: dload.DiffResolverRecv}, 81 {Action: dload.DiffResolverSend}, 82 {Action: dload.DiffResolverRecv}, 83 {Action: dload.DiffResolverEOF}, 84 }, 85 }, 86 { 87 name: "all_send_then_all_recv", 88 src: []obj{{name: "a"}, {name: "b"}, {name: "c"}}, 89 dst: []obj{{name: "d"}, {name: "e"}}, 90 expected: []dload.DiffResolverResult{ 91 {Action: dload.DiffResolverSend}, 92 {Action: dload.DiffResolverSend}, 93 {Action: dload.DiffResolverSend}, 94 {Action: dload.DiffResolverRecv}, 95 {Action: dload.DiffResolverRecv}, 96 {Action: dload.DiffResolverEOF}, 97 }, 98 }, 99 { 100 name: "all_recv_then_all_send", 101 src: []obj{{name: "d"}, {name: "e"}}, 102 dst: []obj{{name: "a"}, {name: "b"}, {name: "c"}}, 103 expected: []dload.DiffResolverResult{ 104 {Action: dload.DiffResolverRecv}, 105 {Action: dload.DiffResolverRecv}, 106 {Action: dload.DiffResolverRecv}, 107 {Action: dload.DiffResolverSend}, 108 {Action: dload.DiffResolverSend}, 109 {Action: dload.DiffResolverEOF}, 110 }, 111 }, 112 { 113 name: "all_delete", 114 src: []obj{{name: "a", remote: true}, {name: "b", remote: true}}, 115 dst: []obj{}, 116 expected: []dload.DiffResolverResult{ 117 {Action: dload.DiffResolverDelete}, 118 {Action: dload.DiffResolverDelete}, 119 {Action: dload.DiffResolverEOF}, 120 }, 121 }, 122 { 123 name: "mixed_send_delete", 124 src: []obj{{name: "a"}, {name: "b", remote: true}, {name: "c"}, {name: "d", remote: true}}, 125 dst: []obj{}, 126 expected: []dload.DiffResolverResult{ 127 {Action: dload.DiffResolverSend}, 128 {Action: dload.DiffResolverDelete}, 129 {Action: dload.DiffResolverSend}, 130 {Action: dload.DiffResolverDelete}, 131 {Action: dload.DiffResolverEOF}, 132 }, 133 }, 134 { 135 name: "all_skip_then_all_recv", 136 src: []obj{{name: "a", remote: true}, {name: "b", remote: true}}, 137 dst: []obj{{name: "a"}, {name: "b"}, {name: "c"}, {name: "d"}}, 138 expected: []dload.DiffResolverResult{ 139 {Action: dload.DiffResolverSkip}, 140 {Action: dload.DiffResolverSkip}, 141 {Action: dload.DiffResolverRecv}, 142 {Action: dload.DiffResolverRecv}, 143 {Action: dload.DiffResolverEOF}, 144 }, 145 }, 146 } 147 148 for _, test := range tests { 149 t.Run(test.name, func(t *testing.T) { 150 ctx := &mockDiffResolverCtx{} 151 dr := dload.NewDiffResolver(ctx) 152 go dr.Start() 153 for _, s := range test.src { 154 lom := &core.LOM{ObjName: s.name} 155 if s.remote { 156 lom.FQN = fromRemoteFQN 157 } 158 dr.PushSrc(lom) 159 } 160 dr.CloseSrc() 161 for _, d := range test.dst { 162 dr.PushDst(&dload.BackendResource{ObjName: d.name}) 163 } 164 dr.CloseDst() 165 166 for i := range len(test.expected) { 167 result, err := dr.Next() 168 tassert.CheckFatal(t, err) 169 170 expectedResult := test.expected[i] 171 tassert.Errorf( 172 t, result.Action == expectedResult.Action, 173 "actions differ: (got: %d, expected: %d)", result.Action, expectedResult.Action, 174 ) 175 tassert.Fatalf(t, result.Err == nil, "error has been set") 176 switch result.Action { 177 case dload.DiffResolverRecv: 178 tassert.Errorf(t, result.Dst != nil, "destination has not been set for recv") 179 case dload.DiffResolverSend: 180 tassert.Errorf(t, result.Src != nil, "source has not been set for send") 181 case dload.DiffResolverDelete: 182 tassert.Errorf(t, result.Src != nil, "source has not been set for delete") 183 case dload.DiffResolverSkip: 184 tassert.Errorf(t, result.Src != nil, "source has not been set for skip") 185 tassert.Errorf(t, result.Dst != nil, "destination has not been set for skip") 186 case dload.DiffResolverEOF: 187 default: 188 debug.Assertf(false, "invalid diff-resolver action %d", result.Action) 189 } 190 } 191 192 // Check that EOF is followed by EOF. 193 for range 2 { 194 result, err := dr.Next() 195 tassert.CheckFatal(t, err) 196 tassert.Errorf(t, result.Action == dload.DiffResolverEOF, "eof not followed by eof") 197 } 198 }) 199 } 200 }