github.com/openconfig/goyang@v1.4.5/pkg/yang/lex_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  	"runtime"
    20  	"testing"
    21  )
    22  
    23  // line returns the line number from which it was called.
    24  // Used to mark where test entries are in the source.
    25  func line() int {
    26  	_, _, line, _ := runtime.Caller(1)
    27  	return line
    28  
    29  }
    30  
    31  // Equal returns true if t and tt are equal (have the same code and text),
    32  // false if not.
    33  func (t *token) Equal(tt *token) bool {
    34  	return t.code == tt.code && t.Text == tt.Text
    35  }
    36  
    37  // T Creates a new token from the provided code and string.
    38  func T(c code, text string) *token { return &token{code: c, Text: text} }
    39  
    40  func TestLex(t *testing.T) {
    41  Tests:
    42  	for _, tt := range []struct {
    43  		line   int
    44  		in     string
    45  		tokens []*token
    46  	}{
    47  		{line(), "", nil},
    48  		{line(), "bob", []*token{
    49  			T(tUnquoted, "bob"),
    50  		}},
    51  		{line(), "bob //bob", []*token{
    52  			T(tUnquoted, "bob"),
    53  		}},
    54  		{line(), "/the/path", []*token{
    55  			T(tUnquoted, "/the/path"),
    56  		}},
    57  		{line(), "+the/path", []*token{
    58  			T(tUnquoted, "+the/path"),
    59  		}},
    60  		{line(), "+the+path", []*token{
    61  			T(tUnquoted, "+the+path"),
    62  		}},
    63  		{line(), "+ the/path", []*token{
    64  			T(tUnquoted, "+"),
    65  			T(tUnquoted, "the/path"),
    66  		}},
    67  		{line(), "{bob}", []*token{
    68  			T('{', "{"),
    69  			T(tUnquoted, "bob"),
    70  			T('}', "}"),
    71  		}},
    72  		{line(), "bob;fred", []*token{
    73  			T(tUnquoted, "bob"),
    74  			T(';', ";"),
    75  			T(tUnquoted, "fred"),
    76  		}},
    77  		{line(), "\t bob\t; fred ", []*token{
    78  			T(tUnquoted, "bob"),
    79  			T(';', ";"),
    80  			T(tUnquoted, "fred"),
    81  		}},
    82  		{line(), `
    83  	bob;
    84  	fred
    85  `, []*token{
    86  			T(tUnquoted, "bob"),
    87  			T(';', ";"),
    88  			T(tUnquoted, "fred"),
    89  		}},
    90  		{line(), `
    91  	// This is a comment
    92  	bob;
    93  	fred
    94  `, []*token{
    95  			T(tUnquoted, "bob"),
    96  			T(';', ";"),
    97  			T(tUnquoted, "fred"),
    98  		}},
    99  		{line(), `
   100  	/* This is a comment */
   101  	bob;
   102  	fred
   103  `, []*token{
   104  			T(tUnquoted, "bob"),
   105  			T(';', ";"),
   106  			T(tUnquoted, "fred"),
   107  		}},
   108  		{line(), `
   109  	/*
   110  	 * This is a comment
   111  	 */
   112  	bob;
   113  	fred
   114  `, []*token{
   115  			T(tUnquoted, "bob"),
   116  			T(';', ";"),
   117  			T(tUnquoted, "fred"),
   118  		}},
   119  		{line(), `
   120  	bob; // This is bob
   121  	fred // This is fred
   122  `, []*token{
   123  			T(tUnquoted, "bob"),
   124  			T(';', ";"),
   125  			T(tUnquoted, "fred"),
   126  		}},
   127  		{line(), `
   128  pattern '[a-zA-Z0-9!#$%&'+"'"+'*+/=?^_` + "`" + `{|}~-]+';
   129  `, []*token{
   130  			T(tUnquoted, "pattern"),
   131  			T(tString, "[a-zA-Z0-9!#$%&"),
   132  			T(tUnquoted, "+"),
   133  			T(tString, "'"),
   134  			T(tUnquoted, "+"),
   135  			T(tString, "*+/=?^_`{|}~-]+"),
   136  			T(';', ";"),
   137  		}},
   138  		{line(), `
   139  // tab indent both lines
   140  	"Broken
   141  	line"
   142  `, []*token{
   143  			T(tString, "Broken\nline"),
   144  		}},
   145  		{line(), `
   146  // tab indent both lines, trailing spaces and tabs
   147  	"Broken
   148  	 line"
   149  `, []*token{
   150  			T(tString, "Broken\nline"),
   151  		}},
   152  		{line(), `
   153  // tab indent first line, spaces and tab second line
   154  	"Broken
   155      	 line"
   156  `, []*token{
   157  			T(tString, "Broken\nline"),
   158  		}},
   159  		{line(), `
   160  // tab indent first line, spaces second linfe
   161  	"Broken
   162           line"
   163  `, []*token{
   164  			T(tString, "Broken\nline"),
   165  		}},
   166  		{line(), `
   167  // extra space in second line
   168  	"Broken
   169            space"
   170  `, []*token{
   171  			T(tString, "Broken\n space"),
   172  		}},
   173  		{line(), `
   174  // spaces first line, tab on second
   175         "Broken
   176  	space"
   177  `, []*token{
   178  			T(tString, "Broken\nspace"),
   179  		}},
   180  		{line(), `
   181  // Odd indenting
   182     "Broken
   183    space"
   184  `, []*token{
   185  			T(tString, "Broken\nspace"),
   186  		}},
   187  		{line(), `
   188  // Odd indenting
   189     "Broken  \t
   190    space with trailing space"
   191  `, []*token{
   192  			T(tString, "Broken\nspace with trailing space"),
   193  		}},
   194  	} {
   195  		l := newLexer(tt.in, "")
   196  		// l.debug = true
   197  		for i := 0; ; i++ {
   198  			token := l.NextToken()
   199  			if token == nil {
   200  				if len(tt.tokens) != i {
   201  					t.Errorf("%d: got %d tokens, want %d", tt.line, i, len(tt.tokens))
   202  				}
   203  				continue Tests
   204  			}
   205  			if len(tt.tokens) > i && !token.Equal(tt.tokens[i]) {
   206  				t.Errorf("%d, %d: got (%v, %q) want (%v, %q)", tt.line, i, token.code, token.Text, tt.tokens[i].code, tt.tokens[i].Text)
   207  			}
   208  		}
   209  	}
   210  }
   211  
   212  func TestLexErrors(t *testing.T) {
   213  	for _, tt := range []struct {
   214  		line   int
   215  		in     string
   216  		errcnt int
   217  		errs   string
   218  	}{
   219  		{line(),
   220  			`1: "no closing quote`,
   221  			1,
   222  			`test.yang:1:4: missing closing "
   223  `,
   224  		},
   225  		{line(),
   226  			`1: on another line
   227  2: there is "no closing quote\"`,
   228  			1,
   229  			`test.yang:2:13: missing closing "
   230  `,
   231  		},
   232  		{line(),
   233  			`1:
   234  2: "Mares eat oats,"
   235  3: "And does eat oats,"
   236  4: "But little lambs eat ivy,"
   237  5: "and if I were a little lamb,"
   238  6: "I'ld eat ivy too.
   239  5: So saith the sage.`,
   240  			1,
   241  			`test.yang:6:4: missing closing "
   242  `,
   243  		},
   244  		{line(),
   245  			`1:
   246  2: "Quoted string"
   247  3: "Missing quote
   248  4: "Another quoted string"
   249  `,
   250  			1,
   251  			`test.yang:4:26: missing closing "
   252  `,
   253  		},
   254  		{line(),
   255  			`1:
   256  2: 'Quoted string'
   257  3: 'Missing quote
   258  4: 'Another quoted string'
   259  `,
   260  			1,
   261  			`test.yang:4:26: missing closing '
   262  `,
   263  		},
   264  		{line(),
   265  			`1: "Quoted string\"
   266  2: Missing end-quote\q`,
   267  			2,
   268  			`test.yang:2:21: invalid escape sequence: \q
   269  test.yang:1:4: missing closing "
   270  `,
   271  		},
   272  		{line(),
   273  			`/* This is a comment
   274  without an ending.
   275  `,
   276  			1,
   277  			`test.yang:1:1: missing closing */
   278  `,
   279  		},
   280  		{line(),
   281  			// Two errors too many.
   282  			`yang-version 1.1;description "\/\/\/\/\/\/\/\/\/\/";`,
   283  			9,
   284  			`test.yang:1:31: invalid escape sequence: \/
   285  test.yang:1:33: invalid escape sequence: \/
   286  test.yang:1:35: invalid escape sequence: \/
   287  test.yang:1:37: invalid escape sequence: \/
   288  test.yang:1:39: invalid escape sequence: \/
   289  test.yang:1:41: invalid escape sequence: \/
   290  test.yang:1:43: invalid escape sequence: \/
   291  test.yang:1:45: invalid escape sequence: \/
   292  ` + tooMany,
   293  		},
   294  	} {
   295  		l := newLexer(tt.in, "test.yang")
   296  		errbuf := &bytes.Buffer{}
   297  		l.errout = errbuf
   298  		for l.NextToken() != nil {
   299  
   300  		}
   301  		if l.errcnt != tt.errcnt {
   302  			t.Errorf("%d: got %d errors, want %v", tt.line, l.errcnt, tt.errcnt)
   303  		}
   304  		errs := errbuf.String()
   305  		if errs != tt.errs {
   306  			t.Errorf("%d: got errors:\n%s\nwant:\n%s", tt.line, errs, tt.errs)
   307  		}
   308  	}
   309  }