Skip to content

Commit

Permalink
Merge pull request #211 from mbland/fix-#193
Browse files Browse the repository at this point in the history
bash-engine: Check stdout readable before getline
  • Loading branch information
SimonKagstrom authored Jul 22, 2017
2 parents e55b297 + 08fb2a4 commit b048ef0
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 30 deletions.
35 changes: 11 additions & 24 deletions src/engines/bash-engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -352,37 +352,24 @@ class BashEngine : public ScriptEngineBase
// Printout lines to stdout, except kcov markers
void handleStdout()
{
// Has input?
if (!file_readable(m_stdout, 0))
return;

char *curLine = NULL;
ssize_t len;
size_t linecap = 0;

len = getline(&curLine, &linecap, m_stdout);
while (len > 0)
while (file_readable(m_stdout, 0) && getline(&curLine, &linecap, m_stdout) > 0)
{
std::string cur(curLine);

if (!m_bashSupportsXtraceFd)
/* Check for line markers to filter these away.
*
* For some reason, redirection sometimes give kkcov@..., so filter that
* in addition to the obvious stuff
*/
size_t kcovMarker = m_bashSupportsXtraceFd ? -1 : std::string(curLine).find("kcov@");

if (kcovMarker != 0 && kcovMarker != 1)
{
/* Check for line markers to filter these away.
*
* For some reason, redirection sometimes give kkcov@..., so filter that
* in addition to the obvious stuff
*/
size_t kcovMarker = cur.find("kcov@");
if (kcovMarker == 0 || kcovMarker == 1)
{
len = getline(&curLine, &linecap, m_stdout);
continue;
}
printf("%s", curLine);
}
printf("%s", cur.c_str());
len = getline(&curLine, &linecap, m_stdout);
}
return;
free(curLine);
}

bool bashCanHandleXtraceFd()
Expand Down
5 changes: 5 additions & 0 deletions tests/bash/handle-all-output.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#! /usr/bin/env bash

for ((i=0; i != 1000; ++i)); do
echo "$i"
done
19 changes: 14 additions & 5 deletions tests/tools/bash.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,23 @@ def runTest(self):
assert parse_cobertura.hitsPerLine(dom, "shell-main", 180) == 1

class bash_subshell(testbase.KcovTestCase):
@unittest.expectedFailure
def runTest(self):
self.setUp()
rv,o = self.do(testbase.kcov + " " + testbase.outbase + "/kcov " + testbase.sources + "/tests/bash/subshell.sh")
dom = parse_cobertura.parseFile(testbase.outbase + "/kcov/subshell.sh/cobertura.xml")
assert parse_cobertura.hitsPerLine(dom, "subshell.sh", 1) == None
assert parse_cobertura.hitsPerLine(dom, "subshell.sh", 4) == 2
assert parse_cobertura.hitsPerLine(dom, "subshell.sh", 8) == None
self.assertIsNone(parse_cobertura.hitsPerLine(dom, "subshell.sh", 1))
self.assertEqual(2, parse_cobertura.hitsPerLine(dom, "subshell.sh", 4))
self.assertEqual(0, parse_cobertura.hitsPerLine(dom, "subshell.sh", 8))

class bash_handle_all_output(testbase.KcovTestCase):
def runTest(self):
script = "handle-all-output.sh"
rv,o = self.do(testbase.kcov + " " + testbase.outbase + "/kcov " +
testbase.sources + "/tests/bash/" + script,
timeout=5.0)
self.assertEqual(0, rv, "kcov exited unsuccessfully")
dom = parse_cobertura.parseFile(testbase.outbase + "/kcov/" + script + "/cobertura.xml")
self.assertIsNone(parse_cobertura.hitsPerLine(dom, script, 1))
self.assertEqual(1000, parse_cobertura.hitsPerLine(dom, script, 4))

class bash_exit_status(testbase.KcovTestCase):
@unittest.expectedFailure
Expand Down
15 changes: 14 additions & 1 deletion tests/tools/testbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys
import subprocess
import time
import threading

kcov = ""
outbase = ""
Expand Down Expand Up @@ -32,7 +33,7 @@ def doShell(self, cmdline):

return rv, output

def do(self, cmdline, kcovKcov = True):
def do(self, cmdline, kcovKcov = True, timeout = None):
output = ""
rv = 0

Expand All @@ -43,7 +44,19 @@ def do(self, cmdline, kcovKcov = True):

cmdline = extra + cmdline
child = subprocess.Popen(cmdline.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
timer = None

if timeout is not None:
def stopChild():
print("\n didn't finish within %s seconds; killing ..." % timeout)
child.terminate()

timer = threading.Timer(timeout, stopChild)
timer.start()

out, err = child.communicate()
if timer is not None:
timer.cancel()
output = out + err
rv = child.returncode

Expand Down

0 comments on commit b048ef0

Please sign in to comment.