github.com/openconfig/goyang@v1.4.5/pkg/yang/modules_test.go (about)

     1  // Copyright 2016 Google Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package yang
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/openconfig/gnmi/errdiff"
    22  )
    23  
    24  var testdataFindModulesText = map[string]string{
    25  	"foo":         `module foo { prefix "foo"; namespace "urn:foo"; }`,
    26  	"bar":         `module bar { prefix "bar"; namespace "urn:bar"; }`,
    27  	"baz":         `module baz { prefix "baz"; namespace "urn:baz"; }`,
    28  	"dup-pre-one": `module dup-pre-one { prefix duplicate; namespace urn:duplicate:one; }`,
    29  	"dup-pre-two": `module dup-pre-two { prefix duplicate; namespace urn:duplicate:two; }`,
    30  	"dup-ns-one":  `module dup-ns-one { prefix ns-one; namespace urn:duplicate; }`,
    31  	"dup-ns-two":  `module dup-ns-two { prefix ns-two; namespace urn:duplicate; }`,
    32  }
    33  
    34  func TestDupModule(t *testing.T) {
    35  	tests := []struct {
    36  		desc      string
    37  		inModules map[string]string
    38  		wantErr   bool
    39  	}{{
    40  		desc: "two modules with the same name",
    41  		inModules: map[string]string{
    42  			"foo": `module foo { prefix "foo"; namespace "urn:foo"; }`,
    43  			"bar": `module foo { prefix "foo"; namespace "urn:foo"; }`,
    44  		},
    45  		wantErr: true,
    46  	}}
    47  
    48  	for _, tt := range tests {
    49  		t.Run(tt.desc, func(t *testing.T) {
    50  			ms := NewModules()
    51  			var err error
    52  			for name, modtext := range tt.inModules {
    53  				if err = ms.Parse(modtext, name+".yang"); err != nil {
    54  					break
    55  				}
    56  			}
    57  			if gotErr := err != nil; gotErr != tt.wantErr {
    58  				t.Fatalf("wantErr: %v, got error: %v", tt.wantErr, err)
    59  			}
    60  		})
    61  	}
    62  }
    63  
    64  func testModulesForTestdataModulesText(t *testing.T) *Modules {
    65  	ms := NewModules()
    66  	for name, modtext := range testdataFindModulesText {
    67  		if err := ms.Parse(modtext, name+".yang"); err != nil {
    68  			t.Fatalf("error importing testdataFindModulesText[%q]: %v", name, err)
    69  		}
    70  	}
    71  	if errs := ms.Process(); errs != nil {
    72  		for _, err := range errs {
    73  			t.Errorf("error: %v", err)
    74  		}
    75  		t.Fatalf("fatal error(s) calling Process()")
    76  	}
    77  	return ms
    78  }
    79  
    80  func testModulesFindByCommonHandler(t *testing.T, i int, got, want *Module, wantError string, err error) {
    81  	if err != nil {
    82  		if wantError != "" {
    83  			if !strings.Contains(err.Error(), wantError) {
    84  				t.Errorf("[%d] want error containing %q, got %q",
    85  					i, wantError, err.Error())
    86  			}
    87  		} else {
    88  			t.Errorf("[%d] unexpected error: %v", i, err)
    89  		}
    90  	} else if wantError != "" {
    91  		t.Errorf("[%d] want error containing %q, got nil", i, wantError)
    92  	} else if want != got {
    93  		t.Errorf("[%d] want module %#v, got %#v", i, want, got)
    94  	}
    95  }
    96  
    97  func TestModulesFindByNamespace(t *testing.T) {
    98  	ms := testModulesForTestdataModulesText(t)
    99  
   100  	for i, tc := range []struct {
   101  		namespace string
   102  		want      *Module
   103  		wantError string
   104  	}{
   105  		{
   106  			namespace: "does-not-exist",
   107  			wantError: `"does-not-exist": no such namespace`,
   108  		},
   109  		{
   110  			namespace: "urn:foo",
   111  			want:      ms.Modules["foo"],
   112  		},
   113  		{
   114  			namespace: "urn:bar",
   115  			want:      ms.Modules["bar"],
   116  		},
   117  		{
   118  			namespace: "urn:baz",
   119  			want:      ms.Modules["baz"],
   120  		},
   121  		{
   122  			namespace: "urn:duplicate",
   123  			wantError: "namespace urn:duplicate matches two or more modules (dup-ns-",
   124  		},
   125  	} {
   126  		got, err := ms.FindModuleByNamespace(tc.namespace)
   127  		testModulesFindByCommonHandler(t, i, got, tc.want, tc.wantError, err)
   128  	}
   129  }
   130  
   131  func TestModuleLinkage(t *testing.T) {
   132  	tests := []struct {
   133  		desc          string
   134  		inMods        map[string]string
   135  		wantErrSubstr string
   136  	}{{
   137  		desc: "invalid import",
   138  		inMods: map[string]string{
   139  			"dev": `
   140  				module dev {
   141  					prefix d;
   142  					namespace "urn:d";
   143  					import sys { prefix sys; }
   144  
   145  					revision 01-01-01 { description "the start of time"; }
   146  
   147  					deviation /sys:sys/sys:hostname {
   148  						deviate not-supported;
   149  					}
   150  				}`,
   151  		},
   152  		wantErrSubstr: "no such module",
   153  	}, {
   154  		desc: "valid include",
   155  		inMods: map[string]string{
   156  			"dev": `
   157  				module dev {
   158  					prefix d;
   159  					namespace "urn:d";
   160  					include sys;
   161  
   162  					revision 01-01-01 { description "the start of time"; }
   163  				}`,
   164  			"sys": `
   165  				submodule sys {
   166  					belongs-to dev {
   167  						prefix "d";
   168  					}
   169  
   170  					revision 01-01-01 { description "the start of time"; }
   171  
   172  					container sys { leaf hostname { type string; } }
   173  				}`,
   174  		},
   175  	}, {
   176  		desc: "invalid include",
   177  		inMods: map[string]string{
   178  			"dev": `
   179  				module dev {
   180  					prefix d;
   181  					namespace "urn:d";
   182  					include sys;
   183  
   184  					revision 01-01-01 { description "the start of time"; }
   185  				}`,
   186  			"sysdb": `
   187  				submodule sysdb {
   188  					belongs-to dev {
   189  						prefix "d";
   190  					}
   191  
   192  					revision 01-01-01 { description "the start of time"; }
   193  
   194  					container sys { leaf hostname { type string; } }
   195  				}`,
   196  		},
   197  		wantErrSubstr: "no such submodule",
   198  	}, {
   199  		desc: "valid include in submodule",
   200  		inMods: map[string]string{
   201  			"dev": `
   202  				module dev {
   203  					prefix d;
   204  					namespace "urn:d";
   205  					include sys;
   206  
   207  					revision 01-01-01 { description "the start of time"; }
   208  				}`,
   209  			"sys": `
   210  				submodule sys {
   211  					belongs-to dev {
   212  						prefix "d";
   213  					}
   214  					include sysdb;
   215  
   216  					revision 01-01-01 { description "the start of time"; }
   217  
   218  					container sys { leaf hostname { type string; } }
   219  				}`,
   220  			"sysdb": `
   221  				submodule sysdb {
   222  					belongs-to dev {
   223  						prefix "d";
   224  					}
   225  
   226  					revision 01-01-01 { description "the start of time"; }
   227  
   228  					container sysdb { leaf hostname { type string; } }
   229  				}`,
   230  		},
   231  	}, {
   232  		desc: "invalid include in submodule",
   233  		inMods: map[string]string{
   234  			"dev": `
   235  				module dev {
   236  					prefix d;
   237  					namespace "urn:d";
   238  					include sys;
   239  
   240  					revision 01-01-01 { description "the start of time"; }
   241  				}`,
   242  			"sys": `
   243  				submodule sys {
   244  					belongs-to dev {
   245  						prefix "d";
   246  					}
   247  					include sysdb;
   248  
   249  					revision 01-01-01 { description "the start of time"; }
   250  
   251  					container sys { leaf hostname { type string; } }
   252  				}`,
   253  			"syyysdb": `
   254  				submodule syyysdb {
   255  					belongs-to dev {
   256  						prefix "d";
   257  					}
   258  
   259  					revision 01-01-01 { description "the start of time"; }
   260  
   261  					container sysdb { leaf hostname { type string; } }
   262  				}`,
   263  		},
   264  		wantErrSubstr: "no such submodule",
   265  	}, {
   266  		desc: "valid import in submodule",
   267  		inMods: map[string]string{
   268  			"dev": `
   269  				module dev {
   270  					prefix d;
   271  					namespace "urn:d";
   272  					include sys;
   273  
   274  					revision 01-01-01 { description "the start of time"; }
   275  				}`,
   276  			"sys": `
   277  				submodule sys {
   278  					belongs-to dev {
   279  						prefix "d";
   280  					}
   281  					import sysdb {
   282  						prefix "sd";
   283  					}
   284  
   285  					revision 01-01-01 { description "the start of time"; }
   286  
   287  					container sys { leaf hostname { type string; } }
   288  				}`,
   289  			"sysdb": `
   290  				module sysdb {
   291  					prefix sd;
   292  					namespace "urn:sd";
   293  
   294  					revision 01-01-01 { description "the start of time"; }
   295  
   296  					container sysdb { leaf hostname { type string; } }
   297  				}`,
   298  		},
   299  	}, {
   300  		desc: "invalid import in submodule",
   301  		inMods: map[string]string{
   302  			"dev": `
   303  				module dev {
   304  					prefix d;
   305  					namespace "urn:d";
   306  					include sys;
   307  
   308  					revision 01-01-01 { description "the start of time"; }
   309  				}`,
   310  			"sys": `
   311  				submodule sys {
   312  					belongs-to dev {
   313  						prefix "d";
   314  					}
   315  					import sysdb {
   316  						prefix "sd";
   317  					}
   318  
   319  					revision 01-01-01 { description "the start of time"; }
   320  
   321  					container sys { leaf hostname { type string; } }
   322  				}`,
   323  			"syyysdb": `
   324  				module syyysdb {
   325  					prefix sd;
   326  					namespace "urn:sd";
   327  
   328  					revision 01-01-01 { description "the start of time"; }
   329  
   330  					container sysdb { leaf hostname { type string; } }
   331  				}`,
   332  		},
   333  		wantErrSubstr: "no such module",
   334  	}}
   335  
   336  	for _, tt := range tests {
   337  		t.Run(tt.desc, func(t *testing.T) {
   338  			ms := NewModules()
   339  
   340  			for n, m := range tt.inMods {
   341  				if err := ms.Parse(m, n); err != nil {
   342  					t.Fatalf("cannot parse module %s, err: %v", n, err)
   343  				}
   344  			}
   345  
   346  			errs := ms.Process()
   347  			var err error
   348  			switch len(errs) {
   349  			case 1:
   350  				err = errs[0]
   351  				fallthrough
   352  			case 0:
   353  				if diff := errdiff.Substring(err, tt.wantErrSubstr); diff != "" {
   354  					t.Fatalf("%s", diff)
   355  				}
   356  			default:
   357  				t.Fatalf("got multiple errors: %v", errs)
   358  			}
   359  		})
   360  	}
   361  }
   362  
   363  func TestModulesTotalProcess(t *testing.T) {
   364  	tests := []struct {
   365  		desc    string
   366  		inMods  map[string]string
   367  		wantErr bool
   368  	}{{
   369  		desc: "import with deviation",
   370  		inMods: map[string]string{
   371  			"dev": `
   372  				module dev {
   373  					prefix d;
   374  					namespace "urn:d";
   375  					import sys { prefix sys; }
   376  
   377  					revision 01-01-01 { description "the start of time"; }
   378  
   379  					deviation /sys:sys/sys:hostname {
   380  						deviate not-supported;
   381  					}
   382  				}`,
   383  			"sys": `
   384  				module sys {
   385  					prefix s;
   386  					namespace "urn:s";
   387  
   388  					revision 01-01-01 { description "the start of time"; }
   389  
   390  					container sys { leaf hostname { type string; } }
   391  				}`,
   392  		},
   393  	}}
   394  
   395  	for _, tt := range tests {
   396  		t.Run(tt.desc, func(t *testing.T) {
   397  			ms := NewModules()
   398  
   399  			for n, m := range tt.inMods {
   400  				if err := ms.Parse(m, n); err != nil {
   401  					t.Fatalf("cannot parse module %s, err: %v", n, err)
   402  				}
   403  			}
   404  
   405  			errs := ms.Process()
   406  			switch {
   407  			case len(errs) == 0 && tt.wantErr:
   408  				t.Fatalf("did not get expected errors, got: %v, wantErr: %v", errs, tt.wantErr)
   409  			case len(errs) != 0 && !tt.wantErr:
   410  				t.Fatalf("got unexpected errors, got: %v, wantErr: %v", errs, tt.wantErr)
   411  			}
   412  		})
   413  	}
   414  }