github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/uroot/uroot_test.go (about)

     1  // Copyright 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 uroot
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"reflect"
    14  	"syscall"
    15  	"testing"
    16  
    17  	"github.com/u-root/u-root/pkg/cpio"
    18  	"github.com/u-root/u-root/pkg/golang"
    19  	"github.com/u-root/u-root/pkg/uroot/builder"
    20  	itest "github.com/u-root/u-root/pkg/uroot/initramfs/test"
    21  )
    22  
    23  type inMemArchive struct {
    24  	*cpio.Archive
    25  }
    26  
    27  // Finish implements initramfs.Writer.Finish.
    28  func (inMemArchive) Finish() error { return nil }
    29  
    30  func TestResolvePackagePaths(t *testing.T) {
    31  	defaultEnv := golang.Default()
    32  	gopath1, err := filepath.Abs("test/gopath1")
    33  	if err != nil {
    34  		t.Fatalf("failure to set up test: %v", err)
    35  	}
    36  	gopath2, err := filepath.Abs("test/gopath2")
    37  	if err != nil {
    38  		t.Fatalf("failure to set up test: %v", err)
    39  	}
    40  	gopath1Env := defaultEnv
    41  	gopath1Env.GOPATH = gopath1
    42  	gopath2Env := defaultEnv
    43  	gopath2Env.GOPATH = gopath2
    44  	everythingEnv := defaultEnv
    45  	everythingEnv.GOPATH = gopath1 + ":" + gopath2
    46  	foopath, err := filepath.Abs("test/gopath1/src/foo")
    47  	if err != nil {
    48  		t.Fatalf("failure to set up test: %v", err)
    49  	}
    50  
    51  	// Why doesn't the log package export this as a default?
    52  	l := log.New(os.Stdout, "", log.LstdFlags)
    53  
    54  	for _, tc := range []struct {
    55  		env      golang.Environ
    56  		in       []string
    57  		expected []string
    58  		wantErr  bool
    59  	}{
    60  		// Nonexistent Package
    61  		{
    62  			env:      defaultEnv,
    63  			in:       []string{"fakepackagename"},
    64  			expected: nil,
    65  			wantErr:  true,
    66  		},
    67  		// Single go package import
    68  		{
    69  			env: defaultEnv,
    70  			in:  []string{"github.com/u-root/u-root/cmds/core/ls"},
    71  			// We expect the full URL format because that's the path in our default GOPATH
    72  			expected: []string{"github.com/u-root/u-root/cmds/core/ls"},
    73  			wantErr:  false,
    74  		},
    75  		// Single package directory relative to working dir
    76  		{
    77  			env:      defaultEnv,
    78  			in:       []string{"test/gopath1/src/foo"},
    79  			expected: []string{"github.com/u-root/u-root/pkg/uroot/test/gopath1/src/foo"},
    80  			wantErr:  false,
    81  		},
    82  		// Single package directory with absolute path
    83  		{
    84  			env:      defaultEnv,
    85  			in:       []string{foopath},
    86  			expected: []string{"github.com/u-root/u-root/pkg/uroot/test/gopath1/src/foo"},
    87  			wantErr:  false,
    88  		},
    89  		// Single package directory relative to GOPATH
    90  		{
    91  			env: gopath1Env,
    92  			in:  []string{"foo"},
    93  			expected: []string{
    94  				"foo",
    95  			},
    96  			wantErr: false,
    97  		},
    98  		// Package directory glob
    99  		{
   100  			env: defaultEnv,
   101  			in:  []string{"test/gopath2/src/mypkg*"},
   102  			expected: []string{
   103  				"github.com/u-root/u-root/pkg/uroot/test/gopath2/src/mypkga",
   104  				"github.com/u-root/u-root/pkg/uroot/test/gopath2/src/mypkgb",
   105  			},
   106  			wantErr: false,
   107  		},
   108  		// GOPATH glob
   109  		{
   110  			env: gopath2Env,
   111  			in:  []string{"mypkg*"},
   112  			expected: []string{
   113  				"mypkga",
   114  				"mypkgb",
   115  			},
   116  			wantErr: false,
   117  		},
   118  		// Single ambiguous package - exists in both GOROOT and GOPATH
   119  		{
   120  			env: gopath1Env,
   121  			in:  []string{"os"},
   122  			expected: []string{
   123  				"os",
   124  			},
   125  			wantErr: false,
   126  		},
   127  		// Packages from different gopaths
   128  		{
   129  			env: everythingEnv,
   130  			in:  []string{"foo", "mypkga"},
   131  			expected: []string{
   132  				"foo",
   133  				"mypkga",
   134  			},
   135  			wantErr: false,
   136  		},
   137  		// Same package specified twice
   138  		{
   139  			env: defaultEnv,
   140  			in:  []string{"test/gopath2/src/mypkga", "test/gopath2/src/mypkga"},
   141  			// TODO: This returns the package twice. Is this preferred?
   142  			expected: []string{
   143  				"github.com/u-root/u-root/pkg/uroot/test/gopath2/src/mypkga",
   144  				"github.com/u-root/u-root/pkg/uroot/test/gopath2/src/mypkga",
   145  			},
   146  			wantErr: false,
   147  		},
   148  		// Excludes
   149  		{
   150  			env: defaultEnv,
   151  			in:  []string{"test/gopath2/src/*", "-test/gopath2/src/mypkga"},
   152  			expected: []string{
   153  				"github.com/u-root/u-root/pkg/uroot/test/gopath2/src/mypkgb",
   154  			},
   155  			wantErr: false,
   156  		},
   157  	} {
   158  		t.Run(fmt.Sprintf("%q", tc.in), func(t *testing.T) {
   159  			out, err := ResolvePackagePaths(l, tc.env, tc.in)
   160  			if (err != nil) != tc.wantErr {
   161  				t.Fatalf("ResolvePackagePaths(%#v, %v) err != nil is %v, want %v\nerr is %v",
   162  					tc.env, tc.in, err != nil, tc.wantErr, err)
   163  			}
   164  			if !reflect.DeepEqual(out, tc.expected) {
   165  				t.Errorf("ResolvePackagePaths(%#v, %v) = %v; want %v",
   166  					tc.env, tc.in, out, tc.expected)
   167  			}
   168  		})
   169  	}
   170  }
   171  
   172  func TestCreateInitramfs(t *testing.T) {
   173  	dir, err := ioutil.TempDir("", "foo")
   174  	if err != nil {
   175  		t.Error(err)
   176  	}
   177  	defer os.RemoveAll(dir)
   178  	syscall.Umask(0)
   179  
   180  	tmp777 := filepath.Join(dir, "tmp777")
   181  	if err := os.MkdirAll(tmp777, 0777); err != nil {
   182  		t.Error(err)
   183  	}
   184  
   185  	// Why doesn't the log package export this as a default?
   186  	l := log.New(os.Stdout, "", log.LstdFlags)
   187  
   188  	for i, tt := range []struct {
   189  		name       string
   190  		opts       Opts
   191  		want       string
   192  		validators []itest.ArchiveValidator
   193  	}{
   194  		{
   195  			name: "BB archive with ls and init",
   196  			opts: Opts{
   197  				Env:             golang.Default(),
   198  				TempDir:         dir,
   199  				ExtraFiles:      nil,
   200  				UseExistingInit: false,
   201  				InitCmd:         "init",
   202  				DefaultShell:    "ls",
   203  				Commands: []Commands{
   204  					{
   205  						Builder: builder.BusyBox,
   206  						Packages: []string{
   207  							"github.com/u-root/u-root/cmds/core/init",
   208  							"github.com/u-root/u-root/cmds/core/ls",
   209  						},
   210  					},
   211  				},
   212  			},
   213  			want: "",
   214  			validators: []itest.ArchiveValidator{
   215  				itest.HasFile{"bbin/bb"},
   216  				itest.HasRecord{cpio.Symlink("bbin/init", "bb")},
   217  				itest.HasRecord{cpio.Symlink("bbin/ls", "bb")},
   218  				itest.HasRecord{cpio.Symlink("bin/defaultsh", "../bbin/ls")},
   219  				itest.HasRecord{cpio.Symlink("bin/sh", "../bbin/ls")},
   220  			},
   221  		},
   222  		{
   223  			name: "no temp dir",
   224  			opts: Opts{
   225  				Env:          golang.Default(),
   226  				InitCmd:      "init",
   227  				DefaultShell: "",
   228  			},
   229  			want: "temp dir \"\" must exist: stat : no such file or directory",
   230  			validators: []itest.ArchiveValidator{
   231  				itest.IsEmpty{},
   232  			},
   233  		},
   234  		{
   235  			name: "no commands",
   236  			opts: Opts{
   237  				Env:     golang.Default(),
   238  				TempDir: dir,
   239  			},
   240  			want: "",
   241  			validators: []itest.ArchiveValidator{
   242  				itest.MissingFile{"bbin/bb"},
   243  			},
   244  		},
   245  		{
   246  			name: "init specified, but not in commands",
   247  			opts: Opts{
   248  				Commands: []Commands{
   249  					{
   250  						Builder: builder.Binary,
   251  						Packages: []string{
   252  							"github.com/u-root/u-root/cmds/core/ls",
   253  						},
   254  					},
   255  				},
   256  				Env:          golang.Default(),
   257  				TempDir:      dir,
   258  				DefaultShell: "zoocar",
   259  				InitCmd:      "foobar",
   260  			},
   261  			want: "could not create symlink from \"init\" to \"foobar\": command or path \"foobar\" not included in u-root build: specify -initcmd=\"\" to ignore this error and build without an init",
   262  			validators: []itest.ArchiveValidator{
   263  				itest.IsEmpty{},
   264  			},
   265  		},
   266  		{
   267  			name: "init symlinked to absolute path",
   268  			opts: Opts{
   269  				Env:     golang.Default(),
   270  				TempDir: dir,
   271  				InitCmd: "/bin/systemd",
   272  			},
   273  			want: "",
   274  			validators: []itest.ArchiveValidator{
   275  				itest.HasRecord{cpio.Symlink("init", "bin/systemd")},
   276  			},
   277  		},
   278  		{
   279  			name: "multi-mode archive",
   280  			opts: Opts{
   281  				Env:             golang.Default(),
   282  				TempDir:         dir,
   283  				ExtraFiles:      nil,
   284  				UseExistingInit: false,
   285  				InitCmd:         "init",
   286  				DefaultShell:    "ls",
   287  				Commands: []Commands{
   288  					{
   289  						Builder: builder.BusyBox,
   290  						Packages: []string{
   291  							"github.com/u-root/u-root/cmds/core/init",
   292  							"github.com/u-root/u-root/cmds/core/ls",
   293  						},
   294  					},
   295  					{
   296  						Builder: builder.Binary,
   297  						Packages: []string{
   298  							"github.com/u-root/u-root/cmds/core/cp",
   299  							"github.com/u-root/u-root/cmds/core/dd",
   300  						},
   301  					},
   302  					{
   303  						Builder: builder.Source,
   304  						Packages: []string{
   305  							"github.com/u-root/u-root/cmds/core/cat",
   306  							"github.com/u-root/u-root/cmds/core/chroot",
   307  							"github.com/u-root/u-root/cmds/core/installcommand",
   308  						},
   309  					},
   310  				},
   311  			},
   312  			want: "",
   313  			validators: []itest.ArchiveValidator{
   314  				itest.HasRecord{cpio.Symlink("init", "bbin/init")},
   315  
   316  				// bb mode.
   317  				itest.HasFile{"bbin/bb"},
   318  				itest.HasRecord{cpio.Symlink("bbin/init", "bb")},
   319  				itest.HasRecord{cpio.Symlink("bbin/ls", "bb")},
   320  				itest.HasRecord{cpio.Symlink("bin/defaultsh", "../bbin/ls")},
   321  				itest.HasRecord{cpio.Symlink("bin/sh", "../bbin/ls")},
   322  
   323  				// binary mode.
   324  				itest.HasFile{"bin/cp"},
   325  				itest.HasFile{"bin/dd"},
   326  
   327  				// source mode.
   328  				itest.HasRecord{cpio.Symlink("buildbin/cat", "installcommand")},
   329  				itest.HasRecord{cpio.Symlink("buildbin/chroot", "installcommand")},
   330  				itest.HasFile{"buildbin/installcommand"},
   331  				itest.HasFile{"src/github.com/u-root/u-root/cmds/core/cat/cat.go"},
   332  				itest.HasFile{"src/github.com/u-root/u-root/cmds/core/chroot/chroot.go"},
   333  				itest.HasFile{"src/github.com/u-root/u-root/cmds/core/installcommand/installcommand.go"},
   334  			},
   335  		},
   336  	} {
   337  		t.Run(fmt.Sprintf("Test %d [%s]", i, tt.name), func(t *testing.T) {
   338  			archive := inMemArchive{cpio.InMemArchive()}
   339  			tt.opts.OutputFile = archive
   340  			// Compare error type or error string.
   341  			if err := CreateInitramfs(l, tt.opts); (err != nil && err.Error() != tt.want) || (len(tt.want) > 0 && err == nil) {
   342  				t.Errorf("CreateInitramfs(%v) = %v, want %v", tt.opts, err, tt.want)
   343  			}
   344  
   345  			for _, v := range tt.validators {
   346  				if err := v.Validate(archive.Archive); err != nil {
   347  					t.Errorf("validator failed: %v / archive:\n%s", err, archive)
   348  				}
   349  			}
   350  		})
   351  	}
   352  }