support fetching errors from cancellation
The API isn't very shiny, but it works. It doesn't matter that much, as most users won't care about these errors. Fixes #295.
This commit is contained in:
parent
b8efcf0691
commit
b481eeac51
11
allocate.go
11
allocate.go
|
@ -22,9 +22,6 @@ type Allocator interface {
|
||||||
// as temporary directories) will be freed.
|
// as temporary directories) will be freed.
|
||||||
Allocate(context.Context, ...BrowserOption) (*Browser, error)
|
Allocate(context.Context, ...BrowserOption) (*Browser, error)
|
||||||
|
|
||||||
// TODO: Wait should probably return an error, which can then be
|
|
||||||
// retrieved by the user if just calling cancel().
|
|
||||||
|
|
||||||
// Wait blocks until an allocator has freed all of its resources.
|
// Wait blocks until an allocator has freed all of its resources.
|
||||||
// Cancelling the context obtained via NewAllocator will already perform
|
// Cancelling the context obtained via NewAllocator will already perform
|
||||||
// this operation, so normally there's no need to call Wait directly.
|
// this operation, so normally there's no need to call Wait directly.
|
||||||
|
@ -113,11 +110,17 @@ func (p *ExecAllocator) Allocate(ctx context.Context, opts ...BrowserOption) (*B
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
// First wait for the process to be finished.
|
// First wait for the process to be finished.
|
||||||
if cmd != nil {
|
if cmd != nil {
|
||||||
|
// TODO: do we care about this error in any scenario? if
|
||||||
|
// the user cancelled the context and killed chrome,
|
||||||
|
// this will most likely just be "signal: killed", which
|
||||||
|
// isn't interesting.
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
}
|
}
|
||||||
// Then delete the temporary user data directory, if needed.
|
// Then delete the temporary user data directory, if needed.
|
||||||
if removeDir {
|
if removeDir {
|
||||||
os.RemoveAll(dataDir)
|
if err := os.RemoveAll(dataDir); c.cancelErr == nil {
|
||||||
|
c.cancelErr = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p.wg.Done()
|
p.wg.Done()
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
|
|
24
chromedp.go
24
chromedp.go
|
@ -55,6 +55,11 @@ type Context struct {
|
||||||
|
|
||||||
// wg allows waiting for a target to be closed on cancellation.
|
// wg allows waiting for a target to be closed on cancellation.
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
// cancelErr is the first error encountered when cancelling this
|
||||||
|
// context, for example if a browser's temporary user data directory
|
||||||
|
// couldn't be deleted.
|
||||||
|
cancelErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext creates a browser context using the parent context.
|
// NewContext creates a browser context using the parent context.
|
||||||
|
@ -98,14 +103,17 @@ func NewContext(parent context.Context, opts ...ContextOption) (context.Context,
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if id := c.Target.SessionID; id != "" {
|
if id := c.Target.SessionID; id != "" {
|
||||||
action := target.DetachFromTarget().WithSessionID(id)
|
action := target.DetachFromTarget().WithSessionID(id)
|
||||||
if err := action.Do(ctx, c.Browser); err != nil {
|
if err := action.Do(ctx, c.Browser); c.cancelErr == nil {
|
||||||
c.Browser.errf("%s", err)
|
c.cancelErr = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if id := c.Target.TargetID; id != "" {
|
if id := c.Target.TargetID; id != "" {
|
||||||
action := target.CloseTarget(id)
|
action := target.CloseTarget(id)
|
||||||
if _, err := action.Do(ctx, c.Browser); err != nil {
|
if ok, err := action.Do(ctx, c.Browser); c.cancelErr == nil {
|
||||||
c.Browser.errf("%s", err)
|
if !ok && err == nil {
|
||||||
|
err = fmt.Errorf("could not close target %q", id)
|
||||||
|
}
|
||||||
|
c.cancelErr = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
|
@ -125,6 +133,14 @@ func FromContext(ctx context.Context) *Context {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CancelError(ctx context.Context) error {
|
||||||
|
c := FromContext(ctx)
|
||||||
|
if c == nil {
|
||||||
|
return ErrInvalidContext
|
||||||
|
}
|
||||||
|
return c.cancelErr
|
||||||
|
}
|
||||||
|
|
||||||
// Run runs an action against the provided context. The provided context must
|
// Run runs an action against the provided context. The provided context must
|
||||||
// contain a valid Allocator; typically, that will be created via NewContext, or
|
// contain a valid Allocator; typically, that will be created via NewContext, or
|
||||||
// via one of the allocator constructors like NewExecAllocator.
|
// via one of the allocator constructors like NewExecAllocator.
|
||||||
|
|
|
@ -28,7 +28,7 @@ var (
|
||||||
func testAllocate(t *testing.T, path string) (_ context.Context, cancel func()) {
|
func testAllocate(t *testing.T, path string) (_ context.Context, cancel func()) {
|
||||||
// Same browser, new tab; not needing to start new chrome browsers for
|
// Same browser, new tab; not needing to start new chrome browsers for
|
||||||
// each test gives a huge speed-up.
|
// each test gives a huge speed-up.
|
||||||
ctx, cancel := NewContext(browserCtx)
|
ctx, cancel_ := NewContext(browserCtx)
|
||||||
|
|
||||||
// Only navigate if we want a path, otherwise leave the blank page.
|
// Only navigate if we want a path, otherwise leave the blank page.
|
||||||
if path != "" {
|
if path != "" {
|
||||||
|
@ -37,7 +37,13 @@ func testAllocate(t *testing.T, path string) (_ context.Context, cancel func())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx, cancel
|
cancelErr := func() {
|
||||||
|
cancel_() // not cancel(); that's the returned one
|
||||||
|
if err := CancelError(ctx); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx, cancelErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
@ -158,3 +164,37 @@ func TestBrowserQuit(t *testing.T) {
|
||||||
t.Fatalf("did not expect a standard context error: %v", err)
|
t.Fatalf("did not expect a standard context error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCancelError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx1, cancel1 := NewContext(context.Background())
|
||||||
|
defer cancel1()
|
||||||
|
if err := Run(ctx1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open and close a target normally; no error.
|
||||||
|
ctx2, cancel2 := NewContext(ctx1)
|
||||||
|
defer cancel2()
|
||||||
|
if err := Run(ctx2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cancel2()
|
||||||
|
if err := CancelError(ctx2); err != nil {
|
||||||
|
t.Fatalf("expected a nil error, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make "cancel" close the wrong target; error.
|
||||||
|
ctx3, cancel3 := NewContext(ctx1)
|
||||||
|
defer cancel3()
|
||||||
|
if err := Run(ctx3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
FromContext(ctx3).Target.TargetID = "wrong"
|
||||||
|
cancel3()
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
if err := CancelError(ctx3); err == nil {
|
||||||
|
t.Fatalf("expected a non-nil error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user