VOL-2337 There is only one Multicast GEM port id value system-wise and it is
predefined in the technology profile. ResourceManager should reserve this GEM
port value and not give it to the GEM port requesters to avoid GEM port id
collisions.

Change-Id: Iaf878e695d3cd427844ec0464fe108ca0c25cca2
diff --git a/pkg/ponresourcemanager/ponresourcemanager_test.go b/pkg/ponresourcemanager/ponresourcemanager_test.go
new file mode 100644
index 0000000..e4d8021
--- /dev/null
+++ b/pkg/ponresourcemanager/ponresourcemanager_test.go
@@ -0,0 +1,206 @@
+/*
+ * 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 ponresourcemanager
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	"github.com/stretchr/testify/assert"
+	"strings"
+	"testing"
+)
+
+const (
+	GEM_POOL_PATH        = "gemport_id_pool"
+	RESERVED_GEM_PORT_ID = uint32(5)
+)
+
+func init() {
+	_, err := log.SetDefaultLogger(log.JSON, log.DebugLevel, nil)
+	if err != nil {
+		panic(err)
+	}
+}
+
+// MockKVClient mocks the AdapterProxy interface.
+type MockResKVClient struct {
+	resourceMap map[string]interface{}
+}
+
+func newMockKvClient() *MockResKVClient {
+	var mockResKVClient MockResKVClient
+	mockResKVClient.resourceMap = make(map[string]interface{})
+	return &mockResKVClient
+}
+
+// List function implemented for KVClient.
+func (kvclient *MockResKVClient) List(ctx context.Context, key string) (map[string]*kvstore.KVPair, error) {
+	return nil, errors.New("key didn't find")
+}
+
+// Get mock function implementation for KVClient
+func (kvclient *MockResKVClient) Get(ctx context.Context, key string) (*kvstore.KVPair, error) {
+	log.Debugw("Get of MockKVClient called", log.Fields{"key": key})
+	if key != "" {
+		if strings.Contains(key, RESERVED_GEMPORT_IDS_PATH) {
+			log.Debug("Getting Key:", RESERVED_GEMPORT_IDS_PATH)
+			reservedGemPorts := []uint32{RESERVED_GEM_PORT_ID}
+			str, _ := json.Marshal(reservedGemPorts)
+			return kvstore.NewKVPair(key, str, "mock", 3000, 1), nil
+		}
+		if strings.Contains(key, GEM_POOL_PATH) {
+			log.Debug("Getting Key:", GEM_POOL_PATH)
+			resource := kvclient.resourceMap[key]
+			return kvstore.NewKVPair(key, resource, "mock", 3000, 1), nil
+		}
+		maps := make(map[string]*kvstore.KVPair)
+		maps[key] = &kvstore.KVPair{Key: key}
+		return maps[key], nil
+	}
+	return nil, errors.New("key didn't find")
+}
+
+// Put mock function implementation for KVClient
+func (kvclient *MockResKVClient) Put(ctx context.Context, key string, value interface{}) error {
+	if key != "" {
+		if strings.Contains(key, GEMPORT_ID_POOL_PATH) && value != nil {
+			kvclient.resourceMap[key] = value
+		}
+		return nil
+	}
+	return errors.New("key didn't find")
+}
+
+// Delete mock function implementation for KVClient
+func (kvclient *MockResKVClient) Delete(ctx context.Context, key string) error {
+	return nil
+}
+
+// Reserve mock function implementation for KVClient
+func (kvclient *MockResKVClient) Reserve(ctx context.Context, key string, value interface{}, ttl int64) (interface{}, error) {
+	return nil, errors.New("key didn't find")
+}
+
+// ReleaseReservation mock function implementation for KVClient
+func (kvclient *MockResKVClient) ReleaseReservation(ctx context.Context, key string) error {
+	return nil
+}
+
+// ReleaseAllReservations mock function implementation for KVClient
+func (kvclient *MockResKVClient) ReleaseAllReservations(ctx context.Context) error {
+	return nil
+}
+
+// RenewReservation mock function implementation for KVClient
+func (kvclient *MockResKVClient) RenewReservation(ctx context.Context, key string) error {
+	return nil
+}
+
+// Watch mock function implementation for KVClient
+func (kvclient *MockResKVClient) Watch(ctx context.Context, key string, withPrefix bool) chan *kvstore.Event {
+	return nil
+}
+
+// AcquireLock mock function implementation for KVClient
+func (kvclient *MockResKVClient) AcquireLock(ctx context.Context, lockName string, timeout int) error {
+	return nil
+}
+
+// ReleaseLock mock function implementation for KVClient
+func (kvclient *MockResKVClient) ReleaseLock(lockName string) error {
+	return nil
+}
+
+// IsConnectionUp mock function implementation for KVClient
+func (kvclient *MockResKVClient) IsConnectionUp(ctx context.Context) bool { // timeout in second
+	return true
+}
+
+// CloseWatch mock function implementation for KVClient
+func (kvclient *MockResKVClient) CloseWatch(key string, ch chan *kvstore.Event) {
+}
+
+// Close mock function implementation for KVClient
+func (kvclient *MockResKVClient) Close() {
+}
+
+func TestExcludeReservedGemPortIdFromThePool(t *testing.T) {
+	PONRMgr, err := NewPONResourceManager("gpon", "onu", "olt1",
+		"etcd", "1", 1)
+	if err != nil {
+		return
+	}
+	PONRMgr.KVStore = &db.Backend{
+		Client: newMockKvClient(),
+	}
+
+	PONRMgr.KVStoreForConfig = &db.Backend{
+		Client: newMockKvClient(),
+	}
+	// create a pool in the range of [1,16]
+	// and exclude id 5 from this pool
+	StartIndex := uint32(1)
+	EndIndex := uint32(16)
+
+	ctx := context.Background()
+	reservedGemPortIds, defined := PONRMgr.getReservedGemPortIdsFromKVStore(ctx)
+	if !defined {
+		return
+	}
+
+	FormatResult, err := PONRMgr.FormatResource(1, StartIndex, EndIndex, reservedGemPortIds)
+	if err != nil {
+		t.Error("Failed to format resource", err)
+		return
+	}
+
+	// Add resource as json in kv store.
+	err = PONRMgr.KVStore.Put(ctx, GEMPORT_ID_POOL_PATH, FormatResult)
+	if err != nil {
+		t.Error("Error in posting data to kv store", GEMPORT_ID_POOL_PATH)
+		return
+	}
+
+	for i := StartIndex; i <= (EndIndex - uint32(len(reservedGemPortIds))); i++ {
+		// get gem port id pool from the kv store
+		resource, err := PONRMgr.GetResource(context.Background(), GEMPORT_ID_POOL_PATH)
+		if err != nil {
+			t.Error("Failed to get resource from gem port id pool", err)
+			return
+		}
+		// get a gem port id from the pool
+		nextID, err := PONRMgr.GenerateNextID(resource)
+		if err != nil {
+			t.Error("Failed to get gem port id from the pool", err)
+			return
+		}
+
+		//given gem port id should not equal to the reserved gem port id
+		assert.NotEqual(t, nextID, RESERVED_GEM_PORT_ID)
+		// put updated gem port id pool into the kv store
+		err = PONRMgr.UpdateResource(context.Background(), GEMPORT_ID_POOL_PATH, resource)
+		if err != nil {
+			t.Error("Failed to put updated gem port id pool into the kv store", err)
+			return
+		}
+	}
+
+}