85 lines
2.3 KiB
Go
85 lines
2.3 KiB
Go
package regression
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// 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, db *gorm.DB, repoID uint, baseSHA, headSHA string) (*Result, error) {
|
|
baseFingerprints, err := fingerprintsForCommit(ctx, db, repoID, baseSHA)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("base commit fingerprints: %w", err)
|
|
}
|
|
|
|
headFingerprints, err := fingerprintsForCommit(ctx, db, 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, db *gorm.DB, repoID uint, sha string) ([]string, error) {
|
|
var fps []string
|
|
err := db.WithContext(ctx).
|
|
Table("artifacts").
|
|
Distinct("crash_signatures.fingerprint").
|
|
Joins("JOIN commits ON commits.id = artifacts.commit_id").
|
|
Joins("JOIN crash_signatures ON crash_signatures.id = artifacts.crash_signature_id").
|
|
Where("artifacts.repository_id = ? AND commits.sha = ?", repoID, sha).
|
|
Where("artifacts.crash_signature_id IS NOT NULL").
|
|
Pluck("crash_signatures.fingerprint", &fps).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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
|
|
}
|