Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 10 additions & 4 deletions evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,14 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
}

// Begin recording frames as we are now in a recording state.
ctx, cancel := context.WithCancel(ctx)
ch := v.Record(ctx)
//
// This context belongs to the recorder alone. teardown cancels it to stop
// the frame capture, and encoding runs after teardown, so the encoders must
// not inherit it: makeMedia builds its ffmpeg commands with
// exec.CommandContext, and a command started on a cancelled context is
// killed at Start before it writes anything.
recordCtx, cancel := context.WithCancel(ctx)
ch := v.Record(recordCtx)

// Clean up temporary files at the end.
defer func() {
Expand All @@ -141,9 +147,9 @@ func Evaluate(ctx context.Context, tape string, out io.Writer, opts ...Evaluator
}()

for _, cmd := range cmds[offset:] {
if ctx.Err() != nil {
if recordCtx.Err() != nil {
teardown()
return []error{ctx.Err()}
return []error{recordCtx.Err()}
}

// When changing the FontFamily, FontSize, LineHeight, Padding
Expand Down
31 changes: 31 additions & 0 deletions render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import (
"context"
"path/filepath"
"testing"
)

// TestRenderReportsEncodeFailure locks in two halves of the same regression.
//
// Render builds its encoders with exec.CommandContext, so a command started on
// a context that is already done is killed at Start and writes nothing. Before
// this was fixed, Evaluate handed Render the recording context, which teardown
// had just cancelled, and Render logged the empty output and returned nil. VHS
// printed "Creating out.gif...", exited 0, and produced no file
// (https://github.com/charmbracelet/vhs/issues/787).
//
// A cancelled context stands in here for any failing encode: whatever the
// cause, Render must report it rather than return nil.
func TestRenderReportsEncodeFailure(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()

v := New()
v.Options.Video.Output.GIF = filepath.Join(t.TempDir(), "out.gif")
v.totalFrames = 1

if err := v.Render(ctx); err == nil {
t.Fatal("Render returned nil for an encode that never ran; encode failures must reach the caller")
}
}
1 change: 1 addition & 0 deletions vhs.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ func (vhs *VHS) Render(ctx context.Context) error {
out, err := cmd.CombinedOutput()
if err != nil {
log.Println(string(out))
return fmt.Errorf("%s: %w", filepath.Base(cmd.Path), err)
}
}

Expand Down