string_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package goja
  2. import (
  3. "strings"
  4. "testing"
  5. "unicode/utf16"
  6. )
  7. func TestStringOOBProperties(t *testing.T) {
  8. const SCRIPT = `
  9. var string = new String("str");
  10. string[4] = 1;
  11. string[4];
  12. `
  13. testScript(SCRIPT, valueInt(1), t)
  14. }
  15. func TestImportedString(t *testing.T) {
  16. vm := New()
  17. testUnaryOp := func(a, expr string, result interface{}, t *testing.T) {
  18. v, err := vm.RunString("a => " + expr)
  19. if err != nil {
  20. t.Fatal(err)
  21. }
  22. var fn func(a Value) (Value, error)
  23. err = vm.ExportTo(v, &fn)
  24. if err != nil {
  25. t.Fatal(err)
  26. }
  27. for _, aa := range []Value{newStringValue(a), vm.ToValue(a)} {
  28. res, err := fn(aa)
  29. if err != nil {
  30. t.Fatal(err)
  31. }
  32. if res.Export() != result {
  33. t.Fatalf("%s, a:%v(%T). expected: %v, actual: %v", expr, aa, aa, result, res)
  34. }
  35. }
  36. }
  37. testBinaryOp := func(a, b, expr string, result interface{}, t *testing.T) {
  38. v, err := vm.RunString("(a, b) => " + expr)
  39. if err != nil {
  40. t.Fatal(err)
  41. }
  42. var fn func(a, b Value) (Value, error)
  43. err = vm.ExportTo(v, &fn)
  44. if err != nil {
  45. t.Fatal(err)
  46. }
  47. for _, aa := range []Value{newStringValue(a), vm.ToValue(a)} {
  48. for _, bb := range []Value{newStringValue(b), vm.ToValue(b)} {
  49. res, err := fn(aa, bb)
  50. if err != nil {
  51. t.Fatal(err)
  52. }
  53. if res.Export() != result {
  54. t.Fatalf("%s, a:%v(%T), b:%v(%T). expected: %v, actual: %v", expr, aa, aa, bb, bb, result, res)
  55. }
  56. }
  57. }
  58. }
  59. strs := []string{"shortAscii", "longlongAscii1234567890123456789", "short юникод", "long юникод 1234567890 юникод \U0001F600", "юникод", "Ascii", "long", "код"}
  60. indexOfResults := [][]int{
  61. /*
  62. const strs = ["shortAscii", "longlongAscii1234567890123456789", "short юникод", "long юникод 1234567890 юникод \u{1F600}", "юникод", "Ascii", "long", "код"];
  63. strs.forEach(a => {
  64. console.log("{", strs.map(b => a.indexOf(b)).join(", "), "},");
  65. });
  66. */
  67. {0, -1, -1, -1, -1, 5, -1, -1},
  68. {-1, 0, -1, -1, -1, 8, 0, -1},
  69. {-1, -1, 0, -1, 6, -1, -1, 9},
  70. {-1, -1, -1, 0, 5, -1, 0, 8},
  71. {-1, -1, -1, -1, 0, -1, -1, 3},
  72. {-1, -1, -1, -1, -1, 0, -1, -1},
  73. {-1, -1, -1, -1, -1, -1, 0, -1},
  74. {-1, -1, -1, -1, -1, -1, -1, 0},
  75. }
  76. lastIndexOfResults := [][]int{
  77. /*
  78. strs.forEach(a => {
  79. console.log("{", strs.map(b => a.lastIndexOf(b)).join(", "), "},");
  80. });
  81. */
  82. {0, -1, -1, -1, -1, 5, -1, -1},
  83. {-1, 0, -1, -1, -1, 8, 4, -1},
  84. {-1, -1, 0, -1, 6, -1, -1, 9},
  85. {-1, -1, -1, 0, 23, -1, 0, 26},
  86. {-1, -1, -1, -1, 0, -1, -1, 3},
  87. {-1, -1, -1, -1, -1, 0, -1, -1},
  88. {-1, -1, -1, -1, -1, -1, 0, -1},
  89. {-1, -1, -1, -1, -1, -1, -1, 0},
  90. }
  91. pad := func(s, p string, n int, start bool) string {
  92. if n == 0 {
  93. return s
  94. }
  95. if p == "" {
  96. p = " "
  97. }
  98. var b strings.Builder
  99. ss := utf16.Encode([]rune(s))
  100. b.Grow(n)
  101. n -= len(ss)
  102. if !start {
  103. b.WriteString(s)
  104. }
  105. if n > 0 {
  106. pp := utf16.Encode([]rune(p))
  107. for n > 0 {
  108. if n > len(pp) {
  109. b.WriteString(p)
  110. n -= len(pp)
  111. } else {
  112. b.WriteString(string(utf16.Decode(pp[:n])))
  113. n = 0
  114. }
  115. }
  116. }
  117. if start {
  118. b.WriteString(s)
  119. }
  120. return b.String()
  121. }
  122. for i, a := range strs {
  123. testUnaryOp(a, "JSON.parse(JSON.stringify(a))", a, t)
  124. testUnaryOp(a, "a.length", int64(len(utf16.Encode([]rune(a)))), t)
  125. for j, b := range strs {
  126. testBinaryOp(a, b, "a === b", a == b, t)
  127. testBinaryOp(a, b, "a == b", a == b, t)
  128. testBinaryOp(a, b, "a + b", a+b, t)
  129. testBinaryOp(a, b, "a > b", strings.Compare(a, b) > 0, t)
  130. testBinaryOp(a, b, "`A${a}B${b}C`", "A"+a+"B"+b+"C", t)
  131. testBinaryOp(a, b, "a.indexOf(b)", int64(indexOfResults[i][j]), t)
  132. testBinaryOp(a, b, "a.lastIndexOf(b)", int64(lastIndexOfResults[i][j]), t)
  133. testBinaryOp(a, b, "a.padStart(32, b)", pad(a, b, 32, true), t)
  134. testBinaryOp(a, b, "a.padEnd(32, b)", pad(a, b, 32, false), t)
  135. testBinaryOp(a, b, "a.replace(b, '')", strings.Replace(a, b, "", 1), t)
  136. }
  137. }
  138. }
  139. func TestStringFromUTF16(t *testing.T) {
  140. s := StringFromUTF16([]uint16{})
  141. if s.Length() != 0 || !s.SameAs(asciiString("")) {
  142. t.Fatal(s)
  143. }
  144. s = StringFromUTF16([]uint16{0xD800})
  145. if s.Length() != 1 || s.CharAt(0) != 0xD800 {
  146. t.Fatal(s)
  147. }
  148. s = StringFromUTF16([]uint16{'A', 'B'})
  149. if !s.SameAs(asciiString("AB")) {
  150. t.Fatal(s)
  151. }
  152. }
  153. func TestStringBuilder(t *testing.T) {
  154. t.Run("writeUTF8String-switch", func(t *testing.T) {
  155. var sb StringBuilder
  156. sb.WriteUTF8String("Head")
  157. sb.WriteUTF8String("1ábc")
  158. if res := sb.String().String(); res != "Head1ábc" {
  159. t.Fatal(res)
  160. }
  161. })
  162. }
  163. func BenchmarkASCIIConcat(b *testing.B) {
  164. vm := New()
  165. b.ResetTimer()
  166. b.ReportAllocs()
  167. for i := 0; i < b.N; i++ {
  168. _, err := vm.RunString(`{let result = "ab";
  169. for (let i = 0 ; i < 10;i++) {
  170. result += result;
  171. }}`)
  172. if err != nil {
  173. b.Fatalf("Unexpected errors %s", err)
  174. }
  175. }
  176. }