github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goarray_reflect_test.go (about) 1 package goja 2 3 import ( 4 "testing" 5 ) 6 7 func TestGoReflectArray(t *testing.T) { 8 vm := New() 9 vm.Set("a", [...]int{1, 2, 3}) 10 _, err := vm.RunString(` 11 if (!Array.isArray(a)) { 12 throw new Error("isArray() returned false"); 13 } 14 if (a[0] !== 1 || a[1] !== 2 || a[2] !== 3) { 15 throw new Error("Array contents is incorrect"); 16 } 17 if (!a.hasOwnProperty("length")) { 18 throw new Error("hasOwnProperty() returned false"); 19 } 20 let desc = Object.getOwnPropertyDescriptor(a, "length"); 21 if (desc.value !== 3 || desc.writable || desc.enumerable || desc.configurable) { 22 throw new Error("incorrect property descriptor: " + JSON.stringify(desc)); 23 } 24 `) 25 if err != nil { 26 t.Fatal(err) 27 } 28 } 29 30 func TestGoReflectArraySort(t *testing.T) { 31 vm := New() 32 vm.Set("a", [...]int{3, 1, 2}) 33 v, err := vm.RunString(` 34 a.sort(); 35 if (a[0] !== 1 || a[1] !== 2 || a[2] !== 3) { 36 throw new Error(a.toString()); 37 } 38 a; 39 `) 40 if err != nil { 41 t.Fatal(err) 42 } 43 res := v.Export() 44 if a, ok := res.([3]int); ok { 45 if a[0] != 1 || a[1] != 2 || a[2] != 3 { 46 t.Fatal(a) 47 } 48 } else { 49 t.Fatalf("Wrong type: %T", res) 50 } 51 } 52 53 func TestGoReflectArrayCopyOnChange(t *testing.T) { 54 vm := New() 55 56 v, err := vm.RunString(` 57 a => { 58 let tmp = a[0]; 59 if (tmp !== a[0]) { 60 throw new Error("tmp !== a[0]"); 61 } 62 63 a[0] = a[1]; 64 if (tmp === a[0]) { 65 throw new Error("tmp === a[0]"); 66 } 67 if (tmp.Test !== "1") { 68 throw new Error("tmp.Test: " + tmp.Test + " (" + typeof tmp.Test + ")"); 69 } 70 if (a[0].Test !== "2") { 71 throw new Error("a[0].Test: " + a[0].Test); 72 } 73 74 a[0].Test = "3"; 75 if (a[0].Test !== "3") { 76 throw new Error("a[0].Test (1): " + a[0].Test); 77 } 78 79 tmp = a[0]; 80 tmp.Test = "4"; 81 if (a[0].Test !== "4") { 82 throw new Error("a[0].Test (2): " + a[0].Test); 83 } 84 85 delete a[0]; 86 if (a[0] && a[0].Test !== "") { 87 throw new Error("a[0].Test (3): " + a[0].Test); 88 } 89 if (tmp.Test !== "4") { 90 throw new Error("tmp.Test (1): " + tmp.Test); 91 } 92 93 a[1] = tmp; 94 if (a[1].Test !== "4") { 95 throw new Error("a[1].Test: " + a[1].Test); 96 } 97 98 // grow 99 tmp = a[1]; 100 a.push(null); 101 if (a.length !== 3) { 102 throw new Error("a.length after push: " + a.length); 103 } 104 105 tmp.Test = "5"; 106 if (a[1].Test !== "5") { 107 throw new Error("a[1].Test (1): " + a[1].Test); 108 } 109 110 // shrink 111 a.length = 1; 112 if (a.length !== 1) { 113 throw new Error("a.length after shrink: " + a.length); 114 } 115 116 if (tmp.Test !== "5") { 117 throw new Error("tmp.Test (shrink): " + tmp.Test); 118 } 119 } 120 `) 121 if err != nil { 122 t.Fatal(err) 123 } 124 125 fn, ok := AssertFunction(v) 126 if !ok { 127 t.Fatal("Not a function") 128 } 129 130 t.Run("[]struct", func(t *testing.T) { 131 a := []struct { 132 Test string 133 }{{"1"}, {"2"}} 134 _, err := fn(nil, vm.ToValue(a)) 135 if err != nil { 136 t.Fatal(err) 137 } 138 if a[0].Test != "" { 139 t.Fatalf("a[0]: %#v", a[0]) 140 } 141 142 if a[1].Test != "4" { 143 t.Fatalf("a0[1]: %#v", a[1]) 144 } 145 }) 146 147 // The copy-on-change mechanism doesn't apply to the types below because the contained values are references. 148 // These tests are here for completeness and to prove that the behaviour is consistent. 149 150 t.Run("[]I", func(t *testing.T) { 151 type I interface { 152 Get() string 153 } 154 155 a := []I{&testGoReflectMethod_O{Test: "1"}, &testGoReflectMethod_O{Test: "2"}} 156 157 _, err = fn(nil, vm.ToValue(a)) 158 if err != nil { 159 t.Fatal(err) 160 } 161 }) 162 163 t.Run("[]interface{}", func(t *testing.T) { 164 a := []interface{}{&testGoReflectMethod_O{Test: "1"}, &testGoReflectMethod_O{Test: "2"}} 165 166 _, err = fn(nil, vm.ToValue(a)) 167 if err != nil { 168 t.Fatal(err) 169 } 170 }) 171 } 172 173 func TestCopyOnChangeReflectSlice(t *testing.T) { 174 vm := New() 175 v, err := vm.RunString(` 176 s => { 177 s.A.push(1); 178 if (s.A.length !== 1) { 179 throw new Error("s.A.length: " + s.A.length); 180 } 181 if (s.A[0] !== 1) { 182 throw new Error("s.A[0]: " + s.A[0]); 183 } 184 let tmp = s.A; 185 if (tmp !== s.A) { 186 throw new Error("tmp !== s.A"); 187 } 188 s.A = [2]; 189 if (tmp === s.A) { 190 throw new Error("tmp === s.A"); 191 } 192 if (tmp[0] !== 1) { 193 throw new Error("tmp[0]: " + tmp[0]); 194 } 195 if (s.A[0] !== 2) { 196 throw new Error("s.A[0] (1): " + s.A[0]); 197 } 198 } 199 `) 200 if err != nil { 201 t.Fatal(err) 202 } 203 fn, ok := AssertFunction(v) 204 if !ok { 205 t.Fatal("Not a function") 206 } 207 208 t.Run("[]int", func(t *testing.T) { 209 type S struct { 210 A []int 211 } 212 var s S 213 _, err := fn(nil, vm.ToValue(&s)) 214 if err != nil { 215 t.Fatal(err) 216 } 217 if len(s.A) != 1 { 218 t.Fatal(s) 219 } 220 if s.A[0] != 2 { 221 t.Fatal(s.A) 222 } 223 }) 224 225 t.Run("[]interface{}", func(t *testing.T) { 226 type S struct { 227 A []interface{} 228 } 229 var s S 230 _, err := fn(nil, vm.ToValue(&s)) 231 if err != nil { 232 t.Fatal(err) 233 } 234 if len(s.A) != 1 { 235 t.Fatal(s) 236 } 237 if s.A[0] != int64(2) { 238 t.Fatal(s.A) 239 } 240 }) 241 } 242 243 func TestCopyOnChangeSort(t *testing.T) { 244 a := []struct { 245 Test string 246 }{{"2"}, {"1"}} 247 248 vm := New() 249 vm.Set("a", &a) 250 251 _, err := vm.RunString(` 252 let a0 = a[0]; 253 let a1 = a[1]; 254 a.sort((a, b) => a.Test.localeCompare(b.Test)); 255 if (a[0].Test !== "1") { 256 throw new Error("a[0]: " + a[0]); 257 } 258 if (a[1].Test !== "2") { 259 throw new Error("a[1]: " + a[1]); 260 } 261 if (a0 !== a[1]) { 262 throw new Error("a0 !== a[1]"); 263 } 264 if (a1 !== a[0]) { 265 throw new Error("a1 !== a[0]"); 266 } 267 `) 268 if err != nil { 269 t.Fatal(err) 270 } 271 272 if a[0].Test != "1" || a[1].Test != "2" { 273 t.Fatal(a) 274 } 275 } 276 277 type testStringerArray [8]byte 278 279 func (a testStringerArray) String() string { 280 return "X" 281 } 282 283 func TestReflectArrayToString(t *testing.T) { 284 vm := New() 285 var a testStringerArray 286 vm.Set("a", &a) 287 res, err := vm.RunString("`${a}`") 288 if err != nil { 289 t.Fatal(err) 290 } 291 if exp := res.Export(); exp != "X" { 292 t.Fatal(exp) 293 } 294 295 var a1 [2]byte 296 vm.Set("a", &a1) 297 res, err = vm.RunString("`${a}`") 298 if err != nil { 299 t.Fatal(err) 300 } 301 if exp := res.Export(); exp != "0,0" { 302 t.Fatal(exp) 303 } 304 }