blob: a97c013fbd13e69761d65e96b2a8c2afaa626b7c [file] [log] [blame]
// Copyright 2015 The Go 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 binres
import (
"bytes"
"encoding"
"fmt"
"io/ioutil"
"log"
"math"
"os"
"path"
"strings"
"testing"
)
func printrecurse(t *testing.T, pl *Pool, el *Element, ws string) {
for _, attr := range el.attrs {
ns := ""
if attr.NS != math.MaxUint32 {
ns = pl.strings[int(attr.NS)]
nss := strings.Split(ns, "/")
ns = nss[len(nss)-1]
}
val := ""
if attr.RawValue != math.MaxUint32 {
val = pl.strings[int(attr.RawValue)]
} else {
switch attr.TypedValue.Type {
case DataIntDec:
val = fmt.Sprintf("%v", attr.TypedValue.Value)
case DataIntBool:
val = fmt.Sprintf("%v", attr.TypedValue.Value == 1)
default:
val = fmt.Sprintf("0x%08X", attr.TypedValue.Value)
}
}
dt := attr.TypedValue.Type
t.Logf("%s|attr:ns(%v) name(%s) val(%s) valtyp(%s)\n", ws, ns, pl.strings[int(attr.Name)], val, dt)
}
t.Log()
for _, e := range el.Children {
printrecurse(t, pl, e, ws+" ")
}
}
func TestBootstrap(t *testing.T) {
bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
if err != nil {
log.Fatal(err)
}
checkMarshal := func(res encoding.BinaryMarshaler, bsize int) {
b, err := res.MarshalBinary()
if err != nil {
t.Error(err)
}
idx := debugIndices[res]
a := bin[idx : idx+bsize]
if !bytes.Equal(a, b) {
x, y := len(a), len(b)
if x != y {
t.Errorf("%v: %T: byte length does not match, have %v, want %v", idx, res, y, x)
}
if x > y {
x, y = y, x
}
mismatch := false
for i := 0; i < x; i++ {
if mismatch = a[i] != b[i]; mismatch {
t.Errorf("%v: %T: first byte mismatch at %v of %v", idx, res, i, bsize)
break
}
}
if mismatch {
// print out a reasonable amount of data to help identify issues
truncate := x > 1300
if truncate {
x = 1300
}
t.Log(" HAVE WANT")
for i := 0; i < x; i += 4 {
he, we := 4, 4
if i+he >= x {
he = x - i
}
if i+we >= y {
we = y - i
}
t.Logf("%3v | % X % X\n", i, b[i:i+he], a[i:i+we])
}
if truncate {
t.Log("... output truncated.")
}
}
}
}
bxml := new(XML)
if err := bxml.UnmarshalBinary(bin); err != nil {
t.Fatal(err)
}
for i, x := range bxml.Pool.strings {
t.Logf("Pool(%v): %q\n", i, x)
}
for _, e := range bxml.Children {
printrecurse(t, bxml.Pool, e, "")
}
checkMarshal(&bxml.chunkHeader, int(bxml.headerByteSize))
checkMarshal(bxml.Pool, bxml.Pool.size())
checkMarshal(bxml.Map, bxml.Map.size())
checkMarshal(bxml.Namespace, bxml.Namespace.size())
for el := range bxml.iterElements() {
checkMarshal(el, el.size())
checkMarshal(el.end, el.end.size())
}
checkMarshal(bxml.Namespace.end, bxml.Namespace.end.size())
checkMarshal(bxml, bxml.size())
}
// WIP approximation of first steps to be taken to encode manifest
func TestEncode(t *testing.T) {
f, err := os.Open("testdata/bootstrap.xml")
if err != nil {
t.Fatal(err)
}
bx, err := UnmarshalXML(f)
if err != nil {
t.Fatal(err)
}
//
bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
if err != nil {
log.Fatal(err)
}
bxml := new(XML)
if err := bxml.UnmarshalBinary(bin); err != nil {
t.Fatal(err)
}
if err := compareStrings(t, bxml.Pool.strings, bx.Pool.strings); err != nil {
t.Fatal(err)
}
}
func compareStrings(t *testing.T, a, b []string) error {
var err error
if len(a) != len(b) {
err = fmt.Errorf("lengths do not match")
}
for i, x := range a {
v := "__"
for j, y := range b {
if x == y {
v = fmt.Sprintf("%2v", j)
break
}
}
if err == nil && v == "__" {
if !strings.HasPrefix(x, "4.0.") {
// as of the time of this writing, the current version of build tools being targetted
// reports 4.0.4-1406430. Previously, this was 4.0.3. This number is likely still due
// to change so only report error if 4.x incremented.
//
// TODO this check has the potential to hide real errors but can be fixed once more
// of the xml document is unmarshalled and XML can be queried to assure this is related
// to platformBuildVersionName.
err = fmt.Errorf("has missing/incorrect values")
}
}
t.Logf("Pool(%2v, %s) %q", i, v, x)
}
contains := func(xs []string, a string) bool {
for _, x := range xs {
if x == a {
return true
}
}
return false
}
if err != nil {
t.Log()
t.Logf("## only in var a")
for i, x := range a {
if !contains(b, x) {
t.Logf("Pool(%2v) %q", i, x)
}
}
t.Log()
t.Logf("## only in var b")
for i, x := range b {
if !contains(a, x) {
t.Logf("Pool(%2v) %q", i, x)
}
}
}
return err
}
func TestOpenTable(t *testing.T) {
sdkdir := os.Getenv("ANDROID_HOME")
if sdkdir == "" {
t.Skip("ANDROID_HOME env var not set")
}
tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar"))
if err != nil {
t.Fatal(err)
}
if len(tbl.pkgs) == 0 {
t.Fatal("failed to decode any resource packages")
}
pkg := tbl.pkgs[0]
t.Log("package name:", pkg.name)
for i, x := range pkg.typePool.strings {
t.Logf("typePool[i=%v]: %s\n", i, x)
}
for i, spec := range pkg.specs {
t.Logf("spec[i=%v]: %v %q\n", i, spec.id, pkg.typePool.strings[spec.id-1])
for j, typ := range spec.types {
t.Logf("\ttype[i=%v]: %v\n", j, typ.id)
for k, nt := range typ.entries {
if nt == nil { // NoEntry
continue
}
t.Logf("\t\tentry[i=%v]: %v %q\n", k, nt.key, pkg.keyPool.strings[nt.key])
if k > 5 {
t.Logf("\t\t... truncating output")
break
}
}
}
}
}
func TestTableRefByName(t *testing.T) {
sdkdir := os.Getenv("ANDROID_HOME")
if sdkdir == "" {
t.Skip("ANDROID_HOME env var not set")
}
tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar"))
if err != nil {
t.Fatal(err)
}
if len(tbl.pkgs) == 0 {
t.Fatal("failed to decode any resource packages")
}
ref, err := tbl.RefByName("@android:style/Theme.NoTitleBar.Fullscreen")
if err != nil {
t.Fatal(err)
}
if want := uint32(0x01030007); uint32(ref) != want {
t.Fatalf("RefByName does not match expected result, have %0#8x, want %0#8x", ref, want)
}
}
func BenchmarkTableRefByName(b *testing.B) {
sdkdir := os.Getenv("ANDROID_HOME")
if sdkdir == "" {
b.Fatal("ANDROID_HOME env var not set")
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar"))
if err != nil {
b.Fatal(err)
}
_, err = tbl.RefByName("@android:style/Theme.NoTitleBar.Fullscreen")
if err != nil {
b.Fatal(err)
}
}
}