// Copyright 2017 Google Inc.
//
// 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 gogen

import (
	"testing"

	"github.com/kylelemons/godebug/pretty"
	"github.com/openconfig/ygot/testutil"
	"github.com/openconfig/ygot/ygen"
	"github.com/openconfig/ygot/ygot"
)

// wantGoStructOut is used to store the expected output of a writeGoStructs
// call.
type wantGoStructOut struct {
	wantErr    bool   // wantErr indicates whether errors are expected.
	structs    string // structs contains code repesenting a the mapped struct.
	keys       string // keys contains code representing structs used as list keys.
	methods    string // methods contains code corresponding to methods associated with the mapped struct.
	interfaces string // interfaces contains code corresponding to interfaces associated with the mapped struct.
}

// TestGoCodeStructGeneration tests the code generation from a known schema generates
// the correct structures, key types and methods for a YANG container.
func TestGoCodeStructGeneration(t *testing.T) {
	tests := []struct {
		name          string
		inStructToMap *ygen.ParsedDirectory
		// inOtherStructMap is the set of other mappable entities that are
		// in the same module as the struct to map
		inOtherStructMap          map[string]*ygen.ParsedDirectory
		inIgnoreShadowSchemaPaths bool
		inGoOpts                  GoOpts
		want                      wantGoStructOut
	}{{
		name: "simple single leaf mapping test",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Fields: map[string]*ygen.NodeDetails{
				"f1": {
					Name: "F1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "f1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/f1",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType:        "int8",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         "0",
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"f1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
				"f2": {
					Name: "F2",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "f2",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/f2",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafListNode,
					LangType: &ygen.MappedType{
						NativeType:        "string",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         `""`,
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"f2"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       [][]string{{"g2"}},
					ShadowMappedPathModules: [][]string{{"exmod2"}},
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:   true,
			ValidateFunctionName: "ValidateProxyFunction",
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	F1	*int8	` + "`" + `path:"f1" module:"exmod"` + "`" + `
	F2	[]string	` + "`" + `path:"f2" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ValidateProxyFunction(opts ...ygot.ValidationOption) error {
	return t.ΛValidate(opts...)
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "simple single leaf mapping test outputting shadow paths",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Fields: map[string]*ygen.NodeDetails{
				"f1": {
					Name: "F1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "f1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/f1",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType:        "int8",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         "0",
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"f1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
				"f2": {
					Name: "F2",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "f2",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/f2",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafListNode,
					LangType: &ygen.MappedType{
						NativeType:        "string",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         `""`,
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"f2"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       [][]string{{"g2"}},
					ShadowMappedPathModules: [][]string{{"exmod2"}},
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			IgnoreShadowSchemaPaths: true,
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	F1	*int8	` + "`" + `path:"f1" module:"exmod"` + "`" + `
	F2	[]string	` + "`" + `path:"f2" module:"exmod" shadow-path:"g2" shadow-module:"exmod2"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with a multi-type union",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "InputStruct",
			Fields: map[string]*ygen.NodeDetails{
				"u1": {
					Name: "U1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "u1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/module/input-struct/u1",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType: "InputStruct_U1_Union",
						UnionTypes: map[string]ygen.MappedUnionSubtype{
							"string": {
								Index: 0,
							},
							"int8": {
								Index: 1,
							},
						},
						IsEnumeratedValue: false,
						ZeroValue:         "nil",
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"u1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/module/input-struct",
			BelongingModule: "exmod",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema: true,
		},
		want: wantGoStructOut{
			structs: `
// InputStruct represents the /module/input-struct YANG schema element.
type InputStruct struct {
	U1	InputStruct_U1_Union	` + "`" + `path:"u1" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that InputStruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*InputStruct) IsYANGGoStruct() {}
`,
			methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *InputStruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["InputStruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *InputStruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of InputStruct.
func (*InputStruct) ΛBelongingModule() string {
	return "exmod"
}
`,
			interfaces: `
// InputStruct_U1_Union is an interface that is implemented by valid types for the union
// for the leaf /module/input-struct/u1 within the YANG schema.
type InputStruct_U1_Union interface {
	Is_InputStruct_U1_Union()
}

// InputStruct_U1_Union_Int8 is used when /module/input-struct/u1
// is to be set to a int8 value.
type InputStruct_U1_Union_Int8 struct {
	Int8	int8
}

// Is_InputStruct_U1_Union ensures that InputStruct_U1_Union_Int8
// implements the InputStruct_U1_Union interface.
func (*InputStruct_U1_Union_Int8) Is_InputStruct_U1_Union() {}

// InputStruct_U1_Union_String is used when /module/input-struct/u1
// is to be set to a string value.
type InputStruct_U1_Union_String struct {
	String	string
}

// Is_InputStruct_U1_Union ensures that InputStruct_U1_Union_String
// implements the InputStruct_U1_Union interface.
func (*InputStruct_U1_Union_String) Is_InputStruct_U1_Union() {}

// To_InputStruct_U1_Union takes an input interface{} and attempts to convert it to a struct
// which implements the InputStruct_U1_Union union. It returns an error if the interface{} supplied
// cannot be converted to a type within the union.
func (t *InputStruct) To_InputStruct_U1_Union(i interface{}) (InputStruct_U1_Union, error) {
	switch v := i.(type) {
	case int8:
		return &InputStruct_U1_Union_Int8{v}, nil
	case string:
		return &InputStruct_U1_Union_String{v}, nil
	default:
		return nil, fmt.Errorf("cannot convert %v to InputStruct_U1_Union, unknown union type, got: %T, want any of [int8, string]", i, i)
	}
}
`,
		},
	}, {
		name: "nested container in struct",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "InputStruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"c1": {
					Name: "C1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "c1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/input-struct/c1",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ContainerNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"c1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/input-struct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/input-struct/c1": {
				Name:            "InputStruct_C1",
				Path:            "/root-module/input-struct/c1",
				BelongingModule: "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema: true,
		},
		want: wantGoStructOut{
			structs: `
// InputStruct represents the /root-module/input-struct YANG schema element.
type InputStruct struct {
	C1	*InputStruct_C1	` + "`" + `path:"c1" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that InputStruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*InputStruct) IsYANGGoStruct() {}
`,
			methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *InputStruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["InputStruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *InputStruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of InputStruct.
func (*InputStruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "nested container in struct with presence container",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "InputStruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"c1": {
					Name: "C1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "c1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/input-struct/c1",
						LeafrefTargetPath: "",
						PresenceStatement: ygot.String("instantiated"),
					},
					Type:                    ygen.ContainerNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"c1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/input-struct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/input-struct/c1": {
				Name:            "InputStruct_C1",
				Path:            "/root-module/input-struct/c1",
				BelongingModule: "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema: true,
			AddYangPresence:    true,
		},
		want: wantGoStructOut{
			structs: `
// InputStruct represents the /root-module/input-struct YANG schema element.
type InputStruct struct {
	C1	*InputStruct_C1	` + "`" + `path:"c1" module:"exmod" yangPresence:"true"` + "`" + `
}

// IsYANGGoStruct ensures that InputStruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*InputStruct) IsYANGGoStruct() {}
`,
			methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *InputStruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["InputStruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *InputStruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of InputStruct.
func (*InputStruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with missing struct referenced",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "AStruct",
			Fields: map[string]*ygen.NodeDetails{
				"elem": {
					Name: "Elem",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "elem",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/a-struct/elem",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ContainerNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"elem"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/a-struct",
			BelongingModule: "exmod",
		},
		want: wantGoStructOut{wantErr: true},
	}, {
		name: "struct with missing list referenced",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "BStruct",
			Fields: map[string]*ygen.NodeDetails{
				"list": {
					Name: "List",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/b-struct/list",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"list"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/b-struct",
			BelongingModule: "exmod",
		},
		want: wantGoStructOut{wantErr: true},
	}, {
		name: "struct with keyless list",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "QStruct",
			Fields: map[string]*ygen.NodeDetails{
				"a-list": {
					Name: "AList",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "a-list",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/q-struct/a-list",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"a-list"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/q-struct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/q-struct/a-list": {
				Name:            "QStruct_AList",
				BelongingModule: "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			GeneratePopulateDefault: true,
		},
		want: wantGoStructOut{
			structs: `
// QStruct represents the /root-module/q-struct YANG schema element.
type QStruct struct {
	AList	[]*QStruct_AList	` + "`" + `path:"a-list" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that QStruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*QStruct) IsYANGGoStruct() {}
`,
			methods: `
// PopulateDefaults recursively populates unset leaf fields in the QStruct
// with default values as specified in the YANG schema, instantiating any nil
// container fields.
func (t *QStruct) PopulateDefaults() {
	if (t == nil) {
		return
	}
	ygot.BuildEmptyTree(t)
	for _, e := range t.AList {
		e.PopulateDefaults()
	}
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *QStruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["QStruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *QStruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of QStruct.
func (*QStruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with single key list",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"listWithKey": {
					Name: "ListWithKey",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list-with-key",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/listWithKey",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"listWithKey"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/tstruct/listWithKey": {
				Name: "ListWithKey",
				Type: ygen.List,
				Fields: map[string]*ygen.NodeDetails{
					"keyLeaf": {
						Name: "keyLeaf",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeaf",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeaf",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
						MappedPaths:             [][]string{{"keyLeaf"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
				},
				ListKeys: map[string]*ygen.ListKey{
					"keyLeaf": {
						Name: "KeyLeaf",
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
				},
				ListKeyYANGNames: []string{"keyLeaf"},
				Path:             "/root-module/tstruct/listWithKey",
				BelongingModule:  "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:   true,
			GenerateRenameMethod: true,
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	ListWithKey	map[string]*ListWithKey	` + "`" + `path:"listWithKey" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			methods: `
// NewListWithKey creates a new entry in the ListWithKey list of the
// Tstruct struct. The keys of the list are populated from the input
// arguments.
func (t *Tstruct) NewListWithKey(KeyLeaf string) (*ListWithKey, error){

	// Initialise the list within the receiver struct if it has not already been
	// created.
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[string]*ListWithKey)
	}

	key := KeyLeaf

	// Ensure that this key has not already been used in the
	// list. Keyed YANG lists do not allow duplicate keys to
	// be created.
	if _, ok := t.ListWithKey[key]; ok {
		return nil, fmt.Errorf("duplicate key %v for list ListWithKey", key)
	}

	t.ListWithKey[key] = &ListWithKey{
		KeyLeaf: &KeyLeaf,
	}

	return t.ListWithKey[key], nil
}

// RenameListWithKey renames an entry in the list ListWithKey within
// the Tstruct struct. The entry with key oldK is renamed to newK updating
// the key within the value.
func (t *Tstruct) RenameListWithKey(oldK, newK string) error {
	if _, ok := t.ListWithKey[newK]; ok {
		return fmt.Errorf("key %v already exists in ListWithKey", newK)
	}

	e, ok := t.ListWithKey[oldK]
	if !ok {
		return fmt.Errorf("key %v not found in ListWithKey", oldK)
	}
	e.KeyLeaf = &newK

	t.ListWithKey[newK] = e
	delete(t.ListWithKey, oldK)
	return nil
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with single key list - ordered-by user",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"listWithKey": {
					Name: "ListWithKey",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list-with-key",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/listWithKey",
						LeafrefTargetPath: "",
						OrderedByUser:     true,
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"listWithKey"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/tstruct/listWithKey": {
				Name: "Tstruct_ListWithKey",
				Type: ygen.List,
				Fields: map[string]*ygen.NodeDetails{
					"keyLeaf": {
						Name: "keyLeaf",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeaf",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeaf",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
						MappedPaths:             [][]string{{"keyLeaf"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
				},
				ListKeys: map[string]*ygen.ListKey{
					"keyLeaf": {
						Name: "KeyLeaf",
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
				},
				ListKeyYANGNames: []string{"keyLeaf"},
				Path:             "/root-module/tstruct/listWithKey",
				BelongingModule:  "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			GenerateAppendMethod:    true,
			GenerateGetters:         true,
			GenerateDeleteMethod:    true,
			GeneratePopulateDefault: true,
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	ListWithKey	*Tstruct_ListWithKey_OrderedMap	` + "`" + `path:"listWithKey" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			methods: `
// GetOrCreateListWithKeyMap returns the ordered map field
// ListWithKey from Tstruct.
//
// It initializes the field if not already initialized.
func (s *Tstruct) GetOrCreateListWithKeyMap() *Tstruct_ListWithKey_OrderedMap {
	if s.ListWithKey == nil {
		s.ListWithKey = &Tstruct_ListWithKey_OrderedMap{}
	}
	return s.ListWithKey
}

// AppendNewListWithKey creates a new entry in the ListWithKey
// ordered map of the Tstruct struct. The keys of the list are
// populated from the input arguments.
func (s *Tstruct) AppendNewListWithKey(KeyLeaf string) (*Tstruct_ListWithKey, error) {
	if s.ListWithKey == nil {
		s.ListWithKey = &Tstruct_ListWithKey_OrderedMap{}
	}
	return s.ListWithKey.AppendNew(KeyLeaf)
}

// AppendListWithKey appends the supplied Tstruct_ListWithKey struct
// to the list ListWithKey of Tstruct. If the key value(s)
// specified in the supplied Tstruct_ListWithKey already exist in the list, an
// error is returned.
func (s *Tstruct) AppendListWithKey(v *Tstruct_ListWithKey) error {
	if s.ListWithKey == nil {
		s.ListWithKey = &Tstruct_ListWithKey_OrderedMap{}
	}
	return s.ListWithKey.Append(v)
}

// GetListWithKey retrieves the value with the specified key from the
// ListWithKey map field of Tstruct. If the receiver
// is nil, or the specified key is not present in the list, nil is returned
// such that Get* methods may be safely chained.
func (s *Tstruct) GetListWithKey(KeyLeaf string) *Tstruct_ListWithKey {
	if s == nil {
		return nil
	}
	key := KeyLeaf
	return s.ListWithKey.Get(key)
}

// DeleteListWithKey deletes the value with the specified keys from
// the receiver Tstruct. If there is no such element, the
// function is a no-op.
func (s *Tstruct) DeleteListWithKey(KeyLeaf string) bool {
	key := KeyLeaf
	return s.ListWithKey.Delete(key)
}

// Tstruct_ListWithKey_OrderedMap is an ordered map that represents the "ordered-by user"
// list elements at /root-module/tstruct/listWithKey.
type Tstruct_ListWithKey_OrderedMap struct {
	keys []string
	valueMap map[string]*Tstruct_ListWithKey
}

// IsYANGOrderedList ensures that Tstruct_ListWithKey_OrderedMap implements the
// ygot.GoOrderedMap interface.
func (*Tstruct_ListWithKey_OrderedMap) IsYANGOrderedList() {}

// init initializes any uninitialized values.
func (o *Tstruct_ListWithKey_OrderedMap) init() {
	if o == nil {
		return
	}
	if o.valueMap == nil {
		o.valueMap = map[string]*Tstruct_ListWithKey{}
	}
}

// Keys returns a copy of the list's keys.
func (o *Tstruct_ListWithKey_OrderedMap) Keys() []string {
	if o == nil {
		return nil
	}
	return append([]string{}, o.keys...)
}

// Values returns the current set of the list's values in order.
func (o *Tstruct_ListWithKey_OrderedMap) Values() []*Tstruct_ListWithKey {
	if o == nil {
		return nil
	}
	var values []*Tstruct_ListWithKey
	for _, key := range o.keys {
		values = append(values, o.valueMap[key])
	}
	return values
}

// Len returns a size of Tstruct_ListWithKey_OrderedMap
func (o *Tstruct_ListWithKey_OrderedMap) Len() int {
	if o == nil {
		return 0
	}
	return len(o.keys)
}

// Get returns the value corresponding to the key. If the key is not found, nil
// is returned.
func (o *Tstruct_ListWithKey_OrderedMap) Get(key string) *Tstruct_ListWithKey {
	if o == nil {
		return nil
	}
	val, _ := o.valueMap[key]
	return val
}

// Delete deletes an element.
func (o *Tstruct_ListWithKey_OrderedMap) Delete(key string) bool {
	if o == nil {
		return false
	}
	if _, ok := o.valueMap[key]; !ok {
		return false
	}
	for i, k := range o.keys {
		if k == key {
			o.keys = append(o.keys[:i], o.keys[i+1:]...)
			delete(o.valueMap, key)
			return true
		}
	}
	return false
}

// Append appends a Tstruct_ListWithKey, returning an error if the key
// already exists in the ordered list or if the key is unspecified.
func (o *Tstruct_ListWithKey_OrderedMap) Append(v *Tstruct_ListWithKey) error {
	if o == nil {
		return fmt.Errorf("nil ordered map, cannot append Tstruct_ListWithKey")
	}
	if v == nil {
		return fmt.Errorf("nil Tstruct_ListWithKey")
	}
	if v.KeyLeaf == nil {
		return fmt.Errorf("invalid nil key received for KeyLeaf")
	}

	key := *v.KeyLeaf

	if _, ok := o.valueMap[key]; ok {
		return fmt.Errorf("duplicate key for list Statement %v", key)
	}
	o.keys = append(o.keys, key)
	o.init()
	o.valueMap[key] = v
	return nil
}

// AppendNew creates and appends a new Tstruct_ListWithKey, returning the
// newly-initialized v. It returns an error if the v already exists.
func (o *Tstruct_ListWithKey_OrderedMap) AppendNew(KeyLeaf string) (*Tstruct_ListWithKey, error) {
	if o == nil {
		return nil, fmt.Errorf("nil ordered map, cannot append Tstruct_ListWithKey")
	}
	key := KeyLeaf

	if _, ok := o.valueMap[key]; ok {
		return nil, fmt.Errorf("duplicate key for list Statement %v", key)
	}
	o.keys = append(o.keys, key)
	newElement := &Tstruct_ListWithKey{
		KeyLeaf: &KeyLeaf,
	}
	o.init()
	o.valueMap[key] = newElement
	return newElement, nil
}

// PopulateDefaults recursively populates unset leaf fields in the Tstruct
// with default values as specified in the YANG schema, instantiating any nil
// container fields.
func (t *Tstruct) PopulateDefaults() {
	if (t == nil) {
		return
	}
	ygot.BuildEmptyTree(t)
	for _, e := range t.ListWithKey.Values() {
		e.PopulateDefaults()
	}
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "missing list definition element",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"listWithKey": {
					Name: "ListWithKey",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list-with-key",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/listWithKey",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"listWithKey"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		want: wantGoStructOut{wantErr: true},
	}, {
		name: "unknown kind",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "AStruct",
			Fields: map[string]*ygen.NodeDetails{
				"anydata": {
					Name: "anydata",
					Type: ygen.AnyDataNode,
				},
			},
			BelongingModule: "exmod",
		},
		want: wantGoStructOut{wantErr: true},
	}, {
		name: "unknown field type",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "AStruct",
			Fields: map[string]*ygen.NodeDetails{
				"idd": {
					Name: "Idd",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "idd",
						Defaults:          nil,
						RootElementModule: "mod",
						Path:              "/mod/container-two/container/idd",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.InvalidNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"idd"}},
					MappedPathModules:       [][]string{{"mod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path: "/mod/container-two/container",
		},
		want: wantGoStructOut{wantErr: true},
	}, {
		name: "struct with multi-key list",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"listWithKey": {
					Name: "ListWithKey",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list-with-key",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/listWithKey",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"listWithKey"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/tstruct/listWithKey": {
				Name: "Tstruct_ListWithKey",
				Type: ygen.List,
				Fields: map[string]*ygen.NodeDetails{
					"keyLeafOne": {
						Name: "keyLeafOne",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeafOne",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeafOne",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
						MappedPaths:             [][]string{{"keyLeafOne"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
					"keyLeafTwo": {
						Name: "keyLeafTwo",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeafTwo",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeafTwo",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "int8",
							UnionTypes: nil,
							ZeroValue:  "0",
						},
						MappedPaths:             [][]string{{"keyLeafTwo"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
				},
				ListKeys: map[string]*ygen.ListKey{
					"keyLeafOne": {
						Name: "KeyLeafOne",
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
					"keyLeafTwo": {
						Name: "KeyLeafTwo",
						LangType: &ygen.MappedType{
							NativeType: "int8",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
				},
				ListKeyYANGNames: []string{"keyLeafOne", "keyLeafTwo"},
				Path:             "/root-module/tstruct/listWithKey",
				BelongingModule:  "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:   true,
			GenerateRenameMethod: true,
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	ListWithKey	map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey	` + "`" + `path:"listWithKey" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			keys: `
// Tstruct_ListWithKey_Key represents the key for list ListWithKey of element /root-module/tstruct.
type Tstruct_ListWithKey_Key struct {
	KeyLeafOne	string	` + "`" + `path:"keyLeafOne"` + "`" + `
	KeyLeafTwo	int8	` + "`" + `path:"keyLeafTwo"` + "`" + `
}

// IsYANGGoKeyStruct ensures that Tstruct_ListWithKey_Key partially implements the
// yang.GoKeyStruct interface. This allows functions that need to
// handle this key struct to identify it as being generated by gogen.
func (Tstruct_ListWithKey_Key) IsYANGGoKeyStruct() {}

// ΛListKeyMap returns the values of the Tstruct_ListWithKey_Key key struct.
func (t Tstruct_ListWithKey_Key) ΛListKeyMap() (map[string]interface{}, error) {
	return map[string]interface{}{
		"keyLeafOne": t.KeyLeafOne,
		"keyLeafTwo": t.KeyLeafTwo,
	}, nil
}
`,
			methods: `
// NewListWithKey creates a new entry in the ListWithKey list of the
// Tstruct struct. The keys of the list are populated from the input
// arguments.
func (t *Tstruct) NewListWithKey(KeyLeafOne string, KeyLeafTwo int8) (*Tstruct_ListWithKey, error){

	// Initialise the list within the receiver struct if it has not already been
	// created.
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey)
	}

	key := Tstruct_ListWithKey_Key{
		KeyLeafOne: KeyLeafOne,
		KeyLeafTwo: KeyLeafTwo,
	}

	// Ensure that this key has not already been used in the
	// list. Keyed YANG lists do not allow duplicate keys to
	// be created.
	if _, ok := t.ListWithKey[key]; ok {
		return nil, fmt.Errorf("duplicate key %v for list ListWithKey", key)
	}

	t.ListWithKey[key] = &Tstruct_ListWithKey{
		KeyLeafOne: &KeyLeafOne,
		KeyLeafTwo: &KeyLeafTwo,
	}

	return t.ListWithKey[key], nil
}

// RenameListWithKey renames an entry in the list ListWithKey within
// the Tstruct struct. The entry with key oldK is renamed to newK updating
// the key within the value.
func (t *Tstruct) RenameListWithKey(oldK, newK Tstruct_ListWithKey_Key) error {
	if _, ok := t.ListWithKey[newK]; ok {
		return fmt.Errorf("key %v already exists in ListWithKey", newK)
	}

	e, ok := t.ListWithKey[oldK]
	if !ok {
		return fmt.Errorf("key %v not found in ListWithKey", oldK)
	}
	e.KeyLeafOne = &newK.KeyLeafOne
	e.KeyLeafTwo = &newK.KeyLeafTwo

	t.ListWithKey[newK] = e
	delete(t.ListWithKey, oldK)
	return nil
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "annotated struct",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"f1": {
					Name: "F1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "f1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/f1",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType:        "int8",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         "0",
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"f1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:  true,
			AddAnnotationFields: true,
			AnnotationPrefix:    "Ω",
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	ΩMetadata	[]ygot.Annotation	` + "`" + `path:"@" ygotAnnotation:"true"` + "`" + `
	F1	*int8	` + "`" + `path:"f1" module:"exmod"` + "`" + `
	ΩF1	[]ygot.Annotation	` + "`" + `path:"@f1" ygotAnnotation:"true"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			methods: `
// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with multi-key list - append and getters",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"listWithKey": {
					Name: "ListWithKey",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list-with-key",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/listWithKey",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"listWithKey"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/tstruct/listWithKey": {
				Name: "Tstruct_ListWithKey",
				Type: ygen.List,
				Fields: map[string]*ygen.NodeDetails{
					"keyLeafOne": {
						Name: "keyLeafOne",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeafOne",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeafOne",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
						MappedPaths:             [][]string{{"keyLeafOne"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
					"keyLeafTwo": {
						Name: "keyLeafTwo",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeafTwo",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeafTwo",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "int8",
							UnionTypes: nil,
							ZeroValue:  "0",
						},
						MappedPaths:             [][]string{{"keyLeafTwo"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
				},
				ListKeys: map[string]*ygen.ListKey{
					"keyLeafOne": {
						Name: "KeyLeafOne",
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
					"keyLeafTwo": {
						Name: "KeyLeafTwo",
						LangType: &ygen.MappedType{
							NativeType: "int8",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
				},
				ListKeyYANGNames: []string{"keyLeafOne", "keyLeafTwo"},
				Path:             "/root-module/tstruct/listWithKey",
				BelongingModule:  "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:   true,
			GenerateAppendMethod: true,
			GenerateGetters:      true,
			GenerateDeleteMethod: true,
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	ListWithKey	map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey	` + "`" + `path:"listWithKey" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			keys: `
// Tstruct_ListWithKey_Key represents the key for list ListWithKey of element /root-module/tstruct.
type Tstruct_ListWithKey_Key struct {
	KeyLeafOne	string	` + "`" + `path:"keyLeafOne"` + "`" + `
	KeyLeafTwo	int8	` + "`" + `path:"keyLeafTwo"` + "`" + `
}

// IsYANGGoKeyStruct ensures that Tstruct_ListWithKey_Key partially implements the
// yang.GoKeyStruct interface. This allows functions that need to
// handle this key struct to identify it as being generated by gogen.
func (Tstruct_ListWithKey_Key) IsYANGGoKeyStruct() {}

// ΛListKeyMap returns the values of the Tstruct_ListWithKey_Key key struct.
func (t Tstruct_ListWithKey_Key) ΛListKeyMap() (map[string]interface{}, error) {
	return map[string]interface{}{
		"keyLeafOne": t.KeyLeafOne,
		"keyLeafTwo": t.KeyLeafTwo,
	}, nil
}
`,
			methods: `
// NewListWithKey creates a new entry in the ListWithKey list of the
// Tstruct struct. The keys of the list are populated from the input
// arguments.
func (t *Tstruct) NewListWithKey(KeyLeafOne string, KeyLeafTwo int8) (*Tstruct_ListWithKey, error){

	// Initialise the list within the receiver struct if it has not already been
	// created.
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey)
	}

	key := Tstruct_ListWithKey_Key{
		KeyLeafOne: KeyLeafOne,
		KeyLeafTwo: KeyLeafTwo,
	}

	// Ensure that this key has not already been used in the
	// list. Keyed YANG lists do not allow duplicate keys to
	// be created.
	if _, ok := t.ListWithKey[key]; ok {
		return nil, fmt.Errorf("duplicate key %v for list ListWithKey", key)
	}

	t.ListWithKey[key] = &Tstruct_ListWithKey{
		KeyLeafOne: &KeyLeafOne,
		KeyLeafTwo: &KeyLeafTwo,
	}

	return t.ListWithKey[key], nil
}

// GetOrCreateListWithKeyMap returns the list (map) from Tstruct.
//
// It initializes the field if not already initialized.
func (t *Tstruct) GetOrCreateListWithKeyMap() map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey {
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey)
	}
	return t.ListWithKey
}

// GetOrCreateListWithKey retrieves the value with the specified keys from
// the receiver Tstruct. If the entry does not exist, then it is created.
// It returns the existing or new list member.
func (t *Tstruct) GetOrCreateListWithKey(KeyLeafOne string, KeyLeafTwo int8) (*Tstruct_ListWithKey){

	key := Tstruct_ListWithKey_Key{
		KeyLeafOne: KeyLeafOne,
		KeyLeafTwo: KeyLeafTwo,
	}

	if v, ok := t.ListWithKey[key]; ok {
		return v
	}
	// Panic if we receive an error, since we should have retrieved an existing
	// list member. This allows chaining of GetOrCreate methods.
	v, err := t.NewListWithKey(KeyLeafOne, KeyLeafTwo)
	if err != nil {
		panic(fmt.Sprintf("GetOrCreateListWithKey got unexpected error: %v", err))
	}
	return v
}

// GetListWithKey retrieves the value with the specified key from
// the ListWithKey map field of Tstruct. If the receiver is nil, or
// the specified key is not present in the list, nil is returned such that Get*
// methods may be safely chained.
func (t *Tstruct) GetListWithKey(KeyLeafOne string, KeyLeafTwo int8) (*Tstruct_ListWithKey){

	if t == nil {
		return nil
	}

  key := Tstruct_ListWithKey_Key{
		KeyLeafOne: KeyLeafOne,
		KeyLeafTwo: KeyLeafTwo,
	}

  if lm, ok := t.ListWithKey[key]; ok {
    return lm
  }
  return nil
}

// DeleteListWithKey deletes the value with the specified keys from
// the receiver Tstruct. If there is no such element, the function
// is a no-op.
func (t *Tstruct) DeleteListWithKey(KeyLeafOne string, KeyLeafTwo int8) {
	key := Tstruct_ListWithKey_Key{
		KeyLeafOne: KeyLeafOne,
		KeyLeafTwo: KeyLeafTwo,
	}

	delete(t.ListWithKey, key)
}

// AppendListWithKey appends the supplied Tstruct_ListWithKey struct to the
// list ListWithKey of Tstruct. If the key value(s) specified in
// the supplied Tstruct_ListWithKey already exist in the list, an error is
// returned.
func (t *Tstruct) AppendListWithKey(v *Tstruct_ListWithKey) error {
	if v.KeyLeafOne == nil {
		return fmt.Errorf("invalid nil key for KeyLeafOne")
	}

	if v.KeyLeafTwo == nil {
		return fmt.Errorf("invalid nil key for KeyLeafTwo")
	}

	key := Tstruct_ListWithKey_Key{
		KeyLeafOne: *v.KeyLeafOne,
		KeyLeafTwo: *v.KeyLeafTwo,
	}

	// Initialise the list within the receiver struct if it has not already been
	// created.
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[Tstruct_ListWithKey_Key]*Tstruct_ListWithKey)
	}

	if _, ok := t.ListWithKey[key]; ok {
		return fmt.Errorf("duplicate key for list ListWithKey %v", key)
	}

	t.ListWithKey[key] = v
	return nil
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with single key list - append and getters",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Tstruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"listWithKey": {
					Name: "ListWithKey",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "list-with-key",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/tstruct/listWithKey",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ListNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"listWithKey"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/tstruct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/tstruct/listWithKey": {
				Name: "Tstruct_ListWithKey",
				Type: ygen.List,
				Fields: map[string]*ygen.NodeDetails{
					"keyLeaf": {
						Name: "keyLeaf",
						YANGDetails: ygen.YANGNodeDetails{
							Name:              "keyLeaf",
							Defaults:          nil,
							RootElementModule: "exmod",
							Path:              "/root-module/tstruct/listWithKey/keyLeaf",
							LeafrefTargetPath: "",
						},
						Type: ygen.LeafNode,
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
						MappedPaths:             [][]string{{"keyLeaf"}},
						MappedPathModules:       [][]string{{"exmod"}},
						ShadowMappedPaths:       nil,
						ShadowMappedPathModules: nil,
					},
				},
				ListKeys: map[string]*ygen.ListKey{
					"keyLeaf": {
						Name: "KeyLeaf",
						LangType: &ygen.MappedType{
							NativeType: "string",
							UnionTypes: nil,
							ZeroValue:  `""`,
						},
					},
				},
				ListKeyYANGNames: []string{"keyLeaf"},
				Path:             "/root-module/tstruct/listWithKey",
				BelongingModule:  "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			GenerateAppendMethod:    true,
			GenerateGetters:         true,
			GenerateDeleteMethod:    true,
			GeneratePopulateDefault: true,
		},
		want: wantGoStructOut{
			structs: `
// Tstruct represents the /root-module/tstruct YANG schema element.
type Tstruct struct {
	ListWithKey	map[string]*Tstruct_ListWithKey	` + "`" + `path:"listWithKey" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that Tstruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Tstruct) IsYANGGoStruct() {}
`,
			methods: `
// NewListWithKey creates a new entry in the ListWithKey list of the
// Tstruct struct. The keys of the list are populated from the input
// arguments.
func (t *Tstruct) NewListWithKey(KeyLeaf string) (*Tstruct_ListWithKey, error){

	// Initialise the list within the receiver struct if it has not already been
	// created.
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[string]*Tstruct_ListWithKey)
	}

	key := KeyLeaf

	// Ensure that this key has not already been used in the
	// list. Keyed YANG lists do not allow duplicate keys to
	// be created.
	if _, ok := t.ListWithKey[key]; ok {
		return nil, fmt.Errorf("duplicate key %v for list ListWithKey", key)
	}

	t.ListWithKey[key] = &Tstruct_ListWithKey{
		KeyLeaf: &KeyLeaf,
	}

	return t.ListWithKey[key], nil
}

// GetOrCreateListWithKeyMap returns the list (map) from Tstruct.
//
// It initializes the field if not already initialized.
func (t *Tstruct) GetOrCreateListWithKeyMap() map[string]*Tstruct_ListWithKey {
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[string]*Tstruct_ListWithKey)
	}
	return t.ListWithKey
}

// GetOrCreateListWithKey retrieves the value with the specified keys from
// the receiver Tstruct. If the entry does not exist, then it is created.
// It returns the existing or new list member.
func (t *Tstruct) GetOrCreateListWithKey(KeyLeaf string) (*Tstruct_ListWithKey){

	key := KeyLeaf

	if v, ok := t.ListWithKey[key]; ok {
		return v
	}
	// Panic if we receive an error, since we should have retrieved an existing
	// list member. This allows chaining of GetOrCreate methods.
	v, err := t.NewListWithKey(KeyLeaf)
	if err != nil {
		panic(fmt.Sprintf("GetOrCreateListWithKey got unexpected error: %v", err))
	}
	return v
}

// GetListWithKey retrieves the value with the specified key from
// the ListWithKey map field of Tstruct. If the receiver is nil, or
// the specified key is not present in the list, nil is returned such that Get*
// methods may be safely chained.
func (t *Tstruct) GetListWithKey(KeyLeaf string) (*Tstruct_ListWithKey){

	if t == nil {
		return nil
	}

  key := KeyLeaf

  if lm, ok := t.ListWithKey[key]; ok {
    return lm
  }
  return nil
}

// DeleteListWithKey deletes the value with the specified keys from
// the receiver Tstruct. If there is no such element, the function
// is a no-op.
func (t *Tstruct) DeleteListWithKey(KeyLeaf string) {
	key := KeyLeaf

	delete(t.ListWithKey, key)
}

// AppendListWithKey appends the supplied Tstruct_ListWithKey struct to the
// list ListWithKey of Tstruct. If the key value(s) specified in
// the supplied Tstruct_ListWithKey already exist in the list, an error is
// returned.
func (t *Tstruct) AppendListWithKey(v *Tstruct_ListWithKey) error {
	if v.KeyLeaf == nil {
		return fmt.Errorf("invalid nil key received for KeyLeaf")
	}

	key := *v.KeyLeaf

	// Initialise the list within the receiver struct if it has not already been
	// created.
	if t.ListWithKey == nil {
		t.ListWithKey = make(map[string]*Tstruct_ListWithKey)
	}

	if _, ok := t.ListWithKey[key]; ok {
		return fmt.Errorf("duplicate key for list ListWithKey %v", key)
	}

	t.ListWithKey[key] = v
	return nil
}

// PopulateDefaults recursively populates unset leaf fields in the Tstruct
// with default values as specified in the YANG schema, instantiating any nil
// container fields.
func (t *Tstruct) PopulateDefaults() {
	if (t == nil) {
		return
	}
	ygot.BuildEmptyTree(t)
	for _, e := range t.ListWithKey {
		e.PopulateDefaults()
	}
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Tstruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Tstruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Tstruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Tstruct.
func (*Tstruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "struct with child container - getters generated",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "InputStruct",
			Type: ygen.Container,
			Fields: map[string]*ygen.NodeDetails{
				"c1": {
					Name: "C1",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "c1",
						Defaults:          nil,
						RootElementModule: "exmod",
						Path:              "/root-module/input-struct/c1",
						LeafrefTargetPath: "",
					},
					Type:                    ygen.ContainerNode,
					LangType:                nil,
					MappedPaths:             [][]string{{"c1"}},
					MappedPathModules:       [][]string{{"exmod"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/root-module/input-struct",
			BelongingModule: "exmod",
		},
		inOtherStructMap: map[string]*ygen.ParsedDirectory{
			"/root-module/input-struct/c1": {
				Name:            "InputStruct_C1",
				Path:            "/root-module/input-struct/c1",
				BelongingModule: "exmod",
			},
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			GenerateGetters:         true,
			GeneratePopulateDefault: true,
		},
		want: wantGoStructOut{
			structs: `
// InputStruct represents the /root-module/input-struct YANG schema element.
type InputStruct struct {
	C1	*InputStruct_C1	` + "`" + `path:"c1" module:"exmod"` + "`" + `
}

// IsYANGGoStruct ensures that InputStruct implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*InputStruct) IsYANGGoStruct() {}
`,
			methods: `
// GetOrCreateC1 retrieves the value of the C1 field
// or returns the existing field if it already exists.
func (t *InputStruct) GetOrCreateC1() *InputStruct_C1 {
	if t.C1 != nil {
		return t.C1
	}
	t.C1 = &InputStruct_C1{}
	return t.C1
}

// GetC1 returns the value of the C1 struct pointer
// from InputStruct. If the receiver or the field C1 is nil, nil
// is returned such that the Get* methods can be safely chained.
func (t *InputStruct) GetC1() *InputStruct_C1 {
	if t != nil && t.C1 != nil {
		return t.C1
	}
	return nil
}

// PopulateDefaults recursively populates unset leaf fields in the InputStruct
// with default values as specified in the YANG schema, instantiating any nil
// container fields.
func (t *InputStruct) PopulateDefaults() {
	if (t == nil) {
		return
	}
	ygot.BuildEmptyTree(t)
	t.C1.PopulateDefaults()
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *InputStruct) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["InputStruct"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *InputStruct) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of InputStruct.
func (*InputStruct) ΛBelongingModule() string {
	return "exmod"
}
`,
		},
	}, {
		name: "container with leaf getters",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Container",
			Fields: map[string]*ygen.NodeDetails{
				"leaf": {
					Name: "Leaf",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "leaf",
						Defaults:          nil,
						RootElementModule: "m1",
						Path:              "/m1/foo/bar/leaf",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType:        "string",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         `""`,
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"bar", "leaf"}},
					MappedPathModules:       [][]string{{"m1", "m1"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/m1/foo",
			BelongingModule: "m1",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			GenerateLeafGetters:     true,
			GeneratePopulateDefault: true,
		},
		want: wantGoStructOut{
			structs: `
// Container represents the /m1/foo YANG schema element.
type Container struct {
	Leaf	*string	` + "`" + `path:"bar/leaf" module:"m1/m1"` + "`" + `
}

// IsYANGGoStruct ensures that Container implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Container) IsYANGGoStruct() {}
`,
			methods: `
// GetLeaf retrieves the value of the leaf Leaf from the Container
// struct. If the field is unset but has a default value in the YANG schema,
// then the default value will be returned.
// Caution should be exercised whilst using this method since when without a
// default value, it will return the Go zero value if the field is explicitly
// unset. If the caller explicitly does not care if Leaf is set, it can
// safely use t.GetLeaf() to retrieve the value. In the case that the
// caller has different actions based on whether the leaf is set or unset, it
// should use 'if t.Leaf == nil' before retrieving the leaf's value.
func (t *Container) GetLeaf() string {
	if t == nil || t.Leaf == nil {
		return ""
	}
	return *t.Leaf
}

// PopulateDefaults recursively populates unset leaf fields in the Container
// with default values as specified in the YANG schema, instantiating any nil
// container fields.
func (t *Container) PopulateDefaults() {
	if (t == nil) {
		return
	}
	ygot.BuildEmptyTree(t)
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Container) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Container"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Container) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Container.
func (*Container) ΛBelongingModule() string {
	return "m1"
}
`,
		},
	}, {
		name: "leaf getter with default value",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Container",
			Fields: map[string]*ygen.NodeDetails{
				"leaf": {
					Name: "Leaf",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "leaf",
						Defaults:          []string{"DEFAULT VALUE"},
						RootElementModule: "m1",
						Path:              "/m1/foo/bar/leaf",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType:        "string",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         `""`,
						DefaultValue:      ygot.String(`"DEFAULT VALUE"`),
					},
					MappedPaths:             [][]string{{"bar", "leaf"}},
					MappedPathModules:       [][]string{{"m1", "m1"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/m1/foo",
			BelongingModule: "m1",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:      true,
			GenerateLeafGetters:     true,
			GeneratePopulateDefault: true,
		},
		want: wantGoStructOut{
			structs: `
// Container represents the /m1/foo YANG schema element.
type Container struct {
	Leaf	*string	` + "`" + `path:"bar/leaf" module:"m1/m1"` + "`" + `
}

// IsYANGGoStruct ensures that Container implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Container) IsYANGGoStruct() {}
`,
			methods: `
// GetLeaf retrieves the value of the leaf Leaf from the Container
// struct. If the field is unset but has a default value in the YANG schema,
// then the default value will be returned.
// Caution should be exercised whilst using this method since when without a
// default value, it will return the Go zero value if the field is explicitly
// unset. If the caller explicitly does not care if Leaf is set, it can
// safely use t.GetLeaf() to retrieve the value. In the case that the
// caller has different actions based on whether the leaf is set or unset, it
// should use 'if t.Leaf == nil' before retrieving the leaf's value.
func (t *Container) GetLeaf() string {
	if t == nil || t.Leaf == nil {
		return "DEFAULT VALUE"
	}
	return *t.Leaf
}

// PopulateDefaults recursively populates unset leaf fields in the Container
// with default values as specified in the YANG schema, instantiating any nil
// container fields.
func (t *Container) PopulateDefaults() {
	if (t == nil) {
		return
	}
	ygot.BuildEmptyTree(t)
	if t.Leaf == nil {
		var v string = "DEFAULT VALUE"
		t.Leaf = &v
	}
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Container) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Container"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Container) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Container.
func (*Container) ΛBelongingModule() string {
	return "m1"
}
`,
		},
	}, {
		name: "container with leaf setters",
		inStructToMap: &ygen.ParsedDirectory{
			Name: "Container",
			Fields: map[string]*ygen.NodeDetails{
				"leafStr": {
					Name: "LeafStr",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "leafStr",
						Defaults:          nil,
						RootElementModule: "m1",
						Path:              "/m1/foo/bar/leafStr",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType:        "string",
						UnionTypes:        nil,
						IsEnumeratedValue: false,
						ZeroValue:         `""`,
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"bar", "leafStr"}},
					MappedPathModules:       [][]string{{"m1", "m1"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
				"leafUnion": {
					Name: "LeafUnion",
					YANGDetails: ygen.YANGNodeDetails{
						Name:              "leafUnion",
						Defaults:          nil,
						RootElementModule: "m1",
						Path:              "/m1/foo/bar/leafUnion",
						LeafrefTargetPath: "",
					},
					Type: ygen.LeafNode,
					LangType: &ygen.MappedType{
						NativeType: "Container_U1_Union",
						UnionTypes: map[string]ygen.MappedUnionSubtype{
							"string": {
								Index: 0,
							},
							"int8": {
								Index: 1,
							},
						},
						IsEnumeratedValue: false,
						ZeroValue:         "nil",
						DefaultValue:      nil,
					},
					MappedPaths:             [][]string{{"bar", "leafUnion"}},
					MappedPathModules:       [][]string{{"m1", "m1"}},
					ShadowMappedPaths:       nil,
					ShadowMappedPathModules: nil,
				},
			},
			Path:            "/m1/foo",
			BelongingModule: "m1",
		},
		inGoOpts: GoOpts{
			GenerateJSONSchema:  true,
			GenerateLeafSetters: true,
		},
		want: wantGoStructOut{
			structs: `
// Container represents the /m1/foo YANG schema element.
type Container struct {
	LeafStr	*string	` + "`" + `path:"bar/leafStr" module:"m1/m1"` + "`" + `
	LeafUnion	Container_U1_Union	` + "`" + `path:"bar/leafUnion" module:"m1/m1"` + "`" + `
}

// IsYANGGoStruct ensures that Container implements the yang.GoStruct
// interface. This allows functions that need to handle this struct to
// identify it as being generated by ygen.
func (*Container) IsYANGGoStruct() {}
`,
			methods: `
// SetLeafStr sets the value of the leaf LeafStr in the Container
// struct.
func (t *Container) SetLeafStr(v string) {
	t.LeafStr = &v
}

// SetLeafUnion sets the value of the leaf LeafUnion in the Container
// struct.
func (t *Container) SetLeafUnion(v Container_U1_Union) {
	t.LeafUnion = v
}

// Validate validates s against the YANG schema corresponding to its type.
func (t *Container) ΛValidate(opts ...ygot.ValidationOption) error {
	if err := ytypes.Validate(SchemaTree["Container"], t, opts...); err != nil {
		return err
	}
	return nil
}

// ΛEnumTypeMap returns a map, keyed by YANG schema path, of the enumerated types
// that are included in the generated code.
func (t *Container) ΛEnumTypeMap() map[string][]reflect.Type { return ΛEnumTypes }

// ΛBelongingModule returns the name of the module that defines the namespace
// of Container.
func (*Container) ΛBelongingModule() string {
	return "m1"
}
`,
			interfaces: `
// Container_U1_Union is an interface that is implemented by valid types for the union
// for the leaf /m1/foo/bar/leafUnion within the YANG schema.
type Container_U1_Union interface {
	Is_Container_U1_Union()
}

// Container_U1_Union_Int8 is used when /m1/foo/bar/leafUnion
// is to be set to a int8 value.
type Container_U1_Union_Int8 struct {
	Int8	int8
}

// Is_Container_U1_Union ensures that Container_U1_Union_Int8
// implements the Container_U1_Union interface.
func (*Container_U1_Union_Int8) Is_Container_U1_Union() {}

// Container_U1_Union_String is used when /m1/foo/bar/leafUnion
// is to be set to a string value.
type Container_U1_Union_String struct {
	String	string
}

// Is_Container_U1_Union ensures that Container_U1_Union_String
// implements the Container_U1_Union interface.
func (*Container_U1_Union_String) Is_Container_U1_Union() {}

// To_Container_U1_Union takes an input interface{} and attempts to convert it to a struct
// which implements the Container_U1_Union union. It returns an error if the interface{} supplied
// cannot be converted to a type within the union.
func (t *Container) To_Container_U1_Union(i interface{}) (Container_U1_Union, error) {
	switch v := i.(type) {
	case int8:
		return &Container_U1_Union_Int8{v}, nil
	case string:
		return &Container_U1_Union_String{v}, nil
	default:
		return nil, fmt.Errorf("cannot convert %v to Container_U1_Union, unknown union type, got: %T, want any of [int8, string]", i, i)
	}
}
`,
		},
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Fill the current directory into the map to reduce test size.
			if tt.inOtherStructMap == nil {
				tt.inOtherStructMap = map[string]*ygen.ParsedDirectory{}
			}
			tt.inOtherStructMap[tt.inStructToMap.Path] = tt.inStructToMap
			// Always generate the JSON schema for this test.
			generatedUnions := map[string]bool{}
			got, errs := writeGoStruct(tt.inStructToMap, tt.inOtherStructMap, generatedUnions, tt.inGoOpts)

			if len(errs) != 0 && !tt.want.wantErr {
				t.Fatalf("%s writeGoStruct(targetStruct: %v): received unexpected errors: %v",
					tt.name, tt.inStructToMap, errs)
			}

			if len(errs) == 0 && tt.want.wantErr {
				t.Fatalf("%s writeGoStruct(targetStruct: %v): did not receive expected errors",
					tt.name, tt.inStructToMap)
			}

			// If we wanted an error, then skip the rest of the tests as the generated code will not
			// be correct.
			if tt.want.wantErr {
				return
			}

			if diff := pretty.Compare(tt.want.structs, got.StructDef); diff != "" {
				if diffl, err := testutil.GenerateUnifiedDiff(tt.want.structs, got.StructDef); err == nil {
					diff = diffl
				}
				t.Errorf("%s writeGoStruct(targetStruct: %v): struct generated code was not correct, diff (-want, +got):\n%s",
					tt.name, tt.inStructToMap, diff)
			}

			if diff := pretty.Compare(tt.want.keys, got.ListKeys); diff != "" {
				if diffl, err := testutil.GenerateUnifiedDiff(tt.want.keys, got.ListKeys); err == nil {
					diff = diffl
				}
				t.Errorf("%s writeGoStruct(targetStruct: %v): structs generated as list keys incorrect, diff (-want, +got):\n%s",
					tt.name, tt.inStructToMap, diff)
			}

			if diff := pretty.Compare(tt.want.methods, got.Methods); diff != "" {
				if diffl, err := testutil.GenerateUnifiedDiff(tt.want.methods, got.Methods); err == nil {
					diff = diffl
				}
				t.Errorf("%s writeGoStruct(targetStruct: %v): generated methods incorrect, diff (-want, +got):\n%s",
					tt.name, tt.inStructToMap, diff)
			}

			if diff := pretty.Compare(tt.want.interfaces, got.Interfaces); diff != "" {
				if diffl, err := testutil.GenerateUnifiedDiff(tt.want.interfaces, got.Interfaces); err == nil {
					diff = diffl
				}
				t.Errorf("%s: writeGoStruct(targetStruct: %v): interfaces generated for struct incorrect, diff (-want, +got):\n%s",
					tt.name, tt.inStructToMap, diff)
			}
		})
	}
}
