blob: 3fb2da4311c3d7d5a7eb140b44598660bc80c86d [file] [log] [blame]
// Copyright 2019 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.
package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"reflect"
"strings"
"testing"
)
func Test_formatSize(t *testing.T) {
tests := []struct {
size int64
expected string
}{
{
0, "0.00 bytes",
},
{
1024, "1.00 KiB",
},
{
1024 * 1024, "1.00 MiB",
},
{
1024 * 1024 * 1024, "1.00 GiB",
},
}
for _, test := range tests {
if result := formatSize(test.size); result != test.expected {
t.Errorf("formatSize(%d) = %s; expect %s", test.size, result, test.expected)
}
}
}
func Test_processSizes(t *testing.T) {
tests := []struct {
name string
file io.Reader
expected map[string]int64
}{
{
"One Line", strings.NewReader(`[{"source_path":"","merkle": "foo","bytes":0,"size":0}]`), map[string]int64{"foo": 0},
},
{
"Two Lines", strings.NewReader(`[{"source_path":"","merkle": "foo","bytes":0,"size":1},{"source_path":"","merkle": "bar","bytes":0,"size":2}]`), map[string]int64{"foo": 1, "bar": 2},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
m, err := processSizes(test.file)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(m, test.expected) {
t.Fatalf("processSizes(%s) = %+v; expect %+v", test.file, m, test.expected)
}
})
}
}
func Test_processManifest(t *testing.T) {
tests := []struct {
name string
blobMap map[string]*Blob
sizeMap map[string]int64
fileName string
file io.Reader
expectedPackage []string
expectedBlobMap map[string]*Blob
}{
{
"Adding New Blob To Empty Map",
map[string]*Blob{},
map[string]int64{"hash": 1},
"fileFoo",
strings.NewReader("hash=foo"),
[]string{},
map[string]*Blob{"hash": {dep: []string{"fileFoo"}, size: 1, name: "foo", hash: "hash"}},
},
{
"Adding New Meta Far To Empty Map",
map[string]*Blob{},
map[string]int64{"hash": 1},
"fileFoo",
strings.NewReader("hash=meta.far"),
[]string{"meta.far"},
map[string]*Blob{"hash": {dep: []string{"fileFoo"}, size: 1, name: "meta.far", hash: "hash"}},
},
{
"Adding A Blob To A Map With That Blob",
map[string]*Blob{"hash": {dep: []string{"foo"}, size: 1, name: "foo", hash: "hash"}},
map[string]int64{"hash": 1},
"fileFoo",
strings.NewReader("hash=foo"),
[]string{},
map[string]*Blob{"hash": {dep: []string{"foo", "fileFoo"}, size: 1, name: "foo", hash: "hash"}},
},
{
"Adding A Meta Far To A Map With That Meta Far",
map[string]*Blob{"hash": {dep: []string{"foo"}, size: 1, name: "meta.far", hash: "hash"}},
map[string]int64{"hash": 1},
"fileFoo",
strings.NewReader("hash=meta.far"),
[]string{},
map[string]*Blob{"hash": {dep: []string{"foo", "fileFoo"}, size: 1, name: "meta.far", hash: "hash"}},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if p := processManifest(test.blobMap, test.sizeMap, test.fileName, test.file); !reflect.DeepEqual(p, test.expectedPackage) {
t.Fatalf("processManifest(%+v, %+v, %s, %+v) = %+v; expect %+v", test.blobMap, test.sizeMap, test.fileName, test.file, p, test.expectedPackage)
}
if !reflect.DeepEqual(test.blobMap["hash"], test.expectedBlobMap["hash"]) {
t.Fatalf("blob map: %+v; expect %+v", test.blobMap["hash"], test.expectedBlobMap["hash"])
}
})
}
}
func Test_processBlobsJSON(t *testing.T) {
tests := []struct {
name string
blobMap map[string]*Blob
assetMap map[string]bool
assetSize int64
blobs []BlobFromJSON
expectedBlobMap map[string]*Blob
expectedSize int64
}{
{
"Adding Asset Blob",
map[string]*Blob{"hash": {size: 1}},
map[string]bool{".asset": true},
0,
[]BlobFromJSON{{Path: "test.asset", Merkle: "hash"}},
map[string]*Blob{},
1,
},
{
"Adding Non-asset Blob",
map[string]*Blob{"hash": {size: 1, dep: []string{"not used"}}},
map[string]bool{".asset": true},
0,
[]BlobFromJSON{{Path: "test.notasset", Merkle: "hash"}},
map[string]*Blob{"hash": {size: 1, dep: []string{"not used"}}},
0,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
processBlobsJSON(test.blobMap, test.assetMap, &test.assetSize, test.blobs, newDummyNode(), "")
if !reflect.DeepEqual(test.blobMap, test.expectedBlobMap) {
t.Fatalf("blob map: %v; expect %v", test.blobMap, test.expectedBlobMap)
}
if test.assetSize != test.expectedSize {
t.Fatalf("asset size: %d; expect %d", test.assetSize, test.expectedSize)
}
})
}
}
func Test_processBlobsJSON_blobLookup(t *testing.T) {
tests := []struct {
name string
pkgPath string
blobMap map[string]*Blob
blob BlobFromJSON
expectedPathInTree string
}{
{
"Adding non config-data blob",
"path/to/pkg/non-config-data",
map[string]*Blob{"hash": {size: 1, dep: []string{"not used"}}},
BlobFromJSON{Path: "data/test/foo.txt", Merkle: "hash"},
"path/to/pkg/non-config-data",
},
{
"Adding config-data blob meta far",
"path/to/pkg/config-data",
map[string]*Blob{"hash": {size: 1, dep: []string{"not used"}}},
BlobFromJSON{Path: "meta/", Merkle: "hash"},
"path/to/pkg/config-data",
},
{
"Adding config-data blob",
"path/to/pkg/config-data",
map[string]*Blob{"hash": {size: 1, dep: []string{"not used"}}},
BlobFromJSON{Path: "data/test/foo.txt", Merkle: "hash"},
"path/to/pkg/config-data/test/foo.txt",
},
}
var dummyAssetMap map[string]bool
var dummyAssetSize int64
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
root := newDummyNode()
processBlobsJSON(test.blobMap, dummyAssetMap, &dummyAssetSize, []BlobFromJSON{test.blob}, root, test.pkgPath)
expectedNode := root.find(test.expectedPathInTree)
if expectedNode == nil {
t.Fatalf("tree.find(%s) returns nil; expect to find a node", test.expectedPathInTree)
}
expectedSize := test.blobMap[test.blob.Merkle].size
if expectedNode.size != expectedSize {
t.Fatalf("tree.find(%s).size returns %d; expect %d", test.expectedPathInTree, expectedNode.size, expectedSize)
}
})
}
}
func Test_checkLimit(t *testing.T) {
tests := []struct {
name string
size int64
limit json.Number
expected string
}{
{
"Size Smaller Than Limit",
1,
json.Number("2"),
"",
},
{
"Size Equal To Limit",
2,
json.Number("2"),
"",
},
{
"Size Greater Than Limit",
3,
json.Number("2"),
"foo (3.00 bytes) has exceeded its limit of 2.00 bytes.",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if result := checkLimit("foo", test.size, test.limit); result != test.expected {
t.Fatalf("checkLimit(foo, %d, %s) = %s; expect %s", test.size, test.limit, result, test.expected)
}
})
}
}
func Test_nodeAdd(t *testing.T) {
root := newDummyNode()
testBlob := Blob{
dep: []string{"not used"},
size: 10,
}
// Test adding a single node
root.add("foo", &testBlob)
child := root.find("foo")
if child == nil {
t.Fatal("foo is not added as the child of the root")
}
if child.size != 10 {
t.Fatalf("the size of the foo node (root's child) is %d; expect 10", child.size)
}
// Test adding a node that shares a common path
root.add("foo/bar", &testBlob)
grandchild := root.find("foo/bar")
if grandchild == nil {
t.Fatal("bar is not added as the grandchild of the root")
}
if child.size != 20 {
t.Fatalf("the size of the foo node (root's child) is %d; expect 20", child.size)
}
if grandchild.size != 10 {
t.Fatalf("the size of the bar node (root's grandchild) is %d; expect 10", grandchild.size)
}
// Test adding a node with .meta suffix
root.add("foo/update.meta", &testBlob)
update := root.find("foo/update")
if update == nil {
t.Fatal("update.meta is not added as the child of the root with the name 'update'")
}
if child.size != 30 {
t.Fatalf("the size of the foo node (root's child) is %d; expect 30", child.size)
}
if update.size != 10 {
t.Fatalf("the size of the update node (root's grandchild, bar's sibling) is %d; expect 10", update.size)
}
}
func Test_nodeFind(t *testing.T) {
tests := []struct {
name string
path string
expected *Node
}{
{
"Find Existing Node",
"foo",
&Node{"foo", 10, make(map[string]*Node)},
},
{
"Find Nonexistent Node",
"NONEXISTENT",
nil,
},
}
root := newDummyNode()
root.add("foo", &Blob{dep: []string{"not used"}, size: 10})
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if node := root.find(test.path); !reflect.DeepEqual(node, test.expected) {
t.Fatalf("node.find(%s) = %+v; expect %+v", test.path, node, test.expected)
}
})
}
}
func Test_processInput(t *testing.T) {
fooSrcRelPath := "foo.src"
input := Input{
AssetLimit: json.Number("1"),
CoreLimit: json.Number("1"),
AssetExt: []string{".txt"},
Components: []Component{
{
Component: "foo",
Limit: json.Number("1"),
Src: []string{"foo-pkg"},
},
},
}
buildDir, err := ioutil.TempDir("", "out")
if err != nil {
t.Fatalf("Failed to create build dir: %v", err)
}
defer os.RemoveAll(buildDir)
pkgDir := path.Join("obj", "foo-pkg")
if err := os.MkdirAll(path.Join(buildDir, pkgDir), 0777); err != nil {
t.Fatalf("Failed to create package dir: %v", err)
}
blobsJSONF, err := os.Create(path.Join(buildDir, pkgDir, BlobsJSON))
if err != nil {
t.Fatalf("Failed to create %s: %v", BlobsJSON, err)
}
metaFarRelPath := path.Join(pkgDir, MetaFar)
blobs := []BlobFromJSON{
{
Merkle: "deadbeef",
Path: "meta/",
SourcePath: metaFarRelPath,
},
{
Merkle: "abc123",
Path: fooSrcRelPath,
SourcePath: fooSrcRelPath,
},
}
blobsJSONBytes, err := json.Marshal(blobs)
if err != nil {
t.Fatalf("Failed to marshal JSON for %s: %v", BlobsJSON, err)
}
if _, err := blobsJSONF.Write(blobsJSONBytes); err != nil {
t.Fatalf("Failed to write %s: %v", BlobsJSON, err)
}
blobsJSONF.Close()
blobManifestRelPath := path.Join(pkgDir, "blobs.manifest")
blobManifestF, err := os.Create(path.Join(buildDir, blobManifestRelPath))
if err != nil {
t.Fatalf("Failed to create blob manifest file: %v", err)
}
blobManifest := fmt.Sprintf("deadbeef=%s\nabc123=%s\n", metaFarRelPath, fooSrcRelPath)
if _, err := blobManifestF.Write([]byte(blobManifest)); err != nil {
t.Fatalf("Failed to write blob manifest: %v", err)
}
blobManifestF.Close()
blobListRelPath := "blob.manifest.list"
blobListF, err := os.Create(path.Join(buildDir, blobListRelPath))
if err != nil {
t.Fatalf("Failed to create blob list file: %v", err)
}
if _, err := blobListF.Write([]byte(blobManifestRelPath)); err != nil {
t.Fatalf("Failed to write blob list: %v", err)
}
blobListF.Close()
blobSizeRelPath := "blobs.json"
blobSizeF, err := os.Create(path.Join(buildDir, blobSizeRelPath))
if err != nil {
t.Fatalf("Failed to create blob size file: %v", err)
}
const singleBlobSize = 4096
if _, err := blobSizeF.Write([]byte(fmt.Sprintf(`[{"source_path":"","merkle":"deadbeef","bytes":0,"size":%d},{"source_path":"","merkle":"abc123","bytes":0,"size":%d}]\n`, singleBlobSize, singleBlobSize))); err != nil {
t.Fatalf("Failed to write blob sizes: %v", err)
}
blobSizeF.Close()
sizes, err := processInput(&input, buildDir, blobListRelPath, blobSizeRelPath)
if err == nil {
t.Fatalf("Expected processInput to return an error because size is above limit")
} else if !strings.Contains(err.Error(), "foo") {
t.Fatalf("Expected error message to mention component name \"foo\". Actual error: %v", err)
} else {
t.Logf("Error returned from processInput (probably expected): %v", err)
}
fooSize, ok := sizes["foo"]
if !ok {
t.Fatalf("Failed to find foo in sizes: %v", sizes)
}
if fooSize != int64(2*singleBlobSize) {
t.Fatalf("Unexpected size for component foo: %v", fooSize)
}
}