92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package regression
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// Result holds the regression comparison between two commits.
|
|
type Result struct {
|
|
BaseSHA string `json:"base_sha"`
|
|
HeadSHA string `json:"head_sha"`
|
|
RepoName string `json:"repo_name"`
|
|
New []string `json:"new"` // Fingerprints in head but not base.
|
|
Fixed []string `json:"fixed"` // Fingerprints in base but not head.
|
|
Recurring []string `json:"recurring"` // Fingerprints in both.
|
|
IsRegression bool `json:"is_regression"`
|
|
}
|
|
|
|
// Compare computes the set difference of crash fingerprints between a base and head commit.
|
|
func Compare(ctx context.Context, pool *pgxpool.Pool, repoID, baseSHA, headSHA string) (*Result, error) {
|
|
baseFingerprints, err := fingerprintsForCommit(ctx, pool, repoID, baseSHA)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("base commit fingerprints: %w", err)
|
|
}
|
|
|
|
headFingerprints, err := fingerprintsForCommit(ctx, pool, repoID, headSHA)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("head commit fingerprints: %w", err)
|
|
}
|
|
|
|
baseSet := toSet(baseFingerprints)
|
|
headSet := toSet(headFingerprints)
|
|
|
|
var newFPs, fixedFPs, recurringFPs []string
|
|
|
|
for fp := range headSet {
|
|
if baseSet[fp] {
|
|
recurringFPs = append(recurringFPs, fp)
|
|
} else {
|
|
newFPs = append(newFPs, fp)
|
|
}
|
|
}
|
|
|
|
for fp := range baseSet {
|
|
if !headSet[fp] {
|
|
fixedFPs = append(fixedFPs, fp)
|
|
}
|
|
}
|
|
|
|
return &Result{
|
|
BaseSHA: baseSHA,
|
|
HeadSHA: headSHA,
|
|
New: newFPs,
|
|
Fixed: fixedFPs,
|
|
Recurring: recurringFPs,
|
|
IsRegression: len(newFPs) > 0,
|
|
}, nil
|
|
}
|
|
|
|
func fingerprintsForCommit(ctx context.Context, pool *pgxpool.Pool, repoID, sha string) ([]string, error) {
|
|
rows, err := pool.Query(ctx, `
|
|
SELECT DISTINCT a.fingerprint
|
|
FROM artifacts a
|
|
JOIN commits c ON c.id = a.commit_id
|
|
WHERE a.repository_id = $1 AND c.sha = $2 AND a.fingerprint IS NOT NULL
|
|
`, repoID, sha)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var fps []string
|
|
for rows.Next() {
|
|
var fp string
|
|
if err := rows.Scan(&fp); err != nil {
|
|
return nil, err
|
|
}
|
|
fps = append(fps, fp)
|
|
}
|
|
return fps, nil
|
|
}
|
|
|
|
func toSet(items []string) map[string]bool {
|
|
s := make(map[string]bool, len(items))
|
|
for _, item := range items {
|
|
s[item] = true
|
|
}
|
|
return s
|
|
}
|