variables_test.go 45.6 KB
Newer Older
1
package service_test
2 3 4

import (
	"fmt"
5
	"runtime"
6 7 8 9
	"sort"
	"strings"
	"testing"

10
	"github.com/derekparker/delve/pkg/goversion"
D
Derek Parker 已提交
11
	"github.com/derekparker/delve/pkg/proc"
12 13
	"github.com/derekparker/delve/pkg/proc/gdbserial"
	"github.com/derekparker/delve/pkg/proc/native"
14
	"github.com/derekparker/delve/service/api"
15

D
Derek Parker 已提交
16
	protest "github.com/derekparker/delve/pkg/proc/test"
17 18
)

19 20 21
var pnormalLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1}
var pshortLoadConfig = proc.LoadConfig{false, 0, 64, 0, 3}

22
type varTest struct {
A
aarzilli 已提交
23 24 25
	name         string
	preserveName bool
	value        string
26
	alternate    string
A
aarzilli 已提交
27 28
	varType      string
	err          error
29 30 31 32 33 34 35 36 37 38 39 40 41
}

func matchStringOrPrefix(output, target string) bool {
	if strings.HasSuffix(target, "…") {
		prefix := target[:len(target)-len("…")]
		b := strings.HasPrefix(output, prefix)
		return b
	} else {
		return output == target
	}
}

func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
A
aarzilli 已提交
42 43 44 45
	if expected.preserveName {
		if variable.Name != expected.name {
			t.Fatalf("Expected %s got %s\n", expected.name, variable.Name)
		}
46 47 48 49 50 51 52 53 54 55 56 57 58
	}

	cv := api.ConvertVar(variable)

	if cv.Type != expected.varType {
		t.Fatalf("Expected %s got %s (for variable %s)\n", expected.varType, cv.Type, expected.name)
	}

	if ss := cv.SinglelineString(); !matchStringOrPrefix(ss, expected.value) {
		t.Fatalf("Expected %#v got %#v (for variable %s)\n", expected.value, ss, expected.name)
	}
}

59 60
func findFirstNonRuntimeFrame(p proc.Process) (proc.Stackframe, error) {
	frames, err := proc.ThreadStacktrace(p.CurrentThread(), 10)
61
	if err != nil {
62 63 64 65 66 67 68
		return proc.Stackframe{}, err
	}

	for _, frame := range frames {
		if frame.Current.Fn != nil && !strings.HasPrefix(frame.Current.Fn.Name, "runtime.") {
			return frame, nil
		}
69
	}
70 71 72 73 74 75 76 77 78 79 80
	return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found")
}

func evalVariable(p proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
	var scope *proc.EvalScope
	var err error

	if testBackend == "rr" {
		var frame proc.Stackframe
		frame, err = findFirstNonRuntimeFrame(p)
		if err == nil {
81
			scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame)
82 83 84 85 86
		}
	} else {
		scope, err = proc.GoroutineScope(p.CurrentThread())
	}

87 88 89 90
	if err != nil {
		return nil, err
	}

91
	return scope.EvalVariable(symbol, cfg)
92 93
}

94
func (tc *varTest) alternateVarTest() varTest {
95
	r := *tc
96
	r.value = r.alternate
97 98 99
	return r
}

100
func setVariable(p proc.Process, symbol, value string) error {
101
	scope, err := proc.GoroutineScope(p.CurrentThread())
102 103 104 105 106 107
	if err != nil {
		return err
	}
	return scope.SetVariable(symbol, value)
}

108
func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture protest.Fixture)) {
109
	fixture := protest.BuildFixture(name, 0)
110
	var p proc.Process
111
	var err error
112
	var tracedir string
113 114
	switch testBackend {
	case "native":
115
		p, err = native.Launch([]string{fixture.Path}, ".")
116
	case "lldb":
117
		p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".")
118 119 120 121 122
	case "rr":
		protest.MustHaveRecordingAllowed(t)
		t.Log("recording")
		p, tracedir, err = gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true)
		t.Logf("replaying %q", tracedir)
123 124 125
	default:
		t.Fatalf("unknown backend %q", testBackend)
	}
126 127 128 129 130
	if err != nil {
		t.Fatal("Launch():", err)
	}

	defer func() {
131
		p.Detach(true)
132 133 134
		if tracedir != "" {
			protest.SafeRemoveAll(tracedir)
		}
135 136 137 138 139 140 141
	}()

	fn(p, fixture)
}

func TestVariableEvaluation(t *testing.T) {
	testcases := []varTest{
142
		{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
A
aarzilli 已提交
143
		{"a11", true, "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
144 145
		{"a12", true, "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "[]main.FooBar", nil},
		{"a13", true, "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "[]*main.FooBar", nil},
A
aarzilli 已提交
146 147 148
		{"a2", true, "6", "10", "int", nil},
		{"a3", true, "7.23", "3.1", "float64", nil},
		{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
149
		{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "[]int", nil},
A
aarzilli 已提交
150 151 152 153
		{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
		{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
		{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
154
		{"baz", true, "\"bazburzum\"", "", "string", nil},
A
aarzilli 已提交
155 156 157 158 159 160
		{"neg", true, "-1", "-20", "int", nil},
		{"f32", true, "1.2", "1.1", "float32", nil},
		{"c64", true, "(1 + 2i)", "(4 + 5i)", "complex64", nil},
		{"c128", true, "(2 + 3i)", "(6.3 + 7i)", "complex128", nil},
		{"a6.Baz", true, "8", "20", "int", nil},
		{"a7.Baz", true, "5", "25", "int", nil},
161
		{"a8.Baz", true, "\"feh\"", "", "string", nil},
A
aarzilli 已提交
162 163 164 165 166 167 168 169 170 171 172 173 174
		{"a9.Baz", true, "nil", "", "int", fmt.Errorf("a9 is nil")},
		{"a9.NonExistent", true, "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
		{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
		{"b1", true, "true", "false", "bool", nil},
		{"b2", true, "false", "true", "bool", nil},
		{"i8", true, "1", "2", "int8", nil},
		{"u16", true, "65535", "0", "uint16", nil},
		{"u32", true, "4294967295", "1", "uint32", nil},
		{"u64", true, "18446744073709551615", "2", "uint64", nil},
		{"u8", true, "255", "3", "uint8", nil},
		{"up", true, "5", "4", "uintptr", nil},
		{"f", true, "main.barfoo", "", "func()", nil},
175
		{"ba", true, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "[]int", nil},
A
aarzilli 已提交
176 177 178 179 180 181 182
		{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)(…", "", "main.Nest", nil},
		{"ms.Nest.Nest", true, "*main.Nest {Level: 2, Nest: *main.Nest {Level: 3, Nest: *(*main.Nest)(…", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest", true, "*main.Nest nil", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest.Nest", true, "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
		{"main.p1", true, "10", "12", "int", nil},
		{"p1", true, "10", "13", "int", nil},
		{"NonExistent", true, "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
183 184
	}

185
	protest.AllowRecording(t)
186
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
187
		err := proc.Continue(p)
188 189 190
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
191
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
192 193 194 195 196 197 198 199 200 201 202 203
			if tc.err == nil {
				assertNoError(err, t, "EvalVariable() returned an error")
				assertVariable(t, variable, tc)
			} else {
				if err == nil {
					t.Fatalf("Expected error %s, got no error: %s\n", tc.err.Error(), api.ConvertVar(variable).SinglelineString())
				}
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}

204
			if tc.alternate != "" && testBackend != "rr" {
205 206
				assertNoError(setVariable(p, tc.name, tc.alternate), t, "SetVariable()")
				variable, err = evalVariable(p, tc.name, pnormalLoadConfig)
207
				assertNoError(err, t, "EvalVariable()")
208
				assertVariable(t, variable, tc.alternateVarTest())
209 210

				assertNoError(setVariable(p, tc.name, tc.value), t, "SetVariable()")
211
				variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
212 213 214 215 216 217 218
				assertNoError(err, t, "EvalVariable()")
				assertVariable(t, variable, tc)
			}
		}
	})
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
func TestVariableEvaluationShort(t *testing.T) {
	testcases := []varTest{
		{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
		{"a11", true, "[3]main.FooBar [...]", "", "[3]main.FooBar", nil},
		{"a12", true, "[]main.FooBar len: 2, cap: 2, [...]", "", "[]main.FooBar", nil},
		{"a13", true, "[]*main.FooBar len: 3, cap: 3, [...]", "", "[]*main.FooBar", nil},
		{"a2", true, "6", "", "int", nil},
		{"a3", true, "7.23", "", "float64", nil},
		{"a4", true, "[2]int [...]", "", "[2]int", nil},
		{"a5", true, "[]int len: 5, cap: 5, [...]", "", "[]int", nil},
		{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
		{"a7", true, "(*main.FooBar)(0x…", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
		{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
		{"baz", true, "\"bazburzum\"", "", "string", nil},
		{"neg", true, "-1", "", "int", nil},
		{"f32", true, "1.2", "", "float32", nil},
		{"c64", true, "(1 + 2i)", "", "complex64", nil},
		{"c128", true, "(2 + 3i)", "", "complex128", nil},
		{"a6.Baz", true, "8", "", "int", nil},
		{"a7.Baz", true, "5", "", "int", nil},
		{"a8.Baz", true, "\"feh\"", "", "string", nil},
		{"a9.Baz", true, "nil", "", "int", fmt.Errorf("a9 is nil")},
		{"a9.NonExistent", true, "nil", "", "int", fmt.Errorf("a9 has no member NonExistent")},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
		{"i32", true, "[2]int32 [...]", "", "[2]int32", nil},
		{"b1", true, "true", "false", "bool", nil},
		{"b2", true, "false", "true", "bool", nil},
		{"i8", true, "1", "2", "int8", nil},
		{"u16", true, "65535", "0", "uint16", nil},
		{"u32", true, "4294967295", "1", "uint32", nil},
		{"u64", true, "18446744073709551615", "2", "uint64", nil},
		{"u8", true, "255", "3", "uint8", nil},
		{"up", true, "5", "4", "uintptr", nil},
		{"f", true, "main.barfoo", "", "func()", nil},
		{"ba", true, "[]int len: 200, cap: 200, [...]", "", "[]int", nil},
		{"ms", true, "main.Nest {Level: 0, Nest: (*main.Nest)(0x…", "", "main.Nest", nil},
		{"ms.Nest.Nest", true, "(*main.Nest)(0x…", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest", true, "*main.Nest nil", "", "*main.Nest", nil},
		{"ms.Nest.Nest.Nest.Nest.Nest.Nest", true, "", "", "*main.Nest", fmt.Errorf("ms.Nest.Nest.Nest.Nest.Nest is nil")},
		{"main.p1", true, "10", "", "int", nil},
		{"p1", true, "10", "", "int", nil},
		{"NonExistent", true, "", "", "", fmt.Errorf("could not find symbol value for NonExistent")},
	}

264
	protest.AllowRecording(t)
265
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
266
		err := proc.Continue(p)
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
			variable, err := evalVariable(p, tc.name, pshortLoadConfig)
			if tc.err == nil {
				assertNoError(err, t, "EvalVariable() returned an error")
				assertVariable(t, variable, tc)
			} else {
				if err == nil {
					t.Fatalf("Expected error %s, got no error: %s\n", tc.err.Error(), api.ConvertVar(variable).SinglelineString())
				}
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
		}
	})
}

286 287
func TestMultilineVariableEvaluation(t *testing.T) {
	testcases := []varTest{
288
		{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
A
aarzilli 已提交
289
		{"a11", true, `[3]main.FooBar [
290 291 292 293
	{Baz: 1, Bur: "a"},
	{Baz: 2, Bur: "b"},
	{Baz: 3, Bur: "c"},
]`, "", "[3]main.FooBar", nil},
A
aarzilli 已提交
294
		{"a12", true, `[]main.FooBar len: 2, cap: 2, [
295 296
	{Baz: 4, Bur: "d"},
	{Baz: 5, Bur: "e"},
297
]`, "", "[]main.FooBar", nil},
A
aarzilli 已提交
298
		{"a13", true, `[]*main.FooBar len: 3, cap: 3, [
299 300 301
	*{Baz: 6, Bur: "f"},
	*{Baz: 7, Bur: "g"},
	*{Baz: 8, Bur: "h"},
302
]`, "", "[]*main.FooBar", nil},
A
aarzilli 已提交
303 304
		{"a2", true, "6", "10", "int", nil},
		{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
305
		{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "[]int", nil},
A
aarzilli 已提交
306 307 308 309 310 311
		{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
		{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
		{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
		{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil}, // reread variable after member
		{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
312
		{"ba", true, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "[]int", nil},
A
aarzilli 已提交
313
		{"ms", true, `main.Nest {
314 315 316 317 318 319
	Level: 0,
	Nest: *main.Nest {
		Level: 1,
		Nest: *(*main.Nest)(…`, "", "main.Nest", nil},
	}

320
	protest.AllowRecording(t)
321
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
322
		err := proc.Continue(p)
323 324 325
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
326
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
			assertNoError(err, t, "EvalVariable() returned an error")
			if ms := api.ConvertVar(variable).MultilineString(""); !matchStringOrPrefix(ms, tc.value) {
				t.Fatalf("Expected %s got %s (variable %s)\n", tc.value, ms, variable.Name)
			}
		}
	})
}

type varArray []*proc.Variable

// Len is part of sort.Interface.
func (s varArray) Len() int {
	return len(s)
}

// Swap is part of sort.Interface.
func (s varArray) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s varArray) Less(i, j int) bool {
	return s[i].Name < s[j].Name
}

func TestLocalVariables(t *testing.T) {
	testcases := []struct {
354
		fn     func(*proc.EvalScope, proc.LoadConfig) ([]*proc.Variable, error)
355 356 357 358
		output []varTest
	}{
		{(*proc.EvalScope).LocalVariables,
			[]varTest{
359 360
				{"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},
				{"a10", true, "\"ofo\"", "", "string", nil},
A
aarzilli 已提交
361
				{"a11", true, "[3]main.FooBar [{Baz: 1, Bur: \"a\"},{Baz: 2, Bur: \"b\"},{Baz: 3, Bur: \"c\"}]", "", "[3]main.FooBar", nil},
362 363
				{"a12", true, "[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: \"d\"},{Baz: 5, Bur: \"e\"}]", "", "[]main.FooBar", nil},
				{"a13", true, "[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: \"f\"},*{Baz: 7, Bur: \"g\"},*{Baz: 8, Bur: \"h\"}]", "", "[]*main.FooBar", nil},
A
aarzilli 已提交
364 365 366
				{"a2", true, "6", "", "int", nil},
				{"a3", true, "7.23", "", "float64", nil},
				{"a4", true, "[2]int [1,2]", "", "[2]int", nil},
367
				{"a5", true, "[]int len: 5, cap: 5, [1,2,3,4,5]", "", "[]int", nil},
A
aarzilli 已提交
368 369 370 371 372 373
				{"a6", true, "main.FooBar {Baz: 8, Bur: \"word\"}", "", "main.FooBar", nil},
				{"a7", true, "*main.FooBar {Baz: 5, Bur: \"strum\"}", "", "*main.FooBar", nil},
				{"a8", true, "main.FooBar2 {Bur: 10, Baz: \"feh\"}", "", "main.FooBar2", nil},
				{"a9", true, "*main.FooBar nil", "", "*main.FooBar", nil},
				{"b1", true, "true", "", "bool", nil},
				{"b2", true, "false", "", "bool", nil},
374
				{"ba", true, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", "", "[]int", nil},
A
aarzilli 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387
				{"c128", true, "(2 + 3i)", "", "complex128", nil},
				{"c64", true, "(1 + 2i)", "", "complex64", nil},
				{"f", true, "main.barfoo", "", "func()", nil},
				{"f32", true, "1.2", "", "float32", nil},
				{"i32", true, "[2]int32 [1,2]", "", "[2]int32", nil},
				{"i8", true, "1", "", "int8", nil},
				{"ms", true, "main.Nest {Level: 0, Nest: *main.Nest {Level: 1, Nest: *(*main.Nest)…", "", "main.Nest", nil},
				{"neg", true, "-1", "", "int", nil},
				{"u16", true, "65535", "", "uint16", nil},
				{"u32", true, "4294967295", "", "uint32", nil},
				{"u64", true, "18446744073709551615", "", "uint64", nil},
				{"u8", true, "255", "", "uint8", nil},
				{"up", true, "5", "", "uintptr", nil}}},
388 389
		{(*proc.EvalScope).FunctionArguments,
			[]varTest{
A
aarzilli 已提交
390
				{"bar", true, "main.FooBar {Baz: 10, Bur: \"lorem\"}", "", "main.FooBar", nil},
391
				{"baz", true, "\"bazburzum\"", "", "string", nil}}},
392 393
	}

394
	protest.AllowRecording(t)
395
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
396
		err := proc.Continue(p)
397 398 399
		assertNoError(err, t, "Continue() returned an error")

		for _, tc := range testcases {
400 401 402 403 404 405 406
			var scope *proc.EvalScope
			var err error

			if testBackend == "rr" {
				var frame proc.Stackframe
				frame, err = findFirstNonRuntimeFrame(p)
				if err == nil {
407
					scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame)
408 409 410 411 412 413
				}
			} else {
				scope, err = proc.GoroutineScope(p.CurrentThread())
			}

			assertNoError(err, t, "scope")
414
			vars, err := tc.fn(scope, pnormalLoadConfig)
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
			assertNoError(err, t, "LocalVariables() returned an error")

			sort.Sort(varArray(vars))

			if len(tc.output) != len(vars) {
				t.Fatalf("Invalid variable count. Expected %d got %d.", len(tc.output), len(vars))
			}

			for i, variable := range vars {
				assertVariable(t, variable, tc.output[i])
			}
		}
	})
}

func TestEmbeddedStruct(t *testing.T) {
431
	protest.AllowRecording(t)
432
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
433
		testcases := []varTest{
434 435 436 437 438 439
			{"b.val", true, "-314", "-314", "int", nil},
			{"b.A.val", true, "-314", "-314", "int", nil},
			{"b.a.val", true, "42", "42", "int", nil},
			{"b.ptr.val", true, "1337", "1337", "int", nil},
			{"b.C.s", true, "\"hello\"", "\"hello\"", "string", nil},
			{"b.s", true, "\"hello\"", "\"hello\"", "string", nil},
440
			{"b2", true, "main.B {A: main.A {val: 42}, C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {A: (*main.A)(0x…", "main.B", nil},
441
		}
442
		assertNoError(proc.Continue(p), t, "Continue()")
443

444 445
		ver, _ := goversion.Parse(runtime.Version())
		if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
446 447 448 449 450 451 452 453 454
			// on go < 1.9 embedded fields had different names
			for i := range testcases {
				if testcases[i].name == "b2" {
					testcases[i].value = "main.B {main.A: main.A {val: 42}, *main.C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}"
					testcases[i].alternate = "main.B {main.A: (*main.A)(0x…"
				}
			}
		}

455
		for _, tc := range testcases {
456
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
457
			if tc.err == nil {
458
				assertNoError(err, t, fmt.Sprintf("EvalVariable(%s) returned an error", tc.name))
459
				assertVariable(t, variable, tc)
460 461 462
				variable, err = evalVariable(p, tc.name, pshortLoadConfig)
				assertNoError(err, t, fmt.Sprintf("EvalVariable(%s, pshortLoadConfig) returned an error", tc.name))
				assertVariable(t, variable, tc.alternateVarTest())
463 464 465 466 467 468 469 470 471 472
			} else {
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
		}
	})
}

func TestComplexSetting(t *testing.T) {
473
	withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
474
		err := proc.Continue(p)
475 476 477 478
		assertNoError(err, t, "Continue() returned an error")

		h := func(setExpr, value string) {
			assertNoError(setVariable(p, "c128", setExpr), t, "SetVariable()")
479
			variable, err := evalVariable(p, "c128", pnormalLoadConfig)
480 481 482 483 484 485 486 487 488
			assertNoError(err, t, "EvalVariable()")
			if s := api.ConvertVar(variable).SinglelineString(); s != value {
				t.Fatalf("Wrong value of c128: \"%s\", expected \"%s\" after setting it to \"%s\"", s, value, setExpr)
			}
		}

		h("3.2i", "(0 + 3.2i)")
		h("1.1", "(1.1 + 0i)")
		h("1 + 3.3i", "(1 + 3.3i)")
489
		h("complex(1.2, 3.4)", "(1.2 + 3.4i)")
490 491
	})
}
A
aarzilli 已提交
492 493 494 495

func TestEvalExpression(t *testing.T) {
	testcases := []varTest{
		// slice/array/string subscript
496 497 498 499 500
		{"s1[0]", false, "\"one\"", "\"one\"", "string", nil},
		{"s1[1]", false, "\"two\"", "\"two\"", "string", nil},
		{"s1[2]", false, "\"three\"", "\"three\"", "string", nil},
		{"s1[3]", false, "\"four\"", "\"four\"", "string", nil},
		{"s1[4]", false, "\"five\"", "\"five\"", "string", nil},
501
		{"s1[5]", false, "", "", "string", fmt.Errorf("index out of bounds")},
502 503 504 505 506
		{"a1[0]", false, "\"one\"", "\"one\"", "string", nil},
		{"a1[1]", false, "\"two\"", "\"two\"", "string", nil},
		{"a1[2]", false, "\"three\"", "\"three\"", "string", nil},
		{"a1[3]", false, "\"four\"", "\"four\"", "string", nil},
		{"a1[4]", false, "\"five\"", "\"five\"", "string", nil},
507
		{"a1[5]", false, "", "", "string", fmt.Errorf("index out of bounds")},
508 509 510 511
		{"str1[0]", false, "48", "48", "byte", nil},
		{"str1[1]", false, "49", "49", "byte", nil},
		{"str1[2]", false, "50", "50", "byte", nil},
		{"str1[10]", false, "48", "48", "byte", nil},
A
aarzilli 已提交
512 513 514
		{"str1[11]", false, "", "", "byte", fmt.Errorf("index out of bounds")},

		// slice/array/string reslicing
515 516 517 518 519 520
		{"a1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "[]string len: 2, cap: 2, [...]", "[]string", nil},
		{"s1[2:4]", false, "[]string len: 2, cap: 2, [\"three\",\"four\"]", "[]string len: 2, cap: 2, [...]", "[]string", nil},
		{"str1[2:4]", false, "\"23\"", "\"23\"", "string", nil},
		{"str1[0:11]", false, "\"01234567890\"", "\"01234567890\"", "string", nil},
		{"str1[:3]", false, "\"012\"", "\"012\"", "string", nil},
		{"str1[3:]", false, "\"34567890\"", "\"34567890\"", "string", nil},
521 522
		{"str1[0:12]", false, "", "", "string", fmt.Errorf("index out of bounds")},
		{"str1[5:3]", false, "", "", "string", fmt.Errorf("index out of bounds")},
A
aarzilli 已提交
523

524 525 526 527 528
		// NaN and Inf floats
		{"pinf", false, "+Inf", "+Inf", "float64", nil},
		{"ninf", false, "-Inf", "-Inf", "float64", nil},
		{"nan", false, "NaN", "NaN", "float64", nil},

A
aarzilli 已提交
529
		// pointers
530 531 532
		{"*p2", false, "5", "5", "int", nil},
		{"p2", true, "*5", "(*int)(0x…", "*int", nil},
		{"p3", true, "*int nil", "*int nil", "*int", nil},
A
aarzilli 已提交
533 534
		{"*p3", false, "", "", "int", fmt.Errorf("nil pointer dereference")},

A
aarzilli 已提交
535
		// channels
A
aarzilli 已提交
536
		{"ch1", true, "chan int 4/10", "chan int 4/10", "chan int", nil},
537
		{"chnil", true, "chan int nil", "chan int nil", "chan int", nil},
A
aarzilli 已提交
538 539
		{"ch1+1", false, "", "", "", fmt.Errorf("can not convert 1 constant to chan int")},

A
aarzilli 已提交
540
		// maps
A
Alessandro Arzilli 已提交
541
		{"m1[\"Malone\"]", false, "main.astruct {A: 2, B: 3}", "main.astruct {A: 2, B: 3}", "main.astruct", nil},
542 543 544 545
		{"m2[1].B", false, "11", "11", "int", nil},
		{"m2[c1.sa[2].B-4].A", false, "10", "10", "int", nil},
		{"m2[*p1].B", false, "11", "11", "int", nil},
		{"m3[as1]", false, "42", "42", "int", nil},
A
aarzilli 已提交
546
		{"mnil[\"Malone\"]", false, "", "", "", fmt.Errorf("key not found")},
547
		{"m1[80:]", false, "", "", "", fmt.Errorf("map index out of bounds")},
A
aarzilli 已提交
548

549
		// interfaces
A
Alessandro Arzilli 已提交
550 551
		{"err1", true, "error(*main.astruct) *{A: 1, B: 2}", "error(*main.astruct) 0x…", "error", nil},
		{"err2", true, "error(*main.bstruct) *{a: main.astruct {A: 1, B: 2}}", "error(*main.bstruct) 0x…", "error", nil},
552
		{"errnil", true, "error nil", "error nil", "error", nil},
A
Alessandro Arzilli 已提交
553
		{"iface1", true, "interface {}(*main.astruct) *{A: 1, B: 2}", "interface {}(*main.astruct) 0x…", "interface {}", nil},
554 555
		{"iface1.A", false, "1", "1", "int", nil},
		{"iface1.B", false, "2", "2", "int", nil},
556
		{"iface2", true, "interface {}(string) \"test\"", "interface {}(string) \"test\"", "interface {}", nil},
557
		{"iface3", true, "interface {}(map[string]go/constant.Value) []", "interface {}(map[string]go/constant.Value) []", "interface {}", nil},
558
		{"iface4", true, "interface {}([]go/constant.Value) [4]", "interface {}([]go/constant.Value) [...]", "interface {}", nil},
559 560
		{"ifacenil", true, "interface {} nil", "interface {} nil", "interface {}", nil},
		{"err1 == err2", false, "false", "false", "", nil},
561
		{"err1 == iface1", false, "", "", "", fmt.Errorf("mismatched types \"error\" and \"interface {}\"")},
562 563 564
		{"errnil == nil", false, "true", "true", "", nil},
		{"errtypednil == nil", false, "false", "false", "", nil},
		{"nil == errnil", false, "true", "true", "", nil},
A
Alessandro Arzilli 已提交
565 566
		{"err1.(*main.astruct)", false, "*main.astruct {A: 1, B: 2}", "(*main.astruct)(0x…", "*main.astruct", nil},
		{"err1.(*main.bstruct)", false, "", "", "", fmt.Errorf("interface conversion: error is *main.astruct, not *main.bstruct")},
567
		{"errnil.(*main.astruct)", false, "", "", "", fmt.Errorf("interface conversion: error is nil, not *main.astruct")},
568
		{"const1", true, "go/constant.Value(go/constant.int64Val) 3", "go/constant.Value(go/constant.int64Val) 3", "go/constant.Value", nil},
569

A
aarzilli 已提交
570
		// combined expressions
571 572 573 574 575
		{"c1.pb.a.A", true, "1", "1", "int", nil},
		{"c1.sa[1].B", false, "3", "3", "int", nil},
		{"s2[5].B", false, "12", "12", "int", nil},
		{"s2[c1.sa[2].B].A", false, "11", "11", "int", nil},
		{"s2[*p2].B", false, "12", "12", "int", nil},
A
aarzilli 已提交
576 577

		// constants
578 579 580 581 582
		{"1.1", false, "1.1", "1.1", "", nil},
		{"10", false, "10", "10", "", nil},
		{"1 + 2i", false, "(1 + 2i)", "(1 + 2i)", "", nil},
		{"true", false, "true", "true", "", nil},
		{"\"test\"", false, "\"test\"", "\"test\"", "", nil},
A
aarzilli 已提交
583 584

		// binary operators
585 586 587 588 589 590 591
		{"i2 + i3", false, "5", "5", "int", nil},
		{"i2 - i3", false, "-1", "-1", "int", nil},
		{"i3 - i2", false, "1", "1", "int", nil},
		{"i2 * i3", false, "6", "6", "int", nil},
		{"i2/i3", false, "0", "0", "int", nil},
		{"f1/2.0", false, "1.5", "1.5", "float64", nil},
		{"i2 << 2", false, "8", "8", "int", nil},
A
aarzilli 已提交
592 593

		// unary operators
594 595 596
		{"-i2", false, "-2", "-2", "int", nil},
		{"+i2", false, "2", "2", "int", nil},
		{"^i2", false, "-3", "-3", "int", nil},
A
aarzilli 已提交
597 598

		// comparison operators
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
		{"i2 == i3", false, "false", "false", "", nil},
		{"i2 == 2", false, "true", "true", "", nil},
		{"i2 == 2", false, "true", "true", "", nil},
		{"i2 == 3", false, "false", "false", "", nil},
		{"i2 != i3", false, "true", "true", "", nil},
		{"i2 < i3", false, "true", "true", "", nil},
		{"i2 <= i3", false, "true", "true", "", nil},
		{"i2 > i3", false, "false", "false", "", nil},
		{"i2 >= i3", false, "false", "false", "", nil},
		{"i2 >= 2", false, "true", "true", "", nil},
		{"str1 == \"01234567890\"", false, "true", "true", "", nil},
		{"str1 < \"01234567890\"", false, "false", "false", "", nil},
		{"str1 < \"11234567890\"", false, "true", "true", "", nil},
		{"str1 > \"00234567890\"", false, "true", "true", "", nil},
		{"str1 == str1", false, "true", "true", "", nil},
		{"c1.pb.a == *(c1.sa[0])", false, "true", "true", "", nil},
		{"c1.pb.a != *(c1.sa[0])", false, "false", "false", "", nil},
		{"c1.pb.a == *(c1.sa[1])", false, "false", "false", "", nil},
		{"c1.pb.a != *(c1.sa[1])", false, "true", "true", "", nil},
618
		{`longstr == "not this"`, false, "false", "false", "", nil},
A
aarzilli 已提交
619

620
		// builtins
621 622
		{"cap(parr)", false, "4", "4", "", nil},
		{"len(parr)", false, "4", "4", "", nil},
623 624
		{"cap(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for cap")},
		{"len(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for len")},
625 626 627 628 629 630
		{"cap(a1)", false, "5", "5", "", nil},
		{"len(a1)", false, "5", "5", "", nil},
		{"cap(s3)", false, "6", "6", "", nil},
		{"len(s3)", false, "0", "0", "", nil},
		{"cap(nilslice)", false, "0", "0", "", nil},
		{"len(nilslice)", false, "0", "0", "", nil},
A
aarzilli 已提交
631 632
		{"cap(ch1)", false, "10", "10", "", nil},
		{"len(ch1)", false, "4", "4", "", nil},
633 634 635 636 637 638 639 640
		{"cap(chnil)", false, "0", "0", "", nil},
		{"len(chnil)", false, "0", "0", "", nil},
		{"len(m1)", false, "41", "41", "", nil},
		{"len(mnil)", false, "0", "0", "", nil},
		{"imag(cpx1)", false, "2", "2", "", nil},
		{"real(cpx1)", false, "1", "1", "", nil},
		{"imag(3i)", false, "3", "3", "", nil},
		{"real(4)", false, "4", "4", "", nil},
641

A
aarzilli 已提交
642
		// nil
643
		{"nil", false, "nil", "nil", "", nil},
A
aarzilli 已提交
644
		{"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")},
645 646
		{"fn1", false, "main.afunc", "main.afunc", "main.functype", nil},
		{"fn2", false, "nil", "nil", "main.functype", nil},
647
		{"nilslice", false, "[]int len: 0, cap: 0, nil", "[]int len: 0, cap: 0, nil", "[]int", nil},
A
aarzilli 已提交
648
		{"fn1 == fn2", false, "", "", "", fmt.Errorf("can not compare func variables")},
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
		{"fn1 == nil", false, "false", "false", "", nil},
		{"fn1 != nil", false, "true", "true", "", nil},
		{"fn2 == nil", false, "true", "true", "", nil},
		{"fn2 != nil", false, "false", "false", "", nil},
		{"c1.sa == nil", false, "false", "false", "", nil},
		{"c1.sa != nil", false, "true", "true", "", nil},
		{"c1.sa[0] == nil", false, "false", "false", "", nil},
		{"c1.sa[0] != nil", false, "true", "true", "", nil},
		{"nilslice == nil", false, "true", "true", "", nil},
		{"nil == nilslice", false, "true", "true", "", nil},
		{"nilslice != nil", false, "false", "false", "", nil},
		{"nilptr == nil", false, "true", "true", "", nil},
		{"nilptr != nil", false, "false", "false", "", nil},
		{"p1 == nil", false, "false", "false", "", nil},
		{"p1 != nil", false, "true", "true", "", nil},
		{"ch1 == nil", false, "false", "false", "", nil},
		{"chnil == nil", false, "true", "true", "", nil},
A
aarzilli 已提交
666
		{"ch1 == chnil", false, "", "", "", fmt.Errorf("can not compare chan variables")},
667
		{"m1 == nil", false, "false", "false", "", nil},
A
aarzilli 已提交
668
		{"mnil == m1", false, "", "", "", fmt.Errorf("can not compare map variables")},
669
		{"mnil == nil", false, "true", "true", "", nil},
670 671
		{"nil == 2", false, "", "", "", fmt.Errorf("can not compare int to nil")},
		{"2 == nil", false, "", "", "", fmt.Errorf("can not compare int to nil")},
A
aarzilli 已提交
672 673 674

		// errors
		{"&3", false, "", "", "", fmt.Errorf("can not take address of \"3\"")},
675
		{"*3", false, "", "", "", fmt.Errorf("expression \"3\" (int) can not be dereferenced")},
A
aarzilli 已提交
676 677 678 679 680 681 682 683
		{"&(i2 + i3)", false, "", "", "", fmt.Errorf("can not take address of \"(i2 + i3)\"")},
		{"i2 + p1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"*int\"")},
		{"i2 + f1", false, "", "", "", fmt.Errorf("mismatched types \"int\" and \"float64\"")},
		{"i2 << f1", false, "", "", "", fmt.Errorf("shift count type float64, must be unsigned integer")},
		{"i2 << -1", false, "", "", "", fmt.Errorf("shift count type int, must be unsigned integer")},
		{"i2 << i3", false, "", "", "int", fmt.Errorf("shift count type int, must be unsigned integer")},
		{"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")},
		{"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")},
684
		{"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("no type entry found, use 'types' for a list of valid types")},
685 686 687 688 689
		{"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")},
		{"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")},
		{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
		{"nil[0]", false, "", "", "", fmt.Errorf("expression \"nil\" (nil) does not support indexing")},
		{"nil[2:10]", false, "", "", "", fmt.Errorf("can not slice \"nil\" (type nil)")},
690
		{"nil.member", false, "", "", "", fmt.Errorf("nil (type nil) is not a struct")},
691
		{"(map[string]main.astruct)(0x4000)", false, "", "", "", fmt.Errorf("can not convert \"0x4000\" to map[string]main.astruct")},
692 693

		// typecasts
694 695 696 697 698 699 700 701
		{"uint(i2)", false, "2", "2", "uint", nil},
		{"int8(i2)", false, "2", "2", "int8", nil},
		{"int(f1)", false, "3", "3", "int", nil},
		{"complex128(f1)", false, "(3 + 0i)", "(3 + 0i)", "complex128", nil},
		{"uint8(i4)", false, "32", "32", "uint8", nil},
		{"uint8(i5)", false, "253", "253", "uint8", nil},
		{"int8(i5)", false, "-3", "-3", "int8", nil},
		{"int8(i6)", false, "12", "12", "int8", nil},
702 703

		// misc
704 705 706 707
		{"i1", true, "1", "1", "int", nil},
		{"mainMenu", true, `main.Menu len: 3, cap: 3, [{Name: "home", Route: "/", Active: 1},{Name: "About", Route: "/about", Active: 1},{Name: "Login", Route: "/login", Active: 1}]`, `main.Menu len: 3, cap: 3, [...]`, "main.Menu", nil},
		{"mainMenu[0]", false, `main.Item {Name: "home", Route: "/", Active: 1}`, `main.Item {Name: "home", Route: "/", Active: 1}`, "main.Item", nil},
		{"sd", false, "main.D {u1: 0, u2: 0, u3: 0, u4: 0, u5: 0, u6: 0}", "main.D {u1: 0, u2: 0, u3: 0,...+3 more}", "main.D", nil},
708 709

		{"ifacearr", false, "[]error len: 2, cap: 2, [*main.astruct {A: 0, B: 0},nil]", "[]error len: 2, cap: 2, [...]", "[]error", nil},
710
		{"efacearr", false, `[]interface {} len: 3, cap: 3, [*main.astruct {A: 0, B: 0},"test",nil]`, "[]interface {} len: 3, cap: 3, [...]", "[]interface {}", nil},
711 712 713

		{"zsslice", false, `[]struct {} len: 3, cap: 3, [{},{},{}]`, `[]struct {} len: 3, cap: 3, [...]`, "[]struct {}", nil},
		{"zsvmap", false, `map[string]struct {} ["testkey": {}, ]`, `map[string]struct {} [...]`, "map[string]struct {}", nil},
714
		{"tm", false, "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [...]}", "main.truncatedMap", nil},
715 716 717 718

		{"emptyslice", false, `[]string len: 0, cap: 0, []`, `[]string len: 0, cap: 0, []`, "[]string", nil},
		{"emptymap", false, `map[string]string []`, `map[string]string []`, "map[string]string", nil},
		{"mnil", false, `map[string]main.astruct nil`, `map[string]main.astruct nil`, "map[string]main.astruct", nil},
A
aarzilli 已提交
719 720 721 722 723 724 725 726 727 728 729 730 731

		// conversions between string/[]byte/[]rune (issue #548)
		{"runeslice", true, `[]int32 len: 4, cap: 4, [116,232,115,116]`, `[]int32 len: 4, cap: 4, [...]`, "[]int32", nil},
		{"byteslice", true, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 5, cap: 5, [...]`, "[]uint8", nil},
		{"[]byte(str1)", false, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]uint8", nil},
		{"[]uint8(str1)", false, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]uint8 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]uint8", nil},
		{"[]rune(str1)", false, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]int32", nil},
		{"[]int32(str1)", false, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, `[]int32 len: 11, cap: 11, [48,49,50,51,52,53,54,55,56,57,48]`, "[]int32", nil},
		{"string(byteslice)", false, `"tèst"`, `""`, "string", nil},
		{"[]int32(string(byteslice))", false, `[]int32 len: 4, cap: 4, [116,232,115,116]`, `[]int32 len: 0, cap: 0, nil`, "[]int32", nil},
		{"string(runeslice)", false, `"tèst"`, `""`, "string", nil},
		{"[]byte(string(runeslice))", false, `[]uint8 len: 5, cap: 5, [116,195,168,115,116]`, `[]uint8 len: 0, cap: 0, nil`, "[]uint8", nil},
		{"*(*[5]byte)(uintptr(&byteslice[0]))", false, `[5]uint8 [116,195,168,115,116]`, `[5]uint8 [...]`, "[5]uint8", nil},
A
aarzilli 已提交
732 733 734 735 736 737

		// access to channel field members
		{"ch1.qcount", false, "4", "4", "uint", nil},
		{"ch1.dataqsiz", false, "10", "10", "uint", nil},
		{"ch1.buf", false, `*[10]int [1,4,3,2,0,0,0,0,0,0]`, `(*[10]int)(…`, "*[10]int", nil},
		{"ch1.buf[0]", false, "1", "1", "int", nil},
A
aarzilli 已提交
738 739
	}

740 741
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) {
742 743 744 745 746 747 748 749
		for i := range testcases {
			if testcases[i].name == "iface3" {
				testcases[i].value = "interface {}(*map[string]go/constant.Value) *[]"
				testcases[i].alternate = "interface {}(*map[string]go/constant.Value) 0x…"
			}
		}
	}

750
	protest.AllowRecording(t)
751
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
752
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
A
aarzilli 已提交
753
		for _, tc := range testcases {
754
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
A
aarzilli 已提交
755 756 757
			if tc.err == nil {
				assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
				assertVariable(t, variable, tc)
758 759 760
				variable, err := evalVariable(p, tc.name, pshortLoadConfig)
				assertNoError(err, t, fmt.Sprintf("EvalExpression(%s, pshortLoadConfig) returned an error", tc.name))
				assertVariable(t, variable, tc.alternateVarTest())
A
aarzilli 已提交
761 762
			} else {
				if err == nil {
763
					t.Fatalf("Expected error %s, got no error (%s)", tc.err.Error(), tc.name)
A
aarzilli 已提交
764 765 766 767 768
				}
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
769

A
aarzilli 已提交
770 771 772 773 774
		}
	})
}

func TestEvalAddrAndCast(t *testing.T) {
775
	protest.AllowRecording(t)
776
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
777
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
778
		c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig)
A
aarzilli 已提交
779 780 781 782 783 784 785
		assertNoError(err, t, "EvalExpression(&c1)")
		c1addrstr := api.ConvertVar(c1addr).SinglelineString()
		t.Logf("&c1 → %s", c1addrstr)
		if !strings.HasPrefix(c1addrstr, "(*main.cstruct)(0x") {
			t.Fatalf("Invalid value of EvalExpression(&c1) \"%s\"", c1addrstr)
		}

786
		aaddr, err := evalVariable(p, "&(c1.pb.a)", pnormalLoadConfig)
A
aarzilli 已提交
787 788 789 790 791 792 793
		assertNoError(err, t, "EvalExpression(&(c1.pb.a))")
		aaddrstr := api.ConvertVar(aaddr).SinglelineString()
		t.Logf("&(c1.pb.a) → %s", aaddrstr)
		if !strings.HasPrefix(aaddrstr, "(*main.astruct)(0x") {
			t.Fatalf("invalid value of EvalExpression(&(c1.pb.a)) \"%s\"", aaddrstr)
		}

794
		a, err := evalVariable(p, "*"+aaddrstr, pnormalLoadConfig)
A
aarzilli 已提交
795 796
		assertNoError(err, t, fmt.Sprintf("EvalExpression(*%s)", aaddrstr))
		t.Logf("*%s → %s", aaddrstr, api.ConvertVar(a).SinglelineString())
A
Alessandro Arzilli 已提交
797
		assertVariable(t, a, varTest{aaddrstr, false, "main.astruct {A: 1, B: 2}", "", "main.astruct", nil})
A
aarzilli 已提交
798 799
	})
}
A
aarzilli 已提交
800 801

func TestMapEvaluation(t *testing.T) {
802
	protest.AllowRecording(t)
803
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
804
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
805
		m1v, err := evalVariable(p, "m1", pnormalLoadConfig)
A
aarzilli 已提交
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
		assertNoError(err, t, "EvalVariable()")
		m1 := api.ConvertVar(m1v)
		t.Logf("m1 = %v", m1.MultilineString(""))

		if m1.Type != "map[string]main.astruct" {
			t.Fatalf("Wrong type: %s", m1.Type)
		}

		if len(m1.Children)/2 != 41 {
			t.Fatalf("Wrong number of children: %d", len(m1.Children)/2)
		}

		found := false
		for i := range m1.Children {
			if i%2 == 0 && m1.Children[i].Value == "Malone" {
				found = true
			}
		}
		if !found {
			t.Fatalf("Could not find Malone")
		}

828
		m1sliced, err := evalVariable(p, "m1[10:]", pnormalLoadConfig)
A
aarzilli 已提交
829 830 831 832 833
		assertNoError(err, t, "EvalVariable(m1[10:])")
		if len(m1sliced.Children)/2 != int(m1.Len-10) {
			t.Fatalf("Wrong number of children (after slicing): %d", len(m1sliced.Children)/2)
		}
	})
A
aarzilli 已提交
834
}
A
aarzilli 已提交
835

A
aarzilli 已提交
836
func TestUnsafePointer(t *testing.T) {
837
	protest.AllowRecording(t)
838
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
839
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
840
		up1v, err := evalVariable(p, "up1", pnormalLoadConfig)
A
aarzilli 已提交
841 842 843 844 845 846
		assertNoError(err, t, "EvalVariable(up1)")
		up1 := api.ConvertVar(up1v)
		if ss := up1.SinglelineString(); !strings.HasPrefix(ss, "unsafe.Pointer(") {
			t.Fatalf("wrong value for up1: %s", ss)
		}
	})
A
aarzilli 已提交
847
}
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864

type issue426TestCase struct {
	name string
	typ  string
}

func TestIssue426(t *testing.T) {
	// type casts using quoted type names
	testcases := []issue426TestCase{
		{"iface1", `interface {}`},
		{"mapanonstruct1", `map[string]struct {}`},
		{"anonstruct1", `struct { val go/constant.Value }`},
		{"anonfunc", `func(struct { i int }, interface {}, struct { val go/constant.Value })`},
		{"anonstruct2", `struct { i int; j int }`},
		{"anoniface1", `interface { OtherFunction(int, int); SomeFunction(struct { val go/constant.Value }) }`},
	}

865 866
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 8, -1, 0, 0, ""}) {
867 868 869 870 871 872
		testcases[2].typ = `struct { main.val go/constant.Value }`
		testcases[3].typ = `func(struct { main.i int }, interface {}, struct { main.val go/constant.Value })`
		testcases[4].typ = `struct { main.i int; main.j int }`
		testcases[5].typ = `interface { OtherFunction(int, int); SomeFunction(struct { main.val go/constant.Value }) }`
	}

873 874
	// Serialization of type expressions (go/ast.Expr) containing anonymous structs or interfaces
	// differs from the serialization used by the linker to produce DWARF type information
875
	protest.AllowRecording(t)
876
	withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
877
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
878 879 880 881 882 883 884 885 886 887
		for _, testcase := range testcases {
			v, err := evalVariable(p, testcase.name, pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", testcase.name))
			t.Logf("%s → %s", testcase.name, v.RealType.String())
			expr := fmt.Sprintf("(*%q)(%d)", testcase.typ, v.Addr)
			_, err = evalVariable(p, expr, pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", expr))
		}
	})
}
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909

func TestPackageRenames(t *testing.T) {
	// Tests that the concrete type of an interface variable is resolved
	// correctly in a few edge cases, in particular:
	// - in the presence of renamed imports
	// - when two packages with the same name are imported
	// - when a package has a canonical name that's different from its
	// path (for example the last element of the path contains a '.' or a
	// '-' or because the package name is different)
	// all of those edge cases are tested within composite types
	testcases := []varTest{
		// Renamed imports
		{"badexpr", true, `interface {}(*go/ast.BadExpr) *{From: 1, To: 2}`, "", "interface {}", nil},
		{"req", true, `interface {}(*net/http.Request) *{Method: "amethod", …`, "", "interface {}", nil},
		{"amap", true, "interface {}(map[go/ast.BadExpr]net/http.Request) [{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},

		// Package name that doesn't match import path
		{"iface3", true, `interface {}(*github.com/derekparker/delve/_fixtures/vendor/dir0/renamedpackage.SomeType) *{A: true}`, "", "interface {}", nil},

		// Interfaces to anonymous types
		{"amap2", true, "interface {}(*map[go/ast.BadExpr]net/http.Request) *[{From: 2, To: 3}: *{Method: \"othermethod\", …", "", "interface {}", nil},
		{"dir0someType", true, "interface {}(*github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType) *{X: 3}", "", "interface {}", nil},
910
		{"dir1someType", true, "interface {}(github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType) {X: 1, Y: 2}", "", "interface {}", nil},
911
		{"amap3", true, "interface {}(map[github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType]github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType) [{X: 4}: {X: 5, Y: 6}, ]", "", "interface {}", nil},
912
		{"anarray", true, `interface {}([2]github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType) [{X: 1},{X: 2}]`, "", "interface {}", nil},
913
		{"achan", true, `interface {}(chan github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType) chan github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType 0/0`, "", "interface {}", nil},
914
		{"aslice", true, `interface {}([]github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType) [{X: 3},{X: 4}]`, "", "interface {}", nil},
915 916
		{"afunc", true, `interface {}(func(github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType, github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType)) main.main.func1`, "", "interface {}", nil},
		{"astruct", true, `interface {}(*struct { A github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType; B github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType }) *{A: github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType {X: 1, Y: 2}, B: github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType {X: 3}}`, "", "interface {}", nil},
917
		{"astruct2", true, `interface {}(*struct { github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType; X int }) *{SomeType: github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType {X: 1, Y: 2}, X: 10}`, "", "interface {}", nil},
918 919 920
		{"iface2iface", true, `interface {}(*interface { AMethod(int) int; AnotherMethod(int) int }) **github.com/derekparker/delve/_fixtures/vendor/dir0/pkg.SomeType {X: 4}`, "", "interface {}", nil},
	}

921 922
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 7, -1, 0, 0, ""}) {
923 924 925 926
		// Not supported on 1.6 or earlier
		return
	}

927
	protest.AllowRecording(t)
928
	withTestProcess("pkgrenames", t, func(p proc.Process, fixture protest.Fixture) {
929
		assertNoError(proc.Continue(p), t, "Continue() returned an error")
930
		for _, tc := range testcases {
931
			if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
932 933 934 935 936
				// before 1.9 embedded struct field have fieldname == type
				if tc.name == "astruct2" {
					tc.value = `interface {}(*struct { github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType; X int }) *{github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType: github.com/derekparker/delve/_fixtures/vendor/dir1/pkg.SomeType {X: 1, Y: 2}, X: 10}`
				}
			}
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
			variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
			if tc.err == nil {
				assertNoError(err, t, fmt.Sprintf("EvalExpression(%s) returned an error", tc.name))
				assertVariable(t, variable, tc)
			} else {
				if err == nil {
					t.Fatalf("Expected error %s, got no error (%s)", tc.err.Error(), tc.name)
				}
				if tc.err.Error() != err.Error() {
					t.Fatalf("Unexpected error. Expected %s got %s", tc.err.Error(), err.Error())
				}
			}
		}
	})
}
952 953 954 955 956 957 958 959 960 961 962 963

func TestConstants(t *testing.T) {
	testcases := []varTest{
		{"a", true, "constTwo", "", "main.ConstType", nil},
		{"b", true, "constThree", "", "main.ConstType", nil},
		{"c", true, "bitZero|bitOne", "", "main.BitFieldType", nil},
		{"d", true, "33", "", "main.BitFieldType", nil},
		{"e", true, "10", "", "main.ConstType", nil},
		{"f", true, "0", "", "main.BitFieldType", nil},
		{"bitZero", true, "1", "", "main.BitFieldType", nil},
		{"bitOne", true, "2", "", "main.BitFieldType", nil},
		{"constTwo", true, "2", "", "main.ConstType", nil},
964
		{"pkg.SomeConst", true, "2", "", "int", nil},
965 966 967 968
	}
	ver, _ := goversion.Parse(runtime.Version())
	if ver.Major > 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) {
		// Not supported on 1.9 or earlier
969
		t.Skip("constants added in go 1.10")
970 971 972 973 974 975 976 977 978 979
	}
	withTestProcess("consts", t, func(p proc.Process, fixture protest.Fixture) {
		assertNoError(proc.Continue(p), t, "Continue")
		for _, testcase := range testcases {
			variable, err := evalVariable(p, testcase.name, pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", testcase.name))
			assertVariable(t, variable, testcase)
		}
	})
}
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004

func setFunctionBreakpoint(p proc.Process, fname string) (*proc.Breakpoint, error) {
	addr, err := proc.FindFunctionLocation(p, fname, true, 0)
	if err != nil {
		return nil, err
	}
	return p.SetBreakpoint(addr, proc.UserBreakpoint, nil)
}

func TestIssue1075(t *testing.T) {
	withTestProcess("clientdo", t, func(p proc.Process, fixture protest.Fixture) {
		_, err := setFunctionBreakpoint(p, "net/http.(*Client).Do")
		assertNoError(err, t, "setFunctionBreakpoint")
		assertNoError(proc.Continue(p), t, "Continue()")
		for i := 0; i < 10; i++ {
			scope, err := proc.GoroutineScope(p.CurrentThread())
			assertNoError(err, t, fmt.Sprintf("GoroutineScope (%d)", i))
			vars, err := scope.LocalVariables(pnormalLoadConfig)
			assertNoError(err, t, fmt.Sprintf("LocalVariables (%d)", i))
			for _, v := range vars {
				api.ConvertVar(v).SinglelineString()
			}
		}
	})
}