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 }