Repository: openconfig/ygot
Pull request #541 (2021-06-10 14:30:37)

Add JoinPaths utility function

coveralls: 
[![Coverage Status](https://coveralls.io/builds/40479104/badge)](https://coveralls.io/builds/40479104)

Coverage decreased (-0.1%) to 90.486% when pulling **10b1f844bfcf956adec31415ad61dcc9d461e7fa on gdennis** into **449c150a7cc7ee98c9032de086e80791412a5617 on master**.

greg-dennis: I'll add unit tests and update the PR

Patch:
diff --git a/util/gnmi.go b/util/gnmi.go
index de61937..abde761 100644
--- a/util/gnmi.go
+++ b/util/gnmi.go
@@ -196,3 +196,27 @@ func FindModelData(mods []*yang.Entry) ([]*gpb.ModelData, error) {
 
 	return modelData, nil
 }
+
+// JoinPaths joins an prefix and suffix paths, returning an error if their
+// target or origin fields are both non-empty but don't match.
+func JoinPaths(prefix, suffix *gpb.Path) (*gpb.Path, error) {
+	joined := &gpb.Path{
+		Origin: prefix.GetOrigin(),
+		Target: prefix.GetTarget(),
+		// Copy the prefix elem to avoid modifying the one the caller passed.
+		Elem: append(append([]*gpb.PathElem{}, prefix.GetElem()...), suffix.GetElem()...),
+	}
+	if sufOrigin := suffix.GetOrigin(); sufOrigin != "" {
+		if preOrigin := prefix.GetOrigin(); preOrigin != "" && preOrigin != sufOrigin {
+			return nil, fmt.Errorf("prefix and suffix have different origins: %s != %s", preOrigin, sufOrigin)
+		}
+		joined.Origin = sufOrigin
+	}
+	if sufTarget := suffix.GetTarget(); sufTarget != "" {
+		if preTarget := prefix.GetTarget(); preTarget != "" && preTarget != sufTarget {
+			return nil, fmt.Errorf("prefix and suffix have different targets: %s != %s", preTarget, sufTarget)
+		}
+		joined.Target = sufTarget
+	}
+	return joined, nil
+}


Test patch:
diff --git a/util/gnmi_test.go b/util/gnmi_test.go
index dbeb207..871c9b4 100644
--- a/util/gnmi_test.go
+++ b/util/gnmi_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/openconfig/goyang/pkg/yang"
 	"google.golang.org/protobuf/encoding/prototext"
 	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/testing/protocmp"
 
 	gpb "github.com/openconfig/gnmi/proto/gnmi"
 )
@@ -766,3 +767,61 @@ func TestFindModelData(t *testing.T) {
 		}
 	}
 }
+
+func TestJoinPaths(t *testing.T) {
+	tests := []struct {
+		desc                 string
+		prefix, suffix, want *gpb.Path
+		wantErrSubstring     string
+	}{{
+		desc:   "all empty",
+		prefix: &gpb.Path{},
+		suffix: &gpb.Path{},
+		want:   &gpb.Path{},
+	}, {
+		desc:   "prefix only",
+		prefix: &gpb.Path{Origin: "o", Target: "t", Elem: []*gpb.PathElem{{Name: "p"}}},
+		suffix: &gpb.Path{},
+		want:   &gpb.Path{Origin: "o", Target: "t", Elem: []*gpb.PathElem{{Name: "p"}}},
+	}, {
+		desc:   "suffix only",
+		prefix: &gpb.Path{},
+		suffix: &gpb.Path{Origin: "o", Target: "t", Elem: []*gpb.PathElem{{Name: "s"}}},
+		want:   &gpb.Path{Origin: "o", Target: "t", Elem: []*gpb.PathElem{{Name: "s"}}},
+	}, {
+		desc:   "elements joined",
+		prefix: &gpb.Path{Elem: []*gpb.PathElem{{Name: "p"}}},
+		suffix: &gpb.Path{Elem: []*gpb.PathElem{{Name: "s"}}},
+		want:   &gpb.Path{Elem: []*gpb.PathElem{{Name: "p"}, {Name: "s"}}},
+	}, {
+		desc:   "same origin and target",
+		prefix: &gpb.Path{Origin: "o", Target: "t"},
+		suffix: &gpb.Path{Origin: "o", Target: "t"},
+		want:   &gpb.Path{Origin: "o", Target: "t"},
+	}, {
+		desc:             "mismatch origins",
+		prefix:           &gpb.Path{Origin: "o1"},
+		suffix:           &gpb.Path{Origin: "o2"},
+		wantErrSubstring: "different origins",
+	}, {
+		desc:             "mismatch targets",
+		prefix:           &gpb.Path{Target: "t1"},
+		suffix:           &gpb.Path{Target: "t2"},
+		wantErrSubstring: "different targets",
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.desc, func(t *testing.T) {
+			got, err := JoinPaths(tt.prefix, tt.suffix)
+			if diff := errdiff.Substring(err, tt.wantErrSubstring); diff != "" {
+				t.Errorf("JoinPaths(%v, %v) got unexpected error diff: %s", tt.prefix, tt.suffix, diff)
+			}
+			if err != nil {
+				return
+			}
+			if diff := cmp.Diff(tt.want, got, protocmp.Transform()); diff != "" {
+				t.Errorf("JoinPaths(%v, %v) got unexpected result diff(-want, +got): %s", tt.prefix, tt.suffix, diff)
+			}
+		})
+	}
+}