mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
fix(ci): the heap corruption's two upgradable causes, and a floor set on the wrong platform
The windows-latest crash is the GC finding a bad pointer while marking: runtime.scanObject in mgcmark_greenteagc.go, under gcDrain. Green Tea is the detector, not the cause — turning it off would move where the corruption surfaces, not stop it, so this does not do that. Two things in that binary can produce it and were both behind. The Go toolchain moves 1.26.6 -> 1.26.8, which carries compiler and runtime fixes; and modernc.org/sqlite moves 1.57.0 -> 1.58.0, taking libc from 1.74.4 to 1.75.6. libc is the only unsafe-heavy code linked into that test binary that is not the standard library, and internal/bot reaches it through historycatalog and projectiondb. Neither upgrade is proven to be the fix: one crash cannot attribute itself, and golang/go#81238 — windows/amd64 exception recovery writing below a goroutine stack — is open, unfixed, and the same class. If it recurs on these versions, that issue is where it goes. covergate was red on a third path this had not read: internal/sandbox is 78.0% on linux against a 78.1% floor, while the same tree measures 78.1% on darwin. The package compiles a different set of files per GOOS, so a floor recorded on one platform can be unreachable on the one the gate runs on. Rather than lower it, three statements nothing reached are now covered — podman's per-user socket, an authority the table does not govern, and a nil context carrying no escape approver — which takes darwin to 78.9% and lifts linux above its floor by the same edges. covergate now names the lines it is short of. A percentage alone left whoever has to restore it measuring a package by hand on a platform they may not be on, which is how this one sat red across three runs.
This commit is contained in:
1 parent
85cba5b991
commit
6e7f189e76
8 files changed
+139
-47
No files matched your search
+4
-4
@@ -2,7 +2,7 @@ module reasonix/desktop
|
||||
|
||||
go 1.26.0
|
||||
|
||||
toolchain go1.26.6
|
||||
toolchain go1.26.8
|
||||
|
||||
// The desktop shell is a nested module so its CGO/WebKit build never touches the
|
||||
// CLI's CGO_ENABLED=0 single-static-binary guarantee. The replace lets it import
|
||||
@@ -77,10 +77,10 @@ require (
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.74.4 // indirect
|
||||
modernc.org/libc v1.75.6 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.57.0 // indirect
|
||||
modernc.org/memory v1.12.1 // indirect
|
||||
modernc.org/sqlite v1.58.0 // indirect
|
||||
mvdan.cc/sh/v3 v3.14.0 // indirect
|
||||
)
|
||||
|
||||
|
||||
+12
-12
@@ -190,30 +190,30 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
|
||||
modernc.org/cc/v4 v4.29.1 h1:MKgdCV3WykTSPqpVrnxdEDS0HEd2FHpKZDzxzU5LyeI=
|
||||
modernc.org/cc/v4 v4.29.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
|
||||
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
|
||||
modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
|
||||
modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w=
|
||||
modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
|
||||
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
|
||||
modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.74.4 h1:fX1Omw4o2/1C2iRkkIsrQTasJQldLhRmuPreXLoWs9k=
|
||||
modernc.org/libc v1.74.4/go.mod h1:eeQAS9W3sZeKYMFubydxJpII9ybHWshk+7or7bLG9co=
|
||||
modernc.org/libc v1.75.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
|
||||
modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
|
||||
modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
|
||||
modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
|
||||
modernc.org/sqlite v1.58.0 h1:38u40/bwkfM7f0Myhosl+SEMltSDxnGdQf8o6Kjmys0=
|
||||
modernc.org/sqlite v1.58.0/go.mod h1:rsD2CckafgObKC4DhBlGBf+RiHxkc3hINGt1Xw32tVY=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -2,7 +2,7 @@ module reasonix
|
||||
|
||||
go 1.26.0
|
||||
|
||||
toolchain go1.26.6
|
||||
toolchain go1.26.8
|
||||
|
||||
require (
|
||||
aead.dev/minisign v0.3.0
|
||||
@@ -43,7 +43,7 @@ require (
|
||||
golang.org/x/term v0.45.0
|
||||
golang.org/x/text v0.41.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.57.0
|
||||
modernc.org/sqlite v1.58.0
|
||||
mvdan.cc/sh/v3 v3.14.0
|
||||
)
|
||||
|
||||
@@ -70,7 +70,7 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
modernc.org/libc v1.74.4 // indirect
|
||||
modernc.org/libc v1.75.6 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/memory v1.12.1 // indirect
|
||||
)
|
||||
@@ -212,30 +212,30 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
|
||||
modernc.org/cc/v4 v4.29.1 h1:MKgdCV3WykTSPqpVrnxdEDS0HEd2FHpKZDzxzU5LyeI=
|
||||
modernc.org/cc/v4 v4.29.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU=
|
||||
modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk=
|
||||
modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
|
||||
modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w=
|
||||
modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI=
|
||||
modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
|
||||
modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.74.4 h1:fX1Omw4o2/1C2iRkkIsrQTasJQldLhRmuPreXLoWs9k=
|
||||
modernc.org/libc v1.74.4/go.mod h1:eeQAS9W3sZeKYMFubydxJpII9ybHWshk+7or7bLG9co=
|
||||
modernc.org/libc v1.75.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
|
||||
modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
|
||||
modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
|
||||
modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
|
||||
modernc.org/sqlite v1.58.0 h1:38u40/bwkfM7f0Myhosl+SEMltSDxnGdQf8o6Kjmys0=
|
||||
modernc.org/sqlite v1.58.0/go.mod h1:rsD2CckafgObKC4DhBlGBf+RiHxkc3hINGt1Xw32tVY=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -162,3 +163,23 @@ func TestAuthorityEndpointsFollowTheClientsResolution(t *testing.T) {
|
||||
t.Errorf("a missing endpoint was kept (%v); masking it would fail the sandbox closed", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthorityEndpointsCoverEveryGovernedClient pins the two answers the
|
||||
// resolution has beyond the defaults: podman's per-user socket, which lives
|
||||
// wherever XDG_RUNTIME_DIR points, and an authority this table does not govern,
|
||||
// which resolves to nothing rather than to a guess.
|
||||
func TestAuthorityEndpointsCoverEveryGovernedClient(t *testing.T) {
|
||||
t.Setenv("CONTAINER_HOST", "")
|
||||
t.Setenv("XDG_RUNTIME_DIR", "/run/user/4242")
|
||||
got := authorityEndpoints(Podman)
|
||||
want := filepath.Join("/run/user/4242", "podman", "podman.sock")
|
||||
if !slices.Contains(got, want) {
|
||||
t.Fatalf("podman endpoints = %v, want the per-user socket %s", got, want)
|
||||
}
|
||||
|
||||
// An authority nothing governs has no endpoints. Returning a default here
|
||||
// would mask a path on the strength of a name this table never resolved.
|
||||
if got := authorityEndpoints(HostAuthority("nothing-governs-this")); got != nil {
|
||||
t.Fatalf("ungoverned authority resolved to %v, want nothing", got)
|
||||
}
|
||||
}
|
||||
@@ -358,3 +358,13 @@ func TestInstalledButUnusableBwrapIsUnavailable(t *testing.T) {
|
||||
t.Fatalf("Command with unusable bwrap = %v, wrapped=%v; want unwrapped shell for caller fail-closed", argv, wrapped)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEscapeApproverFromRefusesANilContext covers the side that fails closed.
|
||||
// A nil context carries no approver, and answering "found one" there would let
|
||||
// a caller that never set one act as though somebody had approved.
|
||||
func TestEscapeApproverFromRefusesANilContext(t *testing.T) {
|
||||
//nolint:staticcheck // the nil context is the case under test.
|
||||
if approver, ok := EscapeApproverFrom(nil); ok || approver != nil {
|
||||
t.Fatalf("EscapeApproverFrom(nil) = %v, %v; want no approver", approver, ok)
|
||||
}
|
||||
}
|
||||
+45
-13
@@ -38,7 +38,7 @@ func main() {
|
||||
if len(globs) == 0 {
|
||||
fail(fmt.Errorf("no `sensitive:` paths declared in the project's host checks"))
|
||||
}
|
||||
measured, err := measure(*root, globs)
|
||||
measured, gaps, err := measure(*root, globs)
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
@@ -70,6 +70,9 @@ func main() {
|
||||
if len(below) > 0 {
|
||||
for _, b := range below {
|
||||
fmt.Fprintln(os.Stderr, "covergate: "+b)
|
||||
for _, g := range topGaps(gaps[strings.SplitN(b, ":", 2)[0]], 8) {
|
||||
fmt.Fprintf(os.Stderr, " %3d statements uncovered at %s\n", g.stmts, g.where)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "\nThese paths decide what the agent may execute and reach. Restore the\ncoverage, or run `go run ./tools/covergate -update -allow-drop` and\njustify the diff in the pull request.")
|
||||
os.Exit(1)
|
||||
@@ -77,6 +80,16 @@ func main() {
|
||||
fmt.Printf("covergate: clean (%d sensitive paths at or above their floor)\n", len(measured))
|
||||
}
|
||||
|
||||
// topGaps is what to write a test for, largest first. A shortfall on a platform
|
||||
// the reader is not on cannot be measured by hand there, and a percentage alone
|
||||
// says only that something moved.
|
||||
func topGaps(list []gap, n int) []gap {
|
||||
if len(list) > n {
|
||||
return list[:n]
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func fail(err error) {
|
||||
fmt.Fprintln(os.Stderr, "covergate:", err)
|
||||
os.Exit(2)
|
||||
@@ -118,7 +131,7 @@ func packagesFor(globs []string) []string {
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func measure(root string, globs []string) (map[string]float64, error) {
|
||||
func measure(root string, globs []string) (map[string]float64, map[string][]gap, error) {
|
||||
profile := filepath.Join(os.TempDir(), "covergate.out")
|
||||
defer os.Remove(profile)
|
||||
args := append([]string{"test", "-count=1", "-coverprofile=" + profile}, packagesFor(globs)...)
|
||||
@@ -126,23 +139,32 @@ func measure(root string, globs []string) (map[string]float64, error) {
|
||||
cmd.Dir = root
|
||||
cmd.Env = append(os.Environ(), "LANG=en_US.UTF-8")
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return nil, fmt.Errorf("go test: %w\n%s", err, out)
|
||||
return nil, nil, fmt.Errorf("go test: %w\n%s", err, out)
|
||||
}
|
||||
return fromProfile(profile, globs)
|
||||
}
|
||||
|
||||
// fromProfile aggregates statements per declared path. Per file, not per
|
||||
// package: a project may call one file sensitive inside a package that is not.
|
||||
func fromProfile(profile string, globs []string) (map[string]float64, error) {
|
||||
// gap is one block of statements nothing executed. A floor that has slipped is
|
||||
// a fact about which lines; reporting only the percentage leaves whoever has to
|
||||
// restore it measuring the package by hand on a platform they may not have.
|
||||
type gap struct {
|
||||
where string
|
||||
stmts int
|
||||
}
|
||||
|
||||
func fromProfile(profile string, globs []string) (map[string]float64, map[string][]gap, error) {
|
||||
f, err := os.Open(profile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
total := map[string][2]int{}
|
||||
gaps := map[string][]gap{}
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
file, stmts, covered, ok := parseProfileLine(scanner.Text())
|
||||
file, at, stmts, covered, ok := parseProfileLine(scanner.Text())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -154,12 +176,22 @@ func fromProfile(profile string, globs []string) (map[string]float64, error) {
|
||||
t[0] += stmts
|
||||
if covered > 0 {
|
||||
t[1] += stmts
|
||||
} else if stmts > 0 {
|
||||
gaps[glob] = append(gaps[glob], gap{where: file + ":" + at, stmts: stmts})
|
||||
}
|
||||
total[glob] = t
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
for glob := range gaps {
|
||||
sort.Slice(gaps[glob], func(i, j int) bool {
|
||||
if gaps[glob][i].stmts != gaps[glob][j].stmts {
|
||||
return gaps[glob][i].stmts > gaps[glob][j].stmts
|
||||
}
|
||||
return gaps[glob][i].where < gaps[glob][j].where
|
||||
})
|
||||
}
|
||||
out := map[string]float64{}
|
||||
for _, glob := range globs {
|
||||
@@ -171,26 +203,26 @@ func fromProfile(profile string, globs []string) (map[string]float64, error) {
|
||||
// floor held to more precision than it is written with fails on itself.
|
||||
out[glob] = round1(float64(t[1]) / float64(t[0]) * 100)
|
||||
}
|
||||
return out, nil
|
||||
return out, gaps, nil
|
||||
}
|
||||
|
||||
func parseProfileLine(line string) (file string, stmts, covered int, ok bool) {
|
||||
func parseProfileLine(line string) (file, at string, stmts, covered int, ok bool) {
|
||||
// reasonix/internal/pkg/file.go:12.34,15.6 3 1
|
||||
colon := strings.LastIndex(line, ":")
|
||||
if colon < 0 {
|
||||
return "", 0, 0, false
|
||||
return "", "", 0, 0, false
|
||||
}
|
||||
fields := strings.Fields(line[colon+1:])
|
||||
if len(fields) != 3 {
|
||||
return "", 0, 0, false
|
||||
return "", "", 0, 0, false
|
||||
}
|
||||
stmts, err1 := strconv.Atoi(fields[1])
|
||||
covered, err2 := strconv.Atoi(fields[2])
|
||||
if err1 != nil || err2 != nil {
|
||||
return "", 0, 0, false
|
||||
return "", "", 0, 0, false
|
||||
}
|
||||
file = strings.TrimPrefix(line[:colon], "reasonix/")
|
||||
return file, stmts, covered, true
|
||||
return file, fields[0], stmts, covered, true
|
||||
}
|
||||
|
||||
// matchesGlob handles the two shapes the host checks use: a `dir/**` subtree
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestFromProfileAggregatesPerDeclaredPath(t *testing.T) {
|
||||
if err := os.WriteFile(profile, []byte(body), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := fromProfile(profile, []string{"internal/control/approval.go", "internal/permission/**"})
|
||||
got, _, err := fromProfile(profile, []string{"internal/control/approval.go", "internal/permission/**"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func TestMeasuredValuesAreRoundedOnce(t *testing.T) {
|
||||
if err := os.WriteFile(profile, []byte("mode: set\nreasonix/internal/x/a.go:1.1,2.2 3 1\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := fromProfile(profile, []string{"internal/x/**"})
|
||||
got, _, err := fromProfile(profile, []string{"internal/x/**"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -128,3 +128,32 @@ func TestSensitiveGlobsComeFromTheProjectHostChecks(t *testing.T) {
|
||||
t.Fatalf("declared sensitive paths not read: %v", globs)
|
||||
}
|
||||
}
|
||||
|
||||
// A floor that has slipped names the lines, largest first. A percentage alone
|
||||
// leaves the reader measuring a package by hand on a platform they may not be
|
||||
// on, which is how a shortfall on one GOOS goes unfixed.
|
||||
func TestUncoveredBlocksAreReportedLargestFirst(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
profile := filepath.Join(dir, "c.out")
|
||||
body := "mode: set\n" +
|
||||
"reasonix/internal/x/a.go:1.1,2.2 3 1\n" +
|
||||
"reasonix/internal/x/a.go:4.1,5.2 2 0\n" +
|
||||
"reasonix/internal/x/b.go:7.1,9.2 5 0\n"
|
||||
if err := os.WriteFile(profile, []byte(body), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, gaps, err := fromProfile(profile, []string{"internal/x/**"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := gaps["internal/x/**"]
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("gaps = %+v, want the two blocks nothing executed", got)
|
||||
}
|
||||
if got[0].stmts != 5 || got[0].where != "internal/x/b.go:7.1,9.2" {
|
||||
t.Fatalf("first gap = %+v, want the largest one", got[0])
|
||||
}
|
||||
if n := len(topGaps(got, 1)); n != 1 {
|
||||
t.Fatalf("topGaps kept %d, want the cap honoured", n)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user