Merge 3d41bc5a00034b81cc0ddeffaafc23d02beddf14 into 74503b06728886efee45120ebb821810347ca353
7 files · +33 -15
M go.mod +1 -1
| @@ -6,7 +6,7 @@ require ( | |||
| 6 | 6 | github.com/bnema/go-wayland-bindings v0.1.0 | |
| 7 | 7 | github.com/bnema/kvconf v0.1.2 | |
| 8 | 8 | github.com/bnema/neferclient v0.3.0 | |
| 9 | - | github.com/bnema/nefergui v0.7.2 | |
| 9 | + | github.com/bnema/nefergui v0.7.3 | |
| 10 | 10 | github.com/bnema/purego-libwayland v0.8.0 | |
| 11 | 11 | github.com/bnema/wlturbo v0.6.2 | |
| 12 | 12 | github.com/bnema/zerowrap v1.4.1 | |
| @@ -6,7 +6,7 @@ require ( | |||||
| 6 | github.com/bnema/go-wayland-bindings v0.1.0 | 6 | github.com/bnema/go-wayland-bindings v0.1.0 | ||
| 7 | github.com/bnema/kvconf v0.1.2 | 7 | github.com/bnema/kvconf v0.1.2 | ||
| 8 | github.com/bnema/neferclient v0.3.0 | 8 | github.com/bnema/neferclient v0.3.0 | ||
| 9 | - | github.com/bnema/nefergui v0.7.2 | 9 | + | github.com/bnema/nefergui v0.7.3 |
| 10 | github.com/bnema/purego-libwayland v0.8.0 | 10 | github.com/bnema/purego-libwayland v0.8.0 | ||
| 11 | github.com/bnema/wlturbo v0.6.2 | 11 | github.com/bnema/wlturbo v0.6.2 | ||
| 12 | github.com/bnema/zerowrap v1.4.1 | 12 | github.com/bnema/zerowrap v1.4.1 | ||
M go.sum +2 -2
| @@ -4,8 +4,8 @@ github.com/bnema/kvconf v0.1.2 h1:1hDKL9jkQSBdR9dSKbwTONhUUBfJgAdh5vu+yFHOb70= | |||
| 4 | 4 | github.com/bnema/kvconf v0.1.2/go.mod h1:+P2X9I/sLx+Lfir45s9a7WA0GnC22WgKbZWE9LK6lcU= | |
| 5 | 5 | github.com/bnema/neferclient v0.3.0 h1:sEBphi11YpgMOPYHW1OcDtQYA8aD3WSaFILnb8fdp90= | |
| 6 | 6 | github.com/bnema/neferclient v0.3.0/go.mod h1:CzZflJIvinLP8u0rYPjdmAnZjl4Ec2cvdnEGvqFO4Yw= | |
| 7 | - | github.com/bnema/nefergui v0.7.2 h1:9gnYhVlMxpBp+cVE+9Uursc/HwPXe/Li16+m4HL/gkU= | |
| 8 | - | github.com/bnema/nefergui v0.7.2/go.mod h1:G2iRRleA9bjSohU1RySwRqqxS0BbkPziK407HkuyEBc= | |
| 7 | + | github.com/bnema/nefergui v0.7.3 h1:T6NamM17arkan3K38+YF1/flsQ/v4ROSytSNNMz+3M4= | |
| 8 | + | github.com/bnema/nefergui v0.7.3/go.mod h1:G2iRRleA9bjSohU1RySwRqqxS0BbkPziK407HkuyEBc= | |
| 9 | 9 | github.com/bnema/purego v0.13.0-bnema.1 h1:fKRdztnvTmRqAjANMoN+utEIH73dvPqVPJh2iZCCAO8= | |
| 10 | 10 | github.com/bnema/purego v0.13.0-bnema.1/go.mod h1:xS1iX3noypxnbOUEUAqOvkfnUzgHuu19VtL4+dfUMJM= | |
| 11 | 11 | github.com/bnema/purego-libwayland v0.8.0 h1:e82G1nMre4HvnR1Q8TvdnVNITUStSc+hD3eMwkYjxQY= | |
| @@ -4,8 +4,8 @@ github.com/bnema/kvconf v0.1.2 h1:1hDKL9jkQSBdR9dSKbwTONhUUBfJgAdh5vu+yFHOb70= | |||||
| 4 | github.com/bnema/kvconf v0.1.2/go.mod h1:+P2X9I/sLx+Lfir45s9a7WA0GnC22WgKbZWE9LK6lcU= | 4 | github.com/bnema/kvconf v0.1.2/go.mod h1:+P2X9I/sLx+Lfir45s9a7WA0GnC22WgKbZWE9LK6lcU= | ||
| 5 | github.com/bnema/neferclient v0.3.0 h1:sEBphi11YpgMOPYHW1OcDtQYA8aD3WSaFILnb8fdp90= | 5 | github.com/bnema/neferclient v0.3.0 h1:sEBphi11YpgMOPYHW1OcDtQYA8aD3WSaFILnb8fdp90= | ||
| 6 | github.com/bnema/neferclient v0.3.0/go.mod h1:CzZflJIvinLP8u0rYPjdmAnZjl4Ec2cvdnEGvqFO4Yw= | 6 | github.com/bnema/neferclient v0.3.0/go.mod h1:CzZflJIvinLP8u0rYPjdmAnZjl4Ec2cvdnEGvqFO4Yw= | ||
| 7 | - | github.com/bnema/nefergui v0.7.2 h1:9gnYhVlMxpBp+cVE+9Uursc/HwPXe/Li16+m4HL/gkU= | 7 | + | github.com/bnema/nefergui v0.7.3 h1:T6NamM17arkan3K38+YF1/flsQ/v4ROSytSNNMz+3M4= |
| 8 | - | github.com/bnema/nefergui v0.7.2/go.mod h1:G2iRRleA9bjSohU1RySwRqqxS0BbkPziK407HkuyEBc= | 8 | + | github.com/bnema/nefergui v0.7.3/go.mod h1:G2iRRleA9bjSohU1RySwRqqxS0BbkPziK407HkuyEBc= |
| 9 | github.com/bnema/purego v0.13.0-bnema.1 h1:fKRdztnvTmRqAjANMoN+utEIH73dvPqVPJh2iZCCAO8= | 9 | github.com/bnema/purego v0.13.0-bnema.1 h1:fKRdztnvTmRqAjANMoN+utEIH73dvPqVPJh2iZCCAO8= | ||
| 10 | github.com/bnema/purego v0.13.0-bnema.1/go.mod h1:xS1iX3noypxnbOUEUAqOvkfnUzgHuu19VtL4+dfUMJM= | 10 | github.com/bnema/purego v0.13.0-bnema.1/go.mod h1:xS1iX3noypxnbOUEUAqOvkfnUzgHuu19VtL4+dfUMJM= | ||
| 11 | github.com/bnema/purego-libwayland v0.8.0 h1:e82G1nMre4HvnR1Q8TvdnVNITUStSc+hD3eMwkYjxQY= | 11 | github.com/bnema/purego-libwayland v0.8.0 h1:e82G1nMre4HvnR1Q8TvdnVNITUStSc+hD3eMwkYjxQY= | ||
M internal/adapters/selection/backdrop_test.go +11 -2
| @@ -31,13 +31,22 @@ func TestWithBackdrops(t *testing.T) { | |||
| 31 | 31 | ||
| 32 | 32 | huge := ports.Frame{Width: maxBackdrop + 1, Height: 1, Stride: (maxBackdrop + 1) * 4, Format: ports.XRGB8888} | |
| 33 | 33 | _, err = New(ports.Screenshot, ports.VideoSettings{}, nil, false).WithBackdrops(map[uint32]ports.Frame{9: huge}) | |
| 34 | - | assert.ErrorContains(t, err, "at most 4096", "the caller freezes only outputs CanBackdrop accepts") | |
| 34 | + | assert.ErrorContains(t, err, "at most 8192", "the caller freezes only outputs CanBackdrop accepts") | |
| 35 | + | ||
| 36 | + | huge = ports.Frame{Width: 7680, Height: 4320, Stride: 7680 * 4, Format: ports.XRGB8888} | |
| 37 | + | _, err = New(ports.Screenshot, ports.VideoSettings{}, nil, false).WithBackdrops(map[uint32]ports.Frame{9: huge}) | |
| 38 | + | assert.ErrorContains(t, err, "67108864 bytes") | |
| 35 | 39 | } | |
| 36 | 40 | ||
| 37 | 41 | func TestCanBackdrop(t *testing.T) { | |
| 38 | 42 | assert.True(t, CanBackdrop(ports.Output{Width: 3840, Height: 2160})) | |
| 39 | 43 | assert.True(t, CanBackdrop(ports.Output{Width: 2160, Height: 4096})) | |
| 40 | - | assert.False(t, CanBackdrop(ports.Output{Width: 5120, Height: 2880})) | |
| 44 | + | assert.True(t, CanBackdrop(ports.Output{Width: 5120, Height: 2160})) | |
| 45 | + | assert.True(t, CanBackdrop(ports.Output{Width: 8192, Height: 2048}), "exact byte budget") | |
| 46 | + | assert.False(t, CanBackdrop(ports.Output{Width: 8192, Height: 2049})) | |
| 47 | + | assert.False(t, CanBackdrop(ports.Output{Width: 7680, Height: 4320})) | |
| 48 | + | assert.False(t, CanBackdrop(ports.Output{Width: 8193, Height: 1})) | |
| 49 | + | assert.False(t, CanBackdrop(ports.Output{Width: 1, Height: 8193})) | |
| 41 | 50 | assert.False(t, CanBackdrop(ports.Output{}), "an unknown mode stays live") | |
| 42 | 51 | } | |
| 43 | 52 | ||
| @@ -31,13 +31,22 @@ func TestWithBackdrops(t *testing.T) { | |||||
| 31 | 31 | ||||
| 32 | huge := ports.Frame{Width: maxBackdrop + 1, Height: 1, Stride: (maxBackdrop + 1) * 4, Format: ports.XRGB8888} | 32 | huge := ports.Frame{Width: maxBackdrop + 1, Height: 1, Stride: (maxBackdrop + 1) * 4, Format: ports.XRGB8888} | ||
| 33 | _, err = New(ports.Screenshot, ports.VideoSettings{}, nil, false).WithBackdrops(map[uint32]ports.Frame{9: huge}) | 33 | _, err = New(ports.Screenshot, ports.VideoSettings{}, nil, false).WithBackdrops(map[uint32]ports.Frame{9: huge}) | ||
| 34 | - | assert.ErrorContains(t, err, "at most 4096", "the caller freezes only outputs CanBackdrop accepts") | 34 | + | assert.ErrorContains(t, err, "at most 8192", "the caller freezes only outputs CanBackdrop accepts") |
| 35 | + | ||||
| 36 | + | huge = ports.Frame{Width: 7680, Height: 4320, Stride: 7680 * 4, Format: ports.XRGB8888} | |||
| 37 | + | _, err = New(ports.Screenshot, ports.VideoSettings{}, nil, false).WithBackdrops(map[uint32]ports.Frame{9: huge}) | |||
| 38 | + | assert.ErrorContains(t, err, "67108864 bytes") | |||
| 35 | } | 39 | } | ||
| 36 | 40 | ||||
| 37 | func TestCanBackdrop(t *testing.T) { | 41 | func TestCanBackdrop(t *testing.T) { | ||
| 38 | assert.True(t, CanBackdrop(ports.Output{Width: 3840, Height: 2160})) | 42 | assert.True(t, CanBackdrop(ports.Output{Width: 3840, Height: 2160})) | ||
| 39 | assert.True(t, CanBackdrop(ports.Output{Width: 2160, Height: 4096})) | 43 | assert.True(t, CanBackdrop(ports.Output{Width: 2160, Height: 4096})) | ||
| 40 | - | assert.False(t, CanBackdrop(ports.Output{Width: 5120, Height: 2880})) | 44 | + | assert.True(t, CanBackdrop(ports.Output{Width: 5120, Height: 2160})) |
| 45 | + | assert.True(t, CanBackdrop(ports.Output{Width: 8192, Height: 2048}), "exact byte budget") | |||
| 46 | + | assert.False(t, CanBackdrop(ports.Output{Width: 8192, Height: 2049})) | |||
| 47 | + | assert.False(t, CanBackdrop(ports.Output{Width: 7680, Height: 4320})) | |||
| 48 | + | assert.False(t, CanBackdrop(ports.Output{Width: 8193, Height: 1})) | |||
| 49 | + | assert.False(t, CanBackdrop(ports.Output{Width: 1, Height: 8193})) | |||
| 41 | assert.False(t, CanBackdrop(ports.Output{}), "an unknown mode stays live") | 50 | assert.False(t, CanBackdrop(ports.Output{}), "an unknown mode stays live") | ||
| 42 | } | 51 | } | ||
| 43 | 52 | ||||
M internal/adapters/selection/selector.go +11 -7
| @@ -87,13 +87,17 @@ func (s *Selector) WithCapabilities(c ports.Capabilities) *Selector { | |||
| 87 | 87 | return s | |
| 88 | 88 | } | |
| 89 | 89 | ||
| 90 | - | // maxBackdrop is the largest side NeferGUI accepts for one image. | |
| 91 | - | const maxBackdrop = 4096 | |
| 90 | + | // These bounds mirror NeferGUI's image dimension and cache byte limits. | |
| 91 | + | const ( | |
| 92 | + | maxBackdrop = 8192 | |
| 93 | + | maxBackdropBytes = 64 << 20 | |
| 94 | + | ) | |
| 92 | 95 | ||
| 93 | - | // CanBackdrop reports whether an output of this physical mode can show a | |
| 94 | - | // frozen frame. A larger output must stay live: freeze only the others. | |
| 96 | + | // CanBackdrop reports whether an output's physical mode fits NeferGUI's image | |
| 97 | + | // limits. Outputs exceeding either the side or byte limit stay live. | |
| 95 | 98 | func CanBackdrop(o ports.Output) bool { | |
| 96 | - | return o.Width > 0 && o.Height > 0 && max(o.Width, o.Height) <= maxBackdrop | |
| 99 | + | return o.Width > 0 && o.Height > 0 && max(o.Width, o.Height) <= maxBackdrop && | |
| 100 | + | o.Width <= maxBackdropBytes/ports.BytesPerPixel/o.Height | |
| 97 | 101 | } | |
| 98 | 102 | ||
| 99 | 103 | // WithBackdrops draws each output's frozen frame under its overlay instead of | |
| @@ -103,8 +107,8 @@ func CanBackdrop(o ports.Output) bool { | |||
| 103 | 107 | func (s *Selector) WithBackdrops(frames map[uint32]ports.Frame) (*Selector, error) { | |
| 104 | 108 | s.backdrops = make(map[uint32]image.Image, len(frames)) | |
| 105 | 109 | for id, f := range frames { | |
| 106 | - | if f.Width > maxBackdrop || f.Height > maxBackdrop { | |
| 107 | - | return s, fmt.Errorf("selection: frozen frame of output %d is %dx%d, at most %d per side", id, f.Width, f.Height, maxBackdrop) | |
| 110 | + | if !CanBackdrop(ports.Output{Width: f.Width, Height: f.Height}) { | |
| 111 | + | return s, fmt.Errorf("selection: frozen frame of output %d is %dx%d, at most %d per side and %d bytes", id, f.Width, f.Height, maxBackdrop, maxBackdropBytes) | |
| 108 | 112 | } | |
| 109 | 113 | s.backdrops[id] = backdropImage(f) | |
| 110 | 114 | } | |
| @@ -87,13 +87,17 @@ func (s *Selector) WithCapabilities(c ports.Capabilities) *Selector { | |||||
| 87 | return s | 87 | return s | ||
| 88 | } | 88 | } | ||
| 89 | 89 | ||||
| 90 | - | // maxBackdrop is the largest side NeferGUI accepts for one image. | 90 | + | // These bounds mirror NeferGUI's image dimension and cache byte limits. |
| 91 | - | const maxBackdrop = 4096 | 91 | + | const ( |
| 92 | + | maxBackdrop = 8192 | |||
| 93 | + | maxBackdropBytes = 64 << 20 | |||
| 94 | + | ) | |||
| 92 | 95 | ||||
| 93 | - | // CanBackdrop reports whether an output of this physical mode can show a | 96 | + | // CanBackdrop reports whether an output's physical mode fits NeferGUI's image |
| 94 | - | // frozen frame. A larger output must stay live: freeze only the others. | 97 | + | // limits. Outputs exceeding either the side or byte limit stay live. |
| 95 | func CanBackdrop(o ports.Output) bool { | 98 | func CanBackdrop(o ports.Output) bool { | ||
| 96 | - | return o.Width > 0 && o.Height > 0 && max(o.Width, o.Height) <= maxBackdrop | 99 | + | return o.Width > 0 && o.Height > 0 && max(o.Width, o.Height) <= maxBackdrop && |
| 100 | + | o.Width <= maxBackdropBytes/ports.BytesPerPixel/o.Height | |||
| 97 | } | 101 | } | ||
| 98 | 102 | ||||
| 99 | // WithBackdrops draws each output's frozen frame under its overlay instead of | 103 | // WithBackdrops draws each output's frozen frame under its overlay instead of | ||
| @@ -103,8 +107,8 @@ func CanBackdrop(o ports.Output) bool { | |||||
| 103 | func (s *Selector) WithBackdrops(frames map[uint32]ports.Frame) (*Selector, error) { | 107 | func (s *Selector) WithBackdrops(frames map[uint32]ports.Frame) (*Selector, error) { | ||
| 104 | s.backdrops = make(map[uint32]image.Image, len(frames)) | 108 | s.backdrops = make(map[uint32]image.Image, len(frames)) | ||
| 105 | for id, f := range frames { | 109 | for id, f := range frames { | ||
| 106 | - | if f.Width > maxBackdrop || f.Height > maxBackdrop { | 110 | + | if !CanBackdrop(ports.Output{Width: f.Width, Height: f.Height}) { |
| 107 | - | return s, fmt.Errorf("selection: frozen frame of output %d is %dx%d, at most %d per side", id, f.Width, f.Height, maxBackdrop) | 111 | + | return s, fmt.Errorf("selection: frozen frame of output %d is %dx%d, at most %d per side and %d bytes", id, f.Width, f.Height, maxBackdrop, maxBackdropBytes) |
| 108 | } | 112 | } | ||
| 109 | s.backdrops[id] = backdropImage(f) | 113 | s.backdrops[id] = backdropImage(f) | ||
| 110 | } | 114 | } | ||
M internal/adapters/wayland/extensions_test.go +2 -0
| @@ -259,6 +259,8 @@ func TestRefusedExclusionDoesNotPinStoppedSession(t *testing.T) { | |||
| 259 | 259 | target := ports.Target{OutputID: firstOutput(t, s).ID} | |
| 260 | 260 | _, err := s.BeginExclusion(context.Background(), target) | |
| 261 | 261 | require.ErrorIs(t, err, ErrExclusionUnavailable) | |
| 262 | + | // BeginExclusion sends destroy but does not wait for the server to read it. | |
| 263 | + | require.NoError(t, s.roundtrip()) | |
| 262 | 264 | require.Equal(t, 1, srv.Stats().DestroyedExclusions, "the failed exclusion object is destroyed") | |
| 263 | 265 | srv.StopSessions() | |
| 264 | 266 | _, err = s.Capture(context.Background(), target) | |
| @@ -259,6 +259,8 @@ func TestRefusedExclusionDoesNotPinStoppedSession(t *testing.T) { | |||||
| 259 | target := ports.Target{OutputID: firstOutput(t, s).ID} | 259 | target := ports.Target{OutputID: firstOutput(t, s).ID} | ||
| 260 | _, err := s.BeginExclusion(context.Background(), target) | 260 | _, err := s.BeginExclusion(context.Background(), target) | ||
| 261 | require.ErrorIs(t, err, ErrExclusionUnavailable) | 261 | require.ErrorIs(t, err, ErrExclusionUnavailable) | ||
| 262 | + | // BeginExclusion sends destroy but does not wait for the server to read it. | |||
| 263 | + | require.NoError(t, s.roundtrip()) | |||
| 262 | require.Equal(t, 1, srv.Stats().DestroyedExclusions, "the failed exclusion object is destroyed") | 264 | require.Equal(t, 1, srv.Stats().DestroyedExclusions, "the failed exclusion object is destroyed") | ||
| 263 | srv.StopSessions() | 265 | srv.StopSessions() | ||
| 264 | _, err = s.Capture(context.Background(), target) | 266 | _, err = s.Capture(context.Background(), target) | ||
M internal/adapters/wayland/source_test.go +4 -1
| @@ -171,8 +171,11 @@ func TestFailedUnknownRepeatsLastFrame(t *testing.T) { | |||
| 171 | 171 | require.NoError(t, err, "recording continues") | |
| 172 | 172 | require.NotEqual(t, want, fresh.Pixels, "with a new frame") | |
| 173 | 173 | ||
| 174 | + | // Capture prefetched the next frame. Wait for the server to answer it | |
| 175 | + | // before arming failures, so it cannot consume the first injected failure. | |
| 176 | + | require.NoError(t, s.roundtrip()) | |
| 174 | 177 | srv.Fail(maxFrameAttempts, 0) | |
| 175 | - | // The capture started after the previous frame was answered before Fail. | |
| 178 | + | // Consume the successful prefetched frame before counting failures. | |
| 176 | 179 | _, err = s.Capture(context.Background(), target) | |
| 177 | 180 | require.NoError(t, err) | |
| 178 | 181 | for range maxFrameAttempts - 1 { | |
| @@ -171,8 +171,11 @@ func TestFailedUnknownRepeatsLastFrame(t *testing.T) { | |||||
| 171 | require.NoError(t, err, "recording continues") | 171 | require.NoError(t, err, "recording continues") | ||
| 172 | require.NotEqual(t, want, fresh.Pixels, "with a new frame") | 172 | require.NotEqual(t, want, fresh.Pixels, "with a new frame") | ||
| 173 | 173 | ||||
| 174 | + | // Capture prefetched the next frame. Wait for the server to answer it | |||
| 175 | + | // before arming failures, so it cannot consume the first injected failure. | |||
| 176 | + | require.NoError(t, s.roundtrip()) | |||
| 174 | srv.Fail(maxFrameAttempts, 0) | 177 | srv.Fail(maxFrameAttempts, 0) | ||
| 175 | - | // The capture started after the previous frame was answered before Fail. | 178 | + | // Consume the successful prefetched frame before counting failures. |
| 176 | _, err = s.Capture(context.Background(), target) | 179 | _, err = s.Capture(context.Background(), target) | ||
| 177 | require.NoError(t, err) | 180 | require.NoError(t, err) | ||
| 178 | for range maxFrameAttempts - 1 { | 181 | for range maxFrameAttempts - 1 { | ||
M internal/app/freeze_test.go +2 -2
| @@ -32,6 +32,6 @@ func TestFreezes(t *testing.T) { | |||
| 32 | 32 | // Outputs too large for a backdrop are not frozen, so their screenshot is | |
| 33 | 33 | // live like their selector. | |
| 34 | 34 | func TestFrozenOutputs(t *testing.T) { | |
| 35 | - | outputs := []ports.Output{{ID: 7, Width: 3840, Height: 2160}, {ID: 8, Width: 5120, Height: 2880}, {ID: 9, Width: 1080, Height: 1920}, {ID: 10}} | |
| 36 | - | assert.Equal(t, []uint32{7, 9}, frozenOutputs(outputs)) | |
| 35 | + | outputs := []ports.Output{{ID: 7, Width: 3840, Height: 2160}, {ID: 8, Width: 5120, Height: 2160}, {ID: 9, Width: 1080, Height: 1920}, {ID: 10}, {ID: 11, Width: 7680, Height: 4320}} | |
| 36 | + | assert.Equal(t, []uint32{7, 8, 9}, frozenOutputs(outputs)) | |
| 37 | 37 | } | |
| @@ -32,6 +32,6 @@ func TestFreezes(t *testing.T) { | |||||
| 32 | // Outputs too large for a backdrop are not frozen, so their screenshot is | 32 | // Outputs too large for a backdrop are not frozen, so their screenshot is | ||
| 33 | // live like their selector. | 33 | // live like their selector. | ||
| 34 | func TestFrozenOutputs(t *testing.T) { | 34 | func TestFrozenOutputs(t *testing.T) { | ||
| 35 | - | outputs := []ports.Output{{ID: 7, Width: 3840, Height: 2160}, {ID: 8, Width: 5120, Height: 2880}, {ID: 9, Width: 1080, Height: 1920}, {ID: 10}} | 35 | + | outputs := []ports.Output{{ID: 7, Width: 3840, Height: 2160}, {ID: 8, Width: 5120, Height: 2160}, {ID: 9, Width: 1080, Height: 1920}, {ID: 10}, {ID: 11, Width: 7680, Height: 4320}} |
| 36 | - | assert.Equal(t, []uint32{7, 9}, frozenOutputs(outputs)) | 36 | + | assert.Equal(t, []uint32{7, 8, 9}, frozenOutputs(outputs)) |
| 37 | } | 37 | } | ||