VOL-4337: Code upgrade for 3/2020 G.988 support and remaining Extended Message Set support
Change-Id: I6c5e1a167216ad9b51e9da89460e9909465ae1bc
diff --git a/meframe/meframe_test.go b/meframe/meframe_test.go
new file mode 100644
index 0000000..9ebacb1
--- /dev/null
+++ b/meframe/meframe_test.go
@@ -0,0 +1,439 @@
+/*
+ * Copyright (c) 2018 - present. Boling Consulting Solutions (bcsw.net)
+ * Copyright 2020-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 meframe_test
+
+import (
+ mapset "github.com/deckarep/golang-set"
+ "github.com/google/gopacket"
+ . "github.com/opencord/omci-lib-go"
+ me "github.com/opencord/omci-lib-go/generated"
+ "github.com/opencord/omci-lib-go/meframe"
+ "github.com/stretchr/testify/assert"
+ "math/rand"
+ "testing"
+)
+
+var messageTypeTestFuncs map[MessageType]func(*testing.T, *me.ManagedEntity, DeviceIdent)
+
+var allMessageTypes = [...]MessageType{
+ CreateRequestType,
+ CreateResponseType,
+ DeleteRequestType,
+ DeleteResponseType,
+ SetRequestType,
+ SetResponseType,
+ GetRequestType,
+ GetResponseType,
+ GetAllAlarmsRequestType,
+ GetAllAlarmsResponseType,
+ GetAllAlarmsNextRequestType,
+ GetAllAlarmsNextResponseType,
+ MibUploadRequestType,
+ MibUploadResponseType,
+ MibUploadNextRequestType,
+ MibUploadNextResponseType,
+ MibResetRequestType,
+ MibResetResponseType,
+ TestRequestType,
+ TestResponseType,
+ StartSoftwareDownloadRequestType,
+ StartSoftwareDownloadResponseType,
+ DownloadSectionRequestType,
+ DownloadSectionResponseType,
+ EndSoftwareDownloadRequestType,
+ EndSoftwareDownloadResponseType,
+ ActivateSoftwareRequestType,
+ ActivateSoftwareResponseType,
+ CommitSoftwareRequestType,
+ CommitSoftwareResponseType,
+ SynchronizeTimeRequestType,
+ SynchronizeTimeResponseType,
+ RebootRequestType,
+ RebootResponseType,
+ GetNextRequestType,
+ GetNextResponseType,
+ GetCurrentDataRequestType,
+ GetCurrentDataResponseType,
+ SetTableRequestType,
+ SetTableResponseType,
+ // Autonomous ONU messages
+ AlarmNotificationType,
+ AttributeValueChangeType,
+ TestResultType,
+}
+
+var allExtendedMessageTypes = [...]MessageType{
+ GetRequestType,
+ GetResponseType,
+}
+
+func init() {
+ messageTypeTestFuncs = make(map[MessageType]func(*testing.T, *me.ManagedEntity, DeviceIdent), 0)
+
+ messageTypeTestFuncs[CreateRequestType] = testCreateRequestTypeMeFrame
+ messageTypeTestFuncs[CreateResponseType] = testCreateResponseTypeMeFrame
+ messageTypeTestFuncs[DeleteRequestType] = testDeleteRequestTypeMeFrame
+ messageTypeTestFuncs[DeleteResponseType] = testDeleteResponseTypeMeFrame
+ messageTypeTestFuncs[SetRequestType] = testSetRequestTypeMeFrame
+ messageTypeTestFuncs[SetResponseType] = testSetResponseTypeMeFrame
+ messageTypeTestFuncs[GetRequestType] = testGetRequestTypeMeFrame
+ messageTypeTestFuncs[GetResponseType] = testGetResponseTypeMeFrame
+ messageTypeTestFuncs[GetAllAlarmsRequestType] = testGetAllAlarmsRequestTypeMeFrame
+ messageTypeTestFuncs[GetAllAlarmsResponseType] = testGetAllAlarmsResponseTypeMeFrame
+ messageTypeTestFuncs[GetAllAlarmsNextRequestType] = testGetAllAlarmsNextRequestTypeMeFrame
+ messageTypeTestFuncs[GetAllAlarmsNextResponseType] = testGetAllAlarmsNextResponseTypeMeFrame
+ messageTypeTestFuncs[MibUploadRequestType] = testMibUploadRequestTypeMeFrame
+ messageTypeTestFuncs[MibUploadResponseType] = testMibUploadResponseTypeMeFrame
+ messageTypeTestFuncs[MibUploadNextRequestType] = testMibUploadNextRequestTypeMeFrame
+ messageTypeTestFuncs[MibUploadNextResponseType] = testMibUploadNextResponseTypeMeFrame
+ messageTypeTestFuncs[MibResetRequestType] = testMibResetRequestTypeMeFrame
+ messageTypeTestFuncs[MibResetResponseType] = testMibResetResponseTypeMeFrame
+ messageTypeTestFuncs[TestRequestType] = testTestRequestTypeMeFrame
+ messageTypeTestFuncs[TestResponseType] = testTestResponseTypeMeFrame
+
+ // For Download section, AR=0 if not response expected, AR=1 if response expected (last section of a window)4
+ messageTypeTestFuncs[StartSoftwareDownloadRequestType] = testStartSoftwareDownloadRequestTypeMeFrame
+ messageTypeTestFuncs[StartSoftwareDownloadResponseType] = testStartSoftwareDownloadResponseTypeMeFrame
+ messageTypeTestFuncs[DownloadSectionRequestType] = testDownloadSectionRequestTypeMeFrame
+ messageTypeTestFuncs[DownloadSectionResponseType] = testDownloadSectionResponseTypeMeFrame
+ messageTypeTestFuncs[EndSoftwareDownloadRequestType] = testEndSoftwareDownloadRequestTypeMeFrame
+ messageTypeTestFuncs[EndSoftwareDownloadResponseType] = testEndSoftwareDownloadResponseTypeMeFrame
+ messageTypeTestFuncs[ActivateSoftwareRequestType] = testActivateSoftwareRequestTypeMeFrame
+ messageTypeTestFuncs[ActivateSoftwareResponseType] = testActivateSoftwareResponseTypeMeFrame
+ messageTypeTestFuncs[CommitSoftwareRequestType] = testCommitSoftwareRequestTypeMeFrame
+ messageTypeTestFuncs[CommitSoftwareResponseType] = testCommitSoftwareResponseTypeMeFrame
+ messageTypeTestFuncs[SynchronizeTimeRequestType] = testSynchronizeTimeRequestTypeMeFrame
+ messageTypeTestFuncs[SynchronizeTimeResponseType] = testSynchronizeTimeResponseTypeMeFrame
+ messageTypeTestFuncs[RebootRequestType] = testRebootRequestTypeMeFrame
+ messageTypeTestFuncs[RebootResponseType] = testRebootResponseTypeMeFrame
+ messageTypeTestFuncs[GetNextRequestType] = testGetNextRequestTypeMeFrame
+ messageTypeTestFuncs[GetNextResponseType] = testGetNextResponseTypeMeFrame
+ messageTypeTestFuncs[GetCurrentDataRequestType] = testGetCurrentDataRequestTypeMeFrame
+ messageTypeTestFuncs[GetCurrentDataResponseType] = testGetCurrentDataResponseTypeMeFrame
+ messageTypeTestFuncs[SetTableRequestType] = testSetTableRequestTypeMeFrame
+ messageTypeTestFuncs[SetTableResponseType] = testSetTableResponseTypeMeFrame
+ messageTypeTestFuncs[AlarmNotificationType] = testAlarmNotificationTypeMeFrame
+ messageTypeTestFuncs[AttributeValueChangeType] = testAttributeValueChangeTypeMeFrame
+ messageTypeTestFuncs[TestResultType] = testTestResultTypeMeFrame
+
+ // Supported Extended message set types here
+ messageTypeTestFuncs[GetRequestType+ExtendedTypeDecodeOffset] = testGetRequestTypeMeFrame
+ messageTypeTestFuncs[GetResponseType+ExtendedTypeDecodeOffset] = testGetResponseTypeMeFrame
+
+ // For Download section, AR=0 if not response expected, AR=1 if response expected (last section of a window)
+ messageTypeTestFuncs[DownloadSectionRequestType+ExtendedTypeDecodeOffset] = testDownloadSectionRequestTypeMeFrame
+ // TODO: messageTypeTestFuncs[DownloadSectionRequestWithResponseType+ExtendedTypeDecodeOffset] = testDownloadSectionLastRequestTypeMeFrame
+ messageTypeTestFuncs[DownloadSectionResponseType+ExtendedTypeDecodeOffset] = testDownloadSectionResponseTypeMeFrame
+
+ messageTypeTestFuncs[AlarmNotificationType+ExtendedTypeDecodeOffset] = testAlarmNotificationTypeMeFrame
+ messageTypeTestFuncs[AttributeValueChangeType+ExtendedTypeDecodeOffset] = testAttributeValueChangeTypeMeFrame
+ messageTypeTestFuncs[TestResultType+ExtendedTypeDecodeOffset] = testTestResultTypeMeFrame
+}
+
+func getMEsThatSupportAMessageType(messageType MessageType) []*me.ManagedEntity {
+ msgType := me.MsgType(byte(messageType) & me.MsgTypeMask)
+
+ entities := make([]*me.ManagedEntity, 0)
+ for _, classID := range me.GetSupportedClassIDs() {
+ if managedEntity, err := me.LoadManagedEntityDefinition(classID); err.StatusCode() == me.Success {
+ supportedTypes := managedEntity.GetManagedEntityDefinition().GetMessageTypes()
+ if supportedTypes.Contains(msgType) {
+ entities = append(entities, managedEntity)
+ }
+ }
+ }
+ return entities
+}
+
+func TestFrameFormatNotYetSupported(t *testing.T) {
+ // We do not yet support a few message types for the extended frame formats.
+ // As we do, add appropriate tests and change this to one that is not supported
+ // Until all are supported
+
+ params := me.ParamData{
+ Attributes: me.AttributeValueMap{"MibDataSync": 0},
+ }
+ managedEntity, omciErr := me.NewOnuData(params)
+ assert.NotNil(t, omciErr)
+ assert.Equal(t, omciErr.StatusCode(), me.Success)
+
+ buffer, err := meframe.GenFrame(managedEntity, SetRequestType, meframe.FrameFormat(ExtendedIdent), meframe.TransactionID(1))
+ assert.Nil(t, buffer)
+ assert.NotNil(t, err)
+}
+
+// TODO: Add more specific get next response tests as we have had issues
+
+func TestGetNextResponseOneFrameOnly(t *testing.T) {
+ // OMCI ME GetRequest for MsgTypes often needs only a single frame and
+ // it is a table of one octet values. Make sure we decode it correctly
+
+ response1 := []uint8{
+ 0, 250, 58, 10, 1, 31, 0, 0, 0, 64, 0,
+ 4, 6, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 40,
+ }
+ // getNextSize is the size returned by the original Get request. Normally you would
+ // do many OMCI requests and append all the results (while decreasing size), but
+ // this is all in on packet.
+ //
+ // Do the buffer loop anyway
+ getNextSize := 23
+ remaining := getNextSize
+
+ dataBuffer := make([]byte, 0)
+ packets := []gopacket.Packet{
+ gopacket.NewPacket(response1, LayerTypeOMCI, gopacket.NoCopy),
+ }
+ for _, packet := range packets {
+ omciLayer := packet.Layer(LayerTypeOMCI)
+ assert.NotNil(t, omciLayer)
+
+ omciObj, omciOk := omciLayer.(*OMCI)
+ assert.True(t, omciOk)
+ assert.NotNil(t, omciObj)
+ assert.Equal(t, uint16(250), omciObj.TransactionID)
+ assert.Equal(t, GetNextResponseType, omciObj.MessageType)
+ assert.Equal(t, BaselineIdent, omciObj.DeviceIdentifier)
+ assert.Equal(t, uint32(0), omciObj.MIC)
+ assert.Equal(t, uint16(40), omciObj.Length)
+
+ msgLayer := packet.Layer(LayerTypeGetNextResponse)
+ msgObj, msgOk := msgLayer.(*GetNextResponse)
+ assert.True(t, msgOk)
+ assert.NotNil(t, msgObj)
+ assert.Equal(t, me.Success, msgObj.Result)
+ assert.Equal(t, uint16(0x4000), msgObj.AttributeMask)
+ assert.Equal(t, 2, len(msgObj.Attributes))
+
+ for attrName, value := range msgObj.Attributes {
+ // Skip Entity ID attribute always stored in attribute list
+ if attrName == "ManagedEntityId" {
+ assert.Equal(t, uint16(0), value.(uint16))
+ continue
+ }
+ assert.Equal(t, "MessageTypeTable", attrName)
+ tmpBuffer, ok := value.([]byte)
+ assert.True(t, ok)
+
+ validOctets := len(tmpBuffer)
+ assert.NotZero(t, validOctets)
+ if validOctets > remaining {
+ validOctets = remaining
+ }
+ remaining -= validOctets
+ dataBuffer = append(dataBuffer, tmpBuffer[:validOctets]...)
+
+ assert.True(t, remaining >= 0)
+ if remaining == 0 {
+ break
+ }
+ }
+ }
+ bufSize := len(dataBuffer)
+ assert.Equal(t, getNextSize, bufSize)
+}
+
+func aTestFailingGetNextResponseTypeMeFrame(t *testing.T) {
+ //params := me.ParamData{
+ // EntityID: 0,
+ // Attributes: me.AttributeValueMap{
+ // "Rmep5DatabaseTable": []uint8{
+ // 0,1,2,3,4,5,6,7,8,9,
+ // 10,11,12,13,14,15,16,17,18,19,
+ // 20,21,22,23,24,25,26,27,28,29,
+ // 30,
+ // },
+ // },
+ //}
+ //meInstance, err := me.NewDot1AgMepCcmDatabase(params)
+ //bitmask := uint16(2048)
+ //assert.NotNil(t, meInstance)
+ //assert.Nil(t, err)
+ //
+ //tid := uint16(rand.Int31n(0xFFFE) + 1) // [1, 0xFFFF]
+ //
+ //frame, omciErr := meframe.GenFrame(meInstance, GetNextResponseType, meframe.TransactionID(tid), meframe.Result(me.Success),
+ // AttributeMask(bitmask))
+ //assert.NotNil(t, frame)
+ //assert.NotZero(t, len(frame))
+ //assert.Nil(t, omciErr)
+ //
+ /////////////////////////////////////////////////////////////////////
+ //// Now decode and compare
+ //cid := meInstance.GetClassID()
+ //assert.NotEqual(t, cid, 0)
+ //packet := gopacket.NewPacket(frame, LayerTypeOMCI, gopacket.NoCopy)
+ //assert.NotNil(t, packet)
+ //
+ //omciLayer := packet.Layer( LayerTypeOMCI)
+ //assert.NotNil(t, omciLayer)
+ //
+ //omciObj, omciOk := omciLayer.(* OMCI)
+ //assert.NotNil(t, omciObj)
+ //assert.True(t, omciOk)
+ //assert.Equal(t, tid, omciObj.TransactionID)
+ //assert.Equal(t, GetNextResponseType, omciObj.MessageType)
+ //assert.Equal(t, BaselineIdent, omciObj.DeviceIdentifier)
+ //
+ //msgLayer := packet.Layer(LayerTypeGetNextResponse)
+ //assert.NotNil(t, msgLayer)
+ //
+ //msgObj, msgOk := msgLayer.(*GetNextResponse)
+ //assert.NotNil(t, msgObj)
+ //assert.True(t, msgOk)
+ //
+ //assert.Equal(t, meInstance.GetClassID(), msgObj.EntityClass)
+ //assert.Equal(t, meInstance.GetEntityID(), msgObj.EntityInstance)
+ //assert.Equal(t, meInstance.GetAttributeMask(), msgObj.AttributeMask)
+
+}
+
+func TestAllMessageTypes(t *testing.T) {
+ // Loop over all message types
+ for _, messageType := range allMessageTypes {
+ //typeTested := false
+ if testRoutine, ok := messageTypeTestFuncs[messageType]; ok {
+ // Loop over all Managed Entities that support that type
+ for _, managedEntity := range getMEsThatSupportAMessageType(messageType) {
+ // Call the test routine
+ testRoutine(t, managedEntity, BaselineIdent)
+ //typeTested = true
+ }
+ }
+ // Verify at least one test ran for this message type
+ // TODO: Enable once all tests are working -> assert.True(t, typeTested)
+ }
+ // Now for the extended message set message types we support
+ for _, messageType := range allExtendedMessageTypes {
+ trueMessageType := messageType - ExtendedTypeDecodeOffset
+
+ if testRoutine, ok := messageTypeTestFuncs[messageType]; ok {
+ // Loop over all Managed Entities that support that type
+ for _, managedEntity := range getMEsThatSupportAMessageType(trueMessageType) {
+ // Call the test routine
+ testRoutine(t, managedEntity, ExtendedIdent)
+ //typeTested = true
+ }
+ }
+ // Verify at least one test ran for this message type
+ // TODO: Enable once all tests are working -> assert.True(t, typeTested)
+ }
+}
+
+//func TestAllThatSupportAlarms(t *testing.T) { TODO: Future
+// // Loop over all Managed Entities and test those with Attributes that support
+//
+// for _, managedEntity := range getMEsThatSupportAMessageType(messageType) {
+// // Call the test routine
+// testRoutine(t, managedEntity)
+// //typeTested = true
+// }
+//}
+
+func getAttributeNameSet(attributes me.AttributeValueMap) mapset.Set {
+ // TODO: For Classes with attribute masks that can set/get/... more than just
+ // a single attribute, test a set/get of just a single attribute to verify
+ // all encoding/decoding methods are working as expected.
+ names := mapset.NewSet()
+ for name := range attributes {
+ names.Add(name)
+ }
+ return names
+}
+
+func pickAValue(attrDef me.AttributeDefinition) interface{} {
+ constraint := attrDef.Constraint
+ defaultVal := attrDef.DefValue
+ size := attrDef.GetSize()
+ _, isOctetString := defaultVal.([]byte)
+
+ if attrDef.IsTableAttribute() || isOctetString {
+ // Table attributes treated as a string of octets. If size is zero, it is
+ // most likely an attribute with variable size. Pick a random size that will
+ // fit into a simple frame (1-33 octets)
+ if size == 0 {
+ size = rand.Intn(32) + 1
+ }
+ value := make([]byte, size)
+ for octet := 0; octet < size; octet++ {
+ value[octet] = byte(octet & 0xff)
+ }
+ return value
+ }
+ switch size {
+ case 1:
+ // Try the default + 1 as a value. Since some defaults are zero
+ // and we want example frames without zeros in them.
+ if value, ok := defaultVal.(uint8); ok {
+ if constraint == nil {
+ return value + 1
+ }
+ if err := constraint(value + 1); err == nil {
+ return value + 1
+ }
+ }
+ return defaultVal.(uint8)
+
+ case 2:
+ // Try the default + 1 as a value. Since some defaults are zero
+ // and we want example frames without zeros in them.
+ if value, ok := defaultVal.(uint16); ok {
+ if constraint == nil {
+ return value + 1
+ }
+ if err := constraint(value + 1); err == nil {
+ return value + 1
+ }
+ }
+ return defaultVal.(uint16)
+
+ case 4:
+ // Try the default + 1 as a value. Since some defaults are zero
+ // and we want example frames without zeros in them.
+ if value, ok := defaultVal.(uint32); ok {
+ if constraint == nil {
+ return value + 1
+ }
+ if err := constraint(value + 1); err == nil {
+ return value + 1
+ }
+ }
+ return defaultVal.(uint32)
+
+ case 8:
+ // Try the default + 1 as a value. Since some defaults are zero
+ // and we want example frames without zeros in them.
+ if value, ok := defaultVal.(uint64); ok {
+ if constraint == nil {
+ return value + 1
+ }
+ if err := constraint(value + 1); err == nil {
+ return value + 1
+ }
+ }
+ return defaultVal.(uint64)
+
+ default:
+ size := attrDef.GetSize()
+ value := make([]uint8, size)
+ for index := 0; index < size; index++ {
+ value[index] = uint8(index & 0xFF)
+ }
+ return value
+ }
+}