Home | History | Annotate | Download | only in api
      1 // Copyright 2011 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 main
      6 
      7 import (
      8 	"bytes"
      9 	"flag"
     10 	"fmt"
     11 	"go/build"
     12 	"io/ioutil"
     13 	"os"
     14 	"os/exec"
     15 	"path/filepath"
     16 	"sort"
     17 	"strings"
     18 	"testing"
     19 )
     20 
     21 var (
     22 	updateGolden = flag.Bool("updategolden", false, "update golden files")
     23 )
     24 
     25 func TestGolden(t *testing.T) {
     26 	td, err := os.Open("testdata/src/pkg")
     27 	if err != nil {
     28 		t.Fatal(err)
     29 	}
     30 	fis, err := td.Readdir(0)
     31 	if err != nil {
     32 		t.Fatal(err)
     33 	}
     34 	for _, fi := range fis {
     35 		if !fi.IsDir() {
     36 			continue
     37 		}
     38 
     39 		// TODO(gri) remove extra pkg directory eventually
     40 		goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt")
     41 		w := NewWalker(nil, "testdata/src/pkg")
     42 		pkg, _ := w.Import(fi.Name())
     43 		w.export(pkg)
     44 
     45 		if *updateGolden {
     46 			os.Remove(goldenFile)
     47 			f, err := os.Create(goldenFile)
     48 			if err != nil {
     49 				t.Fatal(err)
     50 			}
     51 			for _, feat := range w.Features() {
     52 				fmt.Fprintf(f, "%s\n", feat)
     53 			}
     54 			f.Close()
     55 		}
     56 
     57 		bs, err := ioutil.ReadFile(goldenFile)
     58 		if err != nil {
     59 			t.Fatalf("opening golden.txt for package %q: %v", fi.Name(), err)
     60 		}
     61 		wanted := strings.Split(string(bs), "\n")
     62 		sort.Strings(wanted)
     63 		for _, feature := range wanted {
     64 			if feature == "" {
     65 				continue
     66 			}
     67 			_, ok := w.features[feature]
     68 			if !ok {
     69 				t.Errorf("package %s: missing feature %q", fi.Name(), feature)
     70 			}
     71 			delete(w.features, feature)
     72 		}
     73 
     74 		for _, feature := range w.Features() {
     75 			t.Errorf("package %s: extra feature not in golden file: %q", fi.Name(), feature)
     76 		}
     77 	}
     78 }
     79 
     80 func TestCompareAPI(t *testing.T) {
     81 	tests := []struct {
     82 		name                                    string
     83 		features, required, optional, exception []string
     84 		ok                                      bool   // want
     85 		out                                     string // want
     86 	}{
     87 		{
     88 			name:     "feature added",
     89 			features: []string{"A", "B", "C", "D", "E", "F"},
     90 			required: []string{"B", "D"},
     91 			ok:       true,
     92 			out:      "+A\n+C\n+E\n+F\n",
     93 		},
     94 		{
     95 			name:     "feature removed",
     96 			features: []string{"C", "A"},
     97 			required: []string{"A", "B", "C"},
     98 			ok:       false,
     99 			out:      "-B\n",
    100 		},
    101 		{
    102 			name:     "feature added then removed",
    103 			features: []string{"A", "C"},
    104 			optional: []string{"B"},
    105 			required: []string{"A", "C"},
    106 			ok:       true,
    107 			out:      "B\n",
    108 		},
    109 		{
    110 			name:      "exception removal",
    111 			required:  []string{"A", "B", "C"},
    112 			features:  []string{"A", "C"},
    113 			exception: []string{"B"},
    114 			ok:        true,
    115 			out:       "",
    116 		},
    117 		{
    118 			// https://golang.org/issue/4303
    119 			name: "contexts reconverging",
    120 			required: []string{
    121 				"A",
    122 				"pkg syscall (darwin-386), type RawSockaddrInet6 struct",
    123 				"pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
    124 			},
    125 			features: []string{
    126 				"A",
    127 				"pkg syscall, type RawSockaddrInet6 struct",
    128 			},
    129 			ok:  true,
    130 			out: "+pkg syscall, type RawSockaddrInet6 struct\n",
    131 		},
    132 	}
    133 	for _, tt := range tests {
    134 		buf := new(bytes.Buffer)
    135 		gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception, true)
    136 		if gotok != tt.ok {
    137 			t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok)
    138 		}
    139 		if got := buf.String(); got != tt.out {
    140 			t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out)
    141 		}
    142 	}
    143 }
    144 
    145 func TestSkipInternal(t *testing.T) {
    146 	tests := []struct {
    147 		pkg  string
    148 		want bool
    149 	}{
    150 		{"net/http", true},
    151 		{"net/http/internal-foo", true},
    152 		{"net/http/internal", false},
    153 		{"net/http/internal/bar", false},
    154 		{"internal/foo", false},
    155 		{"internal", false},
    156 	}
    157 	for _, tt := range tests {
    158 		got := !internalPkg.MatchString(tt.pkg)
    159 		if got != tt.want {
    160 			t.Errorf("%s is internal = %v; want %v", tt.pkg, got, tt.want)
    161 		}
    162 	}
    163 }
    164 
    165 func BenchmarkAll(b *testing.B) {
    166 	stds, err := exec.Command("go", "list", "std").Output()
    167 	if err != nil {
    168 		b.Fatal(err)
    169 	}
    170 	b.ResetTimer()
    171 	pkgNames := strings.Fields(string(stds))
    172 
    173 	for _, c := range contexts {
    174 		c.Compiler = build.Default.Compiler
    175 	}
    176 
    177 	for i := 0; i < b.N; i++ {
    178 		for _, context := range contexts {
    179 			w := NewWalker(context, filepath.Join(build.Default.GOROOT, "src"))
    180 			for _, name := range pkgNames {
    181 				if name != "unsafe" && !strings.HasPrefix(name, "cmd/") && !internalPkg.MatchString(name) {
    182 					pkg, _ := w.Import(name)
    183 					w.export(pkg)
    184 				}
    185 			}
    186 			w.Features()
    187 		}
    188 	}
    189 }
    190