blob: eafa1c96d80e6b04cf3f67de0c779c2e190335e8 [file] [log] [blame]
// Copyright 2013 Federico Sogaro. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package webdriver
import (
"errors"
"fmt"
"io"
"os"
"os/exec"
"strconv"
"time"
)
type ChromeSwitches map[string]interface{}
type ChromeDriver struct {
WebDriverCore
//The port that ChromeDriver listens on. Default: 9515
Port int
//The URL path prefix to use for all incoming WebDriver REST requests. Default: ""
BaseUrl string
//The number of threads to use for handling HTTP requests. Default: 4
Threads int
//The path to use for the ChromeDriver server log. Default: ./chromedriver.log
LogPath string
// Log file to dump chromedriver stdout/stderr. If "" send to terminal. Default: ""
LogFile string
// Start method fails if Chromedriver doesn't start in less than StartTimeout. Default 20s.
StartTimeout time.Duration
path string
cmd *exec.Cmd
logFile *os.File
}
//create a new service using chromedriver.
//function returns an error if not supported switches are passed. Actual content
//of valid-named switches is not validate and is passed as it is.
//switch silent is removed (output is needed to check if chromedriver started correctly)
func NewChromeDriver(path string) *ChromeDriver {
d := &ChromeDriver{}
d.path = path
d.Port = 9515
d.BaseUrl = ""
d.Threads = 4
d.LogPath = "chromedriver.log"
d.StartTimeout = 20 * time.Second
return d
}
var switchesFormat = "-port=%d -url-base=%s -log-path=%s -http-threads=%d"
var cmdchan = make(chan error)
func (d *ChromeDriver) Start() error {
csferr := "chromedriver start failed: "
if d.cmd != nil {
return errors.New(csferr + "chromedriver already running")
}
if d.LogPath != "" {
//check if log-path is writable
file, err := os.OpenFile(d.LogPath, os.O_WRONLY|os.O_CREATE, 0664)
if err != nil {
return errors.New(csferr + "unable to write in log path: " + err.Error())
}
file.Close()
}
d.url = fmt.Sprintf("http://127.0.0.1:%d%s", d.Port, d.BaseUrl)
var switches []string
switches = append(switches, "-port="+strconv.Itoa(d.Port))
switches = append(switches, "-log-path="+d.LogPath)
switches = append(switches, "-http-threads="+strconv.Itoa(d.Threads))
if d.BaseUrl != "" {
switches = append(switches, "-url-base="+d.BaseUrl)
}
d.cmd = exec.Command(d.path, switches...)
stdout, err := d.cmd.StdoutPipe()
if err != nil {
return errors.New(csferr + err.Error())
}
stderr, err := d.cmd.StderrPipe()
if err != nil {
return errors.New(csferr + err.Error())
}
if err := d.cmd.Start(); err != nil {
return errors.New(csferr + err.Error())
}
if d.LogFile != "" {
flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
d.logFile, err = os.OpenFile(d.LogFile, flags, 0640)
if err != nil {
return err
}
go io.Copy(d.logFile, stdout)
go io.Copy(d.logFile, stderr)
} else {
go io.Copy(os.Stdout, stdout)
go io.Copy(os.Stderr, stderr)
}
if err = probePort(d.Port, d.StartTimeout); err != nil {
return err
}
return nil
}
func (d *ChromeDriver) Stop() error {
if d.cmd == nil {
return errors.New("stop failed: chromedriver not running")
}
defer func() {
d.cmd = nil
}()
d.cmd.Process.Signal(os.Interrupt)
if d.logFile != nil {
d.logFile.Close()
}
return nil
}
func (d *ChromeDriver) NewSession(desired, required Capabilities) (*Session, error) {
//id, capabs, err := d.newSession(desired, required)
//return &Session{id, capabs, d}, err
session, err := d.newSession(desired, required)
if err != nil {
return nil, err
}
session.wd = d
return session, nil
}
func (d *ChromeDriver) Sessions() ([]Session, error) {
sessions, err := d.sessions()
if err != nil {
return nil, err
}
for i := range sessions {
sessions[i].wd = d
}
return sessions, nil
}