/*
 * Copyright 2019-2024 Open Networking Foundation (ONF) and the ONF Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package flows

import (
	"bytes"
	"strings"
	"testing"
	"time"

	ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
	"github.com/stretchr/testify/assert"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var (
	timeoutError     error
	taskFailureError error
)

func init() {
	timeoutError = status.Errorf(codes.Aborted, "timeout")
	taskFailureError = status.Error(codes.Internal, "test failure task")
	timeoutError = status.Errorf(codes.Aborted, "timeout")
}

func TestFlowsAndGroups_AddFlow(t *testing.T) {
	fg := NewFlowsAndGroups()
	allFlows := fg.ListFlows()
	assert.Equal(t, 0, len(allFlows))
	fg.AddFlow(nil)
	allFlows = fg.ListFlows()
	assert.Equal(t, 0, len(allFlows))

	fa := &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(1),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1),
			TunnelId(uint64(1)),
			EthType(0x0800),
			Ipv4Dst(0xffffffff),
			IpProto(17),
			UdpSrc(68),
			UdpDst(67),
		},
		Actions: []*ofp.OfpAction{
			PushVlan(0x8100),
			SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000)),
			Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	fg.AddFlow(flow)

	allFlows = fg.ListFlows()
	assert.Equal(t, 1, len(allFlows))
	assert.True(t, FlowMatch(flow, allFlows[0]))
}

func TestFlowsAndGroups_AddGroup(t *testing.T) {
	var ga *GroupArgs

	fg := NewFlowsAndGroups()
	allGroups := fg.ListGroups()
	assert.Equal(t, 0, len(allGroups))
	fg.AddGroup(nil)
	allGroups = fg.ListGroups()
	assert.Equal(t, 0, len(allGroups))

	ga = &GroupArgs{
		GroupId: 10,
		Buckets: []*ofp.OfpBucket{
			{Actions: []*ofp.OfpAction{
				PopVlan(),
				Output(1),
			},
			},
		},
	}
	group := MkGroupStat(ga)
	fg.AddGroup(group)

	allGroups = fg.ListGroups()
	assert.Equal(t, 1, len(allGroups))
	assert.Equal(t, ga.GroupId, allGroups[0].Desc.GroupId)
}

func TestFlowsAndGroups_Copy(t *testing.T) {
	fg := NewFlowsAndGroups()
	var fa *FlowArgs
	var ga *GroupArgs

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
		},
		Actions: []*ofp.OfpAction{
			SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 10)),
			Output(1),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	fg.AddFlow(flow)

	ga = &GroupArgs{
		GroupId: 10,
		Buckets: []*ofp.OfpBucket{
			{Actions: []*ofp.OfpAction{
				PopVlan(),
				Output(1),
			},
			},
		},
	}
	group := MkGroupStat(ga)
	fg.AddGroup(group)

	fgCopy := fg.Copy()

	allFlows := fgCopy.ListFlows()
	assert.Equal(t, 1, len(allFlows))
	assert.True(t, FlowMatch(flow, allFlows[0]))

	allGroups := fgCopy.ListGroups()
	assert.Equal(t, 1, len(allGroups))
	assert.Equal(t, ga.GroupId, allGroups[0].Desc.GroupId)

	fg = NewFlowsAndGroups()
	fgCopy = fg.Copy()
	allFlows = fgCopy.ListFlows()
	allGroups = fgCopy.ListGroups()
	assert.Equal(t, 0, len(allFlows))
	assert.Equal(t, 0, len(allGroups))
}

func TestFlowsAndGroups_GetFlow(t *testing.T) {
	fg := NewFlowsAndGroups()
	var fa1 *FlowArgs
	var fa2 *FlowArgs
	var ga *GroupArgs

	fa1 = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			Metadata_ofp((1000 << 32) | 1),
			VlanPcp(0),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
		},
	}
	flow1, err := MkFlowStat(fa1)
	assert.Nil(t, err)

	fa2 = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 1500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(5),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
		},
		Actions: []*ofp.OfpAction{
			PushVlan(0x8100),
			SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1000)),
			SetField(VlanPcp(0)),
			Output(2),
		},
	}
	flow2, err := MkFlowStat(fa2)
	assert.Nil(t, err)

	fg.AddFlow(flow1)
	fg.AddFlow(flow2)

	ga = &GroupArgs{
		GroupId: 10,
		Buckets: []*ofp.OfpBucket{
			{Actions: []*ofp.OfpAction{
				PopVlan(),
				Output(1),
			},
			},
		},
	}
	group := MkGroupStat(ga)
	fg.AddGroup(group)

	gf1 := fg.GetFlow(0)
	assert.True(t, FlowMatch(flow1, gf1))

	gf2 := fg.GetFlow(1)
	assert.True(t, FlowMatch(flow2, gf2))

	gf3 := fg.GetFlow(2)
	assert.Nil(t, gf3)

	allFlows := fg.ListFlows()
	assert.True(t, FlowMatch(flow1, allFlows[0]))
	assert.True(t, FlowMatch(flow2, allFlows[1]))
}

func TestFlowsAndGroups_String(t *testing.T) {
	fg := NewFlowsAndGroups()
	var fa *FlowArgs
	var ga *GroupArgs

	str := fg.String()
	time.Sleep(2 * time.Millisecond) // Added sleep to address potential timing issues
	assert.True(t, str == "")

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Group(10),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	fg.AddFlow(flow)

	ga = &GroupArgs{
		GroupId: 10,
		Buckets: []*ofp.OfpBucket{
			{Actions: []*ofp.OfpAction{
				PopVlan(),
				Output(1),
			},
			},
		},
	}
	group := MkGroupStat(ga)
	fg.AddGroup(group)

	str = fg.String()
	time.Sleep(2 * time.Millisecond) // Added sleep to address potential timing issues
	assert.True(t, strings.Contains(str, "id:  11819684229970388353"))
	assert.True(t, strings.Contains(str, "group_id:  10"))
	assert.True(t, strings.Contains(str, "oxm_class:  OFPXMC_OPENFLOW_BASIC"))
	assert.True(t, strings.Contains(str, "type:  OFPXMT_OFB_VLAN_VID"))
	assert.True(t, strings.Contains(str, "vlan_vid:  4096"))
	assert.True(t, strings.Contains(str, "buckets:  {"))
}

func TestFlowsAndGroups_AddFrom(t *testing.T) {
	fg := NewFlowsAndGroups()
	var fa *FlowArgs
	var ga *GroupArgs

	str := fg.String()
	assert.True(t, str == "")

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			Metadata_ofp(1000),
			TunnelId(uint64(1)),
			VlanPcp(0),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
			Output(1),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	fg.AddFlow(flow)

	ga = &GroupArgs{
		GroupId: 10,
		Buckets: []*ofp.OfpBucket{
			{Actions: []*ofp.OfpAction{
				PopVlan(),
				Output(1),
			},
			},
		},
	}
	group := MkGroupStat(ga)
	fg.AddGroup(group)

	fg1 := NewFlowsAndGroups()
	fg1.AddFrom(fg)

	allFlows := fg1.ListFlows()
	allGroups := fg1.ListGroups()
	assert.Equal(t, 1, len(allFlows))
	assert.Equal(t, 1, len(allGroups))
	assert.True(t, FlowMatch(flow, allFlows[0]))
	assert.Equal(t, group.Desc.GroupId, allGroups[0].Desc.GroupId)
}

func TestDeviceRules_FilterRules(t *testing.T) {
	dr := NewDeviceRules()
	rules := dr.GetRules()
	assert.True(t, len(rules) == 0)

	dr.AddFlow("1", nil)
	dr.AddFlow("2", nil)
	dr.AddFlow("3", nil)
	dr.AddFlow("4", nil)
	rules = dr.GetRules()
	assert.True(t, len(rules) == 4)

	keep := map[string]string{"1": "1", "3": "3", "5": "5"}
	result := dr.FilterRules(keep)
	rules = result.GetRules()
	assert.True(t, len(rules) == 2)

	_, ok := rules["1"]
	assert.True(t, ok)
	_, ok = rules["2"]
	assert.False(t, ok)
	_, ok = rules["3"]
	assert.True(t, ok)
	_, ok = rules["4"]
	assert.False(t, ok)
}

func TestDeviceRules_RemoveRule(t *testing.T) {
	dr := NewDeviceRules()
	rules := dr.GetRules()
	assert.True(t, len(rules) == 0)

	dr.AddFlow("1", nil)
	dr.AddFlow("2", nil)
	dr.AddFlow("3", nil)
	dr.AddFlow("4", nil)
	rules = dr.GetRules()
	assert.True(t, len(rules) == 4)

	dr.RemoveRule("3")
	rules = dr.GetRules()
	assert.True(t, len(rules) == 3)

	_, ok := rules["1"]
	assert.True(t, ok)
	_, ok = rules["2"]
	assert.True(t, ok)
	_, ok = rules["3"]
	assert.False(t, ok)
	_, ok = rules["4"]
	assert.True(t, ok)

	dr.RemoveRule("5")
	rules = dr.GetRules()
	assert.True(t, len(rules) == 3)
}

func TestDeviceRules_AddFlow(t *testing.T) {
	dr := NewDeviceRules()
	rules := dr.GetRules()
	assert.True(t, len(rules) == 0)

	dr.AddFlow("123456", nil)
	rules = dr.GetRules()
	assert.True(t, len(rules) == 1)
	val, ok := rules["123456"]
	assert.True(t, ok)
	assert.Equal(t, 0, len(val.ListFlows()))
	assert.Equal(t, 0, len(val.ListGroups()))

	fa := &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			Metadata_ofp(1000),
			TunnelId(uint64(1)),
			VlanPcp(0),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
			Output(1),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	dr.AddFlow("123456", flow)
	rules = dr.GetRules()
	assert.True(t, len(rules) == 1)
	val, ok = rules["123456"]
	assert.True(t, ok)
	assert.Equal(t, 1, len(val.ListFlows()))
	assert.True(t, FlowMatch(flow, val.ListFlows()[0]))
	assert.Equal(t, 0, len(val.ListGroups()))
}

func TestDeviceRules_AddFlowsAndGroup(t *testing.T) {
	fg := NewFlowsAndGroups()
	var fa *FlowArgs
	var ga *GroupArgs

	str := fg.String()
	assert.True(t, str == "")

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 2000},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			Metadata_ofp(1000),
			TunnelId(uint64(1)),
			VlanPcp(0),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
			Output(1),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	fg.AddFlow(flow)

	ga = &GroupArgs{
		GroupId: 10,
		Buckets: []*ofp.OfpBucket{
			{Actions: []*ofp.OfpAction{
				PopVlan(),
				Output(1),
			},
			},
		},
	}
	group := MkGroupStat(ga)
	fg.AddGroup(group)

	dr := NewDeviceRules()
	dr.AddFlowsAndGroup("123456", fg)
	rules := dr.GetRules()
	assert.True(t, len(rules) == 1)
	val, ok := rules["123456"]
	assert.True(t, ok)
	assert.Equal(t, 1, len(val.ListFlows()))
	assert.Equal(t, 1, len(val.ListGroups()))
	assert.True(t, FlowMatch(flow, val.ListFlows()[0]))
	assert.Equal(t, 10, int(val.ListGroups()[0].Desc.GroupId))
}

func TestFlowHasOutPort(t *testing.T) {
	var flow *ofp.OfpFlowStats
	assert.False(t, FlowHasOutPort(flow, 1))

	fa := &FlowArgs{
		KV: OfpFlowModArgs{"priority": 2000},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			Metadata_ofp(1000),
			TunnelId(uint64(1)),
			VlanPcp(0),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
			Output(1),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	assert.True(t, FlowHasOutPort(flow, 1))
	assert.False(t, FlowHasOutPort(flow, 2))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 2000},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
		},
	}
	flow, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowHasOutPort(flow, 1))
}

func TestFlowHasOutGroup(t *testing.T) {
	var flow *ofp.OfpFlowStats
	assert.False(t, FlowHasOutGroup(flow, 10))

	fa := &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Group(10),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	assert.True(t, FlowHasOutGroup(flow, 10))
	assert.False(t, FlowHasOutGroup(flow, 11))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Output(1),
		},
	}
	flow, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowHasOutGroup(flow, 1))
}

func TestMatchFlow(t *testing.T) {
	assert.False(t, FlowMatch(nil, nil))
	fa := &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Group(10),
		},
	}
	flow1, err := MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatch(flow1, nil))

	// different table_id, cookie, flags
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Group(10),
		},
	}
	flow2, err := MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatch(flow1, flow2))
	assert.False(t, FlowMatch(nil, flow2))

	// no difference
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.True(t, FlowMatch(flow1, flow2))

	// different priority
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 501, "table_id": 1, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Group(10),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatch(flow1, flow2))

	// different table id
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 2, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatch(flow1, flow2))

	// different cookie
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268467, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.True(t, FlowMatch(flow1, flow2))

	// different flags
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 14},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.True(t, FlowMatch(flow1, flow2))

	// different match InPort
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(4),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatch(flow1, flow2))

	// different match Ipv4Dst
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatch(flow1, flow2))

	// different actions
	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
			Output(1),
		},
	}
	flow2, err = MkFlowStat(fa)
	assert.Nil(t, err)
	assert.True(t, FlowMatch(flow1, flow2))
}

func TestFlowMatchesMod(t *testing.T) {
	assert.False(t, FlowMatchesMod(nil, nil))
	fa := &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			Output(1),
			Group(10),
		},
	}
	flow, err := MkFlowStat(fa)
	assert.Nil(t, err)
	assert.False(t, FlowMatchesMod(flow, nil))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"priority": 500, "table_id": 1},
		MatchFields: []*ofp.OfpOxmOfbField{
			InPort(2),
			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT)),
			VlanPcp(0),
			EthType(0x800),
			Ipv4Dst(0xe00a0a0a),
		},
		Actions: []*ofp.OfpAction{
			PopVlan(),
			Output(1),
		},
	}
	flowMod := MkSimpleFlowMod(ToOfpOxmField(fa.MatchFields), fa.Actions, fa.Command, fa.KV)
	assert.False(t, FlowMatchesMod(nil, flowMod))
	assert.False(t, FlowMatchesMod(flow, flowMod))
	entry, err := FlowStatsEntryFromFlowModMessage(flowMod)
	assert.Nil(t, err)
	assert.True(t, FlowMatch(flow, entry))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"table_id": uint64(ofp.OfpTable_OFPTT_ALL),
			"cookie_mask": 0,
			"out_port":    uint64(ofp.OfpPortNo_OFPP_ANY),
			"out_group":   uint64(ofp.OfpGroup_OFPG_ANY),
		},
	}
	flowMod = MkSimpleFlowMod(ToOfpOxmField(fa.MatchFields), fa.Actions, fa.Command, fa.KV)
	assert.True(t, FlowMatchesMod(flow, flowMod))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"table_id": 1,
			"cookie_mask": 0,
			"out_port":    uint64(ofp.OfpPortNo_OFPP_ANY),
			"out_group":   uint64(ofp.OfpGroup_OFPG_ANY),
		},
	}
	flowMod = MkSimpleFlowMod(ToOfpOxmField(fa.MatchFields), fa.Actions, fa.Command, fa.KV)
	assert.True(t, FlowMatchesMod(flow, flowMod))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"table_id": 1,
			"cookie_mask": 0,
			"out_port":    1,
			"out_group":   uint64(ofp.OfpGroup_OFPG_ANY),
		},
	}
	flowMod = MkSimpleFlowMod(ToOfpOxmField(fa.MatchFields), fa.Actions, fa.Command, fa.KV)
	assert.True(t, FlowMatchesMod(flow, flowMod))

	fa = &FlowArgs{
		KV: OfpFlowModArgs{"table_id": 1,
			"cookie_mask": 0,
			"out_port":    1,
			"out_group":   10,
		},
	}
	flowMod = MkSimpleFlowMod(ToOfpOxmField(fa.MatchFields), fa.Actions, fa.Command, fa.KV)
	assert.True(t, FlowMatchesMod(flow, flowMod))
}

func TestIsMulticastIpAddress(t *testing.T) {
	isMcastIp := IsMulticastIp(3776315393) //225.22.0.1
	assert.True(t, isMcastIp)
	isMcastIp = IsMulticastIp(3232243777) //192.168.32.65
	assert.True(t, !isMcastIp)
}

func TestConvertToMulticastMac(t *testing.T) {
	mcastIp := uint32(4001431809)                   //238.129.1.1
	expectedMacInBytes := []byte{1, 0, 94, 1, 1, 1} //01:00:5e:01:01:01
	macInBytes := ConvertToMulticastMacBytes(mcastIp)
	assert.True(t, bytes.Equal(macInBytes, expectedMacInBytes))
}
