Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
72a98a3
feat: coverage initial set up
officialasishkumar Jul 11, 2025
82ad3f8
feat: update comments
officialasishkumar Jul 11, 2025
97c399b
feat: update dedupData.yaml with correct values
officialasishkumar Jul 11, 2025
bb44533
refactor: remove the old vendor
officialasishkumar Jul 11, 2025
70ce9c9
feat: add vendor
officialasishkumar Jul 11, 2025
f127770
fix: update comments
officialasishkumar Jul 11, 2025
6c9c869
feat: add clear counters after every test case run
officialasishkumar Jul 11, 2025
2b0838a
feat: update comments
officialasishkumar Jul 13, 2025
7535b0d
refactor: better comments
officialasishkumar Jul 13, 2025
0f4aa64
feat: update go mod file
officialasishkumar Jul 14, 2025
bf077d9
feat: skip reporting in case of mismatched test id
officialasishkumar Jul 14, 2025
507a7ae
fix: bump go version
officialasishkumar Jul 14, 2025
0e5164e
fix: bump go version
officialasishkumar Jul 14, 2025
e336519
fix: bump go version
officialasishkumar Jul 14, 2025
02871c3
fix: add install command in workflow
officialasishkumar Jul 14, 2025
1361064
fix: bump go version
officialasishkumar Jul 14, 2025
016e97a
fix: bump go version in workflow
officialasishkumar Jul 14, 2025
b1f7d07
feat: add an ack in end command
officialasishkumar Jul 28, 2025
fb5e49c
fix: block.count continue
officialasishkumar Jul 28, 2025
87a23a7
feat: bump sdk version
officialasishkumar Jul 28, 2025
b4c5457
refactor: better file name for sock
officialasishkumar Jul 28, 2025
bc82e36
fix: early return and new coverage folder name
officialasishkumar Jul 29, 2025
3243cb1
feat: change socket name
officialasishkumar Jul 29, 2025
99c1c6a
fix: dir creation error
officialasishkumar Jul 30, 2025
67dc6bf
feat: add more instruction for coverage
officialasishkumar Jul 31, 2025
7673905
feat: add golang ci lint
officialasishkumar Jul 31, 2025
333499d
feat: add golang ci lint
officialasishkumar Jul 31, 2025
108b37c
feat: add golang ci lint
officialasishkumar Jul 31, 2025
6c6c3f0
feat: add golang ci lint
officialasishkumar Jul 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/build-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.16
go-version: 1.24

- name: Install dependencies
run: go mod tidy

- name: Build
run: go build -v ./...
Expand Down
11 changes: 2 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
module github.com/keploy/go-sdk/v2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the module v3


go 1.16
go 1.24

//replace go.keploy.io/server => ../keploy

require go.uber.org/zap v1.22.0

require (
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.7.1 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
)
require golang.org/x/tools v0.1.5
60 changes: 1 addition & 59 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,60 +1,2 @@
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
267 changes: 253 additions & 14 deletions keploy/keploy.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,261 @@
// To activate, simply import this package for its side effects:
//
// import _ "github.com/keploy/go-sdk/v2/keploy"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add /v3 here as well

//
// Then, build your application with atomic coverage instrumentation:
//
// go build -cover -covermode=atomic -o your-app . (cover flag is required https://pkg.go.dev/runtime/coverage@go1.25rc2#WriteCounters)
package keploy

import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"net"
"os"
"os/signal"
"syscall"
"os/exec"
"path/filepath"
"runtime/coverage"
"sort"
"strings"
"sync"

"golang.org/x/tools/cover"
)

const (
// controlSocketPath is used by Keploy to send commands (START/END) to the app.
controlSocketPath = "/tmp/coverage_control.sock"
// dataSocketPath is used by the app to send coverage data back to Keploy.
dataSocketPath = "/tmp/keploy-coverage.sock"

Copilot AI Jul 14, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using fixed Unix socket paths under /tmp can lead to symlink or permission vulnerabilities; consider using a secure temporary directory or making the path configurable with proper permissions.

Suggested change
const (
// controlSocketPath is used by Keploy to send commands (START/END) to the app.
controlSocketPath = "/tmp/coverage_control.sock"
// dataSocketPath is used by the app to send coverage data back to Keploy.
dataSocketPath = "/tmp/keploy-coverage.sock"
var (
// controlSocketPath is used by Keploy to send commands (START/END) to the app.
controlSocketPath string
// dataSocketPath is used by the app to send coverage data back to Keploy.
dataSocketPath string
// tempDir is the temporary directory for socket files.
tempDir string

Copilot uses AI. Check for mistakes.
)

// GracefulShutdown is used to signal the user application to exit when SIGTERM is triggered
// from keploy test cmd. This function call can be used when the go application have not employed
// a graceful shutdown mechanism.
func GracefulShutdown() {
stopper := make(chan os.Signal, 1)
// listens for interrupt and SIGTERM signal
signal.Notify(stopper, os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGTERM)
go func() {
select {
case <-stopper:
os.Exit(0)
var (
// controlMu protects access to the currentTestID, ensuring command handling is atomic.
controlMu sync.Mutex
// currentTestID stores the ID of the test case currently being recorded.
currentTestID string
)

// init starts the background control server that listens for commands from the Keploy test runner.
func init() {
go startControlServer()
}

// startControlServer sets up and runs the Unix socket server that listens for commands from Keploy.
func startControlServer() {
err := os.RemoveAll(controlSocketPath)
if err != nil {
log.Printf("[Agent] Failed to remove old control socket: %v", err)
return
}

ln, err := net.Listen("unix", controlSocketPath)
if err != nil {
log.Printf("[Agent] 🚨 FATAL: Could not start control server: %v", err)
return
}
defer ln.Close()

for {
conn, err := ln.Accept()
if err != nil {
if strings.Contains(err.Error(), "use of closed network connection") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were we really getting this error here? "use of closed network connection", Because here you have just accepted the connection.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no i did not get this error. this error can only happen when it gets closed manually.

break
}
log.Printf("[Agent] Error accepting connection: %v", err)
continue
}
go handleControlRequest(conn)
}
}

// handleControlRequest parses commands from Keploy ("START testID", "END testID")
func handleControlRequest(conn net.Conn) {
defer conn.Close()

command, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Printf("[Agent] Error reading command: %v", err)
return
}

// Split the command into action and testID
parts := strings.SplitN(strings.TrimSpace(command), " ", 2)
if len(parts) != 2 {
log.Printf("[Agent] Invalid command format: '%s'", command)
return
}
action, id := parts[0], parts[1]

controlMu.Lock()
defer controlMu.Unlock()

switch action {
case "START":
currentTestID = id
err := coverage.ClearCounters()
if err != nil {
log.Printf("[Agent] Error clearing coverage counters: %v", err)
}
case "END":
if currentTestID != id {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use early return here. And then there is no need of else block.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@officialasishkumar please address this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

log.Printf("[Agent] Warning: Mismatched END command. Expected '%s', got '%s'. Skipping coverage report to avoid inconsistent state.", currentTestID, id)
} else {
err := reportCoverage(id)
if err != nil {
log.Printf("[Agent] 🚨 Error reporting coverage for test %s: %v", id, err)
}
}
// Reset the currentTestID to an empty string to indicate that no test is currently being recorded.
currentTestID = ""

_, err = conn.Write([]byte("ACK\n"))
if err != nil {
log.Printf("[Agent] Error sending ACK to controller: %v", err)
}
}()
default:
log.Printf("[Agent] Unrecognized command: %s", action)
}
}

// reportCoverage dumps, processes, and sends the coverage data.
func reportCoverage(testID string) error {
// Create a temporary directory to store the coverage data.
tempDir, err := os.MkdirTemp("", "keploy-coverage-")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can name the temp folder as keploy-coverage-<testID>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@officialasishkumar please address this comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if err != nil {
return fmt.Errorf("failed to create temp dir: %w", err)
}
defer os.RemoveAll(tempDir)

err = coverage.WriteCountersDir(tempDir)
if err != nil {
return fmt.Errorf("failed to write coverage counters. Ensure the application was built with '-cover -covermode=atomic'. Original error: %w", err)
}

err = coverage.WriteMetaDir(tempDir)
if err != nil {
return fmt.Errorf("failed to write meta dir: %w", err)
}

processedData, err := processCoverageProfilesUsingCovdata(tempDir)
if err != nil {
return fmt.Errorf("failed to process coverage profiles: %w", err)
}

if len(processedData) == 0 {
log.Printf("[Agent-Warning] No covered lines were found for test %s. The report will be empty.", testID)
}

payload := map[string]interface{}{
"id": testID,
"executedLinesByFile": processedData,
}

jsonData, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal coverage data to JSON: %w", err)
}

return sendToSocket(jsonData)
}

// sendToSocket connects to the Keploy data socket and writes the JSON payload.
func sendToSocket(data []byte) error {
conn, err := net.Dial("unix", dataSocketPath)
if err != nil {
return fmt.Errorf("could not connect to keploy data socket at %s: %w", dataSocketPath, err)
}
defer conn.Close()

_, err = conn.Write(data)
return err
}

// processCoverageProfilesUsingCovdata uses the covdata tool to convert binary coverage data to text format
// and then processes it using the standard cover package.
func processCoverageProfilesUsingCovdata(dir string) (map[string][]int, error) {
// Create a temporary file for the text format output
textFile, err := os.CreateTemp("", "coverage-*.txt")
if err != nil {
return nil, fmt.Errorf("failed to create temp file for text coverage: %w", err)
}
defer os.Remove(textFile.Name())
defer textFile.Close()

// Use covdata to convert binary format to text format
cmd := exec.Command("go", "tool", "covdata", "textfmt", "-i="+dir, "-o="+textFile.Name())
var stderr bytes.Buffer
cmd.Stderr = &stderr

err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("failed to convert coverage data to text format: %w\nStderr: %s", err, stderr.String())
}

// Get the module path (e.g., "your/module/path") to resolve file paths correctly.
modulePathCmd := exec.Command("go", "list", "-m")
var stderrModPath bytes.Buffer
modulePathCmd.Stderr = &stderrModPath
modulePathBytes, err := modulePathCmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to get module path with 'go list -m': %w\nStderr: %s", err, stderrModPath.String())
}
modulePath := strings.TrimSpace(string(modulePathBytes))

// Get the module's root directory on the filesystem.
moduleDirCmd := exec.Command("go", "list", "-m", "-f", "{{.Dir}}")
var stderrModDir bytes.Buffer
moduleDirCmd.Stderr = &stderrModDir
moduleDirBytes, err := moduleDirCmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to get module directory with 'go list -m -f {{.Dir}}': %w\nStderr: %s", err, stderrModDir.String())
}
moduleDir := strings.TrimSpace(string(moduleDirBytes))

// Parse the text format using the standard cover package.
profiles, err := cover.ParseProfiles(textFile.Name())
if err != nil {
return nil, fmt.Errorf("failed to parse text coverage profile: %w", err)
}

executedLinesByFile := make(map[string][]int)

for _, profile := range profiles {
var absolutePath string
if strings.HasPrefix(profile.FileName, modulePath) {
relativePath := strings.TrimPrefix(profile.FileName, modulePath)
absolutePath = filepath.Join(moduleDir, relativePath)
} else if !filepath.IsAbs(profile.FileName) {
continue
} else {
absolutePath = profile.FileName
}

lineSet := make(map[int]bool)

// For each block in the profile, if the count is greater than 0, add the lines to the map.
for _, block := range profile.Blocks {
if block.Count <= 0 {
continue
}
for line := block.StartLine; line <= block.EndLine; line++ {
lineSet[line] = true
}
}

// If there are any lines executed, add them to the map.
if len(lineSet) > 0 {
lines := make([]int, 0, len(lineSet))
for line := range lineSet {
lines = append(lines, line)
}
sort.Ints(lines)
executedLinesByFile[absolutePath] = lines
}
}

return executedLinesByFile, nil
}
Loading