VOL-2153 move flow_utils.go from voltha-go to voltha-lib-go;
release 2.2.6

Based on voltha-go commit ec6e61b6239b955b6e7623ba3b604e8030154773

Change-Id: Id52f3eb1cb4bcade3f0e42da0129b643fdc5b0f9
diff --git a/pkg/flows/flow_utils_test.go b/pkg/flows/flow_utils_test.go
new file mode 100644
index 0000000..d611b04
--- /dev/null
+++ b/pkg/flows/flow_utils_test.go
@@ -0,0 +1,695 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * 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 (
+	"github.com/opencord/voltha-lib-go/pkg/log"
+	ofp "github.com/opencord/voltha-protos/go/openflow_13"
+	"github.com/stretchr/testify/assert"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+	"strings"
+	"testing"
+)
+
+var (
+	timeoutError     error
+	taskFailureError error
+)
+
+func init() {
+	log.AddPackage(log.JSON, log.WarnLevel, nil)
+	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))
+
+	var fa *FlowArgs
+	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 := MkFlowStat(fa)
+	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) | 0),
+		},
+		Actions: []*ofp.OfpAction{
+			SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 10)),
+			Output(1),
+		},
+	}
+	flow := MkFlowStat(fa)
+	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 := MkFlowStat(fa1)
+
+	fa2 = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 1500},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(5),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+		},
+		Actions: []*ofp.OfpAction{
+			PushVlan(0x8100),
+			SetField(VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 1000)),
+			SetField(VlanPcp(0)),
+			Output(2),
+		},
+	}
+	flow2 := MkFlowStat(fa2)
+
+	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()
+	assert.True(t, str == "")
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Group(10),
+		},
+	}
+	flow := MkFlowStat(fa)
+	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()
+	assert.True(t, strings.Contains(str, "id: 1143307409938767207"))
+	assert.True(t, strings.Contains(str, "group_id: 10"))
+	assert.True(t, strings.Contains(str, "oxm_class: OFPXMC_OPENFLOW_BASICOFPXMC_OPENFLOW_BASIC"))
+	assert.True(t, strings.Contains(str, "type: OFPXMT_OFB_VLAN_VIDOFPXMT_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) | 0),
+			Metadata_ofp(1000),
+			TunnelId(uint64(1)),
+			VlanPcp(0),
+		},
+		Actions: []*ofp.OfpAction{
+			PopVlan(),
+			Output(1),
+		},
+	}
+	flow := MkFlowStat(fa)
+	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_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()))
+
+	var fa *FlowArgs
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			Metadata_ofp(1000),
+			TunnelId(uint64(1)),
+			VlanPcp(0),
+		},
+		Actions: []*ofp.OfpAction{
+			PopVlan(),
+			Output(1),
+		},
+	}
+	flow := MkFlowStat(fa)
+	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) | 0),
+			Metadata_ofp(1000),
+			TunnelId(uint64(1)),
+			VlanPcp(0),
+		},
+		Actions: []*ofp.OfpAction{
+			PopVlan(),
+			Output(1),
+		},
+	}
+	flow := MkFlowStat(fa)
+	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) | 0),
+			Metadata_ofp(1000),
+			TunnelId(uint64(1)),
+			VlanPcp(0),
+		},
+		Actions: []*ofp.OfpAction{
+			PopVlan(),
+			Output(1),
+		},
+	}
+	flow = MkFlowStat(fa)
+	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) | 0),
+		},
+	}
+	flow = MkFlowStat(fa)
+	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) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Group(10),
+		},
+	}
+	flow = MkFlowStat(fa)
+	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) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Output(1),
+		},
+	}
+	flow = MkFlowStat(fa)
+	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) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Group(10),
+		},
+	}
+	flow1 := MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, nil))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Group(10),
+		},
+	}
+	flow2 := MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+	assert.False(t, FlowMatch(nil, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.True(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 501, "table_id": 1, "cookie": 38268468, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Group(10),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 2, "cookie": 38268468, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268467, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 14},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(4),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	assert.False(t, FlowMatch(flow1, flow2))
+
+	fa = &FlowArgs{
+		KV: OfpFlowModArgs{"priority": 500, "table_id": 1, "cookie": 38268468, "flags": 12},
+		MatchFields: []*ofp.OfpOxmOfbField{
+			InPort(2),
+			VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			PopVlan(),
+			Output(1),
+		},
+	}
+	flow2 = MkFlowStat(fa)
+	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) | 0),
+			VlanPcp(0),
+			EthType(0x800),
+			Ipv4Dst(0xe00a0a0a),
+		},
+		Actions: []*ofp.OfpAction{
+			Output(1),
+			Group(10),
+		},
+	}
+	flow := MkFlowStat(fa)
+	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) | 0),
+			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))
+	assert.True(t, FlowMatch(flow, FlowStatsEntryFromFlowModMessage(flowMod)))
+
+	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))
+}