Skip to content

Commit f7b4fce

Browse files
committed
added daemon mode
1 parent 42abc4a commit f7b4fce

File tree

3 files changed

+37
-30
lines changed

3 files changed

+37
-30
lines changed

browserstack/local.py

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import subprocess, os, time
1+
import subprocess, os, time, json, psutil
22
from browserstack.local_binary import LocalBinary
33
from browserstack.bserrors import BrowserStackLocalError
44

@@ -17,12 +17,17 @@ def __xstr(self, key, value):
1717
return ['-' + key, value]
1818

1919
def _generate_cmd(self):
20-
cmd = [self.binary_path, '-logFile', self.local_logfile_path, self.key]
20+
cmd = [self.binary_path, '-d', 'start', '-logFile', self.local_logfile_path, self.key]
2121
for o in self.options.keys():
2222
if self.options.get(o) is not None:
2323
cmd = cmd + self.__xstr(o, self.options.get(o))
2424
return cmd
2525

26+
def _generate_stop_cmd(self):
27+
cmd = self._generate_cmd()
28+
cmd[2] = 'stop'
29+
return cmd
30+
2631
def start(self, **kwargs):
2732
self.options = kwargs
2833

@@ -43,34 +48,26 @@ def start(self, **kwargs):
4348
if "onlyCommand" in kwargs and kwargs["onlyCommand"]:
4449
return
4550

46-
self.proc = subprocess.Popen(self._generate_cmd(), stdout=subprocess.PIPE)
47-
self.stderr = self.proc.stderr
51+
self.proc = subprocess.Popen(self._generate_cmd(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
52+
(out, err) = self.proc.communicate()
4853

4954
os.system('echo "" > "'+ self.local_logfile_path +'"')
50-
with open(self.local_logfile_path, 'r') as local_logfile:
51-
while True:
52-
line = local_logfile.readline()
53-
if 'Error:' in line.strip():
54-
raise BrowserStackLocalError(line)
55-
elif line.strip() == 'Press Ctrl-C to exit':
56-
break
55+
try:
56+
data = json.loads(out if out else err)
5757

58-
while True:
59-
if self.isRunning():
60-
break
61-
time.sleep(1)
58+
if data['state'] != "connected":
59+
raise BrowserStackLocalError(data["message"])
60+
else:
61+
self.pid = data['pid']
62+
except ValueError:
63+
raise BrowserStackLocalError('Error parsing JSON output from daemon')
6264

6365
def isRunning(self):
64-
if (hasattr(self, 'proc')):
65-
return True if self.proc.poll() is None else False
66-
return False
66+
return hasattr(self, 'pid') and psutil.pid_exists(self.pid)
6767

6868
def stop(self):
6969
try:
70-
self.proc.terminate()
71-
while True:
72-
if not self.isRunning():
73-
break
74-
time.sleep(1)
70+
proc = subprocess.Popen(self._generate_stop_cmd(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
71+
(out, err) = proc.communicate()
7572
except Exception as e:
76-
return
73+
return

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
setup(
33
name = 'browserstack-local',
44
packages = ['browserstack'],
5-
version = '0.3.1',
5+
version = '0.4.0',
66
description = 'Python bindings for Browserstack Local',
77
author = 'BrowserStack',
88
author_email = '[email protected]',

tests/test_local.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ def test_start_local(self):
1212
self.local.start()
1313
self.assertNotEqual(self.local.proc.pid, 0)
1414

15+
def test_running(self):
16+
self.assertFalse(self.local.isRunning())
17+
self.local.start()
18+
self.assertTrue(self.local.isRunning())
19+
20+
def test_multiple(self):
21+
self.assertFalse(self.local.isRunning())
22+
self.local.start()
23+
self.assertTrue(self.local.isRunning())
24+
try:
25+
self.local2 = Local(os.environ['BROWSERSTACK_ACCESS_KEY'])
26+
self.local2.start()
27+
except BrowserStackLocalError as e:
28+
self.assertEqual(e.message, "Either another browserstack local client is running on your machine or some server is listening on port 45691")
29+
1530
def test_verbose(self):
1631
self.local.start(v=True, onlyCommand=True)
1732
self.assertIn('-v', self.local._generate_cmd())
@@ -64,8 +79,3 @@ def test_local_identifier(self):
6479
self.local.start(localIdentifier='mytunnel', onlyCommand=True)
6580
self.assertIn('-localIdentifier', self.local._generate_cmd())
6681
self.assertIn('mytunnel', self.local._generate_cmd())
67-
68-
def test_running(self):
69-
self.assertFalse(self.local.isRunning())
70-
self.local.start()
71-
self.assertTrue(self.local.isRunning())

0 commit comments

Comments
 (0)