BIND 10 trac1290, updated. 673ef8efd5d474d66d62d134348730518160cbf9 [1290] initial version of file-based stdout/stderr checking

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Oct 24 21:05:28 UTC 2011


The branch, trac1290 has been updated
       via  673ef8efd5d474d66d62d134348730518160cbf9 (commit)
      from  4cf570ad0a107cbf70a6e96e8db30eb2c8b8a2ff (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 673ef8efd5d474d66d62d134348730518160cbf9
Author: Jelte Jansen <jelte at isc.org>
Date:   Mon Oct 24 23:04:17 2011 +0200

    [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)

-----------------------------------------------------------------------

Summary of changes:
 tests/lettuce/features/bind10_control.py |   56 ++----------
 tests/lettuce/features/querying.py       |    3 +-
 tests/lettuce/features/steps.py          |    6 +-
 tests/lettuce/features/terrain.py        |  152 ++++++++++++++++++++++++++----
 4 files changed, 147 insertions(+), 70 deletions(-)

-----------------------------------------------------------------------
diff --git a/tests/lettuce/features/bind10_control.py b/tests/lettuce/features/bind10_control.py
index 675d572..66e1dfd 100644
--- a/tests/lettuce/features/bind10_control.py
+++ b/tests/lettuce/features/bind10_control.py
@@ -1,46 +1,12 @@
 from lettuce import *
 import subprocess
+import re
 
 def check_lines(output, lines):
     for line in lines:
         if output.find(line) != -1:
             return line
 
- at 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
-
- at 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):
diff --git a/tests/lettuce/features/querying.py b/tests/lettuce/features/querying.py
index 0b4a50d..689f653 100644
--- a/tests/lettuce/features/querying.py
+++ b/tests/lettuce/features/querying.py
@@ -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]+)')
diff --git a/tests/lettuce/features/steps.py b/tests/lettuce/features/steps.py
index bbe2970..0c4d817 100644
--- a/tests/lettuce/features/steps.py
+++ b/tests/lettuce/features/steps.py
@@ -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):
diff --git a/tests/lettuce/features/terrain.py b/tests/lettuce/features/terrain.py
index 5de0838..14a8883 100644
--- a/tests/lettuce/features/terrain.py
+++ b/tests/lettuce/features/terrain.py
@@ -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] = []
-
- at 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()




More information about the bind10-changes mailing list