1
0

xpj_test.go 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. package xpj
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "io"
  6. "math"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "testing"
  11. )
  12. const testdata = "../../testdata"
  13. func decompress(t *testing.T, path string) []byte {
  14. t.Helper()
  15. fh, err := os.Open(path)
  16. if err != nil {
  17. t.Fatal(err)
  18. }
  19. defer fh.Close()
  20. zr, err := gzip.NewReader(fh)
  21. if err != nil {
  22. t.Fatal(err)
  23. }
  24. raw, err := io.ReadAll(zr)
  25. if err != nil {
  26. t.Fatal(err)
  27. }
  28. return raw
  29. }
  30. // firstDiff describes where two byte slices first differ.
  31. func firstDiff(a, b []byte) string {
  32. n := min(len(a), len(b))
  33. for i := 0; i < n; i++ {
  34. if a[i] != b[i] {
  35. lo := max(0, i-40)
  36. return "at byte " + itoa(i) + ":\n got " + quote(a[lo:min(len(a), i+40)]) + "\n want " + quote(b[lo:min(len(b), i+40)])
  37. }
  38. }
  39. return "lengths " + itoa(len(a)) + " and " + itoa(len(b))
  40. }
  41. func itoa(i int) string { return NewInt(int64(i)).Text }
  42. func quote(b []byte) string {
  43. return strings.ReplaceAll(string(b), "\n", `\n`)
  44. }
  45. // The M1 milestone: every device-saved project re-writes byte-identical.
  46. func TestRoundTripDeviceProjects(t *testing.T) {
  47. for _, name := range []string{"DemoBlank", "Demo02", "DemoFile"} {
  48. t.Run(name, func(t *testing.T) {
  49. roundTrip(t, filepath.Join(testdata, "device", name+".xpj"))
  50. })
  51. }
  52. }
  53. // Files from the reference writer that the device accepted round-trip too.
  54. func TestRoundTripAcceptedProjects(t *testing.T) {
  55. paths, _ := filepath.Glob(filepath.Join(testdata, "accepted", "*.xpj"))
  56. more, _ := filepath.Glob(filepath.Join(testdata, "wav-formats", "*.xpj"))
  57. paths = append(paths, more...)
  58. if len(paths) < 5 {
  59. t.Fatalf("found only %d accepted projects", len(paths))
  60. }
  61. for _, p := range paths {
  62. t.Run(filepath.Base(p), func(t *testing.T) { roundTrip(t, p) })
  63. }
  64. }
  65. func roundTrip(t *testing.T, path string) {
  66. t.Helper()
  67. want := decompress(t, path)
  68. f, err := ReadFile(path)
  69. if err != nil {
  70. t.Fatal(err)
  71. }
  72. got, err := f.Encode()
  73. if err != nil {
  74. t.Fatal(err)
  75. }
  76. if !bytes.Equal(got, want) {
  77. t.Fatalf("re-encoded file differs %s", firstDiff(got, want))
  78. }
  79. // Through gzip and back, as an export would.
  80. var z bytes.Buffer
  81. if err := f.Write(&z); err != nil {
  82. t.Fatal(err)
  83. }
  84. f2, err := Read(&z)
  85. if err != nil {
  86. t.Fatal(err)
  87. }
  88. got2, _ := f2.Encode()
  89. if !bytes.Equal(got2, want) {
  90. t.Fatalf("gzip round trip differs %s", firstDiff(got2, want))
  91. }
  92. }
  93. func TestDeviceProjectShape(t *testing.T) {
  94. f, err := ReadFile(filepath.Join(testdata, "device", "DemoBlank.xpj"))
  95. if err != nil {
  96. t.Fatal(err)
  97. }
  98. if strings.Join(f.Header, "|") != strings.Join(TestedHeader, "|") {
  99. t.Errorf("header = %q", f.Header)
  100. }
  101. d, err := f.Data()
  102. if err != nil {
  103. t.Fatal(err)
  104. }
  105. if len(d.Members) != 66 {
  106. t.Errorf("data has %d keys, want 66", len(d.Members))
  107. }
  108. if v, _ := d.Lookup("version").Int(); v != 28 {
  109. t.Errorf("data.version = %d, want 28", v)
  110. }
  111. pads, err := d.Get("tracks[0].program.drum.instruments")
  112. if err != nil {
  113. t.Fatal(err)
  114. }
  115. if len(pads.Items) != 128 {
  116. t.Errorf("%d pads, want 128", len(pads.Items))
  117. }
  118. seq, err := d.Get("sequences[0].value")
  119. if err != nil {
  120. t.Fatal(err)
  121. }
  122. if seq.Lookup("name").Text != "Sequence 01" {
  123. t.Errorf("sequence name = %q", seq.Lookup("name").Text)
  124. }
  125. if bpm, _ := seq.Lookup("bpm").Float(); bpm != 128 {
  126. t.Errorf("bpm = %v", bpm)
  127. }
  128. }
  129. func TestGzipHeader(t *testing.T) {
  130. f := &File{Header: TestedHeader, Root: NewObject()}
  131. var b bytes.Buffer
  132. if err := f.Write(&b); err != nil {
  133. t.Fatal(err)
  134. }
  135. // ID, CM=deflate, FLG=0, MTIME=0, XFL=0, OS=3: the same ten bytes the device writes.
  136. want := []byte{0x1f, 0x8b, 8, 0, 0, 0, 0, 0, 0, 3}
  137. if got := b.Bytes()[:10]; !bytes.Equal(got, want) {
  138. t.Errorf("gzip header = % x, want % x", got, want)
  139. }
  140. }
  141. func TestLayout(t *testing.T) {
  142. in := `{"a": [1, 2.50, {}], "b": {"x": [], "y": "s<&>/"}, "c": null, "d": true, "e": -1e-05}`
  143. n, err := ParseJSON([]byte(in))
  144. if err != nil {
  145. t.Fatal(err)
  146. }
  147. got, err := n.MarshalJSON()
  148. if err != nil {
  149. t.Fatal(err)
  150. }
  151. want := "{\n\"a\": [\n1,\n2.50,\n{}\n],\n\"b\": {\n\"x\": [],\n\"y\": \"s<&>/\"\n},\n\"c\": null,\n\"d\": true,\n\"e\": -1e-05\n}"
  152. if string(got) != want {
  153. t.Errorf("got\n%s\nwant\n%s", got, want)
  154. }
  155. }
  156. func TestKeyOrderAndDuplicates(t *testing.T) {
  157. n, err := ParseJSON([]byte(`{"z": 1, "a": 2, "z": 3}`))
  158. if err != nil {
  159. t.Fatal(err)
  160. }
  161. got, _ := n.MarshalJSON()
  162. if want := "{\n\"z\": 1,\n\"a\": 2,\n\"z\": 3\n}"; string(got) != want {
  163. t.Errorf("got %q", got)
  164. }
  165. }
  166. func TestStringEscapes(t *testing.T) {
  167. s := "q\" b\\ n\n t\t c\x01 é ✓"
  168. n := NewString(s)
  169. got, _ := n.MarshalJSON()
  170. if want := `"q\" b\\ n\n t\t c\u0001 é ✓"`; string(got) != want {
  171. t.Errorf("got %s want %s", got, want)
  172. }
  173. back, err := ParseJSON(got)
  174. if err != nil || back.Text != s {
  175. t.Errorf("read back %q, %v", back.Text, err)
  176. }
  177. }
  178. func TestParseErrors(t *testing.T) {
  179. for _, in := range []string{``, `{`, `{"a": }`, `[1,]`, `{} {}`, `{"a": 1} x`, `nul`} {
  180. if _, err := ParseJSON([]byte(in)); err == nil {
  181. t.Errorf("ParseJSON(%q) succeeded", in)
  182. }
  183. }
  184. if _, err := Decode([]byte("ACVS\n1\n2\n3\n")); err == nil {
  185. t.Error("short header accepted")
  186. }
  187. if _, err := Decode([]byte("XXXX\n1\n2\n3\n4\n{}")); err == nil {
  188. t.Error("bad magic accepted")
  189. }
  190. if _, err := Decode([]byte("ACVS\n1\n2\n3\n4\n[]")); err == nil {
  191. t.Error("array body accepted")
  192. }
  193. if _, err := Read(strings.NewReader("not gzip")); err == nil {
  194. t.Error("non-gzip accepted")
  195. }
  196. }
  197. func TestInvalidNumberText(t *testing.T) {
  198. for _, s := range []string{"", "1.", ".5", "01", "+1", "1e", "true", `"1"`, "NaN", "1 "} {
  199. n := &Node{Kind: Number, Text: s}
  200. if _, err := n.MarshalJSON(); err == nil {
  201. t.Errorf("number text %q accepted", s)
  202. }
  203. }
  204. }
  205. func TestFormatFloat(t *testing.T) {
  206. // Expected values are Python's repr, which the reference writer used.
  207. cases := map[float64]string{
  208. 96: "96.0",
  209. 20: "20.0",
  210. 0: "0.0",
  211. math.Copysign(0, -1): "-0.0",
  212. 12.399999618530273: "12.399999618530273",
  213. 1e-05: "1e-05",
  214. 0.0001: "0.0001",
  215. 1e16: "1e+16",
  216. 1.5e16: "1.5e+16",
  217. 123456789012345.6: "123456789012345.6",
  218. 1e15: "1000000000000000.0",
  219. -2.5: "-2.5",
  220. 0.7079457640647888: "0.7079457640647888",
  221. 5e-324: "5e-324",
  222. 1.7976931348623157e308: "1.7976931348623157e+308",
  223. 0.1: "0.1",
  224. 100: "100.0",
  225. 9999999999999998: "9999999999999998.0",
  226. }
  227. for f, want := range cases {
  228. if got := FormatFloat(f); got != want {
  229. t.Errorf("FormatFloat(%v) = %q, want %q", f, got, want)
  230. }
  231. }
  232. // Tuning +12 semitones 40 cents, as the device writes it.
  233. if got := NewFloat32(12 + 40.0/100).Text; got != "12.399999618530273" {
  234. t.Errorf("NewFloat32(12.4) = %s", got)
  235. }
  236. if got := NewInt(-1).Text; got != "-1" {
  237. t.Errorf("NewInt(-1) = %s", got)
  238. }
  239. }
  240. func TestPaths(t *testing.T) {
  241. n, err := ParseJSON([]byte(`{"a": [{"b": [10, 20]}, {"c": "x"}], "d": {"e": 1.0}}`))
  242. if err != nil {
  243. t.Fatal(err)
  244. }
  245. get := func(p string) *Node {
  246. t.Helper()
  247. v, err := n.Get(p)
  248. if err != nil {
  249. t.Fatal(err)
  250. }
  251. return v
  252. }
  253. if get("a[0].b[1]").Text != "20" || get("a[1].c").Text != "x" || get("d.e").Text != "1.0" {
  254. t.Error("Get returned wrong nodes")
  255. }
  256. if get("") != n {
  257. t.Error("empty path is not the root")
  258. }
  259. for _, p := range []string{"a[2]", "a[0].x", "d[0]", "d.e.f", "a[", "a[-1]", "a[x]", ".a", "a..b", "a.", "a]", "a.[0]"} {
  260. if _, err := n.Get(p); err == nil {
  261. t.Errorf("Get(%q) succeeded", p)
  262. }
  263. }
  264. if err := n.Set("a[0].b[0]", NewInt(4)); err != nil {
  265. t.Fatal(err)
  266. }
  267. if err := n.Set("d.e", NewFloat(96)); err != nil {
  268. t.Fatal(err)
  269. }
  270. if err := n.Set("d.new", NewString("n")); err != nil {
  271. t.Fatal(err)
  272. }
  273. if err := n.Set("a[5]", NewNull()); err == nil {
  274. t.Error("Set past array end succeeded")
  275. }
  276. if err := n.Set("", NewNull()); err == nil {
  277. t.Error("Set root succeeded")
  278. }
  279. got, _ := n.MarshalJSON()
  280. want := "{\n\"a\": [\n{\n\"b\": [\n4,\n20\n]\n},\n{\n\"c\": \"x\"\n}\n],\n\"d\": {\n\"e\": 96.0,\n\"new\": \"n\"\n}\n}"
  281. if string(got) != want {
  282. t.Errorf("got\n%s", got)
  283. }
  284. }
  285. func TestArrayEditsAndClone(t *testing.T) {
  286. n, _ := ParseJSON([]byte(`{"s": [{"k": 0}]}`))
  287. arr, _ := n.Get("s")
  288. c := arr.Items[0].Clone()
  289. if err := c.SetKey("k", NewInt(1)); err != nil {
  290. t.Fatal(err)
  291. }
  292. if arr.Items[0].Lookup("k").Text != "0" {
  293. t.Fatal("clone shares data with its source")
  294. }
  295. if err := arr.Insert(0, c); err != nil {
  296. t.Fatal(err)
  297. }
  298. if err := arr.Append(NewInt(9)); err != nil {
  299. t.Fatal(err)
  300. }
  301. if err := arr.Insert(5, NewNull()); err == nil {
  302. t.Error("insert out of range succeeded")
  303. }
  304. if err := arr.Remove(1); err != nil {
  305. t.Fatal(err)
  306. }
  307. if err := arr.Remove(2); err == nil {
  308. t.Error("remove out of range succeeded")
  309. }
  310. got, _ := n.MarshalJSON()
  311. if want := "{\n\"s\": [\n{\n\"k\": 1\n},\n9\n]\n}"; string(got) != want {
  312. t.Errorf("got\n%s", got)
  313. }
  314. obj, _ := n.Get("s[0]")
  315. if !obj.DeleteKey("k") || obj.DeleteKey("k") {
  316. t.Error("DeleteKey")
  317. }
  318. if got, _ := obj.MarshalJSON(); string(got) != "{}" {
  319. t.Errorf("emptied object = %s", got)
  320. }
  321. f := &File{Header: TestedHeader, Root: n}
  322. fc := f.Clone()
  323. fc.Header[0] = "X"
  324. _ = fc.Root.SetKey("s", NewNull())
  325. if f.Header[0] != "ACVS" || n.Lookup("s").Kind != Array {
  326. t.Error("File.Clone shares data with its source")
  327. }
  328. }
  329. func TestNumberAccessors(t *testing.T) {
  330. for text, want := range map[string]int64{"4": 4, "-1": -1, "8212": 8212, "20.0": 20} {
  331. if got, err := (&Node{Kind: Number, Text: text}).Int(); err != nil || got != want {
  332. t.Errorf("Int(%s) = %d, %v", text, got, err)
  333. }
  334. }
  335. if _, err := (&Node{Kind: Number, Text: "1.5"}).Int(); err == nil {
  336. t.Error("Int(1.5) succeeded")
  337. }
  338. if _, err := NewString("1").Int(); err == nil {
  339. t.Error("Int on a string succeeded")
  340. }
  341. if f, err := NewFloat32(0.1).Float(); err != nil || f != float64(float32(0.1)) {
  342. t.Errorf("Float = %v, %v", f, err)
  343. }
  344. }