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