File: int_test.go

package info (click to toggle)
golang-starlark 0.0~git20240725.42030a7-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid, trixie
  • size: 1,296 kB
  • sloc: makefile: 8; sh: 8
file content (157 lines) | stat: -rw-r--r-- 4,491 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2017 The Bazel 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 starlark

import (
	"flag"
	"fmt"
	"log"
	"math"
	"math/big"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"testing"
)

// TestIntOpts exercises integer arithmetic, especially at the boundaries.
func TestIntOpts(t *testing.T) {
	f := MakeInt64
	left, right := big.NewInt(math.MinInt32), big.NewInt(math.MaxInt32)

	for i, test := range []struct {
		val  Int
		want string
	}{
		// Add
		{f(math.MaxInt32).Add(f(1)), "80000000"},
		{f(math.MinInt32).Add(f(-1)), "-80000001"},
		// Mul
		{f(math.MaxInt32).Mul(f(math.MaxInt32)), "3fffffff00000001"},
		{f(math.MinInt32).Mul(f(math.MinInt32)), "4000000000000000"},
		{f(math.MaxUint32).Mul(f(math.MaxUint32)), "fffffffe00000001"},
		{f(math.MinInt32).Mul(f(-1)), "80000000"},
		// Div
		{f(math.MinInt32).Div(f(-1)), "80000000"},
		{f(1 << 31).Div(f(2)), "40000000"},
		// And
		{f(math.MaxInt32).And(f(math.MaxInt32)), "7fffffff"},
		{f(math.MinInt32).And(f(math.MinInt32)), "-80000000"},
		{f(1 << 33).And(f(1 << 32)), "0"},
		// Mod
		{f(1 << 32).Mod(f(2)), "0"},
		// Or
		{f(1 << 32).Or(f(0)), "100000000"},
		{f(math.MaxInt32).Or(f(0)), "7fffffff"},
		{f(math.MaxUint32).Or(f(0)), "ffffffff"},
		{f(math.MinInt32).Or(f(math.MinInt32)), "-80000000"},
		// Xor
		{f(math.MinInt32).Xor(f(-1)), "7fffffff"},
		// Not
		{f(math.MinInt32).Not(), "7fffffff"},
		{f(math.MaxInt32).Not(), "-80000000"},
		// Shift
		{f(1).Lsh(31), "80000000"},
		{f(1).Lsh(32), "100000000"},
		{f(math.MaxInt32 + 1).Rsh(1), "40000000"},
		{f(math.MinInt32 * 2).Rsh(1), "-80000000"},
	} {
		if got := fmt.Sprintf("%x", test.val); got != test.want {
			t.Errorf("%d equals %s, want %s", i, got, test.want)
		}
		small, big := test.val.get()
		if small < math.MinInt32 || math.MaxInt32 < small {
			t.Errorf("expected big, %d %s", i, test.val)
		}
		if big == nil {
			continue
		}
		if small != 0 {
			t.Errorf("expected 0 small, %d %s with %d", i, test.val, small)
		}
		if big.Cmp(left) >= 0 && big.Cmp(right) <= 0 {
			t.Errorf("expected small, %d %s", i, test.val)
		}
	}
}

func TestImmutabilityMakeBigInt(t *testing.T) {
	// use max int64 for the test
	expect := int64(^uint64(0) >> 1)

	mutint := big.NewInt(expect)
	value := MakeBigInt(mutint)
	mutint.Set(big.NewInt(1))

	got, _ := value.Int64()
	if got != expect {
		t.Errorf("expected %d, got %d", expect, got)
	}
}

func TestImmutabilityBigInt(t *testing.T) {
	// use 1 and max int64 for the test
	for _, expect := range []int64{1, int64(^uint64(0) >> 1)} {
		value := MakeBigInt(big.NewInt(expect))

		bigint := value.BigInt()
		bigint.Set(big.NewInt(2))

		got, _ := value.Int64()
		if got != expect {
			t.Errorf("expected %d, got %d", expect, got)
		}
	}
}

// TestIntFallback creates a small Int value in a child process with
// limited address space to ensure that it still works, but prints a warning.
func TestIntFallback(t *testing.T) {
	if runtime.GOOS != "linux" {
		t.Skipf("test disabled on this platform (requires ulimit -v)")
	}
	exe, err := os.Executable()
	if err != nil {
		t.Fatalf("can't find file name of executable: %v", err)
	}
	// ulimit -v limits the address space in KB. Not portable.
	// 4GB is enough for the Go runtime but not for the optimization.
	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("ulimit -v 4000000 && %q --entry=intfallback", exe))
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("intfallback subcommand failed: %v\n%s", err, out)
	}

	// Check the warning was printed.
	if !strings.Contains(string(out), "Integer performance may suffer") {
		t.Errorf("expected warning was not printed. Output=<<%s>>", out)
	}
}

// intfallback is called in a child process with limited address space.
func intfallback() {
	const want = 123
	if got, _ := MakeBigInt(big.NewInt(want)).Int64(); got != want {
		log.Fatalf("intfallback: got %d, want %d", got, want)
	}
}

// The --entry flag invokes an alternate entry point, for use in subprocess tests.
var testEntry = flag.String("entry", "", "child process entry-point")

func TestMain(m *testing.M) {
	// In some build systems, notably Blaze, flag.Parse is called before TestMain,
	// in violation of the TestMain contract, making this second call a no-op.
	flag.Parse()
	switch *testEntry {
	case "":
		os.Exit(m.Run()) // normal case
	case "intfallback":
		intfallback()
	default:
		log.Fatalf("unknown entry point: %s", *testEntry)
	}
}