github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/go/constant/value_test.go (about) 1 // Copyright 2013 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 5 package constant 6 7 import ( 8 "fmt" 9 "go/token" 10 "strings" 11 "testing" 12 ) 13 14 // TODO(gri) expand this test framework 15 16 var opTests = []string{ 17 // unary operations 18 `+ 0 = 0`, 19 `+ ? = ?`, 20 `- 1 = -1`, 21 `- ? = ?`, 22 `^ 0 = -1`, 23 `^ ? = ?`, 24 25 `! true = false`, 26 `! false = true`, 27 `! ? = ?`, 28 29 // etc. 30 31 // binary operations 32 `"" + "" = ""`, 33 `"foo" + "" = "foo"`, 34 `"" + "bar" = "bar"`, 35 `"foo" + "bar" = "foobar"`, 36 37 `0 + 0 = 0`, 38 `0 + 0.1 = 0.1`, 39 `0 + 0.1i = 0.1i`, 40 `0.1 + 0.9 = 1`, 41 `1e100 + 1e100 = 2e100`, 42 `? + 0 = ?`, 43 `0 + ? = ?`, 44 45 `0 - 0 = 0`, 46 `0 - 0.1 = -0.1`, 47 `0 - 0.1i = -0.1i`, 48 `1e100 - 1e100 = 0`, 49 `? - 0 = ?`, 50 `0 - ? = ?`, 51 52 `0 * 0 = 0`, 53 `1 * 0.1 = 0.1`, 54 `1 * 0.1i = 0.1i`, 55 `1i * 1i = -1`, 56 `? * 0 = ?`, 57 `0 * ? = ?`, 58 59 `0 / 0 = "division_by_zero"`, 60 `10 / 2 = 5`, 61 `5 / 3 = 5/3`, 62 `5i / 3i = 5/3`, 63 `? / 0 = ?`, 64 `0 / ? = ?`, 65 66 `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for / 67 `10 % 3 = 1`, 68 `? % 0 = ?`, 69 `0 % ? = ?`, 70 71 `0 & 0 = 0`, 72 `12345 & 0 = 0`, 73 `0xff & 0xf = 0xf`, 74 `? & 0 = ?`, 75 `0 & ? = ?`, 76 77 `0 | 0 = 0`, 78 `12345 | 0 = 12345`, 79 `0xb | 0xa0 = 0xab`, 80 `? | 0 = ?`, 81 `0 | ? = ?`, 82 83 `0 ^ 0 = 0`, 84 `1 ^ -1 = -2`, 85 `? ^ 0 = ?`, 86 `0 ^ ? = ?`, 87 88 `0 &^ 0 = 0`, 89 `0xf &^ 1 = 0xe`, 90 `1 &^ 0xf = 0`, 91 // etc. 92 93 // shifts 94 `0 << 0 = 0`, 95 `1 << 10 = 1024`, 96 `0 >> 0 = 0`, 97 `1024 >> 10 == 1`, 98 `? << 0 == ?`, 99 `? >> 10 == ?`, 100 // etc. 101 102 // comparisons 103 `false == false = true`, 104 `false == true = false`, 105 `true == false = false`, 106 `true == true = true`, 107 108 `false != false = false`, 109 `false != true = true`, 110 `true != false = true`, 111 `true != true = false`, 112 113 `"foo" == "bar" = false`, 114 `"foo" != "bar" = true`, 115 `"foo" < "bar" = false`, 116 `"foo" <= "bar" = false`, 117 `"foo" > "bar" = true`, 118 `"foo" >= "bar" = true`, 119 120 `0 == 0 = true`, 121 `0 != 0 = false`, 122 `0 < 10 = true`, 123 `10 <= 10 = true`, 124 `0 > 10 = false`, 125 `10 >= 10 = true`, 126 127 `1/123456789 == 1/123456789 == true`, 128 `1/123456789 != 1/123456789 == false`, 129 `1/123456789 < 1/123456788 == true`, 130 `1/123456788 <= 1/123456789 == false`, 131 `0.11 > 0.11 = false`, 132 `0.11 >= 0.11 = true`, 133 134 `? == 0 = false`, 135 `? != 0 = false`, 136 `? < 10 = false`, 137 `? <= 10 = false`, 138 `? > 10 = false`, 139 `? >= 10 = false`, 140 141 `0 == ? = false`, 142 `0 != ? = false`, 143 `0 < ? = false`, 144 `10 <= ? = false`, 145 `0 > ? = false`, 146 `10 >= ? = false`, 147 148 // etc. 149 } 150 151 func TestOps(t *testing.T) { 152 for _, test := range opTests { 153 a := strings.Split(test, " ") 154 i := 0 // operator index 155 156 var x, x0 Value 157 switch len(a) { 158 case 4: 159 // unary operation 160 case 5: 161 // binary operation 162 x, x0 = val(a[0]), val(a[0]) 163 i = 1 164 default: 165 t.Errorf("invalid test case: %s", test) 166 continue 167 } 168 169 op, ok := optab[a[i]] 170 if !ok { 171 panic("missing optab entry for " + a[i]) 172 } 173 174 y, y0 := val(a[i+1]), val(a[i+1]) 175 176 got := doOp(x, op, y) 177 want := val(a[i+3]) 178 if !eql(got, want) { 179 t.Errorf("%s: got %s; want %s", test, got, want) 180 continue 181 } 182 183 if x0 != nil && !eql(x, x0) { 184 t.Errorf("%s: x changed to %s", test, x) 185 continue 186 } 187 188 if !eql(y, y0) { 189 t.Errorf("%s: y changed to %s", test, y) 190 continue 191 } 192 } 193 } 194 195 func eql(x, y Value) bool { 196 _, ux := x.(unknownVal) 197 _, uy := y.(unknownVal) 198 if ux || uy { 199 return ux == uy 200 } 201 return Compare(x, token.EQL, y) 202 } 203 204 // ---------------------------------------------------------------------------- 205 // String tests 206 207 var xxx = strings.Repeat("x", 68) 208 var issue14262 = `"بموجب الشروط التالية نسب المصنف — يجب عليك أن تنسب العمل بالطريقة التي تحددها المؤلف أو المرخص (ولكن ليس بأي حال من الأحوال أن توحي وتقترح بتحول أو استخدامك للعمل). المشاركة على قدم المساواة — إذا كنت يعدل ، والتغيير ، أو الاستفادة من هذا العمل ، قد ينتج عن توزيع العمل إلا في ظل تشابه او تطابق فى واحد لهذا الترخيص."` 209 210 var stringTests = []struct { 211 input, short, exact string 212 }{ 213 // Unknown 214 {"", "unknown", "unknown"}, 215 {"0x", "unknown", "unknown"}, 216 {"'", "unknown", "unknown"}, 217 {"1f0", "unknown", "unknown"}, 218 {"unknown", "unknown", "unknown"}, 219 220 // Bool 221 {"true", "true", "true"}, 222 {"false", "false", "false"}, 223 224 // String 225 {`""`, `""`, `""`}, 226 {`"foo"`, `"foo"`, `"foo"`}, 227 {`"` + xxx + `xx"`, `"` + xxx + `xx"`, `"` + xxx + `xx"`}, 228 {`"` + xxx + `xxx"`, `"` + xxx + `...`, `"` + xxx + `xxx"`}, 229 {`"` + xxx + xxx + `xxx"`, `"` + xxx + `...`, `"` + xxx + xxx + `xxx"`}, 230 {issue14262, `"بموجب الشروط التالية نسب المصنف — يجب عليك أن تنسب العمل بالطريقة ال...`, issue14262}, 231 232 // Int 233 {"0", "0", "0"}, 234 {"-1", "-1", "-1"}, 235 {"12345", "12345", "12345"}, 236 {"-12345678901234567890", "-12345678901234567890", "-12345678901234567890"}, 237 {"12345678901234567890", "12345678901234567890", "12345678901234567890"}, 238 239 // Float 240 {"0.", "0", "0"}, 241 {"-0.0", "0", "0"}, 242 {"10.0", "10", "10"}, 243 {"2.1", "2.1", "21/10"}, 244 {"-2.1", "-2.1", "-21/10"}, 245 {"1e9999", "1e+9999", "0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216"}, 246 {"1e-9999", "1e-9999", "0x.83b01ba6d8c0425eec1b21e96f7742d63c2653ed0a024cf8a2f9686df578d7b07d7a83d84df6a2ec70a921d1f6cd5574893a7eda4d28ee719e13a5dce2700759p-33215"}, 247 {"2.71828182845904523536028747135266249775724709369995957496696763", "2.71828", "271828182845904523536028747135266249775724709369995957496696763/100000000000000000000000000000000000000000000000000000000000000"}, 248 {"0e9999999999", "0", "0"}, // issue #16176 249 {"-6e-1886451601", "0", "0"}, // issue #20228 250 251 // Complex 252 {"0i", "(0 + 0i)", "(0 + 0i)"}, 253 {"-0i", "(0 + 0i)", "(0 + 0i)"}, 254 {"10i", "(0 + 10i)", "(0 + 10i)"}, 255 {"-10i", "(0 + -10i)", "(0 + -10i)"}, 256 {"1e9999i", "(0 + 1e+9999i)", "(0 + 0x.f8d4a9da224650a8cb2959e10d985ad92adbd44c62917e608b1f24c0e1b76b6f61edffeb15c135a4b601637315f7662f325f82325422b244286a07663c9415d2p+33216i)"}, 257 } 258 259 func TestString(t *testing.T) { 260 for _, test := range stringTests { 261 x := val(test.input) 262 if got := x.String(); got != test.short { 263 t.Errorf("%s: got %q; want %q as short string", test.input, got, test.short) 264 } 265 if got := x.ExactString(); got != test.exact { 266 t.Errorf("%s: got %q; want %q as exact string", test.input, got, test.exact) 267 } 268 } 269 } 270 271 // ---------------------------------------------------------------------------- 272 // Support functions 273 274 func val(lit string) Value { 275 if len(lit) == 0 { 276 return MakeUnknown() 277 } 278 279 switch lit { 280 case "?": 281 return MakeUnknown() 282 case "true": 283 return MakeBool(true) 284 case "false": 285 return MakeBool(false) 286 } 287 288 if i := strings.IndexByte(lit, '/'); i >= 0 { 289 // assume fraction 290 a := MakeFromLiteral(lit[:i], token.INT, 0) 291 b := MakeFromLiteral(lit[i+1:], token.INT, 0) 292 return BinaryOp(a, token.QUO, b) 293 } 294 295 tok := token.INT 296 switch first, last := lit[0], lit[len(lit)-1]; { 297 case first == '"' || first == '`': 298 tok = token.STRING 299 lit = strings.Replace(lit, "_", " ", -1) 300 case first == '\'': 301 tok = token.CHAR 302 case last == 'i': 303 tok = token.IMAG 304 default: 305 if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") { 306 tok = token.FLOAT 307 } 308 } 309 310 return MakeFromLiteral(lit, tok, 0) 311 } 312 313 var optab = map[string]token.Token{ 314 "!": token.NOT, 315 316 "+": token.ADD, 317 "-": token.SUB, 318 "*": token.MUL, 319 "/": token.QUO, 320 "%": token.REM, 321 322 "<<": token.SHL, 323 ">>": token.SHR, 324 325 "&": token.AND, 326 "|": token.OR, 327 "^": token.XOR, 328 "&^": token.AND_NOT, 329 330 "==": token.EQL, 331 "!=": token.NEQ, 332 "<": token.LSS, 333 "<=": token.LEQ, 334 ">": token.GTR, 335 ">=": token.GEQ, 336 } 337 338 func panicHandler(v *Value) { 339 switch p := recover().(type) { 340 case nil: 341 // nothing to do 342 case string: 343 *v = MakeString(p) 344 case error: 345 *v = MakeString(p.Error()) 346 default: 347 panic(p) 348 } 349 } 350 351 func doOp(x Value, op token.Token, y Value) (z Value) { 352 defer panicHandler(&z) 353 354 if x == nil { 355 return UnaryOp(op, y, 0) 356 } 357 358 switch op { 359 case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: 360 return MakeBool(Compare(x, op, y)) 361 case token.SHL, token.SHR: 362 s, _ := Int64Val(y) 363 return Shift(x, op, uint(s)) 364 default: 365 return BinaryOp(x, op, y) 366 } 367 } 368 369 // ---------------------------------------------------------------------------- 370 // Other tests 371 372 var fracTests = []string{ 373 "0", 374 "1", 375 "-1", 376 "1.2", 377 "-0.991", 378 "2.718281828", 379 "3.14159265358979323e-10", 380 "1e100", 381 "1e1000", 382 } 383 384 func TestFractions(t *testing.T) { 385 for _, test := range fracTests { 386 x := val(test) 387 // We don't check the actual numerator and denominator because they 388 // are unlikely to be 100% correct due to floatVal rounding errors. 389 // Instead, we compute the fraction again and compare the rounded 390 // result. 391 q := BinaryOp(Num(x), token.QUO, Denom(x)) 392 got := q.String() 393 want := x.String() 394 if got != want { 395 t.Errorf("%s: got quotient %s, want %s", x, got, want) 396 } 397 } 398 } 399 400 var bytesTests = []string{ 401 "0", 402 "1", 403 "123456789", 404 "123456789012345678901234567890123456789012345678901234567890", 405 } 406 407 func TestBytes(t *testing.T) { 408 for _, test := range bytesTests { 409 x := val(test) 410 bytes := Bytes(x) 411 412 // special case 0 413 if Sign(x) == 0 && len(bytes) != 0 { 414 t.Errorf("%s: got %v; want empty byte slice", test, bytes) 415 } 416 417 if n := len(bytes); n > 0 && bytes[n-1] == 0 { 418 t.Errorf("%s: got %v; want no leading 0 byte", test, bytes) 419 } 420 421 if got := MakeFromBytes(bytes); !eql(got, x) { 422 t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes) 423 } 424 } 425 } 426 427 func TestUnknown(t *testing.T) { 428 u := MakeUnknown() 429 var values = []Value{ 430 u, 431 MakeBool(false), // token.ADD ok below, operation is never considered 432 MakeString(""), 433 MakeInt64(1), 434 MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0), 435 MakeFloat64(1.2), 436 MakeImag(MakeFloat64(1.2)), 437 } 438 for _, val := range values { 439 x, y := val, u 440 for i := range [2]int{} { 441 if i == 1 { 442 x, y = y, x 443 } 444 if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown { 445 t.Errorf("%s + %s: got %s; want %s", x, y, got, u) 446 } 447 if got := Compare(x, token.EQL, y); got { 448 t.Errorf("%s == %s: got true; want false", x, y) 449 } 450 } 451 } 452 } 453 454 func BenchmarkStringAdd(b *testing.B) { 455 for size := 1; size <= 65536; size *= 4 { 456 b.Run(fmt.Sprint(size), func(b *testing.B) { 457 b.ReportAllocs() 458 n := int64(0) 459 for i := 0; i < b.N; i++ { 460 x := MakeString(strings.Repeat("x", 100)) 461 y := x 462 for j := 0; j < size-1; j++ { 463 y = BinaryOp(y, token.ADD, x) 464 } 465 n += int64(len(StringVal(y))) 466 } 467 if n != int64(b.N)*int64(size)*100 { 468 b.Fatalf("bad string %d != %d", n, int64(b.N)*int64(size)*100) 469 } 470 }) 471 } 472 }