blob: f2dc95ce62333694150ec50cc0c8a06be9d11c44 [file] [log] [blame]
// Copyright 2018 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 system_updater
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
)
type byteReadCloser struct {
*bytes.Reader
}
func newByteReadCloser(d []byte) *byteReadCloser {
return &byteReadCloser{bytes.NewReader(d)}
}
func (b *byteReadCloser) Close() error {
return nil
}
func TestParsePackagesLineFormatted(t *testing.T) {
expectedPkgs := [2]string{
"fuchsia-pkg://fuchsia.com/amber/0?hash=abcdef",
"fuchsia-pkg://fuchsia.com/pkgfs/0?hash=123456789",
}
pFile := newByteReadCloser([]byte("amber/0=abcdef\npkgfs/0=123456789"))
pkgs, err := ParsePackagesLineFormatted(pFile)
if err != nil {
t.Fatalf("Error processing packages: %s", err)
}
if len(expectedPkgs) != len(pkgs) {
t.Errorf("Length of parsed packages != expected")
}
for i, pkgURI := range pkgs {
if expectedPkgs[i] != pkgURI {
t.Errorf("Expected URI does not match, expected %q, found %q", expectedPkgs[i], pkgURI)
}
}
}
func TestParsePackagesJson(t *testing.T) {
for _, tc := range []struct {
name string
version string
wantError bool
}{
{
name: "version string success",
version: "\"1\"",
wantError: false,
},
{
name: "version int success",
version: "1",
wantError: false,
},
{
name: "version string unsupported fail",
version: "\"2\"",
wantError: true,
},
{
name: "version int unsupported fail",
version: "2",
wantError: true,
},
{
name: "version is bananas fail",
version: "bananas",
wantError: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
expectedPkgs := make([]string, 0)
if !tc.wantError {
expectedPkgs = append(
expectedPkgs,
"fuchsia-pkg://fuchsia.com/amber/0?hash=abcdef",
"fuchsia-pkg://fuchsia.com/pkgfs/0?hash=123456789",
)
}
pFile := newByteReadCloser([]byte(fmt.Sprintf(`
{
"version": %s,
"content": [
"fuchsia-pkg://fuchsia.com/amber/0?hash=abcdef",
"fuchsia-pkg://fuchsia.com/pkgfs/0?hash=123456789"
]
}
`, tc.version)))
pkgs, err := ParsePackagesJson(pFile)
if (err == nil) == tc.wantError {
t.Fatalf("ParsePackagesJson() got error [%v], want error? %t", err, tc.wantError)
}
if len(expectedPkgs) != len(pkgs) {
t.Errorf("Length of parsed packages != expected")
}
for i, pkgURI := range pkgs {
if expectedPkgs[i] != pkgURI {
t.Errorf("Expected URI does not match, expected %q, found %q", expectedPkgs[i], pkgURI)
}
}
})
}
}
// Verifies that ParseImages() using the given image file and update package
// contents produces the expected image list.
func TestParseImages(t *testing.T) {
for _, tc := range []struct {
name string
imageFileContents string
updatePackageFiles []string
expectedImages []Image
}{
{
name: "parse images",
imageFileContents: "dc38ffa1029c3fd44\n",
updatePackageFiles: []string{},
expectedImages: []Image{
// Untyped files don't care whether they exist in the package or not.
{Name: "dc38ffa1029c3fd44", Type: ""},
},
},
{
name: "parse images with type",
imageFileContents: "firmware[_type]\n",
updatePackageFiles: []string{"firmware", "firmware_abc", "firmware_123"},
expectedImages: []Image{
{Name: "firmware", Type: ""},
{Name: "firmware", Type: "abc"},
{Name: "firmware", Type: "123"},
},
},
{
name: "parse images type requires underscores",
imageFileContents: "firmware[_type]\n",
updatePackageFiles: []string{"firmware_a", "firmware2"},
expectedImages: []Image{
{Name: "firmware", Type: "a"},
// firmware2 doesn't follow the <base>_<type> format, should be ignored.
},
},
{
name: "parse images type multiple underscores",
imageFileContents: "firmware[_type]\n",
updatePackageFiles: []string{"firmware_a_b", "firmware_firmware_2"},
expectedImages: []Image{
{Name: "firmware", Type: "a_b"},
{Name: "firmware", Type: "firmware_2"},
},
},
{
name: "parse images empty type",
imageFileContents: "firmware[_type]\n",
updatePackageFiles: []string{"firmware_"},
expectedImages: []Image{
{Name: "firmware", Type: ""},
},
},
{
name: "parse images no type matches",
imageFileContents: "firmware[_type]\n",
updatePackageFiles: []string{"not_firmware"},
expectedImages: []Image{},
},
{
name: "parse images untyped with underscore",
imageFileContents: "untyped_firmware\n",
updatePackageFiles: []string{"untyped_firmware_2"},
expectedImages: []Image{
{Name: "untyped_firmware", Type: ""},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
iFile := newByteReadCloser([]byte(tc.imageFileContents))
images, err := ParseImages(iFile, tc.updatePackageFiles)
if err != nil {
t.Fatalf("Error parsing images: %s", err)
}
if !reflect.DeepEqual(tc.expectedImages, images) {
t.Errorf("Expected %+v but got %+v", tc.expectedImages, images)
}
})
}
}
func TestImageFilenameWithoutType(t *testing.T) {
image := Image{Name: "name", Type: ""}
if image.Filename() != "name" {
t.Errorf("Expected %q, got %q\n", "name", image.Filename())
}
}
func TestImageFilenameWithType(t *testing.T) {
image := Image{Name: "name", Type: "type"}
if image.Filename() != "name_type" {
t.Errorf("Expected %q, got %q\n", "name_type", image.Filename())
}
}
func genValidUpdateModeJson(mode string) string {
return fmt.Sprintf(`
{
"version": "1",
"content": {
"mode": "%s"
}
}
`, mode)
}
func TestParseUpdateMode(t *testing.T) {
for _, tc := range []struct {
name string
modeFileData []byte
expectedMode UpdateMode
errorTarget error
}{
{
name: "success normal",
modeFileData: []byte(genValidUpdateModeJson("normal")),
expectedMode: UpdateModeNormal,
},
{
name: "success force-recovery",
modeFileData: []byte(genValidUpdateModeJson("force-recovery")),
expectedMode: UpdateModeForceRecovery,
},
{
name: "success update-mode file does not exist",
modeFileData: nil, // since data is nil, we won't write an update-mode file.
expectedMode: UpdateModeNormal,
},
{
name: "fail update mode not supported",
modeFileData: []byte(genValidUpdateModeJson("potato")),
expectedMode: "",
errorTarget: updateModeNotSupportedError(""),
},
{
name: "fail json bad formatting",
modeFileData: []byte("invalid-json"),
expectedMode: "",
errorTarget: jsonUnmarshalError{fmt.Errorf("")},
},
} {
t.Run(tc.name, func(t *testing.T) {
// Generate update package based on test inputs.
dirPath, err := ioutil.TempDir("", "update")
if err != nil {
t.Fatalf("Unable to create tempdir for update package: %v", err)
}
defer os.RemoveAll(dirPath)
if tc.modeFileData != nil {
if err := ioutil.WriteFile(filepath.Join(dirPath, "update-mode"), tc.modeFileData, 0666); err != nil {
t.Fatalf("Unable to write update-mode file to tempdir: %v", err)
}
}
dir, err := os.Open(dirPath)
if err != nil {
t.Fatalf("Unable to open tempdir: %v", err)
}
updatePackage := UpdatePackage{dir}
// Parse update mode from the package.
mode, err := ParseUpdateMode(&updatePackage)
// Verify expected outcome.
if got, want := reflect.TypeOf(err), reflect.TypeOf(tc.errorTarget); got != want {
t.Fatalf("ParseUpdateMode() got error type %v, want %v", got, want)
}
if mode != tc.expectedMode {
t.Fatalf("ParseUpdateMode() got mode %q, want %q", mode, tc.expectedMode)
}
})
}
}
func TestValidateImages(t *testing.T) {
for _, tc := range []struct {
name string
images []Image
updateMode UpdateMode
wantError bool
}{
{
name: "success normal update package has zbi",
images: []Image{{Name: "zbi"}},
updateMode: UpdateModeNormal,
wantError: false,
},
{
name: "success force-recovery update package does not have zbi",
images: []Image{},
updateMode: UpdateModeForceRecovery,
wantError: false,
},
{
name: "failure normal update package does not have zbi",
images: []Image{},
updateMode: UpdateModeNormal,
wantError: true,
},
{
name: "failure force-recovery update package has zbi",
images: []Image{{Name: "zbi"}},
updateMode: UpdateModeForceRecovery,
wantError: true,
},
} {
t.Run(tc.name, func(t *testing.T) {
// Generate update package based on test inputs.
dirPath, err := ioutil.TempDir("", "update")
if err != nil {
t.Fatalf("Unable to create tempdir for update package: %v", err)
}
defer os.RemoveAll(dirPath)
if err := ioutil.WriteFile(filepath.Join(dirPath, "update-mode"), []byte(genValidUpdateModeJson(string(tc.updateMode))), 0666); err != nil {
t.Fatalf("Unable to write update-mode file to tempdir: %v", err)
}
for _, img := range tc.images {
if err := ioutil.WriteFile(filepath.Join(dirPath, img.Name), []byte(""), 0666); err != nil {
t.Fatalf("Unable to write image file to tempdir: %v", err)
}
}
dir, err := os.Open(dirPath)
if err != nil {
t.Fatalf("Unable to open tempdir: %v", err)
}
updatePackage := UpdatePackage{dir}
// Assert expected result.
if err := ValidateImgs(tc.images, &updatePackage, tc.updateMode); (err == nil) == tc.wantError {
t.Fatalf("ValidateImages() got err [%v] want err? %t", err, tc.wantError)
}
})
}
}