github.com/system-transparency/u-root@v6.0.1-0.20190919065413-ed07a650de4c+incompatible/uroot_test.go (about)

     1  // Copyright 2015-2018 the u-root 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  	"crypto/sha256"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"testing"
    17  
    18  	"github.com/u-root/u-root/pkg/cpio"
    19  	"github.com/u-root/u-root/pkg/testutil"
    20  	itest "github.com/u-root/u-root/pkg/uroot/initramfs/test"
    21  )
    22  
    23  var twocmds = []string{
    24  	"github.com/u-root/u-root/cmds/core/ls",
    25  	"github.com/u-root/u-root/cmds/core/init",
    26  }
    27  
    28  var srcmds = []string{
    29  	"github.com/u-root/u-root/cmds/core/ls",
    30  	"github.com/u-root/u-root/cmds/core/init",
    31  	"github.com/u-root/u-root/cmds/core/installcommand",
    32  }
    33  
    34  type buildSourceValidator struct {
    35  	gopath string
    36  	goroot string
    37  	env    []string
    38  }
    39  
    40  func (b buildSourceValidator) Validate(a *cpio.Archive) error {
    41  	dir, err := ioutil.TempDir("", "u-root-source-")
    42  	if err != nil {
    43  		return err
    44  	}
    45  	defer os.RemoveAll(dir)
    46  
    47  	if err := os.Mkdir(filepath.Join(dir, "tmp"), 0755); err != nil {
    48  		return err
    49  	}
    50  
    51  	// Unpack into dir.
    52  	err = cpio.ForEachRecord(a.Reader(), func(r cpio.Record) error {
    53  		return cpio.CreateFileInRoot(r, dir, false)
    54  	})
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	goroot := filepath.Join(dir, b.goroot)
    60  	gopath := filepath.Join(dir, b.gopath)
    61  	// go build ./src/...
    62  	c := exec.Command(filepath.Join(goroot, "bin/go"), "build", filepath.Join(gopath, "src/..."))
    63  	c.Env = append(b.env,
    64  		fmt.Sprintf("GOPATH=%s", gopath),
    65  		fmt.Sprintf("GOCACHE=%s", filepath.Join(dir, "tmp")),
    66  		fmt.Sprintf("GOROOT=%s", goroot))
    67  	out, err := c.CombinedOutput()
    68  	if err != nil {
    69  		return fmt.Errorf("could not build go source %v; output\n%s", err, out)
    70  	}
    71  	return nil
    72  }
    73  
    74  func TestUrootCmdline(t *testing.T) {
    75  	samplef, err := ioutil.TempFile("", "u-root-sample-")
    76  	if err != nil {
    77  		t.Fatal(err)
    78  	}
    79  	samplef.Close()
    80  	defer os.RemoveAll(samplef.Name())
    81  
    82  	for _, tt := range []struct {
    83  		name       string
    84  		env        []string
    85  		args       []string
    86  		err        error
    87  		validators []itest.ArchiveValidator
    88  	}{
    89  		{
    90  			name: "include one extra file",
    91  			args: []string{"-nocmd", "-files=/bin/bash"},
    92  			err:  nil,
    93  			validators: []itest.ArchiveValidator{
    94  				itest.HasFile{"bin/bash"},
    95  			},
    96  		},
    97  		{
    98  			name: "fix usage of an absolute path",
    99  			args: []string{"-nocmd", "-files=/bin:/bin"},
   100  			err:  nil,
   101  			validators: []itest.ArchiveValidator{
   102  				itest.HasFile{"bin/bash"},
   103  			},
   104  		},
   105  		{
   106  			name: "include multiple extra files",
   107  			args: []string{"-nocmd", "-files=/bin/bash", "-files=/bin/ls", fmt.Sprintf("-files=%s", samplef.Name())},
   108  			validators: []itest.ArchiveValidator{
   109  				itest.HasFile{"bin/bash"},
   110  				itest.HasFile{"bin/ls"},
   111  				itest.HasFile{samplef.Name()},
   112  			},
   113  		},
   114  		{
   115  			name: "include one extra file with rename",
   116  			args: []string{"-nocmd", "-files=/bin/bash:bin/bush"},
   117  			validators: []itest.ArchiveValidator{
   118  				itest.HasFile{"bin/bush"},
   119  			},
   120  		},
   121  		{
   122  			name: "hosted source mode",
   123  			args: append([]string{"-build=source", "-base=/dev/null", "-defaultsh=", "-initcmd="}, srcmds...),
   124  		},
   125  		{
   126  			name: "hosted bb mode",
   127  			args: append([]string{"-build=bb", "-base=/dev/null", "-defaultsh=", "-initcmd="}, twocmds...),
   128  		},
   129  		{
   130  			name: "AMD64 bb build",
   131  			env:  []string{"GOARCH=amd64"},
   132  			args: []string{"-build=bb"},
   133  		},
   134  		{
   135  			name: "AMD64 source build",
   136  			env:  []string{"GOARCH=amd64"},
   137  			args: []string{"-build=source"},
   138  			validators: []itest.ArchiveValidator{
   139  				buildSourceValidator{
   140  					goroot: "/go",
   141  					gopath: ".",
   142  					env:    []string{"GOARCH=amd64"},
   143  				},
   144  			},
   145  		},
   146  		{
   147  			name: "ARM7 bb build",
   148  			env:  []string{"GOARCH=arm", "GOARM=7"},
   149  			args: []string{"-build=bb"},
   150  		},
   151  		{
   152  			name: "ARM64 bb build",
   153  			env:  []string{"GOARCH=arm64"},
   154  			args: []string{"-build=bb"},
   155  		},
   156  		{
   157  			name: "Power 64bit bb build",
   158  			env:  []string{"GOARCH=ppc64le"},
   159  			args: []string{"-build=bb"},
   160  		},
   161  	} {
   162  		t.Run(tt.name, func(t *testing.T) {
   163  			delFiles := true
   164  			f, sum1 := buildIt(t, tt.args, tt.env, tt.err)
   165  			defer func() {
   166  				if delFiles {
   167  					os.RemoveAll(f.Name())
   168  				}
   169  			}()
   170  
   171  			a, err := itest.ReadArchive(f.Name())
   172  			if err != nil {
   173  				t.Fatal(err)
   174  			}
   175  
   176  			for _, v := range tt.validators {
   177  				if err := v.Validate(a); err != nil {
   178  					t.Errorf("validator failed: %v / archive:\n%s", err, a)
   179  				}
   180  			}
   181  
   182  			f2, sum2 := buildIt(t, tt.args, tt.env, tt.err)
   183  			defer func() {
   184  				if delFiles {
   185  					os.RemoveAll(f2.Name())
   186  				}
   187  			}()
   188  			if !bytes.Equal(sum1, sum2) {
   189  				delFiles = false
   190  				t.Errorf("not reproducible, hashes don't match")
   191  				t.Errorf("env: %v args: %v", tt.env, tt.args)
   192  				t.Errorf("file1: %v file2: %v", f.Name(), f2.Name())
   193  			}
   194  		})
   195  	}
   196  }
   197  
   198  func buildIt(t *testing.T, args, env []string, want error) (*os.File, []byte) {
   199  	f, err := ioutil.TempFile("", "u-root-")
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  
   204  	arg := append([]string{"-o", f.Name()}, args...)
   205  	c := testutil.Command(t, arg...)
   206  	t.Logf("Commandline: %v", arg)
   207  	c.Env = append(c.Env, env...)
   208  	if out, err := c.CombinedOutput(); err != want {
   209  		t.Fatalf("Error: %v\nOutput:\n%s", err, out)
   210  	} else if err != nil {
   211  		h1 := sha256.New()
   212  		if _, err := io.Copy(h1, f); err != nil {
   213  			t.Fatal()
   214  		}
   215  		return f, h1.Sum(nil)
   216  	}
   217  	return f, nil
   218  }
   219  
   220  func TestMain(m *testing.M) {
   221  	testutil.Run(m, main)
   222  }