blob: 8200ae7fcb01de22395257c7a37e9a8a5f598bf8 [file] [log] [blame]
khenaidoo6e55d9e2019-12-12 18:26:26 -05001/*
2* Copyright 2019-present Open Networking Foundation
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15 */
16package core
17
18import (
19 "context"
20 "github.com/gogo/protobuf/proto"
21 "github.com/opencord/voltha-go/rw_core/config"
22 com "github.com/opencord/voltha-lib-go/v2/pkg/adapters/common"
23 "github.com/opencord/voltha-lib-go/v2/pkg/kafka"
24 "github.com/opencord/voltha-lib-go/v2/pkg/log"
25 lm "github.com/opencord/voltha-lib-go/v2/pkg/mocks"
khenaidoob2121e52019-12-16 17:17:22 -050026 ofp "github.com/opencord/voltha-protos/v2/go/openflow_13"
khenaidoo6e55d9e2019-12-12 18:26:26 -050027 "github.com/opencord/voltha-protos/v2/go/voltha"
28 "github.com/phayes/freeport"
29 "github.com/stretchr/testify/assert"
30 "math/rand"
khenaidoob2121e52019-12-16 17:17:22 -050031 "sort"
khenaidoo6e55d9e2019-12-12 18:26:26 -050032 "strings"
33 "sync"
34 "testing"
35 "time"
36)
37
38type DATest struct {
39 etcdServer *lm.EtcdServer
40 core *Core
41 kClient kafka.Client
42 kvClientPort int
43 oltAdapterName string
44 onuAdapterName string
45 coreInstanceID string
46 defaultTimeout time.Duration
47 maxTimeout time.Duration
48 device *voltha.Device
49 done chan int
50}
51
52func newDATest() *DATest {
53 test := &DATest{}
54 // Start the embedded etcd server
55 var err error
56 test.etcdServer, test.kvClientPort, err = startEmbeddedEtcdServer("voltha.rwcore.da.test", "voltha.rwcore.da.etcd", "error")
57 if err != nil {
58 log.Fatal(err)
59 }
60 // Create the kafka client
61 test.kClient = lm.NewKafkaClient()
62 test.oltAdapterName = "olt_adapter_mock"
63 test.onuAdapterName = "onu_adapter_mock"
64 test.coreInstanceID = "rw-da-test"
65 test.defaultTimeout = 5 * time.Second
66 test.maxTimeout = 20 * time.Second
67 test.done = make(chan int)
68 parentID := com.GetRandomString(10)
69 test.device = &voltha.Device{
70 Type: "onu_adapter_mock",
71 ParentId: parentID,
72 ParentPortNo: 1,
73 VendorId: "onu_adapter_mock",
74 Adapter: "onu_adapter_mock",
75 Vlan: 100,
76 Address: nil,
77 ProxyAddress: &voltha.Device_ProxyAddress{
78 DeviceId: parentID,
79 DeviceType: "olt_adapter_mock",
80 ChannelId: 100,
81 ChannelGroupId: 0,
82 ChannelTermination: "",
83 OnuId: 2,
84 },
85 AdminState: voltha.AdminState_PREPROVISIONED,
86 OperStatus: voltha.OperStatus_UNKNOWN,
87 Reason: "All good",
88 ConnectStatus: voltha.ConnectStatus_UNKNOWN,
89 Custom: nil,
90 Ports: []*voltha.Port{
91 {PortNo: 1, Label: "pon-1", Type: voltha.Port_PON_ONU, AdminState: voltha.AdminState_ENABLED,
92 OperStatus: voltha.OperStatus_ACTIVE, Peers: []*voltha.Port_PeerPort{{DeviceId: parentID, PortNo: 1}}},
93 {PortNo: 100, Label: "uni-100", Type: voltha.Port_ETHERNET_UNI, AdminState: voltha.AdminState_ENABLED,
94 OperStatus: voltha.OperStatus_ACTIVE},
95 },
96 }
97
98 return test
99}
100
101func (dat *DATest) startCore(inCompeteMode bool) {
102 cfg := config.NewRWCoreFlags()
103 cfg.CorePairTopic = "rw_core"
104 cfg.DefaultRequestTimeout = dat.defaultTimeout.Nanoseconds() / 1000000 //TODO: change when Core changes to Duration
105 cfg.KVStorePort = dat.kvClientPort
106 cfg.InCompetingMode = inCompeteMode
107 grpcPort, err := freeport.GetFreePort()
108 if err != nil {
109 log.Fatal("Cannot get a freeport for grpc")
110 }
111 cfg.GrpcPort = grpcPort
112 cfg.GrpcHost = "127.0.0.1"
113 setCoreCompeteMode(inCompeteMode)
114 client := setupKVClient(cfg, dat.coreInstanceID)
115 dat.core = NewCore(dat.coreInstanceID, cfg, client, dat.kClient)
116 dat.core.Start(context.Background())
117}
118
119func (dat *DATest) stopAll() {
120 if dat.kClient != nil {
121 dat.kClient.Stop()
122 }
123 if dat.core != nil {
124 dat.core.Stop(context.Background())
125 }
126 if dat.etcdServer != nil {
127 stopEmbeddedEtcdServer(dat.etcdServer)
128 }
129}
130
131func (dat *DATest) createDeviceAgent(t *testing.T) *DeviceAgent {
132 deviceMgr := dat.core.deviceMgr
133 clonedDevice := proto.Clone(dat.device).(*voltha.Device)
134 deviceAgent := newDeviceAgent(deviceMgr.adapterProxy, clonedDevice, deviceMgr, deviceMgr.clusterDataProxy, deviceMgr.defaultTimeout)
135 d, err := deviceAgent.start(context.TODO(), clonedDevice)
136 assert.Nil(t, err)
137 assert.NotNil(t, d)
138 deviceMgr.addDeviceAgentToMap(deviceAgent)
139 return deviceAgent
140}
141
142func (dat *DATest) updateDeviceConcurrently(t *testing.T, da *DeviceAgent, globalWG *sync.WaitGroup) {
143 originalDevice := da.getDevice()
144 assert.NotNil(t, originalDevice)
145 var localWG sync.WaitGroup
146
147 // Update device routine
148 var (
149 root = false
150 vendor = "onu_adapter_mock"
151 model = "go-mock"
152 serialNumber = com.GetRandomSerialNumber()
153 macAddress = strings.ToUpper(com.GetRandomMacAddress())
154 vlan = rand.Uint32()
155 reason = "testing concurrent device update"
156 portToAdd = &voltha.Port{PortNo: 101, Label: "uni-101", Type: voltha.Port_ETHERNET_UNI, AdminState: voltha.AdminState_ENABLED,
157 OperStatus: voltha.OperStatus_ACTIVE}
158 )
159 localWG.Add(1)
160 go func() {
161 deviceToUpdate := proto.Clone(originalDevice).(*voltha.Device)
162 deviceToUpdate.Root = root
163 deviceToUpdate.Vendor = vendor
164 deviceToUpdate.Model = model
165 deviceToUpdate.SerialNumber = serialNumber
166 deviceToUpdate.MacAddress = macAddress
167 deviceToUpdate.Vlan = vlan
168 deviceToUpdate.Reason = reason
169 err := da.updateDeviceUsingAdapterData(deviceToUpdate)
170 assert.Nil(t, err)
171 localWG.Done()
172 }()
173
174 // Update the device status routine
175 localWG.Add(1)
176 go func() {
177 err := da.updateDeviceStatus(voltha.OperStatus_ACTIVE, voltha.ConnectStatus_REACHABLE)
178 assert.Nil(t, err)
179 localWG.Done()
180 }()
181
182 // Add a port routine
183 localWG.Add(1)
184 go func() {
185 err := da.addPort(portToAdd)
186 assert.Nil(t, err)
187 localWG.Done()
188 }()
189
190 // wait for go routines to be done
191 localWG.Wait()
192
193 expectedChange := proto.Clone(originalDevice).(*voltha.Device)
194 expectedChange.OperStatus = voltha.OperStatus_ACTIVE
195 expectedChange.ConnectStatus = voltha.ConnectStatus_REACHABLE
196 expectedChange.Ports = append(expectedChange.Ports, portToAdd)
197 expectedChange.Root = root
198 expectedChange.Vendor = vendor
199 expectedChange.Model = model
200 expectedChange.SerialNumber = serialNumber
201 expectedChange.MacAddress = macAddress
202 expectedChange.Vlan = vlan
203 expectedChange.Reason = reason
204
205 updatedDevice := da.getDevice()
206 assert.NotNil(t, updatedDevice)
207 assert.True(t, proto.Equal(expectedChange, updatedDevice))
208
209 globalWG.Done()
210}
211
212func TestConcurrentDevices(t *testing.T) {
213 da := newDATest()
214 assert.NotNil(t, da)
215 defer da.stopAll()
216
217 // Start the Core
218 da.startCore(false)
219
220 var wg sync.WaitGroup
221 numConCurrentDeviceAgents := 20
222 for i := 0; i < numConCurrentDeviceAgents; i++ {
223 wg.Add(1)
224 a := da.createDeviceAgent(t)
225 go da.updateDeviceConcurrently(t, a, &wg)
226 }
227
228 wg.Wait()
229}
khenaidoob2121e52019-12-16 17:17:22 -0500230
231func isFlowSliceEqual(a, b []*ofp.OfpFlowStats) bool {
232 if len(a) != len(b) {
233 return false
234 }
235 sort.Slice(a, func(i, j int) bool {
236 return a[i].Id < a[j].Id
237 })
238 sort.Slice(b, func(i, j int) bool {
239 return b[i].Id < b[j].Id
240 })
241 for idx := range a {
242 if !proto.Equal(a[idx], b[idx]) {
243 return false
244 }
245 }
246 return true
247}
248
249func isGroupSliceEqual(a, b []*ofp.OfpGroupEntry) bool {
250 if len(a) != len(b) {
251 return false
252 }
253 sort.Slice(a, func(i, j int) bool {
254 return a[i].Desc.GroupId < a[j].Desc.GroupId
255 })
256 sort.Slice(b, func(i, j int) bool {
257 return b[i].Desc.GroupId < b[j].Desc.GroupId
258 })
259 for idx := range a {
260 if !proto.Equal(a[idx], b[idx]) {
261 return false
262 }
263 }
264 return true
265}
266
267func TestFlowsToUpdateToDelete_EmptySlices(t *testing.T) {
268 newFlows := []*ofp.OfpFlowStats{}
269 existingFlows := []*ofp.OfpFlowStats{}
270 expectedNewFlows := []*ofp.OfpFlowStats{}
271 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
272 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{}
273 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
274 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
275 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
276 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
277}
278
279func TestFlowsToUpdateToDelete_NoExistingFlows(t *testing.T) {
280 newFlows := []*ofp.OfpFlowStats{
281 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
282 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
283 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
284 }
285 existingFlows := []*ofp.OfpFlowStats{}
286 expectedNewFlows := []*ofp.OfpFlowStats{
287 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
288 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
289 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
290 }
291 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
292 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
293 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
294 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
295 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
296 }
297 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
298 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
299 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
300 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
301}
302
303func TestFlowsToUpdateToDelete_UpdateNoDelete(t *testing.T) {
304 newFlows := []*ofp.OfpFlowStats{
305 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
306 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
307 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
308 }
309 existingFlows := []*ofp.OfpFlowStats{
310 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
311 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
312 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
313 }
314 expectedNewFlows := []*ofp.OfpFlowStats{
315 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
316 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
317 }
318 expectedFlowsToDelete := []*ofp.OfpFlowStats{}
319 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
320 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
321 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
322 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
323 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
324 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
325 }
326 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
327 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
328 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
329 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
330}
331
332func TestFlowsToUpdateToDelete_UpdateAndDelete(t *testing.T) {
333 newFlows := []*ofp.OfpFlowStats{
334 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
335 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
336 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
337 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
338 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
339 }
340 existingFlows := []*ofp.OfpFlowStats{
341 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
342 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
343 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
344 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
345 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
346 }
347 expectedNewFlows := []*ofp.OfpFlowStats{
348 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
349 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
350 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
351 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
352 }
353 expectedFlowsToDelete := []*ofp.OfpFlowStats{
354 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 0},
355 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1250000, PacketCount: 0},
356 }
357 expectedUpdatedAllFlows := []*ofp.OfpFlowStats{
358 {Id: 121, TableId: 1210, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1210000, PacketCount: 0},
359 {Id: 122, TableId: 1220, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1220000, PacketCount: 0},
360 {Id: 123, TableId: 1230, Priority: 100, IdleTimeout: 0, Flags: 0, Cookie: 1230000, PacketCount: 20},
361 {Id: 124, TableId: 1240, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1240000, PacketCount: 0},
362 {Id: 125, TableId: 1250, Priority: 1000, IdleTimeout: 10, Flags: 0, Cookie: 1250000, PacketCount: 0},
363 {Id: 126, TableId: 1260, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1260000, PacketCount: 0},
364 {Id: 127, TableId: 1270, Priority: 1000, IdleTimeout: 0, Flags: 0, Cookie: 1270000, PacketCount: 0},
365 }
366 uNF, fD, uAF := flowsToUpdateToDelete(newFlows, existingFlows)
367 assert.True(t, isFlowSliceEqual(uNF, expectedNewFlows))
368 assert.True(t, isFlowSliceEqual(fD, expectedFlowsToDelete))
369 assert.True(t, isFlowSliceEqual(uAF, expectedUpdatedAllFlows))
370}
371
372func TestGroupsToUpdateToDelete_EmptySlices(t *testing.T) {
373 newGroups := []*ofp.OfpGroupEntry{}
374 existingGroups := []*ofp.OfpGroupEntry{}
375 expectedNewGroups := []*ofp.OfpGroupEntry{}
376 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
377 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{}
378 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
379 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
380 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
381 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
382}
383
384func TestGroupsToUpdateToDelete_NoExistingGroups(t *testing.T) {
385 newGroups := []*ofp.OfpGroupEntry{
386 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
387 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
388 }
389 existingGroups := []*ofp.OfpGroupEntry{}
390 expectedNewGroups := []*ofp.OfpGroupEntry{
391 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
392 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
393 }
394 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
395 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
396 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
397 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
398 }
399 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
400 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
401 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
402 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
403}
404
405func TestGroupsToUpdateToDelete_UpdateNoDelete(t *testing.T) {
406 newGroups := []*ofp.OfpGroupEntry{
407 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
408 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
409 }
410 existingGroups := []*ofp.OfpGroupEntry{
411 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
412 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
413 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
414 }
415 expectedNewGroups := []*ofp.OfpGroupEntry{
416 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
417 }
418 expectedGroupsToDelete := []*ofp.OfpGroupEntry{}
419 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
420 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
421 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
422 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
423 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
424 }
425 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
426 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
427 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
428 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
429}
430
431func TestGroupsToUpdateToDelete_UpdateWithDelete(t *testing.T) {
432 newGroups := []*ofp.OfpGroupEntry{
433 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
434 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
435 }
436 existingGroups := []*ofp.OfpGroupEntry{
437 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
438 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
439 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
440 }
441 expectedNewGroups := []*ofp.OfpGroupEntry{
442 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
443 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
444 }
445 expectedGroupsToDelete := []*ofp.OfpGroupEntry{
446 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: nil}},
447 }
448 expectedUpdatedAllGroups := []*ofp.OfpGroupEntry{
449 {Desc: &ofp.OfpGroupDesc{Type: 1, GroupId: 10, Buckets: nil}},
450 {Desc: &ofp.OfpGroupDesc{Type: 2, GroupId: 20, Buckets: []*ofp.OfpBucket{{WatchPort: 10}}}},
451 {Desc: &ofp.OfpGroupDesc{Type: 3, GroupId: 30, Buckets: nil}},
452 {Desc: &ofp.OfpGroupDesc{Type: 4, GroupId: 40, Buckets: nil}},
453 }
454 uNG, gD, uAG := groupsToUpdateToDelete(newGroups, existingGroups)
455 assert.True(t, isGroupSliceEqual(uNG, expectedNewGroups))
456 assert.True(t, isGroupSliceEqual(gD, expectedGroupsToDelete))
457 assert.True(t, isGroupSliceEqual(uAG, expectedUpdatedAllGroups))
458}