github.com/openconfig/goyang@v1.4.5/pkg/yang/parse_test.go (about) 1 // Copyright 2015 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 "bytes" 19 "testing" 20 ) 21 22 func (s1 *Statement) equal(s2 *Statement) bool { 23 if s1.Keyword != s2.Keyword || 24 s1.HasArgument != s2.HasArgument || 25 s1.Argument != s2.Argument || 26 len(s1.statements) != len(s2.statements) { 27 return false 28 } 29 30 for x, ss := range s1.statements { 31 if !ss.equal(s2.statements[x]) { 32 return false 33 } 34 } 35 return true 36 } 37 38 // SA returns a statement with an argument and optional substatements. 39 func SA(k, a string, ss ...*Statement) *Statement { 40 return &Statement{ 41 Keyword: k, 42 Argument: a, 43 HasArgument: true, 44 statements: ss, 45 } 46 } 47 48 // S returns a statement with no argument and optional substatements. 49 func S(k string, ss ...*Statement) *Statement { 50 return &Statement{ 51 Keyword: k, 52 statements: ss, 53 } 54 } 55 56 func TestParse(t *testing.T) { 57 for _, tt := range []struct { 58 line int 59 in string 60 out []*Statement 61 err string 62 }{ 63 {line: line()}, 64 {line: line(), in: ` 65 foo; 66 `, 67 out: []*Statement{ 68 S("foo"), 69 }, 70 }, 71 {line: line(), in: ` 72 foo {} 73 `, 74 out: []*Statement{ 75 S("foo"), 76 }, 77 }, 78 {line: line(), in: ` 79 foo ""; 80 `, 81 out: []*Statement{ 82 SA("foo", ""), 83 }, 84 }, 85 {line: line(), in: ` 86 foo bar; 87 `, 88 out: []*Statement{ 89 SA("foo", "bar"), 90 }, 91 }, 92 {line: line(), in: ` 93 foo "bar"; 94 `, 95 out: []*Statement{ 96 SA("foo", "bar"), 97 }, 98 }, 99 {line: line(), in: ` 100 foo "\\ \S \n"; 101 `, 102 err: `test.yang:2:9: invalid escape sequence: \S`, 103 }, 104 {line: line(), in: ` 105 pattern "\\ \S \n"; 106 `, 107 out: []*Statement{ 108 SA("pattern", `\ \S 109 `), 110 }, 111 }, 112 {line: line(), in: ` 113 foo '\\ \S \n'; 114 `, 115 out: []*Statement{ 116 SA("foo", `\\ \S \n`), 117 }, 118 }, 119 {line: line(), in: ` 120 pattern '\\ \S \n'; 121 `, 122 out: []*Statement{ 123 SA("pattern", `\\ \S \n`), 124 }, 125 }, 126 {line: line(), in: ` 127 foo "bar" + "baz"; 128 `, 129 out: []*Statement{ 130 SA("foo", "barbaz"), 131 }, 132 }, 133 {line: line(), in: ` 134 foo "bar" + "+" + "baz"; 135 `, 136 out: []*Statement{ 137 SA("foo", "bar+baz"), 138 }, 139 }, 140 {line: line(), in: ` 141 foo "bar" 142 `, 143 err: `test.yang: unexpected EOF`, 144 }, 145 {line: line(), in: ` 146 foo "bar" + "baz" 147 `, 148 err: `test.yang: unexpected EOF`, 149 }, 150 {line: line(), in: ` 151 foo "bar" baz; 152 `, 153 err: `test.yang:2:11: baz: syntax error, expected ';' or '{' 154 test.yang:2:14: ;: keyword token not an unquoted string`, 155 }, 156 {line: line(), in: ` 157 foo "bar" + baz; 158 `, 159 err: `test.yang:2:11: +: syntax error, expected ';' or '{'`, 160 }, 161 {line: line(), in: ` 162 foo "bar" + 163 `, 164 err: `test.yang:2:11: +: syntax error, expected ';' or '{'`, 165 }, 166 {line: line(), in: ` 167 foo "bar"; 168 `, 169 out: []*Statement{ 170 SA("foo", "bar"), 171 }, 172 }, 173 {line: line(), in: ` 174 foo "bar" {} 175 `, 176 out: []*Statement{ 177 SA("foo", "bar"), 178 }, 179 }, 180 {line: line(), in: ` 181 foo 'bar' + 'baz'; 182 `, 183 out: []*Statement{ 184 SA("foo", "barbaz"), 185 }, 186 }, 187 {line: line(), in: ` 188 foo 'bar' + '+' + 'baz'; 189 `, 190 out: []*Statement{ 191 SA("foo", "bar+baz"), 192 }, 193 }, 194 {line: line(), in: ` 195 foo 'bar' 196 `, 197 err: `test.yang: unexpected EOF`, 198 }, 199 {line: line(), in: ` 200 foo 'bar' + 'baz' 201 `, 202 err: `test.yang: unexpected EOF`, 203 }, 204 {line: line(), in: ` 205 foo 'bar' baz; 206 `, 207 err: `test.yang:2:11: baz: syntax error, expected ';' or '{' 208 test.yang:2:14: ;: keyword token not an unquoted string`, 209 }, 210 {line: line(), in: ` 211 foo 'bar' + baz; 212 `, 213 err: `test.yang:2:11: +: syntax error, expected ';' or '{'`, 214 }, 215 {line: line(), in: ` 216 foo 'bar' + 217 `, 218 err: `test.yang:2:11: +: syntax error, expected ';' or '{'`, 219 }, 220 {line: line(), in: ` 221 foo 'bar'; 222 `, 223 out: []*Statement{ 224 SA("foo", "bar"), 225 }, 226 }, 227 {line: line(), in: ` 228 foo 'bar' {} 229 `, 230 out: []*Statement{ 231 SA("foo", "bar"), 232 }, 233 }, 234 {line: line(), in: ` 235 foo bar; 236 red black; 237 `, 238 out: []*Statement{ 239 SA("foo", "bar"), 240 SA("red", "black"), 241 }, 242 }, 243 {line: line(), in: ` 244 foo { 245 key value; 246 } 247 `, 248 out: []*Statement{ 249 S("foo", 250 SA("key", "value"), 251 ), 252 }, 253 }, 254 {line: line(), in: ` 255 foo { 256 key value; 257 } 258 `, 259 out: []*Statement{ 260 S("foo", 261 SA("key", "value"), 262 ), 263 }, 264 }, 265 {line: line(), in: ` 266 foo { 267 key "value1 value2 268 269 value3"; 270 } 271 `, 272 out: []*Statement{ 273 S("foo", 274 SA("key", "value1 value2\n\n value3"), 275 ), 276 }, 277 }, 278 {line: line(), in: ` 279 foo { 280 key value; 281 key2; 282 } 283 `, 284 out: []*Statement{ 285 S("foo", 286 SA("key", "value"), 287 S("key2"), 288 ), 289 }, 290 }, 291 {line: line(), in: ` 292 foo1 { 293 key value1; 294 } 295 foo2 { 296 key value2; 297 } 298 foo3 value3; 299 `, 300 out: []*Statement{ 301 S("foo1", 302 SA("key", "value1"), 303 ), 304 S("foo2", 305 SA("key", "value2"), 306 ), 307 SA("foo3", "value3"), 308 }, 309 }, 310 {line: line(), in: ` 311 foo1 { 312 key value1; 313 foo2 { 314 key value2; 315 } 316 } 317 `, 318 out: []*Statement{ 319 S("foo1", 320 SA("key", "value1"), 321 S("foo2", 322 SA("key", "value2"), 323 ), 324 ), 325 }, 326 }, 327 {line: line(), in: ` 328 foo1 { 329 key value1; 330 foo2 { 331 pattern '[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_` + "`" + `{|}~-]+' 332 + '(\.[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_` + "`" + `{|}~-]+)*' 333 + '@' 334 + '[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_` + "`" + `{|}~-]+' 335 + '(\.[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_` + "`" + `{|}~-]+)*'; 336 } 337 } 338 `, 339 out: []*Statement{ 340 S("foo1", 341 SA("key", "value1"), 342 S("foo2", 343 SA("pattern", "[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*"), 344 ), 345 ), 346 }, 347 }, 348 {line: line(), in: ` 349 } 350 `, 351 err: `test.yang:2:2: unexpected }`, 352 }, 353 {line: line(), in: ` 354 id 355 `, 356 err: `test.yang: unexpected EOF`, 357 }, 358 {line: line(), in: ` 359 { 360 `, 361 err: `test.yang:2:4: {: keyword token not an unquoted string`, 362 }, 363 {line: line(), in: ` 364 ; 365 `, 366 err: `test.yang:2:1: ;: keyword token not an unquoted string`, 367 }, 368 {line: line(), in: ` 369 statement one two { } 370 `, 371 err: `test.yang:2:15: two: syntax error, expected ';' or '{' 372 test.yang:2:19: {: keyword token not an unquoted string 373 test.yang:2:21: unexpected }`, 374 }, 375 {line: line(), in: ` 376 } 377 foo { 378 key: "value"; 379 } 380 `, 381 err: `test.yang:2:5: unexpected }`, 382 }, 383 {line: line(), in: ` 384 { 385 something: "bad"; 386 } 387 foo { 388 key: "\Value"; 389 key2: "value2"; 390 bar { 391 key3: "value\3; 392 } 393 }`, 394 err: `test.yang:2:1: {: keyword token not an unquoted string 395 test.yang:4:1: unexpected } 396 test.yang:6:8: invalid escape sequence: \V 397 test.yang:9:15: invalid escape sequence: \3 398 test.yang:9:9: missing closing " 399 test.yang: unexpected EOF`, 400 }, 401 {line: line(), in: ` 402 module base { 403 container top-missing-close-brace { 404 leaf my-leaf { 405 type string; 406 } 407 } 408 `, 409 err: "test.yang:8:0: missing 1 closing brace", 410 }, 411 {line: line(), in: ` 412 module base { 413 container top-missing-close-brace { 414 leaf my-leaf { 415 type string; 416 } 417 `, 418 err: "test.yang:7:0: missing 2 closing braces", 419 }, 420 } { 421 s, err := Parse(tt.in, "test.yang") 422 if (s == nil) != (tt.out == nil) { 423 if s == nil { 424 t.Errorf("%d: did not get expected statements: %v", tt.line, tt.out) 425 } else { 426 t.Errorf("%d: get unexpected statements: %v", tt.line, s) 427 } 428 } 429 switch { 430 case err == nil && tt.err == "": 431 case tt.err == "": 432 t.Errorf("%d: unexpected error %v", tt.line, err) 433 continue 434 case err == nil: 435 t.Errorf("%d: did not get expected error %v", tt.line, tt.err) 436 continue 437 case err.Error() == tt.err: 438 continue 439 default: 440 t.Errorf("%d: got error:\n%s\nwant:\n%s", tt.line, err, tt.err) 441 continue 442 } 443 s1 := &Statement{statements: s} 444 s2 := &Statement{statements: tt.out} 445 if !s1.equal(s2) { 446 t.Errorf("%d: got:\n%v\nwant:\n%v", tt.line, s1, s2) 447 } 448 } 449 } 450 451 func TestWrite(t *testing.T) { 452 Testing: 453 for _, tt := range []struct { 454 line int 455 in string 456 out string 457 }{ 458 {line: line(), 459 in: `key arg { substatement; }`, 460 out: `key "arg" { 461 substatement; 462 } 463 `, 464 }, 465 {line: line(), 466 in: `key { substatement { key arg; }}`, 467 out: `key { 468 substatement { 469 key "arg"; 470 } 471 } 472 `, 473 }, 474 {line: line(), 475 in: ` 476 module base { 477 namespace "urn:mod"; 478 prefix "base"; 479 480 typedef base-type { type int32; } 481 482 grouping base-group { 483 description 484 "The base-group is used to test the 485 'uses' statement below. This description 486 is here to simply include a multi-line 487 string as an example of multi-line strings"; 488 leaf base-group-leaf { 489 config false; 490 type string; 491 } 492 } 493 uses base-group; 494 } 495 `, out: `module "base" { 496 namespace "urn:mod"; 497 prefix "base"; 498 typedef "base-type" { 499 type "int32"; 500 } 501 grouping "base-group" { 502 description "The base-group is used to test the 503 'uses' statement below. This description 504 is here to simply include a multi-line 505 string as an example of multi-line strings"; 506 leaf "base-group-leaf" { 507 config "false"; 508 type "string"; 509 } 510 } 511 uses "base-group"; 512 } 513 `, 514 }, 515 } { 516 in := tt.in 517 // Run twice. The first time we are parsing tt.in, the second 518 // time we are parsing the output from the first parsing. 519 for i := 0; i < 2; i++ { 520 s, err := Parse(in, "test.yang") 521 if err != nil { 522 t.Errorf("%d: unexpected error %v", tt.line, err) 523 continue Testing 524 } 525 if len(s) != 1 { 526 t.Errorf("%d: got %d statements, expected 1", tt.line, len(s)) 527 continue Testing 528 } 529 var buf bytes.Buffer 530 s[0].Write(&buf, "") 531 out := buf.String() 532 if out != tt.out { 533 t.Errorf("%d: got:\n%swant:\n%s", tt.line, out, tt.out) 534 continue Testing 535 } 536 in = out 537 } 538 } 539 }