github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/syntax/parser_test.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package syntax
     6  
     7  import (
     8  	"bytes"
     9  	"cmd/internal/src"
    10  	"flag"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"sync"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  var fast = flag.Bool("fast", false, "parse package files in parallel")
    22  var src_ = flag.String("src", "parser.go", "source file to parse")
    23  var verify = flag.Bool("verify", false, "verify idempotent printing")
    24  
    25  func TestParse(t *testing.T) {
    26  	_, err := ParseFile(*src_, nil, nil, 0)
    27  	if err != nil {
    28  		t.Fatal(err)
    29  	}
    30  }
    31  
    32  func TestStdLib(t *testing.T) {
    33  	if testing.Short() {
    34  		t.Skip("skipping test in short mode")
    35  	}
    36  
    37  	var m1 runtime.MemStats
    38  	runtime.ReadMemStats(&m1)
    39  	start := time.Now()
    40  
    41  	type parseResult struct {
    42  		filename string
    43  		lines    uint
    44  	}
    45  
    46  	results := make(chan parseResult)
    47  	go func() {
    48  		defer close(results)
    49  		for _, dir := range []string{
    50  			runtime.GOROOT(),
    51  		} {
    52  			walkDirs(t, dir, func(filename string) {
    53  				if debug {
    54  					fmt.Printf("parsing %s\n", filename)
    55  				}
    56  				ast, err := ParseFile(filename, nil, nil, 0)
    57  				if err != nil {
    58  					t.Error(err)
    59  					return
    60  				}
    61  				if *verify {
    62  					verifyPrint(filename, ast)
    63  				}
    64  				results <- parseResult{filename, ast.Lines}
    65  			})
    66  		}
    67  	}()
    68  
    69  	var count, lines uint
    70  	for res := range results {
    71  		count++
    72  		lines += res.lines
    73  		if testing.Verbose() {
    74  			fmt.Printf("%5d  %s (%d lines)\n", count, res.filename, res.lines)
    75  		}
    76  	}
    77  
    78  	dt := time.Since(start)
    79  	var m2 runtime.MemStats
    80  	runtime.ReadMemStats(&m2)
    81  	dm := float64(m2.TotalAlloc-m1.TotalAlloc) / 1e6
    82  
    83  	fmt.Printf("parsed %d lines (%d files) in %v (%d lines/s)\n", lines, count, dt, int64(float64(lines)/dt.Seconds()))
    84  	fmt.Printf("allocated %.3fMb (%.3fMb/s)\n", dm, dm/dt.Seconds())
    85  }
    86  
    87  func walkDirs(t *testing.T, dir string, action func(string)) {
    88  	fis, err := ioutil.ReadDir(dir)
    89  	if err != nil {
    90  		t.Error(err)
    91  		return
    92  	}
    93  
    94  	var files, dirs []string
    95  	for _, fi := range fis {
    96  		if fi.Mode().IsRegular() {
    97  			if strings.HasSuffix(fi.Name(), ".go") {
    98  				path := filepath.Join(dir, fi.Name())
    99  				files = append(files, path)
   100  			}
   101  		} else if fi.IsDir() && fi.Name() != "testdata" {
   102  			path := filepath.Join(dir, fi.Name())
   103  			if !strings.HasSuffix(path, "/test") {
   104  				dirs = append(dirs, path)
   105  			}
   106  		}
   107  	}
   108  
   109  	if *fast {
   110  		var wg sync.WaitGroup
   111  		wg.Add(len(files))
   112  		for _, filename := range files {
   113  			go func(filename string) {
   114  				defer wg.Done()
   115  				action(filename)
   116  			}(filename)
   117  		}
   118  		wg.Wait()
   119  	} else {
   120  		for _, filename := range files {
   121  			action(filename)
   122  		}
   123  	}
   124  
   125  	for _, dir := range dirs {
   126  		walkDirs(t, dir, action)
   127  	}
   128  }
   129  
   130  func verifyPrint(filename string, ast1 *File) {
   131  	var buf1 bytes.Buffer
   132  	_, err := Fprint(&buf1, ast1, true)
   133  	if err != nil {
   134  		panic(err)
   135  	}
   136  
   137  	ast2, err := ParseBytes(src.NewFileBase(filename, filename), buf1.Bytes(), nil, nil, 0)
   138  	if err != nil {
   139  		panic(err)
   140  	}
   141  
   142  	var buf2 bytes.Buffer
   143  	_, err = Fprint(&buf2, ast2, true)
   144  	if err != nil {
   145  		panic(err)
   146  	}
   147  
   148  	if bytes.Compare(buf1.Bytes(), buf2.Bytes()) != 0 {
   149  		fmt.Printf("--- %s ---\n", filename)
   150  		fmt.Printf("%s\n", buf1.Bytes())
   151  		fmt.Println()
   152  
   153  		fmt.Printf("--- %s ---\n", filename)
   154  		fmt.Printf("%s\n", buf2.Bytes())
   155  		fmt.Println()
   156  		panic("not equal")
   157  	}
   158  }
   159  
   160  func TestIssue17697(t *testing.T) {
   161  	_, err := ParseBytes(nil, nil, nil, nil, 0) // return with parser error, don't panic
   162  	if err == nil {
   163  		t.Errorf("no error reported")
   164  	}
   165  }
   166  
   167  func TestParseFile(t *testing.T) {
   168  	_, err := ParseFile("", nil, nil, 0)
   169  	if err == nil {
   170  		t.Error("missing io error")
   171  	}
   172  
   173  	var first error
   174  	_, err = ParseFile("", func(err error) {
   175  		if first == nil {
   176  			first = err
   177  		}
   178  	}, nil, 0)
   179  	if err == nil || first == nil {
   180  		t.Error("missing io error")
   181  	}
   182  	if err != first {
   183  		t.Errorf("got %v; want first error %v", err, first)
   184  	}
   185  }
   186  
   187  func TestLineDirectives(t *testing.T) {
   188  	for _, test := range []struct {
   189  		src, msg  string
   190  		filename  string
   191  		line, col uint
   192  	}{
   193  		// test validity of //line directive
   194  		{`//line :`, "invalid line number: ", "", 1, 8},
   195  		{`//line :x`, "invalid line number: x", "", 1, 8},
   196  		{`//line foo :`, "invalid line number: ", "", 1, 12},
   197  		{`//line foo:123abc`, "invalid line number: 123abc", "", 1, 11},
   198  		{`/**///line foo:x`, "syntax error: package statement must be first", "", 1, 16}, //line directive not at start of line - ignored
   199  		{`//line foo:0`, "invalid line number: 0", "", 1, 11},
   200  		{fmt.Sprintf(`//line foo:%d`, lineMax+1), fmt.Sprintf("invalid line number: %d", lineMax+1), "", 1, 11},
   201  
   202  		// test effect of //line directive on (relative) position information
   203  		{"//line foo:123\n   foo", "syntax error: package statement must be first", "foo", 123, 3},
   204  		{"//line foo:123\n//line bar:345\nfoo", "syntax error: package statement must be first", "bar", 345, 0},
   205  	} {
   206  		_, err := ParseBytes(nil, []byte(test.src), nil, nil, 0)
   207  		if err == nil {
   208  			t.Errorf("%s: no error reported", test.src)
   209  			continue
   210  		}
   211  		perr, ok := err.(Error)
   212  		if !ok {
   213  			t.Errorf("%s: got %v; want parser error", test.src, err)
   214  			continue
   215  		}
   216  		if msg := perr.Msg; msg != test.msg {
   217  			t.Errorf("%s: got msg = %q; want %q", test.src, msg, test.msg)
   218  		}
   219  		if filename := perr.Pos.RelFilename(); filename != test.filename {
   220  			t.Errorf("%s: got filename = %q; want %q", test.src, filename, test.filename)
   221  		}
   222  		if line := perr.Pos.RelLine(); line != test.line {
   223  			t.Errorf("%s: got line = %d; want %d", test.src, line, test.line)
   224  		}
   225  		if col := perr.Pos.Col(); col != test.col {
   226  			t.Errorf("%s: got col = %d; want %d", test.src, col, test.col)
   227  		}
   228  	}
   229  }