binres_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package binres
  5. import (
  6. "bytes"
  7. "encoding"
  8. "encoding/xml"
  9. "fmt"
  10. "io/ioutil"
  11. "log"
  12. "math"
  13. "os"
  14. "sort"
  15. "strings"
  16. "testing"
  17. "golang.org/x/mobile/internal/sdkpath"
  18. )
  19. func init() {
  20. skipSynthesize = true
  21. }
  22. func printrecurse(t *testing.T, pl *Pool, el *Element, ws string) {
  23. t.Logf("%s+elem:ns(%v) name(%s)", ws, el.NS, el.Name.Resolve(pl))
  24. for _, attr := range el.attrs {
  25. ns := ""
  26. if attr.NS != math.MaxUint32 {
  27. ns = pl.strings[int(attr.NS)]
  28. nss := strings.Split(ns, "/")
  29. ns = nss[len(nss)-1]
  30. }
  31. val := ""
  32. if attr.RawValue != NoEntry {
  33. val = pl.strings[int(attr.RawValue)]
  34. } else {
  35. switch attr.TypedValue.Type {
  36. case DataIntDec:
  37. val = fmt.Sprintf("%v", attr.TypedValue.Value)
  38. case DataIntBool:
  39. val = fmt.Sprintf("%v", attr.TypedValue.Value == 0xFFFFFFFF)
  40. default:
  41. val = fmt.Sprintf("0x%08X", attr.TypedValue.Value)
  42. }
  43. }
  44. dt := attr.TypedValue.Type
  45. t.Logf("%s|attr:ns(%v) name(%s) val(%s) valtyp(%s)\n", ws, ns, pl.strings[int(attr.Name)], val, dt)
  46. }
  47. t.Logf("\n")
  48. for _, e := range el.Children {
  49. printrecurse(t, pl, e, ws+" ")
  50. }
  51. }
  52. func compareBytes(a, b []byte) error {
  53. if bytes.Equal(a, b) {
  54. return nil
  55. }
  56. buf := new(bytes.Buffer)
  57. x, y := len(a), len(b)
  58. if x != y {
  59. fmt.Fprintf(buf, "byte length does not match, have %v, want %v\n", y, x)
  60. }
  61. if x > y {
  62. x, y = y, x
  63. }
  64. mismatch := false
  65. for i := 0; i < x; i++ {
  66. if mismatch = a[i] != b[i]; mismatch {
  67. fmt.Fprintf(buf, "first byte mismatch at %v\n", i)
  68. break
  69. }
  70. }
  71. if mismatch {
  72. // print out a reasonable amount of data to help identify issues
  73. truncate := x > 3300
  74. if truncate {
  75. x = 3300
  76. }
  77. buf.WriteString(" HAVE WANT\n")
  78. for i := 0; i < x; i += 4 {
  79. he, we := 4, 4
  80. if i+he >= x {
  81. he = x - i
  82. }
  83. if i+we >= y {
  84. we = y - i
  85. }
  86. notequal := ""
  87. if !bytes.Equal(b[i:i+he], a[i:i+we]) {
  88. notequal = "***"
  89. }
  90. fmt.Fprintf(buf, "%3v | % X % X %s\n", i, b[i:i+he], a[i:i+we], notequal)
  91. }
  92. if truncate {
  93. fmt.Fprint(buf, "... output truncated.\n")
  94. }
  95. }
  96. return fmt.Errorf(buf.String())
  97. }
  98. func TestBootstrap(t *testing.T) {
  99. bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
  100. if err != nil {
  101. log.Fatal(err)
  102. }
  103. // unmarshal binary xml and store byte indices of decoded resources.
  104. debugIndices := make(map[encoding.BinaryMarshaler]int)
  105. trackUnmarshal := func(buf []byte) (*XML, error) {
  106. bx := new(XML)
  107. if err := (&bx.chunkHeader).UnmarshalBinary(buf); err != nil {
  108. return nil, err
  109. }
  110. buf = buf[8:]
  111. debugIndex := 8
  112. for len(buf) > 0 {
  113. k, err := bx.unmarshalBinaryKind(buf)
  114. if err != nil {
  115. return nil, err
  116. }
  117. debugIndices[k.(encoding.BinaryMarshaler)] = debugIndex
  118. debugIndex += k.size()
  119. buf = buf[k.size():]
  120. }
  121. return bx, nil
  122. }
  123. checkMarshal := func(res encoding.BinaryMarshaler, bsize int) {
  124. b, err := res.MarshalBinary()
  125. if err != nil {
  126. t.Error(err)
  127. }
  128. idx := debugIndices[res]
  129. a := bin[idx : idx+bsize]
  130. if !bytes.Equal(a, b) {
  131. x, y := len(a), len(b)
  132. if x != y {
  133. t.Errorf("%v: %T: byte length does not match, have %v, want %v", idx, res, y, x)
  134. }
  135. if x > y {
  136. x, y = y, x
  137. }
  138. mismatch := false
  139. for i := 0; i < x; i++ {
  140. if mismatch = a[i] != b[i]; mismatch {
  141. t.Errorf("%v: %T: first byte mismatch at %v of %v", idx, res, i, bsize)
  142. break
  143. }
  144. }
  145. if mismatch {
  146. // print out a reasonable amount of data to help identify issues
  147. truncate := x > 1300
  148. if truncate {
  149. x = 1300
  150. }
  151. t.Log(" HAVE WANT")
  152. for i := 0; i < x; i += 4 {
  153. he, we := 4, 4
  154. if i+he >= x {
  155. he = x - i
  156. }
  157. if i+we >= y {
  158. we = y - i
  159. }
  160. t.Logf("%3v | % X % X\n", i, b[i:i+he], a[i:i+we])
  161. }
  162. if truncate {
  163. t.Log("... output truncated.")
  164. }
  165. }
  166. }
  167. }
  168. bxml, err := trackUnmarshal(bin)
  169. if err != nil {
  170. t.Fatal(err)
  171. }
  172. for i, x := range bxml.Pool.strings {
  173. t.Logf("Pool(%v): %q\n", i, x)
  174. }
  175. for _, e := range bxml.Children {
  176. printrecurse(t, bxml.Pool, e, "")
  177. }
  178. checkMarshal(&bxml.chunkHeader, int(bxml.headerByteSize))
  179. checkMarshal(bxml.Pool, bxml.Pool.size())
  180. checkMarshal(bxml.Map, bxml.Map.size())
  181. checkMarshal(bxml.Namespace, bxml.Namespace.size())
  182. for el := range bxml.iterElements() {
  183. checkMarshal(el, el.size())
  184. checkMarshal(el.end, el.end.size())
  185. }
  186. checkMarshal(bxml.Namespace.end, bxml.Namespace.end.size())
  187. checkMarshal(bxml, bxml.size())
  188. }
  189. func TestEncode(t *testing.T) {
  190. f, err := os.Open("testdata/bootstrap.xml")
  191. if err != nil {
  192. t.Fatal(err)
  193. }
  194. bx, err := UnmarshalXML(f, false)
  195. if err != nil {
  196. t.Fatal(err)
  197. }
  198. bin, err := ioutil.ReadFile("testdata/bootstrap.bin")
  199. if err != nil {
  200. log.Fatal(err)
  201. }
  202. bxml := new(XML)
  203. if err := bxml.UnmarshalBinary(bin); err != nil {
  204. t.Fatal(err)
  205. }
  206. if err := compareStrings(t, bxml.Pool.strings, bx.Pool.strings); err != nil {
  207. t.Error(err)
  208. }
  209. if err := compareUint32s(t, rtou(bxml.Map.rs), rtou(bx.Map.rs)); err != nil {
  210. t.Error(err)
  211. }
  212. if err := compareNamespaces(bx.Namespace, bxml.Namespace); err != nil {
  213. t.Error(err)
  214. }
  215. if err := compareElements(bx, bxml); err != nil {
  216. t.Error(err)
  217. }
  218. // Current output byte-for-byte of pkg binres is close, but not exact, to output of aapt.
  219. // The current exceptions to this are as follows:
  220. // * sort order of certain attributes
  221. // * typed value of minSdkVersion
  222. // The below check will produce an error, listing differences in the byte output of each.
  223. // have, err := bx.MarshalBinary()
  224. // if err != nil {
  225. // t.Fatal(err)
  226. // }
  227. // if err := compareBytes(bin, have); err != nil {
  228. // t.Fatal(err)
  229. // }
  230. }
  231. func TestRawValueByName(t *testing.T) {
  232. f, err := os.Open("testdata/bootstrap.xml")
  233. if err != nil {
  234. t.Fatal(err)
  235. }
  236. bx, err := UnmarshalXML(f, false)
  237. if err != nil {
  238. t.Fatal(err)
  239. }
  240. pkgname, err := bx.RawValueByName("manifest", xml.Name{Local: "package"})
  241. if want := "com.zentus.balloon"; err != nil || pkgname != want {
  242. t.Fatalf("have (%q, %v), want (%q, nil)", pkgname, err, want)
  243. }
  244. }
  245. type byAttrName []*Attribute
  246. func (a byAttrName) Len() int { return len(a) }
  247. func (a byAttrName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  248. func (a byAttrName) Less(i, j int) bool { return a[i].Name < a[j].Name }
  249. func compareElements(have, want *XML) error {
  250. h, w := have.iterElements(), want.iterElements()
  251. buf := new(bytes.Buffer)
  252. for {
  253. a, b := <-h, <-w
  254. if a == nil || b == nil {
  255. break
  256. }
  257. if a.Name.Resolve(have.Pool) == "uses-sdk" {
  258. a = <-h // discard uses-sdk token from tests since it's synthesized internally
  259. }
  260. if a.NS != b.NS ||
  261. a.Name != b.Name {
  262. return fmt.Errorf("elements don't match, have %+v, want %+v", a, b)
  263. }
  264. if a.end.NS != b.end.NS ||
  265. a.end.Name != b.end.Name {
  266. return fmt.Errorf("element ends don't match, have %+v, want %+v", a.end, b.end)
  267. }
  268. if len(a.attrs) != len(b.attrs) {
  269. return fmt.Errorf("element attribute lengths don't match, have %v, want %v", len(a.attrs), len(b.attrs))
  270. }
  271. // discards order of aapt and binres as some sorting details of apt have eluded this package but do not
  272. // affect final output from functioning correctly
  273. sort.Sort(byAttrName(a.attrs))
  274. sort.Sort(byAttrName(b.attrs))
  275. for i, attr := range a.attrs {
  276. bttr := b.attrs[i]
  277. if attr.NS != bttr.NS ||
  278. attr.Name != bttr.Name ||
  279. attr.RawValue != bttr.RawValue ||
  280. attr.TypedValue.Type != bttr.TypedValue.Type ||
  281. attr.TypedValue.Value != bttr.TypedValue.Value {
  282. // single exception to check for minSdkVersion which has peculiar output from aapt
  283. // but following same format of all other like-types appears to work correctly.
  284. // BUG(dskinner) this check is brittle as it will skip over any attribute in
  285. // bootstrap.xml that has value == MinSDK.
  286. if attr.TypedValue.Value == MinSDK {
  287. continue
  288. }
  289. fmt.Fprintf(buf, "attrs don't match\nhave: %+v\nwant: %+v\n", attr, bttr)
  290. }
  291. }
  292. if buf.Len() > 0 {
  293. buf.WriteString("-------------\n")
  294. }
  295. }
  296. if buf.Len() > 0 {
  297. return fmt.Errorf(buf.String())
  298. }
  299. return nil
  300. }
  301. func compareNamespaces(have, want *Namespace) error {
  302. if have == nil || want == nil ||
  303. have.LineNumber != want.LineNumber ||
  304. have.Comment != want.Comment ||
  305. have.prefix != want.prefix ||
  306. have.uri != want.uri {
  307. return fmt.Errorf("namespaces don't match, have %+v, want %+v", have, want)
  308. }
  309. if have.end != nil || want.end != nil {
  310. return compareNamespaces(have.end, want.end)
  311. }
  312. return nil
  313. }
  314. func rtou(a []TableRef) (b []uint32) {
  315. for _, x := range a {
  316. b = append(b, uint32(x))
  317. }
  318. return
  319. }
  320. func compareUint32s(t *testing.T, a, b []uint32) error {
  321. var err error
  322. if len(a) != len(b) {
  323. err = fmt.Errorf("lengths do not match")
  324. }
  325. n := len(a)
  326. if n < len(b) {
  327. n = len(b)
  328. }
  329. var buf bytes.Buffer
  330. buf.WriteString("a.Map.rs b.Map.rs\n")
  331. for i := 0; i < n; i++ {
  332. var c, d string
  333. if i < len(a) {
  334. c = fmt.Sprintf("%0#8x ", a[i])
  335. } else {
  336. c = "__________ "
  337. }
  338. if i < len(b) {
  339. d = fmt.Sprintf("%0#8x ", b[i])
  340. } else {
  341. d = "__________ "
  342. }
  343. if err == nil && c != d {
  344. err = fmt.Errorf("has missing/incorrect values")
  345. }
  346. buf.WriteString(c + " " + d + "\n")
  347. }
  348. if err != nil {
  349. err = fmt.Errorf("%s\n%s", err, buf.String())
  350. }
  351. return err
  352. }
  353. func compareStrings(t *testing.T, a, b []string) error {
  354. var err error
  355. if len(a) != len(b) {
  356. err = fmt.Errorf("lengths do not match")
  357. }
  358. buf := new(bytes.Buffer)
  359. for i, x := range a {
  360. v := "__"
  361. for j, y := range b {
  362. if x == y {
  363. v = fmt.Sprintf("%2v", j)
  364. break
  365. }
  366. }
  367. if err == nil && v == "__" {
  368. if !strings.HasPrefix(x, "4.1.") {
  369. // as of the time of this writing, the current version of build tools being targeted
  370. // reports 4.1.2-1425332.
  371. //
  372. // TODO this check has the potential to hide real errors but can be fixed once more
  373. // of the xml document is unmarshalled and XML can be queried to assure this is related
  374. // to platformBuildVersionName.
  375. err = fmt.Errorf("has missing/incorrect values")
  376. }
  377. }
  378. fmt.Fprintf(buf, "Pool(%2v, %s) %q\n", i, v, x)
  379. }
  380. contains := func(xs []string, a string) bool {
  381. for _, x := range xs {
  382. if x == a {
  383. return true
  384. }
  385. }
  386. return false
  387. }
  388. if err != nil {
  389. buf.WriteString("\n## only in var a\n")
  390. for i, x := range a {
  391. if !contains(b, x) {
  392. fmt.Fprintf(buf, "Pool(%2v) %q\n", i, x)
  393. }
  394. }
  395. buf.WriteString("\n## only in var b\n")
  396. for i, x := range b {
  397. if !contains(a, x) {
  398. fmt.Fprintf(buf, "Pool(%2v) %q\n", i, x)
  399. }
  400. }
  401. }
  402. if err != nil {
  403. err = fmt.Errorf("%s\n%s", err, buf.String())
  404. }
  405. return err
  406. }
  407. func TestOpenTable(t *testing.T) {
  408. if _, err := sdkpath.AndroidHome(); err != nil {
  409. t.Skipf("Could not locate Android SDK: %v", err)
  410. }
  411. tbl, err := OpenTable()
  412. if err != nil {
  413. t.Fatal(err)
  414. }
  415. if len(tbl.pkgs) == 0 {
  416. t.Fatal("failed to decode any resource packages")
  417. }
  418. pkg := tbl.pkgs[0]
  419. t.Log("package name:", pkg.name)
  420. for i, x := range pkg.typePool.strings {
  421. t.Logf("typePool[i=%v]: %s\n", i, x)
  422. }
  423. for i, spec := range pkg.specs {
  424. t.Logf("spec[i=%v]: %v %q\n", i, spec.id, pkg.typePool.strings[spec.id-1])
  425. for j, typ := range spec.types {
  426. t.Logf("\ttype[i=%v]: %v\n", j, typ.id)
  427. for k, nt := range typ.entries {
  428. if nt == nil { // NoEntry
  429. continue
  430. }
  431. t.Logf("\t\tentry[i=%v]: %v %q\n", k, nt.key, pkg.keyPool.strings[nt.key])
  432. if k > 5 {
  433. t.Logf("\t\t... truncating output")
  434. break
  435. }
  436. }
  437. }
  438. }
  439. }
  440. func TestTableRefByName(t *testing.T) {
  441. checkResources(t)
  442. tbl, err := OpenSDKTable()
  443. if err != nil {
  444. t.Fatal(err)
  445. }
  446. if len(tbl.pkgs) == 0 {
  447. t.Fatal("failed to decode any resource packages")
  448. }
  449. ref, err := tbl.RefByName("@android:style/Theme.NoTitleBar.Fullscreen")
  450. if err != nil {
  451. t.Fatal(err)
  452. }
  453. if want := uint32(0x01030007); uint32(ref) != want {
  454. t.Fatalf("RefByName does not match expected result, have %0#8x, want %0#8x", ref, want)
  455. }
  456. }
  457. func TestTableMarshal(t *testing.T) {
  458. checkResources(t)
  459. tbl, err := OpenSDKTable()
  460. if err != nil {
  461. t.Fatal(err)
  462. }
  463. bin, err := tbl.MarshalBinary()
  464. if err != nil {
  465. t.Fatal(err)
  466. }
  467. xtbl := new(Table)
  468. if err := xtbl.UnmarshalBinary(bin); err != nil {
  469. t.Fatal(err)
  470. }
  471. if len(tbl.pool.strings) != len(xtbl.pool.strings) {
  472. t.Fatal("tbl.pool lengths don't match")
  473. }
  474. if len(tbl.pkgs) != len(xtbl.pkgs) {
  475. t.Fatal("tbl.pkgs lengths don't match")
  476. }
  477. pkg, xpkg := tbl.pkgs[0], xtbl.pkgs[0]
  478. if err := compareStrings(t, pkg.typePool.strings, xpkg.typePool.strings); err != nil {
  479. t.Fatal(err)
  480. }
  481. if err := compareStrings(t, pkg.keyPool.strings, xpkg.keyPool.strings); err != nil {
  482. t.Fatal(err)
  483. }
  484. if len(pkg.specs) != len(xpkg.specs) {
  485. t.Fatal("pkg.specs lengths don't match")
  486. }
  487. for i, spec := range pkg.specs {
  488. xspec := xpkg.specs[i]
  489. if spec.id != xspec.id {
  490. t.Fatal("spec.id doesn't match")
  491. }
  492. if spec.entryCount != xspec.entryCount {
  493. t.Fatal("spec.entryCount doesn't match")
  494. }
  495. if len(spec.entries) != len(xspec.entries) {
  496. t.Fatal("spec.entries lengths don't match")
  497. }
  498. for j, mask := range spec.entries {
  499. xmask := xspec.entries[j]
  500. if mask != xmask {
  501. t.Fatal("entry mask doesn't match")
  502. }
  503. }
  504. if len(spec.types) != len(xspec.types) {
  505. t.Fatal("spec.types length don't match")
  506. }
  507. for j, typ := range spec.types {
  508. xtyp := xspec.types[j]
  509. if typ.id != xtyp.id {
  510. t.Fatal("typ.id doesn't match")
  511. }
  512. if typ.entryCount != xtyp.entryCount {
  513. t.Fatal("typ.entryCount doesn't match")
  514. }
  515. // Config size can differ after serialization due to the loss of extended fields
  516. // during reserialization, but the fixed portions of the Type header must not change.
  517. if uint32(typ.headerByteSize)-typ.config.size != uint32(xtyp.headerByteSize)-uint32(xtyp.config.size) {
  518. t.Fatal("fixed size header portions don't match")
  519. }
  520. if len(typ.indices) != len(xtyp.indices) {
  521. t.Fatal("typ.indices length don't match")
  522. }
  523. for k, index := range typ.indices {
  524. xindex := xtyp.indices[k]
  525. if index != xindex {
  526. t.Errorf("type index doesn't match at %v, have %v, want %v", k, xindex, index)
  527. }
  528. }
  529. if len(typ.entries) != len(xtyp.entries) {
  530. t.Fatal("typ.entries lengths don't match")
  531. }
  532. for k, nt := range typ.entries {
  533. xnt := xtyp.entries[k]
  534. if nt == nil {
  535. if xnt != nil {
  536. t.Fatal("nt is nil but xnt is not")
  537. }
  538. continue
  539. }
  540. if nt.size != xnt.size {
  541. t.Fatal("entry.size doesn't match")
  542. }
  543. if nt.flags != xnt.flags {
  544. t.Fatal("entry.flags don't match")
  545. }
  546. if nt.key != xnt.key {
  547. t.Fatal("entry.key doesn't match")
  548. }
  549. if nt.parent != xnt.parent {
  550. t.Fatal("entry.parent doesn't match")
  551. }
  552. if nt.count != xnt.count {
  553. t.Fatal("entry.count doesn't match")
  554. }
  555. for l, val := range nt.values {
  556. xval := xnt.values[l]
  557. if val.name != xval.name {
  558. t.Fatal("value.name doesn't match")
  559. }
  560. }
  561. }
  562. }
  563. }
  564. }
  565. func checkResources(t *testing.T) {
  566. t.Helper()
  567. if _, err := sdkpath.AndroidHome(); err != nil {
  568. t.Skip("Could not locate Android SDK")
  569. }
  570. rscPath, err := apiResourcesPath()
  571. if err != nil {
  572. t.Skipf("failed to find resources: %v", err)
  573. }
  574. if _, err := os.Stat(rscPath); err != nil {
  575. t.Skipf("failed to find resources: %v", err)
  576. }
  577. }
  578. func BenchmarkTableRefByName(b *testing.B) {
  579. if _, err := sdkpath.AndroidHome(); err != nil {
  580. b.Fatal("Could not locate Android SDK")
  581. }
  582. b.ReportAllocs()
  583. b.ResetTimer()
  584. for n := 0; n < b.N; n++ {
  585. tbl, err := OpenTable()
  586. if err != nil {
  587. b.Fatal(err)
  588. }
  589. _, err = tbl.RefByName("@android:style/Theme.NoTitleBar.Fullscreen")
  590. if err != nil {
  591. b.Fatal(err)
  592. }
  593. }
  594. }