{"action":"opened","number":31,"pull_request":{"url":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/31","id":85841419,"html_url":"https://github.com/sourcegraph/go-selenium/pull/31","diff_url":"https://github.com/sourcegraph/go-selenium/pull/31.diff","patch_url":"https://github.com/sourcegraph/go-selenium/pull/31.patch","issue_url":"https://api.github.com/repos/sourcegraph/go-selenium/issues/31","number":31,"state":"open","locked":false,"title":"Add support for context.Context automatic Quit()","user":{"login":"pwaller","id":438648,"avatar_url":"https://avatars.githubusercontent.com/u/438648?v=3","gravatar_id":"","url":"https://api.github.com/users/pwaller","html_url":"https://github.com/pwaller","followers_url":"https://api.github.com/users/pwaller/followers","following_url":"https://api.github.com/users/pwaller/following{/other_user}","gists_url":"https://api.github.com/users/pwaller/gists{/gist_id}","starred_url":"https://api.github.com/users/pwaller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pwaller/subscriptions","organizations_url":"https://api.github.com/users/pwaller/orgs","repos_url":"https://api.github.com/users/pwaller/repos","events_url":"https://api.github.com/users/pwaller/events{/privacy}","received_events_url":"https://api.github.com/users/pwaller/received_events","type":"User","site_admin":false},"body":"It's annoying if you interrupt tests to have them leave a browser\nwindow open.\n\nYou might have to wait a while before you can run your tests again.\n\nThis adds support for go 1.7's `context.Context`. It instruments all\nrequests, causing them to cancel the outgoing HTTP request to selenium\nif the context is canceled.\n\nIn addition, it checks on entry and exit to a HTTP request if the\ncontext is canceled, to ensure we notice rapidly.\n\nIf the context is canceled, `wd.Quit()` is called. For convenience, it's\nmade legal to call quit more than once, so that code which does:\n\n```go\nwd := makeWD()\ndefer wd.Quit()\n```\n\nRemains legal.\n\nAn example of use:\n\n```go\nvar cancel func()\nctx, cancel = context.WithCancel(context.Background())\ngo func() {\n\tinterrupt := make(chan os.Signal)\n\tsignal.Notify(interrupt, os.Interrupt)\n\t<-interrupt\n\tlog.Printf(\"Received interrupt.\")\n\tcancel()\n}()\n\nwd := makeWD()\nwd.SetContext(ctx)\n\nwd.Get(...)\n... etc, etc.\n```\n\nThe net effect is that interrupting the tests now give `X failed:\ncanceled`, and the browser window quits.\ndiff --git a/remote.go b/remote.go\nindex 1557a4a..54c0560 100644\n--- a/remote.go\n+++ b/remote.go\n@@ -7,6 +7,7 @@ package selenium\n\n import (\n \t\"bytes\"\n+\t\"context\"\n \t\"encoding/base64\"\n \t\"encoding/json\"\n \t\"errors\"\n@@ -17,6 +18,7 @@ import (\n \t\"net/http/httputil\"\n \t\"os\"\n \t\"strings\"\n+\t\"sync\"\n )\n\n var Log = log.New(os.Stderr, \"[selenium] \", log.Ltime|log.Lmicroseconds)\n@@ -56,6 +58,14 @@ type remoteWebDriver struct {\n \tcapabilities Capabilities\n \t// FIXME\n \t// profile             BrowserProfile\n+\tctx context.Context\n+\n+\thaveQuitMu sync.Mutex\n+\thaveQuit   bool\n+}\n+\n+func (wd *remoteWebDriver) SetContext(ctx context.Context) {\n+\twd.ctx = ctx\n }\n\n func (wd *remoteWebDriver) url(template string, args ...interface{}) string {\n@@ -73,7 +83,29 @@ func (wd *remoteWebDriver) send(method, url string, data []byte) (r *reply, err\n \treturn\n }\n\n-func (wd *remoteWebDriver) execute(method, url string, data []byte) ([]byte, error) {\n+// ErrCanceled is returned when the context is cancelled.\n+var ErrCanceled = errors.New(\"cancelled\")\n+\n+func (wd *remoteWebDriver) execute(method, url string, data []byte) (buf []byte, err error) {\n+\tselect {\n+\tcase <-wd.ctx.Done():\n+\t\terr = ErrCanceled\n+\t\twd.ctx = context.Background()\n+\t\t_ = wd.Quit()\n+\t\treturn\n+\tdefault:\n+\t}\n+\tdefer func() {\n+\t\tselect {\n+\t\tcase <-wd.ctx.Done():\n+\t\t\terr = ErrCanceled\n+\t\t\twd.ctx = context.Background()\n+\t\t\t_ = wd.Quit()\n+\t\t\treturn\n+\t\tdefault:\n+\t\t}\n+\t}()\n+\n \tif Log != nil {\n \t\tLog.Printf(\"-> %s %s [%d bytes]\", method, url, len(data))\n \t}\n@@ -92,6 +124,8 @@ func (wd *remoteWebDriver) execute(method, url string, data []byte) ([]byte, err\n \t\t}\n \t}\n\n+\treq = req.WithContext(wd.ctx)\n+\n \tres, err := httpClient.Do(req)\n \tif err != nil {\n \t\treturn nil, err\n@@ -103,7 +137,7 @@ func (wd *remoteWebDriver) execute(method, url string, data []byte) ([]byte, err\n \t\t}\n \t}\n\n-\tbuf, err := ioutil.ReadAll(res.Body)\n+\tbuf, err = ioutil.ReadAll(res.Body)\n \tif err != nil {\n \t\treturn nil, err\n \t}\n@@ -194,7 +228,11 @@ func NewRemote(capabilities Capabilities, executor string) (WebDriver, error) {\n \t\texecutor = defaultExecutor\n \t}\n\n-\twd := &remoteWebDriver{executor: executor, capabilities: capabilities}\n+\twd := &remoteWebDriver{\n+\t\texecutor:     executor,\n+\t\tcapabilities: capabilities,\n+\t\tctx:          context.Background(),\n+\t}\n \t// FIXME: Handle profile\n\n \t_, err := wd.NewSession()\n@@ -318,6 +356,18 @@ func (wd *remoteWebDriver) ActivateEngine(engine string) (err error) {\n }\n\n func (wd *remoteWebDriver) Quit() (err error) {\n+\twd.haveQuitMu.Lock()\n+\tdefer wd.haveQuitMu.Unlock()\n+\tif wd.haveQuit {\n+\t\t// Double-Quit is an error-free no-op.\n+\t\treturn nil\n+\t}\n+\twd.haveQuit = true\n+\t// Quit is the one method which cannot be canceled.\n+\t// It's also the last thing that happens in a webdriver, so we can\n+\t// kill the context here.\n+\twd.ctx = context.Background()\n+\n \tif _, err = wd.execute(\"DELETE\", wd.url(\"/session/%s\", wd.id), nil); err == nil {\n \t\twd.id = \"\"\n \t}\ndiff --git a/selenium.go b/selenium.go\nindex a85cc78..7a3c9db 100644\n--- a/selenium.go\n+++ b/selenium.go\n@@ -1,4 +1,5 @@\n package selenium // import \"sourcegraph.com/sourcegraph/go-selenium\"\n+import \"context\"\n\n /* Element finding options */\n const (\n@@ -121,6 +122,8 @@ type Cookie struct {\n }\n\n type WebDriver interface {\n+\tSetContext(context.Context)\n+\n \t/* Status (info) on server */\n \tStatus() (*Status, error)","created_at":"2016-09-19T18:06:44Z","updated_at":"2016-09-19T18:06:44Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/31/commits","review_comments_url":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/31/comments","review_comment_url":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sourcegraph/go-selenium/issues/31/comments","statuses_url":"https://api.github.com/repos/sourcegraph/go-selenium/statuses/d083a0d754d4c4222713d4aa35132de69b639a93","head":{"label":"pwaller:context-support","ref":"context-support","sha":"d083a0d754d4c4222713d4aa35132de69b639a93","user":{"login":"pwaller","id":438648,"avatar_url":"https://avatars.githubusercontent.com/u/438648?v=3","gravatar_id":"","url":"https://api.github.com/users/pwaller","html_url":"https://github.com/pwaller","followers_url":"https://api.github.com/users/pwaller/followers","following_url":"https://api.github.com/users/pwaller/following{/other_user}","gists_url":"https://api.github.com/users/pwaller/gists{/gist_id}","starred_url":"https://api.github.com/users/pwaller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pwaller/subscriptions","organizations_url":"https://api.github.com/users/pwaller/orgs","repos_url":"https://api.github.com/users/pwaller/repos","events_url":"https://api.github.com/users/pwaller/events{/privacy}","received_events_url":"https://api.github.com/users/pwaller/received_events","type":"User","site_admin":false},"repo":{"id":41820252,"name":"go-selenium","full_name":"pwaller/go-selenium","owner":{"login":"pwaller","id":438648,"avatar_url":"https://avatars.githubusercontent.com/u/438648?v=3","gravatar_id":"","url":"https://api.github.com/users/pwaller","html_url":"https://github.com/pwaller","followers_url":"https://api.github.com/users/pwaller/followers","following_url":"https://api.github.com/users/pwaller/following{/other_user}","gists_url":"https://api.github.com/users/pwaller/gists{/gist_id}","starred_url":"https://api.github.com/users/pwaller/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pwaller/subscriptions","organizations_url":"https://api.github.com/users/pwaller/orgs","repos_url":"https://api.github.com/users/pwaller/repos","events_url":"https://api.github.com/users/pwaller/events{/privacy}","received_events_url":"https://api.github.com/users/pwaller/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pwaller/go-selenium","description":"Selenium WebDriver client for Go","fork":true,"url":"https://api.github.com/repos/pwaller/go-selenium","forks_url":"https://api.github.com/repos/pwaller/go-selenium/forks","keys_url":"https://api.github.com/repos/pwaller/go-selenium/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pwaller/go-selenium/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pwaller/go-selenium/teams","hooks_url":"https://api.github.com/repos/pwaller/go-selenium/hooks","issue_events_url":"https://api.github.com/repos/pwaller/go-selenium/issues/events{/number}","events_url":"https://api.github.com/repos/pwaller/go-selenium/events","assignees_url":"https://api.github.com/repos/pwaller/go-selenium/assignees{/user}","branches_url":"https://api.github.com/repos/pwaller/go-selenium/branches{/branch}","tags_url":"https://api.github.com/repos/pwaller/go-selenium/tags","blobs_url":"https://api.github.com/repos/pwaller/go-selenium/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pwaller/go-selenium/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pwaller/go-selenium/git/refs{/sha}","trees_url":"https://api.github.com/repos/pwaller/go-selenium/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pwaller/go-selenium/statuses/{sha}","languages_url":"https://api.github.com/repos/pwaller/go-selenium/languages","stargazers_url":"https://api.github.com/repos/pwaller/go-selenium/stargazers","contributors_url":"https://api.github.com/repos/pwaller/go-selenium/contributors","subscribers_url":"https://api.github.com/repos/pwaller/go-selenium/subscribers","subscription_url":"https://api.github.com/repos/pwaller/go-selenium/subscription","commits_url":"https://api.github.com/repos/pwaller/go-selenium/commits{/sha}","git_commits_url":"https://api.github.com/repos/pwaller/go-selenium/git/commits{/sha}","comments_url":"https://api.github.com/repos/pwaller/go-selenium/comments{/number}","issue_comment_url":"https://api.github.com/repos/pwaller/go-selenium/issues/comments{/number}","contents_url":"https://api.github.com/repos/pwaller/go-selenium/contents/{+path}","compare_url":"https://api.github.com/repos/pwaller/go-selenium/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pwaller/go-selenium/merges","archive_url":"https://api.github.com/repos/pwaller/go-selenium/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pwaller/go-selenium/downloads","issues_url":"https://api.github.com/repos/pwaller/go-selenium/issues{/number}","pulls_url":"https://api.github.com/repos/pwaller/go-selenium/pulls{/number}","milestones_url":"https://api.github.com/repos/pwaller/go-selenium/milestones{/number}","notifications_url":"https://api.github.com/repos/pwaller/go-selenium/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pwaller/go-selenium/labels{/name}","releases_url":"https://api.github.com/repos/pwaller/go-selenium/releases{/id}","deployments_url":"https://api.github.com/repos/pwaller/go-selenium/deployments","created_at":"2015-09-02T18:39:46Z","updated_at":"2015-09-02T18:39:47Z","pushed_at":"2016-09-19T18:06:43Z","git_url":"git://github.com/pwaller/go-selenium.git","ssh_url":"git@github.com:pwaller/go-selenium.git","clone_url":"https://github.com/pwaller/go-selenium.git","svn_url":"https://github.com/pwaller/go-selenium","homepage":"https://sourcegraph.com/github.com/sourcegraph/go-selenium","size":337,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"sourcegraph:master","ref":"master","sha":"78feb0e174a0a753c417f38839ca3937a7c1fac0","user":{"login":"sourcegraph","id":3979584,"avatar_url":"https://avatars.githubusercontent.com/u/3979584?v=3","gravatar_id":"","url":"https://api.github.com/users/sourcegraph","html_url":"https://github.com/sourcegraph","followers_url":"https://api.github.com/users/sourcegraph/followers","following_url":"https://api.github.com/users/sourcegraph/following{/other_user}","gists_url":"https://api.github.com/users/sourcegraph/gists{/gist_id}","starred_url":"https://api.github.com/users/sourcegraph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sourcegraph/subscriptions","organizations_url":"https://api.github.com/users/sourcegraph/orgs","repos_url":"https://api.github.com/users/sourcegraph/repos","events_url":"https://api.github.com/users/sourcegraph/events{/privacy}","received_events_url":"https://api.github.com/users/sourcegraph/received_events","type":"Organization","site_admin":false},"repo":{"id":10403471,"name":"go-selenium","full_name":"sourcegraph/go-selenium","owner":{"login":"sourcegraph","id":3979584,"avatar_url":"https://avatars.githubusercontent.com/u/3979584?v=3","gravatar_id":"","url":"https://api.github.com/users/sourcegraph","html_url":"https://github.com/sourcegraph","followers_url":"https://api.github.com/users/sourcegraph/followers","following_url":"https://api.github.com/users/sourcegraph/following{/other_user}","gists_url":"https://api.github.com/users/sourcegraph/gists{/gist_id}","starred_url":"https://api.github.com/users/sourcegraph/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sourcegraph/subscriptions","organizations_url":"https://api.github.com/users/sourcegraph/orgs","repos_url":"https://api.github.com/users/sourcegraph/repos","events_url":"https://api.github.com/users/sourcegraph/events{/privacy}","received_events_url":"https://api.github.com/users/sourcegraph/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sourcegraph/go-selenium","description":"Selenium WebDriver client for Go","fork":false,"url":"https://api.github.com/repos/sourcegraph/go-selenium","forks_url":"https://api.github.com/repos/sourcegraph/go-selenium/forks","keys_url":"https://api.github.com/repos/sourcegraph/go-selenium/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sourcegraph/go-selenium/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sourcegraph/go-selenium/teams","hooks_url":"https://api.github.com/repos/sourcegraph/go-selenium/hooks","issue_events_url":"https://api.github.com/repos/sourcegraph/go-selenium/issues/events{/number}","events_url":"https://api.github.com/repos/sourcegraph/go-selenium/events","assignees_url":"https://api.github.com/repos/sourcegraph/go-selenium/assignees{/user}","branches_url":"https://api.github.com/repos/sourcegraph/go-selenium/branches{/branch}","tags_url":"https://api.github.com/repos/sourcegraph/go-selenium/tags","blobs_url":"https://api.github.com/repos/sourcegraph/go-selenium/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sourcegraph/go-selenium/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sourcegraph/go-selenium/git/refs{/sha}","trees_url":"https://api.github.com/repos/sourcegraph/go-selenium/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sourcegraph/go-selenium/statuses/{sha}","languages_url":"https://api.github.com/repos/sourcegraph/go-selenium/languages","stargazers_url":"https://api.github.com/repos/sourcegraph/go-selenium/stargazers","contributors_url":"https://api.github.com/repos/sourcegraph/go-selenium/contributors","subscribers_url":"https://api.github.com/repos/sourcegraph/go-selenium/subscribers","subscription_url":"https://api.github.com/repos/sourcegraph/go-selenium/subscription","commits_url":"https://api.github.com/repos/sourcegraph/go-selenium/commits{/sha}","git_commits_url":"https://api.github.com/repos/sourcegraph/go-selenium/git/commits{/sha}","comments_url":"https://api.github.com/repos/sourcegraph/go-selenium/comments{/number}","issue_comment_url":"https://api.github.com/repos/sourcegraph/go-selenium/issues/comments{/number}","contents_url":"https://api.github.com/repos/sourcegraph/go-selenium/contents/{+path}","compare_url":"https://api.github.com/repos/sourcegraph/go-selenium/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sourcegraph/go-selenium/merges","archive_url":"https://api.github.com/repos/sourcegraph/go-selenium/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sourcegraph/go-selenium/downloads","issues_url":"https://api.github.com/repos/sourcegraph/go-selenium/issues{/number}","pulls_url":"https://api.github.com/repos/sourcegraph/go-selenium/pulls{/number}","milestones_url":"https://api.github.com/repos/sourcegraph/go-selenium/milestones{/number}","notifications_url":"https://api.github.com/repos/sourcegraph/go-selenium/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sourcegraph/go-selenium/labels{/name}","releases_url":"https://api.github.com/repos/sourcegraph/go-selenium/releases{/id}","deployments_url":"https://api.github.com/repos/sourcegraph/go-selenium/deployments","created_at":"2013-05-31T11:20:20Z","updated_at":"2016-09-09T15:59:14Z","pushed_at":"2016-07-25T13:00:40Z","git_url":"git://github.com/sourcegraph/go-selenium.git","ssh_url":"git@github.com:sourcegraph/go-selenium.git","clone_url":"https://github.com/sourcegraph/go-selenium.git","svn_url":"https://github.com/sourcegraph/go-selenium","homepage":"https://sourcegraph.com/github.com/sourcegraph/go-selenium","size":287,"stargazers_count":119,"watchers_count":119,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":28,"mirror_url":null,"open_issues_count":11,"forks":28,"open_issues":11,"watchers":119,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/31"},"html":{"href":"https://github.com/sourcegraph/go-selenium/pull/31"},"issue":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/issues/31"},"comments":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/issues/31/comments"},"review_comments":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/31/comments"},"review_comment":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/pulls/31/commits"},"statuses":{"href":"https://api.github.com/repos/sourcegraph/go-selenium/statuses/d083a0d754d4c4222713d4aa35132de69b639a93"}},"merged":false,"mergeable":null,"mergeable_state":"unknown","merged_by":null,"comments":0,"review_comments":0,"commits":1,"additions":56,"deletions":3,"changed_files":2}}
{"actor":{"display_login":"pwaller"}}