blob: 3637e13aac25ecd654155d4b2ebc39faa687e998 [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 (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"sort"
"strings"
"syscall"
"syscall/zx"
"syscall/zx/fdio"
"syscall/zx/fidl"
zxio "syscall/zx/io"
"testing"
"fuchsia.googlesource.com/merkle"
"fuchsia.googlesource.com/pm/build"
"fuchsia.googlesource.com/pmd/amberer"
)
// 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 (
pkgfsDir fdio.Directory
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)
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(int64(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()
nc, sc, err := zx.NewChannel(0)
if err != nil {
panic(err)
}
pkgfsDir = fdio.Directory{fdio.Node{(*zxio.NodeInterface)(&fidl.ChannelProxy{nc})}}
go func() {
if err := pkgfs.Serve(sc); err != nil {
panic(err)
}
}()
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 := pkgfsOpen(filepath.Join("install/pkg", merkleroot), zxio.OpenRightWritable|zxio.OpenFlagCreate, zxio.ModeTypeFile)
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)
}
manifest, err := cfg.Manifest()
if err != nil {
t.Fatal(err)
}
// TODO(raggi): extract into constant in testutil
packageName := "testpackage"
packageVersion := "0"
f, err = pkgfsOpen(filepath.Join("packages", packageName, packageVersion), zxio.OpenRightReadable, zxio.ModeTypeFile)
if err == nil {
f.Close()
t.Error("package appeared in the pkgfs package tree before needs fulfilled")
}
f, err = pkgfsOpen(filepath.Join("needs", "blobs"), zxio.OpenRightReadable, zxio.ModeTypeDirectory)
if err != nil {
t.Fatal(err)
}
needs, err := f.Readdirnames(256)
f.Close()
if err != nil {
t.Fatal(err)
}
for i := range needs {
needs[i] = filepath.Base(needs[i])
}
sort.Strings(needs)
f, err = pkgfsOpen(filepath.Join("needs", "packages", merkleroot), zxio.OpenRightReadable, zxio.ModeTypeDirectory)
needs2, err := f.Readdirnames(256)
f.Close()
if err != nil {
t.Fatal(err)
}
for i := range needs2 {
needs2[i] = filepath.Base(needs2[i])
}
sort.Strings(needs2)
if len(needs) != len(needs2) {
t.Errorf("expected needs dirs to be the same: %d != %d", len(needs), len(needs2))
}
for i, need := range needs {
if needs2[i] != need {
t.Errorf("needs from needs/blobs didn't match package needs at %d", i)
}
}
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
}
f, err = pkgfsOpen(filepath.Join("install", "blob", root), zxio.OpenRightWritable|zxio.OpenFlagCreate, zxio.ModeTypeFile)
if err != nil {
t.Fatal(err)
}
from, err := os.Open(manifest.Paths[name])
if err != nil {
t.Fatal(err)
}
if _, err := io.Copy(f, from); err != nil {
t.Fatal(err)
}
}
var info os.FileInfo
if info, err = pkgfsStat(filepath.Join("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 = pkgfsStat(filepath.Join("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 := pkgfsReadFile(filepath.Join("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 := pkgfsReadFile(filepath.Join("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 pkgfsOpen(path string, flags, mode uint32) (*os.File, error) {
f, err := pkgfsDir.Open(path, flags, mode)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(syscall.OpenFDIO(f)), path), err
}
func pkgfsReadFile(path string) ([]byte, error) {
f, err := pkgfsOpen(path, zxio.OpenRightReadable, zxio.ModeTypeFile)
if err != nil {
return nil, err
}
defer f.Close()
buf := bytes.Buffer{}
if _, err := io.Copy(&buf, f); err != io.EOF && err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func pkgfsStat(path string) (os.FileInfo, error) {
f, err := pkgfsOpen(path, zxio.OpenRightReadable, zxio.ModeTypeFile|zxio.ModeTypeDirectory)
if err != nil {
return nil, err
}
defer f.Close()
return f.Stat()
}
func TestListContainsStatic(t *testing.T) {
//names, err := filepath.Glob(filepath.Join(pkgfsMount, "packages", "*", "*"))
f, err := pkgfsOpen("packages/static-package/0", zxio.OpenRightReadable, zxio.ModeTypeDirectory)
if err != nil {
t.Fatal(err)
}
names, err := f.Readdirnames(-1)
f.Close()
if err != nil {
t.Fatal(err)
}
if len(names) <= 0 {
t.Errorf("static-package appears to be empty or missing")
}
}
func TestListRoot(t *testing.T) {
f, err := pkgfsOpen(".", zxio.OpenRightReadable, zxio.ModeTypeDirectory)
if err != nil {
t.Fatal(err)
}
names, err := f.Readdirnames(-1)
f.Close()
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) {
f, err := pkgfsOpen("versions", zxio.OpenRightReadable, zxio.ModeTypeDirectory)
if err != nil {
t.Fatal(err)
}
names, err := f.Readdirnames(-1)
f.Close()
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 := pkgfsReadFile(filepath.Join("versions", 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)
}
}
}