2015-01-11 14:09:43 -05:00
|
|
|
// 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 main
|
|
|
|
|
|
|
|
import (
|
2015-10-21 03:12:09 -05:00
|
|
|
"archive/zip"
|
2015-01-11 14:09:43 -05:00
|
|
|
"bytes"
|
|
|
|
"flag"
|
2015-11-11 23:11:29 -06:00
|
|
|
"fmt"
|
2015-01-11 14:09:43 -05:00
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
2015-10-21 03:12:09 -05:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2015-01-11 14:09:43 -05:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
var dump = flag.Bool("dump", false, "dump junk.bin binary output")
|
|
|
|
|
|
|
|
var (
|
|
|
|
origSortPool = sortPool
|
|
|
|
origSortAttr = sortAttr
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestBinaryXML(t *testing.T) {
|
|
|
|
sortPool, sortAttr = sortToMatchTest, sortAttrToMatchTest
|
|
|
|
defer func() { sortPool, sortAttr = origSortPool, origSortAttr }()
|
|
|
|
|
2015-11-11 23:11:29 -06:00
|
|
|
bin, err := binaryXML(bytes.NewBufferString(fmt.Sprintf(input, "")))
|
2015-01-11 14:09:43 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if *dump {
|
2015-10-21 03:12:09 -05:00
|
|
|
if err := ioutil.WriteFile("junk.bin", bin, 0660); err != nil {
|
2015-01-11 14:09:43 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-21 03:12:09 -05:00
|
|
|
if exec.Command("which", "aapt").Run() != nil {
|
|
|
|
t.Skip("command aapt not found, skipping")
|
2015-01-11 14:09:43 -05:00
|
|
|
}
|
|
|
|
|
2015-10-21 03:12:09 -05:00
|
|
|
apiPath, err := androidAPIPath()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
androidJar := filepath.Join(apiPath, "android.jar")
|
|
|
|
|
|
|
|
tmpdir, err := ioutil.TempDir("", "gomobile-test-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
zw := zip.NewWriter(buf)
|
|
|
|
zf, err := zw.Create("AndroidManifest.xml")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if _, err := zf.Write(bin); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if err := zw.Close(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mblapk := filepath.Join(tmpdir, "mbl.apk")
|
|
|
|
if err := ioutil.WriteFile(mblapk, buf.Bytes(), 0755); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
got, err := exec.Command("aapt", "d", "xmltree", mblapk, "AndroidManifest.xml").Output()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
manifest := filepath.Join(tmpdir, "AndroidManifest.xml")
|
2015-11-11 23:11:29 -06:00
|
|
|
inp := fmt.Sprintf(input, "<uses-sdk android:minSdkVersion=\"15\" />")
|
|
|
|
if err := ioutil.WriteFile(manifest, []byte(inp), 0755); err != nil {
|
2015-10-21 03:12:09 -05:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sdkapk := filepath.Join(tmpdir, "sdk.apk")
|
|
|
|
if _, err := exec.Command("aapt", "p", "-M", manifest, "-I", androidJar, "-F", sdkapk).Output(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sdkout, err := exec.Command("aapt", "d", "xmltree", sdkapk, "AndroidManifest.xml").Output()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-10-22 00:11:00 -05:00
|
|
|
if *dump {
|
|
|
|
b, err := ioutil.ReadFile(sdkapk)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if err := ioutil.WriteFile("junk.sdk.apk", b, 0755); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2015-10-21 03:12:09 -05:00
|
|
|
|
|
|
|
// manifests contain platformBuildVersionCode and platformBuildVersionName
|
|
|
|
// which are not present in gomobile output.
|
|
|
|
var fo [][]byte
|
|
|
|
for _, line := range bytes.Split(sdkout, []byte{'\n'}) {
|
|
|
|
if bytes.Contains(line, []byte("platformBuildVersionCode")) || bytes.Contains(line, []byte("platformBuildVersionName")) {
|
2015-01-11 14:09:43 -05:00
|
|
|
continue
|
|
|
|
}
|
2015-10-21 03:12:09 -05:00
|
|
|
fo = append(fo, line)
|
|
|
|
}
|
|
|
|
want := bytes.Join(fo, []byte{'\n'})
|
|
|
|
|
|
|
|
if !bytes.Equal(want, got) {
|
2015-10-22 00:11:00 -05:00
|
|
|
t.Fatalf("output does not match\nWANT\n%s\nGOT\n%s\n", want, got)
|
2015-01-11 14:09:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The output of the Android encoder seems to be arbitrary. So for testing,
|
|
|
|
// we sort the string pool order to match the output we have seen.
|
|
|
|
func sortToMatchTest(p *binStringPool) {
|
|
|
|
var names = []string{
|
|
|
|
"versionCode",
|
|
|
|
"versionName",
|
|
|
|
"minSdkVersion",
|
2015-10-22 00:11:00 -05:00
|
|
|
"theme",
|
2015-01-11 14:09:43 -05:00
|
|
|
"label",
|
|
|
|
"hasCode",
|
|
|
|
"debuggable",
|
|
|
|
"name",
|
2015-10-21 03:12:09 -05:00
|
|
|
"screenOrientation",
|
2015-01-11 14:09:43 -05:00
|
|
|
"configChanges",
|
|
|
|
"value",
|
|
|
|
"android",
|
|
|
|
"http://schemas.android.com/apk/res/android",
|
|
|
|
"",
|
|
|
|
"package",
|
|
|
|
"manifest",
|
|
|
|
"com.zentus.balloon",
|
|
|
|
"1.0",
|
|
|
|
"uses-sdk",
|
|
|
|
"application",
|
|
|
|
"Balloon世界",
|
|
|
|
"activity",
|
|
|
|
"android.app.NativeActivity",
|
|
|
|
"Balloon",
|
|
|
|
"meta-data",
|
|
|
|
"android.app.lib_name",
|
|
|
|
"balloon",
|
|
|
|
"intent-filter",
|
|
|
|
"\there is some text\n",
|
|
|
|
"action",
|
|
|
|
"android.intent.action.MAIN",
|
|
|
|
"category",
|
|
|
|
"android.intent.category.LAUNCHER",
|
|
|
|
}
|
|
|
|
|
|
|
|
s := make([]*bstring, 0)
|
|
|
|
m := make(map[string]*bstring)
|
|
|
|
|
|
|
|
for _, str := range names {
|
|
|
|
bstr := p.m[str]
|
|
|
|
if bstr == nil {
|
|
|
|
log.Printf("missing %q", str)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
bstr.ind = uint32(len(s))
|
|
|
|
s = append(s, bstr)
|
|
|
|
m[str] = bstr
|
|
|
|
delete(p.m, str)
|
|
|
|
}
|
|
|
|
// add unexpected strings
|
|
|
|
for str, bstr := range p.m {
|
|
|
|
log.Printf("unexpected %q", str)
|
|
|
|
bstr.ind = uint32(len(s))
|
|
|
|
s = append(s, bstr)
|
|
|
|
}
|
|
|
|
p.s = s
|
|
|
|
p.m = m
|
|
|
|
}
|
|
|
|
|
|
|
|
func sortAttrToMatchTest(e *binStartElement, p *binStringPool) {
|
|
|
|
order := []string{
|
|
|
|
"versionCode",
|
|
|
|
"versionName",
|
|
|
|
"versionPackage",
|
|
|
|
|
2015-10-22 00:11:00 -05:00
|
|
|
"theme",
|
2015-01-11 14:09:43 -05:00
|
|
|
"label",
|
|
|
|
"name",
|
2015-10-21 03:12:09 -05:00
|
|
|
"screenOrientation",
|
2015-01-11 14:09:43 -05:00
|
|
|
"configChanges",
|
|
|
|
}
|
|
|
|
ordered := make([]*binAttr, len(order))
|
|
|
|
|
|
|
|
outer:
|
|
|
|
for i, n := range order {
|
|
|
|
for j, a := range e.attr {
|
|
|
|
if a != nil && a.name.str == n {
|
|
|
|
ordered[i] = a
|
|
|
|
e.attr[j] = nil
|
|
|
|
continue outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var attr []*binAttr
|
|
|
|
for _, a := range ordered {
|
|
|
|
if a != nil {
|
|
|
|
attr = append(attr, a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, a := range e.attr {
|
|
|
|
if a != nil {
|
|
|
|
attr = append(attr, a)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
e.attr = attr
|
|
|
|
}
|
|
|
|
|
|
|
|
const input = `<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
<!--
|
|
|
|
Copyright 2014 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.
|
|
|
|
-->
|
|
|
|
<manifest
|
|
|
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
|
|
package="com.zentus.balloon"
|
|
|
|
android:versionCode="1"
|
|
|
|
android:versionName="1.0">
|
|
|
|
|
2015-11-11 23:11:29 -06:00
|
|
|
%s
|
2015-01-11 14:09:43 -05:00
|
|
|
<application android:label="Balloon世界" android:hasCode="false" android:debuggable="true">
|
|
|
|
<activity android:name="android.app.NativeActivity"
|
2015-10-22 00:11:00 -05:00
|
|
|
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
2015-01-11 14:09:43 -05:00
|
|
|
android:label="Balloon"
|
2015-10-21 03:12:09 -05:00
|
|
|
android:screenOrientation="portrait"
|
2015-01-11 14:09:43 -05:00
|
|
|
android:configChanges="orientation|keyboardHidden">
|
|
|
|
<meta-data android:name="android.app.lib_name" android:value="balloon" />
|
|
|
|
<intent-filter>
|
|
|
|
here is some text
|
|
|
|
<action android:name="android.intent.action.MAIN" />
|
|
|
|
<category android:name="android.intent.category.LAUNCHER" />
|
|
|
|
</intent-filter>
|
|
|
|
</activity>
|
|
|
|
</application>
|
|
|
|
</manifest>`
|