go.starlark.net@v0.0.0-20231101134539-556fd59b42f6/starlark/int_test.go (about) 1 // Copyright 2017 The Bazel 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 starlark 6 7 import ( 8 "flag" 9 "fmt" 10 "log" 11 "math" 12 "math/big" 13 "os" 14 "os/exec" 15 "runtime" 16 "strings" 17 "testing" 18 ) 19 20 // TestIntOpts exercises integer arithmetic, especially at the boundaries. 21 func TestIntOpts(t *testing.T) { 22 f := MakeInt64 23 left, right := big.NewInt(math.MinInt32), big.NewInt(math.MaxInt32) 24 25 for i, test := range []struct { 26 val Int 27 want string 28 }{ 29 // Add 30 {f(math.MaxInt32).Add(f(1)), "80000000"}, 31 {f(math.MinInt32).Add(f(-1)), "-80000001"}, 32 // Mul 33 {f(math.MaxInt32).Mul(f(math.MaxInt32)), "3fffffff00000001"}, 34 {f(math.MinInt32).Mul(f(math.MinInt32)), "4000000000000000"}, 35 {f(math.MaxUint32).Mul(f(math.MaxUint32)), "fffffffe00000001"}, 36 {f(math.MinInt32).Mul(f(-1)), "80000000"}, 37 // Div 38 {f(math.MinInt32).Div(f(-1)), "80000000"}, 39 {f(1 << 31).Div(f(2)), "40000000"}, 40 // And 41 {f(math.MaxInt32).And(f(math.MaxInt32)), "7fffffff"}, 42 {f(math.MinInt32).And(f(math.MinInt32)), "-80000000"}, 43 {f(1 << 33).And(f(1 << 32)), "0"}, 44 // Mod 45 {f(1 << 32).Mod(f(2)), "0"}, 46 // Or 47 {f(1 << 32).Or(f(0)), "100000000"}, 48 {f(math.MaxInt32).Or(f(0)), "7fffffff"}, 49 {f(math.MaxUint32).Or(f(0)), "ffffffff"}, 50 {f(math.MinInt32).Or(f(math.MinInt32)), "-80000000"}, 51 // Xor 52 {f(math.MinInt32).Xor(f(-1)), "7fffffff"}, 53 // Not 54 {f(math.MinInt32).Not(), "7fffffff"}, 55 {f(math.MaxInt32).Not(), "-80000000"}, 56 // Shift 57 {f(1).Lsh(31), "80000000"}, 58 {f(1).Lsh(32), "100000000"}, 59 {f(math.MaxInt32 + 1).Rsh(1), "40000000"}, 60 {f(math.MinInt32 * 2).Rsh(1), "-80000000"}, 61 } { 62 if got := fmt.Sprintf("%x", test.val); got != test.want { 63 t.Errorf("%d equals %s, want %s", i, got, test.want) 64 } 65 small, big := test.val.get() 66 if small < math.MinInt32 || math.MaxInt32 < small { 67 t.Errorf("expected big, %d %s", i, test.val) 68 } 69 if big == nil { 70 continue 71 } 72 if small != 0 { 73 t.Errorf("expected 0 small, %d %s with %d", i, test.val, small) 74 } 75 if big.Cmp(left) >= 0 && big.Cmp(right) <= 0 { 76 t.Errorf("expected small, %d %s", i, test.val) 77 } 78 } 79 } 80 81 func TestImmutabilityMakeBigInt(t *testing.T) { 82 // use max int64 for the test 83 expect := int64(^uint64(0) >> 1) 84 85 mutint := big.NewInt(expect) 86 value := MakeBigInt(mutint) 87 mutint.Set(big.NewInt(1)) 88 89 got, _ := value.Int64() 90 if got != expect { 91 t.Errorf("expected %d, got %d", expect, got) 92 } 93 } 94 95 func TestImmutabilityBigInt(t *testing.T) { 96 // use 1 and max int64 for the test 97 for _, expect := range []int64{1, int64(^uint64(0) >> 1)} { 98 value := MakeBigInt(big.NewInt(expect)) 99 100 bigint := value.BigInt() 101 bigint.Set(big.NewInt(2)) 102 103 got, _ := value.Int64() 104 if got != expect { 105 t.Errorf("expected %d, got %d", expect, got) 106 } 107 } 108 } 109 110 // TestIntFallback creates a small Int value in a child process with 111 // limited address space to ensure that it still works, but prints a warning. 112 func TestIntFallback(t *testing.T) { 113 if runtime.GOOS != "linux" { 114 t.Skipf("test disabled on this platform (requires ulimit -v)") 115 } 116 exe, err := os.Executable() 117 if err != nil { 118 t.Fatalf("can't find file name of executable: %v", err) 119 } 120 // ulimit -v limits the address space in KB. Not portable. 121 // 4GB is enough for the Go runtime but not for the optimization. 122 cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("ulimit -v 4000000 && %q --entry=intfallback", exe)) 123 out, err := cmd.CombinedOutput() 124 if err != nil { 125 t.Fatalf("intfallback subcommand failed: %v\n%s", err, out) 126 } 127 128 // Check the warning was printed. 129 if !strings.Contains(string(out), "Integer performance may suffer") { 130 t.Errorf("expected warning was not printed. Output=<<%s>>", out) 131 } 132 } 133 134 // intfallback is called in a child process with limited address space. 135 func intfallback() { 136 const want = 123 137 if got, _ := MakeBigInt(big.NewInt(want)).Int64(); got != want { 138 log.Fatalf("intfallback: got %d, want %d", got, want) 139 } 140 } 141 142 // The --entry flag invokes an alternate entry point, for use in subprocess tests. 143 var testEntry = flag.String("entry", "", "child process entry-point") 144 145 func TestMain(m *testing.M) { 146 // In some build systems, notably Blaze, flag.Parse is called before TestMain, 147 // in violation of the TestMain contract, making this second call a no-op. 148 flag.Parse() 149 switch *testEntry { 150 case "": 151 os.Exit(m.Run()) // normal case 152 case "intfallback": 153 intfallback() 154 default: 155 log.Fatalf("unknown entry point: %s", *testEntry) 156 } 157 }