Source file src/cmd/internal/archive/archive_test.go

     1  // Copyright 2017 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 archive
     6  
     7  import (
     8  	"bytes"
     9  	"debug/elf"
    10  	"debug/macho"
    11  	"debug/pe"
    12  	"fmt"
    13  	"internal/testenv"
    14  	"internal/xcoff"
    15  	"io"
    16  	"io/ioutil"
    17  	"os"
    18  	"os/exec"
    19  	"path/filepath"
    20  	"runtime"
    21  	"testing"
    22  	"unicode/utf8"
    23  )
    24  
    25  var (
    26  	buildDir   string
    27  	go1obj     string
    28  	go2obj     string
    29  	goarchive  string
    30  	cgoarchive string
    31  )
    32  
    33  func TestMain(m *testing.M) {
    34  	if !testenv.HasGoBuild() {
    35  		return
    36  	}
    37  
    38  	if err := buildGoobj(); err != nil {
    39  		fmt.Println(err)
    40  		os.RemoveAll(buildDir)
    41  		os.Exit(1)
    42  	}
    43  
    44  	exit := m.Run()
    45  
    46  	os.RemoveAll(buildDir)
    47  	os.Exit(exit)
    48  }
    49  
    50  func copyDir(dst, src string) error {
    51  	err := os.MkdirAll(dst, 0777)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	fis, err := ioutil.ReadDir(src)
    56  	if err != nil {
    57  		return err
    58  	}
    59  	for _, fi := range fis {
    60  		err = copyFile(filepath.Join(dst, fi.Name()), filepath.Join(src, fi.Name()))
    61  		if err != nil {
    62  			return err
    63  		}
    64  	}
    65  	return nil
    66  }
    67  
    68  func copyFile(dst, src string) (err error) {
    69  	var s, d *os.File
    70  	s, err = os.Open(src)
    71  	if err != nil {
    72  		return err
    73  	}
    74  	defer s.Close()
    75  	d, err = os.Create(dst)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	defer func() {
    80  		e := d.Close()
    81  		if err == nil {
    82  			err = e
    83  		}
    84  	}()
    85  	_, err = io.Copy(d, s)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	return nil
    90  }
    91  
    92  func buildGoobj() error {
    93  	var err error
    94  
    95  	buildDir, err = ioutil.TempDir("", "TestGoobj")
    96  	if err != nil {
    97  		return err
    98  	}
    99  
   100  	go1obj = filepath.Join(buildDir, "go1.o")
   101  	go2obj = filepath.Join(buildDir, "go2.o")
   102  	goarchive = filepath.Join(buildDir, "go.a")
   103  
   104  	gotool, err := testenv.GoTool()
   105  	if err != nil {
   106  		return err
   107  	}
   108  
   109  	go1src := filepath.Join("testdata", "go1.go")
   110  	go2src := filepath.Join("testdata", "go2.go")
   111  
   112  	out, err := exec.Command(gotool, "tool", "compile", "-o", go1obj, go1src).CombinedOutput()
   113  	if err != nil {
   114  		return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go1obj, go1src, err, out)
   115  	}
   116  	out, err = exec.Command(gotool, "tool", "compile", "-o", go2obj, go2src).CombinedOutput()
   117  	if err != nil {
   118  		return fmt.Errorf("go tool compile -o %s %s: %v\n%s", go2obj, go2src, err, out)
   119  	}
   120  	out, err = exec.Command(gotool, "tool", "pack", "c", goarchive, go1obj, go2obj).CombinedOutput()
   121  	if err != nil {
   122  		return fmt.Errorf("go tool pack c %s %s %s: %v\n%s", goarchive, go1obj, go2obj, err, out)
   123  	}
   124  
   125  	if testenv.HasCGO() {
   126  		gopath := filepath.Join(buildDir, "gopath")
   127  		err = copyDir(filepath.Join(gopath, "src", "mycgo"), filepath.Join("testdata", "mycgo"))
   128  		if err == nil {
   129  			err = ioutil.WriteFile(filepath.Join(gopath, "src", "mycgo", "go.mod"), []byte("module mycgo\n"), 0666)
   130  		}
   131  		if err != nil {
   132  			return err
   133  		}
   134  		cmd := exec.Command(gotool, "install", "-gcflags=all="+os.Getenv("GO_GCFLAGS"), "mycgo")
   135  		cmd.Dir = filepath.Join(gopath, "src", "mycgo")
   136  		cmd.Env = append(os.Environ(), "GOPATH="+gopath)
   137  		out, err = cmd.CombinedOutput()
   138  		if err != nil {
   139  			return fmt.Errorf("go install mycgo: %v\n%s", err, out)
   140  		}
   141  		pat := filepath.Join(gopath, "pkg", "*", "mycgo.a")
   142  		ms, err := filepath.Glob(pat)
   143  		if err != nil {
   144  			return err
   145  		}
   146  		if len(ms) == 0 {
   147  			return fmt.Errorf("cannot found paths for pattern %s", pat)
   148  		}
   149  		cgoarchive = ms[0]
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  func TestParseGoobj(t *testing.T) {
   156  	path := go1obj
   157  
   158  	f, err := os.Open(path)
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  	defer f.Close()
   163  
   164  	a, err := Parse(f, false)
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	if len(a.Entries) != 2 {
   169  		t.Errorf("expect 2 entry, found %d", len(a.Entries))
   170  	}
   171  	for _, e := range a.Entries {
   172  		if e.Type == EntryPkgDef {
   173  			continue
   174  		}
   175  		if e.Type != EntryGoObj {
   176  			t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
   177  		}
   178  		if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
   179  			t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
   180  		}
   181  	}
   182  }
   183  
   184  func TestParseArchive(t *testing.T) {
   185  	path := goarchive
   186  
   187  	f, err := os.Open(path)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	defer f.Close()
   192  
   193  	a, err := Parse(f, false)
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  	if len(a.Entries) != 3 {
   198  		t.Errorf("expect 3 entry, found %d", len(a.Entries))
   199  	}
   200  	var found1 bool
   201  	var found2 bool
   202  	for _, e := range a.Entries {
   203  		if e.Type == EntryPkgDef {
   204  			continue
   205  		}
   206  		if e.Type != EntryGoObj {
   207  			t.Errorf("wrong type of object: want EntryGoObj, got %v", e.Type)
   208  		}
   209  		if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
   210  			t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
   211  		}
   212  		if e.Name == "go1.o" {
   213  			found1 = true
   214  		}
   215  		if e.Name == "go2.o" {
   216  			found2 = true
   217  		}
   218  	}
   219  	if !found1 {
   220  		t.Errorf(`object "go1.o" not found`)
   221  	}
   222  	if !found2 {
   223  		t.Errorf(`object "go2.o" not found`)
   224  	}
   225  }
   226  
   227  func TestParseCGOArchive(t *testing.T) {
   228  	testenv.MustHaveCGO(t)
   229  
   230  	path := cgoarchive
   231  
   232  	f, err := os.Open(path)
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  	defer f.Close()
   237  
   238  	a, err := Parse(f, false)
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  
   243  	c1 := "c1"
   244  	c2 := "c2"
   245  	switch runtime.GOOS {
   246  	case "darwin", "ios":
   247  		c1 = "_" + c1
   248  		c2 = "_" + c2
   249  	case "windows":
   250  		if runtime.GOARCH == "386" {
   251  			c1 = "_" + c1
   252  			c2 = "_" + c2
   253  		}
   254  	case "aix":
   255  		c1 = "." + c1
   256  		c2 = "." + c2
   257  	}
   258  
   259  	var foundgo, found1, found2 bool
   260  
   261  	for _, e := range a.Entries {
   262  		switch e.Type {
   263  		default:
   264  			t.Errorf("unknown object type")
   265  		case EntryPkgDef:
   266  			continue
   267  		case EntryGoObj:
   268  			foundgo = true
   269  			if !bytes.Contains(e.Obj.TextHeader, []byte(runtime.GOARCH)) {
   270  				t.Errorf("text header does not contain GOARCH %s: %q", runtime.GOARCH, e.Obj.TextHeader)
   271  			}
   272  			continue
   273  		case EntryNativeObj:
   274  		}
   275  
   276  		obj := io.NewSectionReader(f, e.Offset, e.Size)
   277  		switch runtime.GOOS {
   278  		case "darwin", "ios":
   279  			mf, err := macho.NewFile(obj)
   280  			if err != nil {
   281  				t.Fatal(err)
   282  			}
   283  			if mf.Symtab == nil {
   284  				continue
   285  			}
   286  			for _, s := range mf.Symtab.Syms {
   287  				switch s.Name {
   288  				case c1:
   289  					found1 = true
   290  				case c2:
   291  					found2 = true
   292  				}
   293  			}
   294  		case "windows":
   295  			pf, err := pe.NewFile(obj)
   296  			if err != nil {
   297  				t.Fatal(err)
   298  			}
   299  			for _, s := range pf.Symbols {
   300  				switch s.Name {
   301  				case c1:
   302  					found1 = true
   303  				case c2:
   304  					found2 = true
   305  				}
   306  			}
   307  		case "aix":
   308  			xf, err := xcoff.NewFile(obj)
   309  			if err != nil {
   310  				t.Fatal(err)
   311  			}
   312  			for _, s := range xf.Symbols {
   313  				switch s.Name {
   314  				case c1:
   315  					found1 = true
   316  				case c2:
   317  					found2 = true
   318  				}
   319  			}
   320  		default: // ELF
   321  			ef, err := elf.NewFile(obj)
   322  			if err != nil {
   323  				t.Fatal(err)
   324  			}
   325  			syms, err := ef.Symbols()
   326  			if err != nil {
   327  				t.Fatal(err)
   328  			}
   329  			for _, s := range syms {
   330  				switch s.Name {
   331  				case c1:
   332  					found1 = true
   333  				case c2:
   334  					found2 = true
   335  				}
   336  			}
   337  		}
   338  	}
   339  
   340  	if !foundgo {
   341  		t.Errorf(`go object not found`)
   342  	}
   343  	if !found1 {
   344  		t.Errorf(`symbol %q not found`, c1)
   345  	}
   346  	if !found2 {
   347  		t.Errorf(`symbol %q not found`, c2)
   348  	}
   349  }
   350  
   351  func TestExactly16Bytes(t *testing.T) {
   352  	var tests = []string{
   353  		"",
   354  		"a",
   355  		"日本語",
   356  		"1234567890123456",
   357  		"12345678901234567890",
   358  		"1234567890123本語4567890",
   359  		"12345678901234日本語567890",
   360  		"123456789012345日本語67890",
   361  		"1234567890123456日本語7890",
   362  		"1234567890123456日本語7日本語890",
   363  	}
   364  	for _, str := range tests {
   365  		got := exactly16Bytes(str)
   366  		if len(got) != 16 {
   367  			t.Errorf("exactly16Bytes(%q) is %q, length %d", str, got, len(got))
   368  		}
   369  		// Make sure it is full runes.
   370  		for _, c := range got {
   371  			if c == utf8.RuneError {
   372  				t.Errorf("exactly16Bytes(%q) is %q, has partial rune", str, got)
   373  			}
   374  		}
   375  	}
   376  }
   377  

View as plain text