// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package constant import ( "k8s.io/kubernetes/third_party/golang/go/token" "strings" "testing" ) // TODO(gri) expand this test framework var opTests = []string{ // unary operations `+ 0 = 0`, `+ ? = ?`, `- 1 = -1`, `- ? = ?`, `^ 0 = -1`, `^ ? = ?`, `! true = false`, `! false = true`, `! ? = ?`, // etc. // binary operations `"" + "" = ""`, `"foo" + "" = "foo"`, `"" + "bar" = "bar"`, `"foo" + "bar" = "foobar"`, `0 + 0 = 0`, `0 + 0.1 = 0.1`, `0 + 0.1i = 0.1i`, `0.1 + 0.9 = 1`, `1e100 + 1e100 = 2e100`, `? + 0 = ?`, `0 + ? = ?`, `0 - 0 = 0`, `0 - 0.1 = -0.1`, `0 - 0.1i = -0.1i`, `1e100 - 1e100 = 0`, `? - 0 = ?`, `0 - ? = ?`, `0 * 0 = 0`, `1 * 0.1 = 0.1`, `1 * 0.1i = 0.1i`, `1i * 1i = -1`, `? * 0 = ?`, `0 * ? = ?`, `0 / 0 = "division_by_zero"`, `10 / 2 = 5`, `5 / 3 = 5/3`, `5i / 3i = 5/3`, `? / 0 = ?`, `0 / ? = ?`, `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for / `10 % 3 = 1`, `? % 0 = ?`, `0 % ? = ?`, `0 & 0 = 0`, `12345 & 0 = 0`, `0xff & 0xf = 0xf`, `? & 0 = ?`, `0 & ? = ?`, `0 | 0 = 0`, `12345 | 0 = 12345`, `0xb | 0xa0 = 0xab`, `? | 0 = ?`, `0 | ? = ?`, `0 ^ 0 = 0`, `1 ^ -1 = -2`, `? ^ 0 = ?`, `0 ^ ? = ?`, `0 &^ 0 = 0`, `0xf &^ 1 = 0xe`, `1 &^ 0xf = 0`, // etc. // shifts `0 << 0 = 0`, `1 << 10 = 1024`, `0 >> 0 = 0`, `1024 >> 10 == 1`, `? << 0 == ?`, `? >> 10 == ?`, // etc. // comparisons `false == false = true`, `false == true = false`, `true == false = false`, `true == true = true`, `false != false = false`, `false != true = true`, `true != false = true`, `true != true = false`, `"foo" == "bar" = false`, `"foo" != "bar" = true`, `"foo" < "bar" = false`, `"foo" <= "bar" = false`, `"foo" > "bar" = true`, `"foo" >= "bar" = true`, `0 == 0 = true`, `0 != 0 = false`, `0 < 10 = true`, `10 <= 10 = true`, `0 > 10 = false`, `10 >= 10 = true`, `1/123456789 == 1/123456789 == true`, `1/123456789 != 1/123456789 == false`, `1/123456789 < 1/123456788 == true`, `1/123456788 <= 1/123456789 == false`, `0.11 > 0.11 = false`, `0.11 >= 0.11 = true`, `? == 0 = false`, `? != 0 = false`, `? < 10 = false`, `? <= 10 = false`, `? > 10 = false`, `? >= 10 = false`, `0 == ? = false`, `0 != ? = false`, `0 < ? = false`, `10 <= ? = false`, `0 > ? = false`, `10 >= ? = false`, // etc. } func TestOps(t *testing.T) { for _, test := range opTests { a := strings.Split(test, " ") i := 0 // operator index var x, x0 Value switch len(a) { case 4: // unary operation case 5: // binary operation x, x0 = val(a[0]), val(a[0]) i = 1 default: t.Errorf("invalid test case: %s", test) continue } op, ok := optab[a[i]] if !ok { panic("missing optab entry for " + a[i]) } y, y0 := val(a[i+1]), val(a[i+1]) got := doOp(x, op, y) want := val(a[i+3]) if !eql(got, want) { t.Errorf("%s: got %s; want %s", test, got, want) } if x0 != nil && !eql(x, x0) { t.Errorf("%s: x changed to %s", test, x) } if !eql(y, y0) { t.Errorf("%s: y changed to %s", test, y) } } } func eql(x, y Value) bool { _, ux := x.(unknownVal) _, uy := y.(unknownVal) if ux || uy { return ux == uy } return Compare(x, token.EQL, y) } // ---------------------------------------------------------------------------- // Support functions func val(lit string) Value { if len(lit) == 0 { return MakeUnknown() } switch lit { case "?": return MakeUnknown() case "true": return MakeBool(true) case "false": return MakeBool(false) } tok := token.INT switch first, last := lit[0], lit[len(lit)-1]; { case first == '"' || first == '`': tok = token.STRING lit = strings.Replace(lit, "_", " ", -1) case first == '\'': tok = token.CHAR case last == 'i': tok = token.IMAG default: if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") { tok = token.FLOAT } } return MakeFromLiteral(lit, tok, 0) } var optab = map[string]token.Token{ "!": token.NOT, "+": token.ADD, "-": token.SUB, "*": token.MUL, "/": token.QUO, "%": token.REM, "<<": token.SHL, ">>": token.SHR, "&": token.AND, "|": token.OR, "^": token.XOR, "&^": token.AND_NOT, "==": token.EQL, "!=": token.NEQ, "<": token.LSS, "<=": token.LEQ, ">": token.GTR, ">=": token.GEQ, } func panicHandler(v *Value) { switch p := recover().(type) { case nil: // nothing to do case string: *v = MakeString(p) case error: *v = MakeString(p.Error()) default: panic(p) } } func doOp(x Value, op token.Token, y Value) (z Value) { defer panicHandler(&z) if x == nil { return UnaryOp(op, y, 0) } switch op { case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ: return MakeBool(Compare(x, op, y)) case token.SHL, token.SHR: s, _ := Int64Val(y) return Shift(x, op, uint(s)) default: return BinaryOp(x, op, y) } } // ---------------------------------------------------------------------------- // Other tests var fracTests = []string{ "0 0 1", "1 1 1", "-1 -1 1", "1.2 6 5", "-0.991 -991 1000", "1e100 1e100 1", } func TestFractions(t *testing.T) { for _, test := range fracTests { a := strings.Split(test, " ") if len(a) != 3 { t.Errorf("invalid test case: %s", test) continue } x := val(a[0]) n := val(a[1]) d := val(a[2]) if got := Num(x); !eql(got, n) { t.Errorf("%s: got num = %s; want %s", test, got, n) } if got := Denom(x); !eql(got, d) { t.Errorf("%s: got denom = %s; want %s", test, got, d) } } } var bytesTests = []string{ "0", "1", "123456789", "123456789012345678901234567890123456789012345678901234567890", } func TestBytes(t *testing.T) { for _, test := range bytesTests { x := val(test) bytes := Bytes(x) // special case 0 if Sign(x) == 0 && len(bytes) != 0 { t.Errorf("%s: got %v; want empty byte slice", test, bytes) } if n := len(bytes); n > 0 && bytes[n-1] == 0 { t.Errorf("%s: got %v; want no leading 0 byte", test, bytes) } if got := MakeFromBytes(bytes); !eql(got, x) { t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes) } } } func TestUnknown(t *testing.T) { u := MakeUnknown() var values = []Value{ u, MakeBool(false), // token.ADD ok below, operation is never considered MakeString(""), MakeInt64(1), MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0), MakeFloat64(1.2), MakeImag(MakeFloat64(1.2)), } for _, val := range values { x, y := val, u for i := range [2]int{} { if i == 1 { x, y = y, x } if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown { t.Errorf("%s + %s: got %s; want %s", x, y, got, u) } if got := Compare(x, token.EQL, y); got { t.Errorf("%s == %s: got true; want false", x, y) } } } }