Commit 673ef8ef authored by Jelte Jansen's avatar Jelte Jansen
Browse files

[1290] initial version of file-based stdout/stderr checking

and a bit more of a framework to keep track of running processes
(still in the works)
partial support for keeping output files, but no method of triggering that
(we may want to use a specific world.error or something)
parent 4cf570ad
from lettuce import *
import subprocess
import re
def check_lines(output, lines):
for line in lines:
if output.find(line) != -1:
return line
@world.absorb
def wait_for_output_lines_stdout(process_name, lines, examine_past = True):
assert process_name in world.processes
if examine_past:
for output in world.processes_stdout[process_name]:
for line in lines:
if output.find(line) != -1:
return line
found = False
while not found:
output = world.processes[process_name].stdout.readline()
# store any line, for examine_skipped
world.processes_stdout[process_name].append(output)
for line in lines:
if output.find(line) != -1:
return line
@world.absorb
def wait_for_output_lines_stderr(process_name, lines, examine_past = True):
assert process_name in world.processes,\
"No process named '" + process_name + "' known"
if examine_past:
for output in world.processes_stderr[process_name]:
for line in lines:
if output.find(line) != -1:
return line
found = False
while not found:
output = world.processes[process_name].stderr.readline()
# store any line, for examine_skipped
world.processes_stderr[process_name].append(output)
for line in lines:
if output.find(line) != -1:
return line
@step('start bind10(?: with configuration (\S+))?' +\
'(?: with cmdctl port (\d+))?(?: as (\S+))?')
def start_bind10(step, config_file, cmdctl_port, process_name):
......@@ -60,26 +26,20 @@ def start_bind10(step, config_file, cmdctl_port, process_name):
args.append('-m')
args.append(process_name + '_msgq.socket')
assert process_name not in world.processes,\
"There already seems to be a process named " + process_name
world.processes[process_name] = subprocess.Popen(args, 1, None,
subprocess.PIPE,
subprocess.PIPE,
subprocess.PIPE)
world.processes_stdout[process_name] = []
world.processes_stderr[process_name] = []
world.processes.add_process(step, process_name, args)
# check output to know when startup has been completed
# TODO what to do on failure?
message = world.wait_for_output_lines_stderr(process_name,
["BIND10_STARTUP_COMPLETE",
"BIND10_STARTUP_ERROR"])
assert message == "BIND10_STARTUP_COMPLETE", "Got: " + message
message = world.processes.wait_for_stderr_str(process_name,
["BIND10_STARTUP_COMPLETE",
"BIND10_STARTUP_ERROR"])
assert message == "BIND10_STARTUP_COMPLETE", "Got: " + str(message)
@step('wait for bind10 auth (?:of (\w+) )?to start')
def wait_for_auth(step, process_name):
if process_name is None:
process_name = "bind10"
world.wait_for_output_lines_stderr(process_name, ['AUTH_SERVER_STARTED'])
world.processes.wait_for_stderr_str(process_name, ['AUTH_SERVER_STARTED'])
@step('have bind10 running(?: with configuration ([\w.]+))?')
def have_bind10_running(step, config_file):
......
......@@ -118,7 +118,8 @@ def query(step, query_name, qtype, qclass, addr, port, rcode):
if port is None:
port = 47806
query_result = QueryResult(query_name, qtype, qclass, addr, port)
assert query_result.rcode == rcode, "Got " + query_result.rcode
assert query_result.rcode == rcode,\
"Expected: " + rcode + ", got " + query_result.rcode
world.last_query_result = query_result
@step('The SOA serial for ([\w.]+) should be ([0-9]+)')
......
......@@ -8,15 +8,15 @@ import os
@step('stop process (\w+)')
def stop_a_named_process(step, process_name):
world.stop_process(process_name)
world.processes.stop_process(process_name)
@step('wait for (new )?(\w+) stderr message (\w+)')
def wait_for_message(step, new, process_name, message):
world.wait_for_output_lines_stderr(process_name, [message], new is None)
world.processes.wait_for_stderr_str(process_name, [message], new)
@step('wait for (new )?(\w+) stdout message (\w+)')
def wait_for_message(step, process_name, message):
world.wait_for_output_lines_stdout(process_name, [message], new is None)
world.processes.wait_for_stdout_str(process_name, [message], new)
@step('Given I have no database')
def given_i_have_no_database(step):
......
......@@ -11,6 +11,8 @@ from lettuce import *
import subprocess
import os.path
import shutil
import re
import time
# This is a list of files that are freshly copied before each scenario
# The first element is the original, the second is the target that will be
......@@ -19,13 +21,139 @@ copylist = [
["configurations/example.org.config.orig", "configurations/example.org.config"]
]
# class that keeps track of one running process and the files
# we created for it. This needs to be moved to our framework-framework
# as it is not specifically for bind10
class RunningProcess:
def __init__(self, step, process_name, args):
# set it to none first so destructor won't error if initializer did
self.process = None
self.step = step
self.process_name = process_name
self.remove_files_on_exit = True
self._create_filenames()
self._start_process(args)
def _start_process(self, args):
stderr_write = open(self.stderr_filename, "w")
stdout_write = open(self.stdout_filename, "w")
self.process = subprocess.Popen(args, 1, None, subprocess.PIPE,
stdout_write, stderr_write)
# open them again, this time for reading
self.stderr = open(self.stderr_filename, "r")
self.stdout = open(self.stdout_filename, "r")
def mangle_filename(self, filebase, extension):
filebase = re.sub("\s+", "_", filebase)
filebase = re.sub("[^a-zA-Z.\-_]", "", filebase)
return filebase + "." + extension
def _create_filenames(self):
filebase = self.step.scenario.feature.name + "-" +\
self.step.scenario.name + "-" + self.process_name
self.stderr_filename = self.mangle_filename(filebase, "stderr")
self.stdout_filename = self.mangle_filename(filebase, "stdout")
def stop_process(self):
if self.process is not None:
self.process.terminate()
self.process.wait()
self.process = None
if self.remove_files_on_exit:
self._remove_files()
def _remove_files(self):
os.remove(self.stderr_filename)
os.remove(self.stdout_filename)
def _wait_for_output_str(self, filename, running_file, strings, only_new):
if not only_new:
full_file = open(filename, "r")
for line in full_file:
for string in strings:
if line.find(string) != -1:
full_file.close()
return string
while True:
where = running_file.tell()
line = running_file.readline()
if line:
for string in strings:
if line.find(string) != -1:
return string
else:
time.sleep(0.5)
running_file.seek(where)
def wait_for_stderr_str(self, strings, only_new = True):
return self._wait_for_output_str(self.stderr_filename, self.stderr,
strings, only_new)
def wait_for_stdout_str(self, strings, only_new = True):
return self._wait_for_output_str(self.stdout_filename, self.stdout,
strings, only_new)
# Container class for a number of running processes
# i.e. servers like bind10, etc
# one-shot programs like dig or bindctl are started and closed separately
class RunningProcesses:
def __init__(self):
self.processes = {}
def add_process(self, step, process_name, args):
assert process_name not in self.processes,\
"Process " + name + " already running"
self.processes[process_name] = RunningProcess(step, process_name, args)
def get_process(self, process_name):
assert process_name in self.processes,\
"Process " + name + " unknown"
return self.processes[process_name]
def stop_process(self, process_name):
assert process_name in self.processes,\
"Process " + name + " unknown"
self.processes[process_name].stop_process()
del self.processes[process_name]
def stop_all_processes(self):
for process in self.processes.values():
process.stop_process()
def keep_files(self):
for process in self.processes.values():
process.remove_files_on_exit = False
def wait_for_stderr_str(self, process_name, strings, only_new = True):
"""Wait for any of the given strings in the given processes stderr
output. If only_new is True, it will only look at the lines that are
printed to stderr since the last time this method was called. If
False, it will also look at the previously printed lines. This will
block until one of the strings is found. TODO: we may want to put in
a timeout for this... Returns the string that is found"""
assert process_name in self.processes,\
"Process " + process_name + " unknown"
return self.processes[process_name].wait_for_stderr_str(strings,
only_new)
def wait_for_stdout_str(self, process_name, strings, only_new = True):
"""Wait for any of the given strings in the given processes stderr
output. If only_new is True, it will only look at the lines that are
printed to stderr since the last time this method was called. If
False, it will also look at the previously printed lines. This will
block until one of the strings is found. TODO: we may want to put in
a timeout for this... Returns the string that is found"""
assert process_name in self.processes,\
"Process " + process_name + " unknown"
return self.processes[process_name].wait_for_stdout_str(strings,
only_new)
@before.each_scenario
def initialize(feature):
# just make sure our cleanup won't fail if we never did
# run the bind10 instance
world.processes = {}
world.processes_stdout = {}
world.processes_stderr = {}
# Keep track of running processes
world.processes = RunningProcesses()
# Convenience variable to access the last query result from querying.py
world.last_query_result = None
# Some tests can modify the settings. If the tests fail half-way, or
......@@ -37,16 +165,4 @@ def initialize(feature):
@after.each_scenario
def cleanup(feature):
# Stop any running processes we may have had around
for name in world.processes:
world.processes[name].terminate()
world.processes[name].wait()
world.processes_stdout[name] = []
world.processes_stderr[name] = []
@world.absorb
def stop_process(process_name):
if process_name in world.processes:
p = world.processes[process_name]
p.terminate()
p.wait()
del world.processes[process_name]
world.processes.stop_all_processes()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment