[VOL-4627] Add COMBO pon support with configuration file

Change-Id: Ib7eec5640dfd5e5eb39e722ec7d2c8ee2b59f060
diff --git a/internal/common/option_test.go b/internal/common/option_test.go
index 2a57624..bf8d139 100644
--- a/internal/common/option_test.go
+++ b/internal/common/option_test.go
@@ -17,8 +17,11 @@
 package common
 
 import (
-	"gotest.tools/assert"
+	"fmt"
 	"testing"
+
+	"github.com/imdario/mergo"
+	"gotest.tools/assert"
 )
 
 func TestLoadBBSimServices(t *testing.T) {
@@ -36,3 +39,186 @@
 	assert.Equal(t, services[0].NeedsIgmp, false)
 	assert.Equal(t, services[0].TechnologyProfileID, 64)
 }
+
+func TestLoadPonsConfigDefaults(t *testing.T) {
+	Config = getDefaultOps()
+	// The default options define 1 PON per OLT
+	// and 1 ONU per PON
+
+	Services = []ServiceYaml{
+		{
+			Name: "test",
+		},
+	}
+
+	ponsConf, err := getDefaultPonsConfig()
+
+	assert.NilError(t, err, "Can't get defaults")
+
+	assert.Equal(t, ponsConf.Number, uint32(1))
+	assert.Equal(t, len(ponsConf.Ranges), 1)
+
+	ranges := ponsConf.Ranges
+
+	//The default should replicate the old way bbsim used to compute ranges
+	assert.Equal(t, ranges[0].PonRange, IdRange{0, 0})
+	assert.Equal(t, ranges[0].Technology, XGSPON.String())
+	assert.Equal(t, ranges[0].OnuRange, IdRange{defaultOnuIdStart, defaultOnuIdStart})
+	assert.Equal(t, ranges[0].AllocIdRange, IdRange{defaultAllocIdStart, defaultAllocIdStart + 4})
+	assert.Equal(t, ranges[0].GemportRange, IdRange{defaultGemportIdStart, defaultGemportIdStart + 32})
+
+	assert.NilError(t, validatePonsConfig(ponsConf), "Configuration is not valid")
+}
+
+func TestLoadPonsConfigFile(t *testing.T) {
+
+	Config = getDefaultOps()
+
+	Services = []ServiceYaml{
+		{
+			Name: "test",
+		},
+	}
+
+	ponsConf, err := getDefaultPonsConfig()
+
+	assert.NilError(t, err, "Can't get defaults")
+
+	yamlConf, err := loadBBSimPons("../../configs/pon-interfaces.yaml")
+
+	assert.NilError(t, err, "Can't read config file")
+
+	// merging Yaml and Default Values
+	err = mergo.Merge(ponsConf, yamlConf, mergo.WithOverride)
+	assert.NilError(t, err, "Can't merge YAML and Config")
+
+	assert.Equal(t, ponsConf.Number, uint32(16))
+	assert.Equal(t, len(ponsConf.Ranges), 1)
+
+	ranges := ponsConf.Ranges
+
+	assert.Equal(t, ranges[0].PonRange, IdRange{0, 15})
+	assert.Equal(t, ranges[0].Technology, XGSPON.String())
+	assert.Equal(t, ranges[0].OnuRange, IdRange{1, 255})
+	assert.Equal(t, ranges[0].AllocIdRange, IdRange{256, 1024})
+	assert.Equal(t, ranges[0].GemportRange, IdRange{256, 1024})
+
+	assert.NilError(t, validatePonsConfig(ponsConf), "Configuration is not valid")
+}
+
+func getTestPonsConfiguration() *PonPortsConfig {
+	return &PonPortsConfig{
+		Number: 16,
+		Ranges: []PonRangeConfig{
+			{
+				PonRange:     IdRange{0, 7},
+				Technology:   GPON.String(),
+				OnuRange:     IdRange{defaultOnuIdStart, defaultOnuIdStart},
+				AllocIdRange: IdRange{defaultAllocIdStart, defaultAllocIdStart + 4},
+				GemportRange: IdRange{defaultGemportIdStart, defaultGemportIdStart + 32},
+			},
+			{
+				PonRange:     IdRange{8, 15},
+				Technology:   XGSPON.String(),
+				OnuRange:     IdRange{defaultOnuIdStart, defaultOnuIdStart},
+				AllocIdRange: IdRange{defaultAllocIdStart, defaultAllocIdStart + 4},
+				GemportRange: IdRange{defaultGemportIdStart, defaultGemportIdStart + 32},
+			},
+		},
+	}
+}
+
+func TestPonsValidationTechnology(t *testing.T) {
+	ponsConf := getTestPonsConfiguration()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Test configuration is not valid")
+
+	ponsConf.Ranges[0].Technology = XGSPON.String()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Correct technology considered invalid")
+
+	ponsConf.Ranges[0].Technology = GPON.String()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Correct technology considered invalid")
+
+	ponsConf.Ranges[0].Technology = "TEST"
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "technology", "Incorrect technology considered valid")
+}
+
+func TestPonsValidationPortsInRanges(t *testing.T) {
+	ponsConf := getTestPonsConfiguration()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Test configuration is not valid")
+
+	//The second range now misses pon 8
+	ponsConf.Ranges[1].PonRange.StartId = 9
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "not-defined", "Missing pon definition considered valid")
+
+	//The second range defines pon 7 a second time
+	ponsConf.Ranges[1].PonRange.StartId = 7
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "duplicate", "Duplicate pon definition considered valid")
+
+	//Get back to a known good configuration
+	ponsConf = getTestPonsConfiguration()
+	//The second range uses an Id that is out of range
+	ponsConf.Ranges[1].PonRange.EndId = 16
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "max", "Out of range pon definition considered valid")
+
+	//Fix the start of the second range
+	ponsConf.Ranges[1].PonRange.EndId = 15
+
+	ponsConf.Number = 0
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "no-pon-ports", "Zero pons considered valid")
+}
+
+func TestPonsValidationRangeLimits(t *testing.T) {
+	ponsConf := getTestPonsConfiguration()
+	assert.NilError(t, validatePonsConfig(ponsConf), "Test configuration is not valid")
+
+	ponsConf.Ranges[0].PonRange = IdRange{0, 0}
+	ponsConf.Ranges[1].PonRange = IdRange{1, 15}
+
+	assert.NilError(t, validatePonsConfig(ponsConf), "Single pon range considered invalid")
+
+	//Get back to a known good configuration
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].PonRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid pons range limits considered valid")
+
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].OnuRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid onus range limits considered valid")
+
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].AllocIdRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid alloc-ids range limits considered valid")
+
+	ponsConf = getTestPonsConfiguration()
+	ponsConf.Ranges[0].GemportRange = IdRange{5, 4}
+
+	assert.ErrorContains(t, validatePonsConfig(ponsConf), "limits", "Invalid gemports range limits considered valid")
+}
+
+func TestGetPonConfigById(t *testing.T) {
+	PonsConfig = getTestPonsConfiguration()
+
+	for id := uint32(0); id < PonsConfig.Number-1; id++ {
+		conf, err := GetPonConfigById(id)
+		assert.NilError(t, err, fmt.Sprintf("Cannot get configuration for pon %d", id))
+
+		if id > conf.PonRange.EndId || id < conf.PonRange.StartId {
+			assert.NilError(t, err, fmt.Sprintf("Got wrong configuration for pon %d", id))
+		}
+	}
+
+	_, err := GetPonConfigById(16)
+
+	assert.Assert(t, err != nil, "Invalid pon id returned configuration")
+
+	_, err = GetPonConfigById(100)
+
+	assert.Assert(t, err != nil, "Invalid pon id returned configuration")
+}