Index: pyenvjasmine/runner.py
===================================================================
--- pyenvjasmine/runner.py	(revision 22)
+++ pyenvjasmine/runner.py	(revision 25)
@@ -1,4 +1,7 @@
 import os
+import sys
 import subprocess
+import threading
+import signal
 
 
@@ -12,4 +15,83 @@
         environment = '--environment=UNIX'
     return environment
+
+
+def print_no_newline(string):
+    sys.stdout.write(str(string))
+    sys.stdout.flush()
+
+
+def run_popen_with_timeout(
+        command, timeout, input_data, stdin, stdout, stderr, env=None):
+    """
+    Run a sub-program in subprocess.Popen, pass it the input_data,
+    kill it if the specified timeout has passed.
+    returns a tuple of success, stdout, stderr
+
+    sample usage:
+
+    timeout = 60  # seconds
+    path = '/path/to/event.log'
+    command = ['/usr/bin/tail', '-30', path]
+    input_data = ''
+    success, stdout, stderr = run_popen_with_timeout(command, timeout,
+                                                     input_data)
+    if not success:
+        print('timeout on tail event.log output')
+    tail_output = stdout
+    """
+    kill_check = threading.Event()
+
+    def _kill_process_after_a_timeout(pid):
+        try:
+            os.kill(pid, signal.SIGTERM)
+        except OSError:
+            # catch a possible race condition, the process terminated normally
+            # between the timer firing and our kill
+            return
+        kill_check.set()  # tell the main routine that we had to kill
+        # use SIGKILL if hard to kill...
+        return
+
+    stdout_l = []
+
+    # don't use shell if command/options come in as list
+    use_shell = not isinstance(command, list)
+    try:
+        p = subprocess.Popen(command, bufsize=1, shell=use_shell,
+                             stdin=stdin, stdout=stdout,
+                             stderr=stderr, env=env)
+    except OSError as error_message:
+        stderr = 'OSError: ' + str(error_message)
+        return (False, '', stderr)
+    pid = p.pid
+
+    watchdog = threading.Timer(timeout, _kill_process_after_a_timeout,
+                               args=(pid, ))
+    watchdog.start()
+
+    while True:
+        output = p.stdout.readline(1)
+        if output == '' and p.poll() is not None:
+            break
+        if output == '\n':
+            print(output)
+        else:
+            print_no_newline(output)
+        stdout_l.append(output)
+
+    try:
+        (stdout, stderr) = p.communicate(input_data)
+    except OSError as error_message:
+        stdout = ''
+        stderr = 'OSError: ' + str(error_message)
+        p.returncode = -666
+
+    watchdog.cancel()  # if it's still waiting to run
+
+    # if it timed out, success is False
+    success = (not kill_check.isSet()) and p.returncode >= 0
+    kill_check.clear()
+    return (success, ''.join(stdout_l), stderr)
 
 
@@ -47,9 +129,12 @@
         self.runner_html = os.path.join(here, 'runner.html')
 
-    def run(self, spec=None, capture_output=True):
-        """
-        Run the js tests with envjasmine.
+    def run(self, spec=None, timeout=None):
+        """
+        Run the js tests with envjasmine, return success (true/false) and
+        the captured stdout data
+
         spec: (relative) path to a spec file (run only that spec)
-        Returns the output
+        timeout: Set it to a given number of seconds and the process running
+                 the js tests will be killed passed that time
         """
         environment = get_environment()
@@ -85,15 +170,25 @@
 
         shell = False
-        stdout = None
-        stderr = None
-        if capture_output:
-            # override if we want to capture the output of the test run
-            stdout = subprocess.PIPE
-            stderr = subprocess.PIPE
-
-        p = subprocess.Popen(command, shell=shell, stdout=stdout,
-                             stderr=stderr)
-        (res, stderr) = p.communicate()
-        return res
+        stdin = None
+        stdout = subprocess.PIPE
+        stderr = subprocess.PIPE
+        input_data = ''
+
+        success, stdout, stderr = run_popen_with_timeout(
+            command, timeout, input_data, stdin, stdout, stderr
+        )
+
+        # success will be true if the subprocess did not timeout, now look
+        # for actual failures if there was not a timeout
+        if success:
+            success = self.did_test_pass(stdout)
+        return success, stdout
+
+    def did_test_pass(self, stdout):
+        for line in stdout.splitlines():
+            if 'Failed' in line:
+                failed = line.split(':')[1].strip()
+                return failed == '0'
+        return False
 
     def write_browser_htmlfile(self):
