blob: 8986612c82e5e963af9789a2c29d8c70e3cb75aa [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// +build fuchsia
package pkgfs
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"fuchsia.googlesource.com/merkle"
"fuchsia.googlesource.com/pm/build"
"fuchsia.googlesource.com/pmd/amberer"
"fuchsia.googlesource.com/pmd/ramdisk"
)
// Adding a file to /in writes the file to blobfs
// Adding a file that is a meta.far to /in creates the package in the package filesystem
// If not all of a packages contents are available, opening the package directory should fail
// A package directory should contain all files from meta.far and listed by meta/contents
var (
pkgfsMount string
blobfsPath string
)
// tmain exists for the defer convenience, so that defers are run before os.Exit gets called.
func tmain(m *testing.M) int {
// Undo the defaults that print to the system log...
log.SetOutput(os.Stdout)
var err error
blobfsPath, err = ioutil.TempDir("", "pkgfs-test-blobfs")
if err != nil {
panic(err)
}
defer os.RemoveAll(blobfsPath)
rd, err := ramdisk.New(10 * 1024 * 1024)
if err != nil {
panic(err)
}
defer rd.Destroy()
if err := rd.MkfsBlobfs(); err != nil {
panic(err)
}
if err := rd.MountBlobfs(blobfsPath); err != nil {
panic(err)
}
defer rd.Umount(blobfsPath)
fmt.Printf("blobfs mounted at %s\n", blobfsPath)
cfg := build.TestConfig()
defer os.RemoveAll(filepath.Dir(cfg.TempDir))
build.TestPackage(cfg)
err = build.Update(cfg)
if err != nil {
panic(err)
}
err = build.Sign(cfg)
if err != nil {
panic(err)
}
_, err = build.Seal(cfg)
if err != nil {
panic(err)
}
src, err := os.Open(filepath.Join(cfg.OutputDir, "meta.far"))
if err != nil {
panic(err)
}
var tree merkle.Tree
_, err = tree.ReadFrom(src)
if err != nil {
panic(err)
}
merkleroot := fmt.Sprintf("%x", tree.Root())
src.Seek(0, os.SEEK_SET)
f, err := os.Create(filepath.Join(blobfsPath, merkleroot))
if err != nil {
panic(err)
}
fi, err := src.Stat()
if err != nil {
panic(err)
}
if err := f.Truncate(fi.Size()); err != nil {
panic(err)
}
if _, err := io.Copy(f, src); err != nil {
panic(err)
}
if err := f.Close(); err != nil {
panic(err)
}
staticFile, err := ioutil.TempFile("", "pkgfs-test-static-index")
if err != nil {
panic(err)
}
fmt.Fprintf(staticFile, "static-package/0=%s\n", merkleroot)
staticFile.Close()
staticPath := staticFile.Name()
defer os.RemoveAll(staticPath)
indexPath, err := ioutil.TempDir("", "pkgfs-test-index")
if err != nil {
panic(err)
}
defer os.RemoveAll(indexPath)
d, err := ioutil.TempDir("", "pkgfs-test-mount")
if err != nil {
panic(err)
}
defer os.RemoveAll(d)
pkgfs, err := New(indexPath, blobfsPath, amberer.NewAmberClient())
if err != nil {
panic(err)
}
sf, err := os.Open(staticPath)
if err != nil {
panic(err)
}
pkgfs.static.LoadFrom(sf)
sf.Close()
go func() {
if err := pkgfs.Mount(d); err != nil {
panic(err)
}
}()
defer pkgfs.Unmount()
pkgfsMount = d
return m.Run()
}
func TestMain(m *testing.M) {
println("starting tests")
v := tmain(m)
println("cleaned up tests")
os.Exit(v)
}
func TestAddPackage(t *testing.T) {
cfg := build.TestConfig()
defer os.RemoveAll(filepath.Dir(cfg.TempDir))
build.TestPackage(cfg)
var err error
err = build.Update(cfg)
if err != nil {
t.Fatal(err)
}
err = build.Sign(cfg)
if err != nil {
t.Fatal(err)
}
_, err = build.Seal(cfg)
if err != nil {
t.Fatal(err)
}
src, err := os.Open(filepath.Join(cfg.OutputDir, "meta.far"))
if err != nil {
t.Fatal(err)
}
var tree merkle.Tree
_, err = tree.ReadFrom(src)
if err != nil {
t.Error(err)
}
merkleroot := fmt.Sprintf("%x", tree.Root())
fi, err := src.Stat()
if err != nil {
t.Fatal(err)
}
f, err := os.Create(filepath.Join(pkgfsMount, "install", "pkg", merkleroot))
if err != nil {
t.Fatal(err)
}
if err := f.Truncate(fi.Size()); err != nil {
t.Fatal(err)
}
src.Seek(0, os.SEEK_SET)
if _, err := io.Copy(f, src); err != nil {
t.Fatal(err)
}
src.Close()
err = f.Close()
if err != nil {
t.Fatal(err)
}
_, err = os.Stat(filepath.Join(blobfsPath, merkleroot))
if err != nil {
t.Fatalf("package blob missing after package write: %s", err)
}
// TODO(raggi): check that the pacakge content blobs appear in the needs tree
manifest, err := cfg.Manifest()
if err != nil {
t.Fatal(err)
}
// TODO(raggi): extract into constant in testutil
packageName := "testpackage"
packageVersion := "0"
if _, err = os.Stat(filepath.Join(pkgfsMount, "packages", packageName, packageVersion)); err == nil {
t.Error("package appeared in the pkgfs package tree before needs fulfilled")
}
needs, err := filepath.Glob(filepath.Join(pkgfsMount, "needs", "blobs", "*"))
if err != nil {
t.Fatal(err)
}
for i := range needs {
needs[i] = filepath.Base(needs[i])
}
sort.Strings(needs)
contents, err := ioutil.ReadFile(manifest.Paths["meta/contents"])
if err != nil {
t.Fatal(err)
}
lines := strings.Split(string(contents), "\n")
for _, line := range lines {
if line == "" {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
name := parts[0]
root := parts[1]
idx := sort.SearchStrings(needs, root)
if idx == len(needs) {
t.Errorf("need of blob %q (file %q) not found in needs glob: %v", root, name, needs)
continue
}
// write the real content into the target to fulfill the need
err := copyBlob(filepath.Join(pkgfsMount, "install", "blob", root), manifest.Paths[name])
if err != nil {
t.Fatal(err)
}
}
var info os.FileInfo
if info, err = os.Stat(filepath.Join(pkgfsMount, "packages", packageName)); err != nil {
t.Fatalf("package did not appear in the pkgfs package tree: %s", err)
}
if !info.IsDir() {
t.Errorf("os.Stat on package directory says it's not a directory")
}
if info, err = os.Stat(filepath.Join(pkgfsMount, "packages", packageName, packageVersion)); err != nil {
t.Fatalf("package version did not appear in the pkgfs package tree: %s", err)
}
if !info.IsDir() {
t.Errorf("os.Stat on package version directory says it's not a directory")
}
// put the files into needs and expect the pacakage to be live
for f := range manifest.Content() {
b, err := ioutil.ReadFile(filepath.Join(pkgfsMount, "packages", packageName, packageVersion, f))
if err != nil {
t.Fatal(err)
}
if got, want := string(b), f+"\n"; got != want {
t.Errorf("got %q, want %q", got, want)
}
}
// assert that the dynamically added package appears in /versions
contents2, err := ioutil.ReadFile(filepath.Join(pkgfsMount, "versions", merkleroot, "meta", "contents"))
if err != nil {
t.Fatal(err)
}
if got, want := string(contents2), string(contents); got != want {
t.Errorf("add dynamic package, bad version: got %q, want %q", got, want)
}
}
func TestListContainsStatic(t *testing.T) {
names, err := filepath.Glob(filepath.Join(pkgfsMount, "packages", "*", "*"))
if err != nil {
t.Fatal(err)
}
name := ""
for _, path := range names {
if strings.Contains(path, "static-") {
name = path
}
}
want := "static-package/0"
if !strings.HasSuffix(name, want) {
t.Errorf("did not find %q in %v (%q)", want, names, name)
}
}
func TestListRoot(t *testing.T) {
names, err := filepath.Glob(filepath.Join(pkgfsMount, "*"))
if err != nil {
t.Fatal(err)
}
want := []string{"garbage", "install", "needs", "packages", "system", "versions", "validation"}
sort.Strings(names)
sort.Strings(want)
if len(names) != len(want) {
t.Fatalf("got %v, want %v", names, want)
}
for i, name := range names {
got := filepath.Base(name)
if want := want[i]; got != want {
t.Errorf("got %q, want %q", got, want)
}
}
}
func TestVersions(t *testing.T) {
names, err := filepath.Glob(filepath.Join(pkgfsMount, "versions", "*"))
if err != nil {
t.Fatal(err)
}
if len(names) == 0 {
t.Fatal("observed no versions")
}
for _, name := range names {
if !merklePat.MatchString(filepath.Base(name)) {
t.Fatalf("got non-merkle version: %s", name)
}
b, err := ioutil.ReadFile(filepath.Join(name, "meta"))
if err != nil {
t.Fatal(err)
}
if got, want := string(b), filepath.Base(name); got != want {
t.Errorf("got %q, want %q", got, want)
}
}
}
func copyBlob(dest, src string) error {
d, err := os.Create(dest)
if err != nil {
return err
}
defer d.Close()
s, err := os.Open(src)
if err != nil {
return err
}
defer s.Close()
info, err := s.Stat()
if err != nil {
return err
}
d.Truncate(info.Size())
if _, err := io.Copy(d, s); err != nil {
return err
}
return d.Close()
}