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  }