...
Run Format

Source file src/go/constant/value_test.go

Documentation: go/constant

     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("''", token.CHAR, 0),
   435  		MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
   436  		MakeFloat64(1.2),
   437  		MakeImag(MakeFloat64(1.2)),
   438  	}
   439  	for _, val := range values {
   440  		x, y := val, u
   441  		for i := range [2]int{} {
   442  			if i == 1 {
   443  				x, y = y, x
   444  			}
   445  			if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
   446  				t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
   447  			}
   448  			if got := Compare(x, token.EQL, y); got {
   449  				t.Errorf("%s == %s: got true; want false", x, y)
   450  			}
   451  		}
   452  	}
   453  }
   454  
   455  func BenchmarkStringAdd(b *testing.B) {
   456  	for size := 1; size <= 65536; size *= 4 {
   457  		b.Run(fmt.Sprint(size), func(b *testing.B) {
   458  			b.ReportAllocs()
   459  			n := int64(0)
   460  			for i := 0; i < b.N; i++ {
   461  				x := MakeString(strings.Repeat("x", 100))
   462  				y := x
   463  				for j := 0; j < size-1; j++ {
   464  					y = BinaryOp(y, token.ADD, x)
   465  				}
   466  				n += int64(len(StringVal(y)))
   467  			}
   468  			if n != int64(b.N)*int64(size)*100 {
   469  				b.Fatalf("bad string %d != %d", n, int64(b.N)*int64(size)*100)
   470  			}
   471  		})
   472  	}
   473  }
   474  

View as plain text