Repository: openconfig/goyang (go)

Issue:
identities that have a base identity are not sorted
In YANG when a set of individual identities have a common base identity, these individual identities are gathered under the base, but their order is non deterministic, as they are extracted from a Go map.

This has 2 negative consequences
* YGOT `generate` command can produce a different JSON schema each time it it run because the order of identities changes, even though there are no real changes
* the order of these identities will be different to the enum entries in the generated code (which are sorted alphabetically). Therefore is is not possible to reference enums in the generated code by the order of the identities in the JSON schema

Pull request description:
identities that have a base identity are not sorted Fixes #234
See issue #234 

Patch:
diff --git a/pkg/yang/identity.go b/pkg/yang/identity.go
index 7979b83..a766859 100644
--- a/pkg/yang/identity.go
+++ b/pkg/yang/identity.go
@@ -16,6 +16,7 @@ package yang
 
 import (
 	"fmt"
+	"sort"
 	"sync"
 )
 
@@ -179,6 +180,9 @@ func (ms *Modules) resolveIdentities() []error {
 		for _, j := range i.Identity.Values {
 			newValues = addChildren(j, newValues)
 		}
+		sort.SliceStable(newValues, func(j, k int) bool {
+			return newValues[j].Name < newValues[k].Name
+		})
 		i.Identity.Values = newValues
 	}
 


Test patch:
diff --git a/pkg/yang/identity_test.go b/pkg/yang/identity_test.go
index d717866..631680c 100644
--- a/pkg/yang/identity_test.go
+++ b/pkg/yang/identity_test.go
@@ -388,19 +388,19 @@ var treeTestCases = []identityTestCase{
 				module: "base",
 				name:   "GREATGRANDFATHER",
 				values: []string{
+					"BROTHER", // Order is alphabetical
+					"FATHER",
 					"GRANDFATHER",
 					"GREATUNCLE",
-					"FATHER",
-					"UNCLE",
 					"SON",
-					"BROTHER",
+					"UNCLE",
 				},
 			},
 			{
 				module:    "base",
 				name:      "GRANDFATHER",
 				baseNames: []string{"GREATGRANDFATHER"},
-				values:    []string{"FATHER", "UNCLE", "SON", "BROTHER"},
+				values:    []string{"BROTHER", "FATHER", "SON", "UNCLE"},
 			},
 			{
 				module:    "base",
@@ -411,7 +411,7 @@ var treeTestCases = []identityTestCase{
 				module:    "base",
 				name:      "FATHER",
 				baseNames: []string{"GRANDFATHER"},
-				values:    []string{"SON", "BROTHER"},
+				values:    []string{"BROTHER", "SON"},
 			},
 			{
 				module:    "base",
@@ -426,6 +426,7 @@ var treeTestCases = []identityTestCase{
 		},
 	},
 	{
+		name: "tree-test-case-3",
 		in: []inputModule{
 			{
 				name: "base.yang",
@@ -468,6 +469,7 @@ var treeTestCases = []identityTestCase{
 		},
 	},
 	{
+		name: "tree-test-case-4",
 		in: []inputModule{
 			{
 				name: "base.yang",
@@ -514,6 +516,7 @@ var treeTestCases = []identityTestCase{
 		},
 	},
 	{
+		name: "tree-test-case-5",
 		in: []inputModule{
 			{
 				name: "base.yang",
@@ -717,13 +720,17 @@ func TestIdentityTree(t *testing.T) {
 
 				valueMap := make(map[string]bool)
 
-				for _, val := range chkID.values {
+				for i, val := range chkID.values {
 					valueMap[val] = false
 					// Check that IsDefined returns the right result
 					if !foundID.IsDefined(val) {
 						t.Errorf("Couldn't find defined value %s  for %s", val, chkID.name)
 					}
 
+					// Check that the values are sorted in a consistent order
+					if foundID.Values[i].Name != val {
+						t.Errorf("Invalid order for value #%d. Expecting %s Got %s", i, foundID.Values[i].Name, val)
+					}
 					// Check that GetValue returns the right Identity
 					idval := foundID.GetValue(val)
 					if idval == nil {