blob: 54ef383929645ecd085922dbd4c741f548e79e5e [file] [log] [blame]
// Copyright 2016 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package breakpoints
import (
"testing"
"cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug"
"cloud.google.com/go/internal/testutil"
cd "google.golang.org/api/clouddebugger/v2"
)
var (
testPC1 uint64 = 0x1234
testPC2 uint64 = 0x5678
testPC3 uint64 = 0x3333
testFile = "foo.go"
testLine uint64 = 42
testLine2 uint64 = 99
testLogPC uint64 = 0x9abc
testLogLine uint64 = 43
testBadPC uint64 = 0xdef0
testBadLine uint64 = 44
testBP = &cd.Breakpoint{
Action: "CAPTURE",
Id: "TestBreakpoint",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testLine)},
}
testBP2 = &cd.Breakpoint{
Action: "CAPTURE",
Id: "TestBreakpoint2",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testLine2)},
}
testLogBP = &cd.Breakpoint{
Action: "LOG",
Id: "TestLogBreakpoint",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testLogLine)},
}
testBadBP = &cd.Breakpoint{
Action: "BEEP",
Id: "TestBadBreakpoint",
IsFinalState: false,
Location: &cd.SourceLocation{Path: testFile, Line: int64(testBadLine)},
}
)
func TestBreakpointStore(t *testing.T) {
p := &Program{breakpointPCs: make(map[uint64]bool)}
bs := NewBreakpointStore(p)
checkPCs := func(expected map[uint64]bool) {
if !testutil.Equal(p.breakpointPCs, expected) {
t.Errorf("got breakpoint map %v want %v", p.breakpointPCs, expected)
}
}
bs.ProcessBreakpointList([]*cd.Breakpoint{testBP, testBP2, testLogBP, testBadBP})
checkPCs(map[uint64]bool{
testPC1: true,
testPC2: true,
testPC3: true,
testLogPC: true,
})
for _, test := range []struct {
pc uint64
expected []*cd.Breakpoint
}{
{testPC1, []*cd.Breakpoint{testBP}},
{testPC2, []*cd.Breakpoint{testBP}},
{testPC3, []*cd.Breakpoint{testBP2}},
{testLogPC, []*cd.Breakpoint{testLogBP}},
} {
if bps := bs.BreakpointsAtPC(test.pc); !testutil.Equal(bps, test.expected) {
t.Errorf("BreakpointsAtPC(%x): got %v want %v", test.pc, bps, test.expected)
}
}
testBP2.IsFinalState = true
bs.ProcessBreakpointList([]*cd.Breakpoint{testBP, testBP2, testLogBP, testBadBP})
checkPCs(map[uint64]bool{
testPC1: true,
testPC2: true,
testPC3: false,
testLogPC: true,
})
bs.RemoveBreakpoint(testBP)
checkPCs(map[uint64]bool{
testPC1: false,
testPC2: false,
testPC3: false,
testLogPC: true,
})
for _, pc := range []uint64{testPC1, testPC2, testPC3} {
if bps := bs.BreakpointsAtPC(pc); len(bps) != 0 {
t.Errorf("BreakpointsAtPC(%x): got %v want []", pc, bps)
}
}
// bs.ErrorBreakpoints should return testBadBP.
errorBps := bs.ErrorBreakpoints()
if len(errorBps) != 1 {
t.Errorf("ErrorBreakpoints: got %d want 1", len(errorBps))
} else {
bp := errorBps[0]
if bp.Id != testBadBP.Id {
t.Errorf("ErrorBreakpoints: got id %q want 1", bp.Id)
}
if bp.Status == nil || !bp.Status.IsError {
t.Errorf("ErrorBreakpoints: got %v, want error", bp.Status)
}
}
// The error should have been removed by the last call to bs.ErrorBreakpoints.
errorBps = bs.ErrorBreakpoints()
if len(errorBps) != 0 {
t.Errorf("ErrorBreakpoints: got %d want 0", len(errorBps))
}
// Even if testBadBP is sent in a new list, it should not be returned again.
bs.ProcessBreakpointList([]*cd.Breakpoint{testBadBP})
errorBps = bs.ErrorBreakpoints()
if len(errorBps) != 0 {
t.Errorf("ErrorBreakpoints: got %d want 0", len(errorBps))
}
}
// Program implements the similarly-named interface in x/debug.
// ValueCollector should only call its BreakpointAtLine and DeleteBreakpoints methods.
type Program struct {
debug.Program
// breakpointPCs contains the state of code breakpoints -- true if the
// breakpoint is currently set, false if it has been deleted.
breakpointPCs map[uint64]bool
}
func (p *Program) BreakpointAtLine(file string, line uint64) ([]uint64, error) {
var pcs []uint64
switch {
case file == testFile && line == testLine:
pcs = []uint64{testPC1, testPC2}
case file == testFile && line == testLine2:
pcs = []uint64{testPC3}
case file == testFile && line == testLogLine:
pcs = []uint64{testLogPC}
default:
pcs = []uint64{0xbad}
}
for _, pc := range pcs {
p.breakpointPCs[pc] = true
}
return pcs, nil
}
func (p *Program) DeleteBreakpoints(pcs []uint64) error {
for _, pc := range pcs {
p.breakpointPCs[pc] = false
}
return nil
}