[CalendarServer-changes] [12634] CalDAVTester/trunk

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 12 11:16:53 PDT 2014


Revision: 12634
          http://trac.calendarserver.org//changeset/12634
Author:   cdaboo at apple.com
Date:     2014-02-10 11:57:08 -0800 (Mon, 10 Feb 2014)
Log Message:
-----------
Add "observers" to allow customization of the output from tests. Switch to cElementTree.

Modified Paths:
--------------
    CalDAVTester/trunk/README.txt
    CalDAVTester/trunk/src/caldavtest.py
    CalDAVTester/trunk/src/manager.py
    CalDAVTester/trunk/verifiers/aclItems.py
    CalDAVTester/trunk/verifiers/dataMatch.py
    CalDAVTester/trunk/verifiers/multistatusItems.py
    CalDAVTester/trunk/verifiers/postFreeBusy.py
    CalDAVTester/trunk/verifiers/prepostcondition.py
    CalDAVTester/trunk/verifiers/propfindItems.py
    CalDAVTester/trunk/verifiers/propfindValues.py
    CalDAVTester/trunk/verifiers/xmlDataMatch.py
    CalDAVTester/trunk/verifiers/xmlElementMatch.py

Added Paths:
-----------
    CalDAVTester/trunk/src/observers/
    CalDAVTester/trunk/src/observers/__init__.py
    CalDAVTester/trunk/src/observers/base.py
    CalDAVTester/trunk/src/observers/jsondump.py
    CalDAVTester/trunk/src/observers/log.py
    CalDAVTester/trunk/src/observers/trace.py

Modified: CalDAVTester/trunk/README.txt
===================================================================
--- CalDAVTester/trunk/README.txt	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/README.txt	2014-02-10 19:57:08 UTC (rev 12634)
@@ -23,6 +23,7 @@
 	[--always-print-request] \
 	[--always-print-response] \
 	[--exclude filename] \
+	[--observer OBSERVER] \
 	file1 file2 ...
 
 	-s : filename specifies the file to use for server information
@@ -56,6 +57,13 @@
 	
 	--exclude FILE : when running with --all, exclude the file from the test run. 
 
+	--observer OBSEREVER : specify one or more times to change which classes are
+	used to process log and trace messages during a test. The OBSERVER name must
+	be the name of a module in the observers package. The default observer is the
+	"log" observer which produces an output similar to Python unit tests. The
+	"trace" observer produces an output similar to the original output format.
+	The "jsondump" observer prints a JSON representation of the test results.
+ 
 	file1 file2 ...: a list of test files to execute tests from.
 
 QUICKSTART

Modified: CalDAVTester/trunk/src/caldavtest.py
===================================================================
--- CalDAVTester/trunk/src/caldavtest.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/src/caldavtest.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -27,7 +27,7 @@
 from src.request import stats
 from src.testsuite import testsuite
 from src.xmlUtils import nodeForPath, xmlPathSplit
-from xml.etree.ElementTree import ElementTree, tostring
+from xml.etree.cElementTree import ElementTree, tostring
 import commands
 import httplib
 import os
@@ -39,8 +39,6 @@
 import urllib
 import urlparse
 
-STATUSTXT_WIDTH = 60
-
 """
 Patch the HTTPConnection.send to record full request details
 """
@@ -96,33 +94,28 @@
 
     def run(self):
         if len(self.missingFeatures()) != 0:
-            self.manager.log(manager.LOG_HIGH, "----- Ignoring Tests from \"%s\"... -----" % self.name, before=1)
-            self.manager.log(manager.LOG_HIGH, "      Missing features: %s" % (", ".join(sorted(self.missingFeatures())),))
+            self.manager.testFile(self.name, "Missing features: %s" % (", ".join(sorted(self.missingFeatures()),)), manager.RESULT_IGNORED)
             return 0, 0, 1
         if len(self.excludedFeatures()) != 0:
-            self.manager.log(manager.LOG_HIGH, "----- Ignoring Tests from \"%s\"... -----" % self.name, before=1)
-            self.manager.log(manager.LOG_HIGH, "      Excluded features: %s" % (", ".join(sorted(self.excludedFeatures())),))
+            self.manager.testFile(self.name, "Excluded features: %s" % (", ".join(sorted(self.excludedFeatures()),)), manager.RESULT_IGNORED)
             return 0, 0, 1
 
         self.only = any([suite.only for suite in self.suites])
         try:
-            self.manager.log(manager.LOG_HIGH, "----- Running Tests from \"%s\"... -----" % self.name, before=1)
-            result = self.dorequests("Executing Start Requests...", self.start_requests, False, True, label="%s | %s" % (self.name, "START_REQUESTS"))
+            result = self.dorequests("Start Requests...", self.start_requests, False, True, label="%s | %s" % (self.name, "START_REQUESTS"))
             if not result:
-                self.manager.log(manager.LOG_ERROR, "Start items failed - tests will not be run.")
-                ok = 0
-                failed = 1
-                ignored = 0
+                self.manager.testFile(self.name, "Start items failed - tests will not be run.", manager.RESULT_ERROR)
+                ok, failed, ignored = (0, 1, 0,)
             else:
                 ok, failed, ignored = self.run_tests(label=self.name)
             self.doenddelete("Deleting Requests...", label="%s | %s" % (self.name, "END_DELETE"))
-            self.dorequests("Executing End Requests...", self.end_requests, False, label="%s | %s" % (self.name, "END_REQUESTS"))
+            self.dorequests("End Requests...", self.end_requests, False, label="%s | %s" % (self.name, "END_REQUESTS"))
             return ok, failed, ignored
         except socket.error, msg:
-            self.manager.log(manager.LOG_ERROR, "SOCKET ERROR: %s" % (msg,), before=2)
+            self.manager.testFile(self.name, "SOCKET ERROR: %s" % (msg,), manager.RESULT_ERROR)
             return 0, 1, 0
         except Exception, e:
-            self.manager.log(manager.LOG_ERROR, "FATAL ERROR: %s" % (e,), before=2)
+            self.manager.testFile(self.name, "FATAL ERROR: %s" % (e,), manager.RESULT_ERROR)
             return 0, 1, 0
 
 
@@ -130,71 +123,65 @@
         ok = 0
         failed = 0
         ignored = 0
+        testfile = self.manager.testFile(self.name, self.description)
         for suite in self.suites:
-            o, f, i = self.run_test_suite(suite, label="%s | %s" % (label, suite.name))
+            o, f, i = self.run_test_suite(testfile, suite, label="%s | %s" % (label, suite.name))
             ok += o
             failed += f
             ignored += i
         return (ok, failed, ignored)
 
 
-    def run_test_suite(self, suite, label=""):
-        descriptor = "    Test Suite: %s" % suite.name
-        descriptor += " " * max(1, STATUSTXT_WIDTH - len(descriptor))
-        self.manager.log(manager.LOG_HIGH, "%s" % (descriptor,), before=1, after=0)
+    def run_test_suite(self, testfile, suite, label=""):
+        result_name = suite.name
         ok = 0
         failed = 0
         ignored = 0
         postgresCount = None
         if self.only and not suite.only or suite.ignore:
-            self.manager.log(manager.LOG_HIGH, "[IGNORED]")
+            self.manager.testSuite(testfile, result_name, "    Deliberately ignored", manager.RESULT_IGNORED)
             ignored = len(suite.tests)
         elif len(suite.missingFeatures()) != 0:
-            self.manager.log(manager.LOG_HIGH, "[IGNORED]")
-            self.manager.log(manager.LOG_HIGH, "      Missing features: %s" % (", ".join(sorted(suite.missingFeatures())),))
+            self.manager.testSuite(testfile, result_name, "    Missing features: %s" % (", ".join(sorted(suite.missingFeatures())),), manager.RESULT_IGNORED)
             ignored = len(suite.tests)
         elif len(suite.excludedFeatures()) != 0:
-            self.manager.log(manager.LOG_HIGH, "[IGNORED]")
-            self.manager.log(manager.LOG_HIGH, "      Excluded features: %s" % (", ".join(sorted(suite.excludedFeatures())),))
+            self.manager.testSuite(testfile, result_name, "    Excluded features: %s" % (", ".join(sorted(suite.excludedFeatures())),), manager.RESULT_IGNORED)
             ignored = len(suite.tests)
         else:
-            self.manager.log(manager.LOG_HIGH, "")
             postgresCount = self.postgresInit()
             if self.manager.memUsage:
                 start_usage = self.manager.getMemusage()
             etags = {}
             only_tests = any([test.only for test in suite.tests])
+            testsuite = self.manager.testSuite(testfile, result_name, "")
             for test in suite.tests:
-                result = self.run_test(test, etags, only_tests, label="%s | %s" % (label, test.name))
+                result = self.run_test(testsuite, test, etags, only_tests, label="%s | %s" % (label, test.name))
                 if result == "t":
                     ok += 1
                 elif result == "f":
                     failed += 1
                 else:
                     ignored += 1
+
             if self.manager.memUsage:
                 end_usage = self.manager.getMemusage()
-                self.manager.log(manager.LOG_HIGH, "Mem. Usage: RSS=%s%% VSZ=%s%%" % (str(((end_usage[1] - start_usage[1]) * 100) / start_usage[1]), str(((end_usage[0] - start_usage[0]) * 100) / start_usage[0])))
-        self.manager.log(manager.LOG_HIGH, "Suite Results: %d PASSED, %d FAILED, %d IGNORED" % (ok, failed, ignored), before=1, indent=4)
+                self.manager.message("trace", "    Mem. Usage: RSS=%s%% VSZ=%s%%" % (str(((end_usage[1] - start_usage[1]) * 100) / start_usage[1]), str(((end_usage[0] - start_usage[0]) * 100) / start_usage[0])))
+
+        self.manager.message("trace", "  Suite Results: %d PASSED, %d FAILED, %d IGNORED\n" % (ok, failed, ignored))
         if postgresCount is not None:
             self.postgresResult(postgresCount, indent=4)
         return (ok, failed, ignored)
 
 
-    def run_test(self, test, etags, only, label=""):
-        descriptor = "        Test: %s" % test.name
-        descriptor += " " * max(1, STATUSTXT_WIDTH - len(descriptor))
-        self.manager.log(manager.LOG_HIGH, "%s" % (descriptor,), before=1, after=0)
+    def run_test(self, testsuite, test, etags, only, label=""):
         if test.ignore or only and not test.only:
-            self.manager.log(manager.LOG_HIGH, "[IGNORED]")
+            self.manager.testResult(testsuite, test.name, "      Deliberately ignored", manager.RESULT_IGNORED)
             return "i"
         elif len(test.missingFeatures()) != 0:
-            self.manager.log(manager.LOG_HIGH, "[IGNORED]")
-            self.manager.log(manager.LOG_HIGH, "      Missing features: %s" % (", ".join(sorted(test.missingFeatures())),))
+            self.manager.testResult(testsuite, test.name, "      Missing features: %s" % (", ".join(sorted(test.missingFeatures())),), manager.RESULT_IGNORED)
             return "i"
         elif len(test.excludedFeatures()) != 0:
-            self.manager.log(manager.LOG_HIGH, "[IGNORED]")
-            self.manager.log(manager.LOG_HIGH, "      Excluded features: %s" % (", ".join(sorted(test.excludedFeatures())),))
+            self.manager.testResult(testsuite, test.name, "      Excluded features: %s" % (", ".join(sorted(test.excludedFeatures())),), manager.RESULT_IGNORED)
             return "i"
         else:
             result = True
@@ -225,13 +212,12 @@
                     if failed:
                         break
 
-            loglevel = [manager.LOG_ERROR, manager.LOG_HIGH][result]
-            self.manager.log(loglevel, ["[FAILED]", "[OK]"][result])
+            self.manager.testResult(testsuite, test.name, resulttxt, manager.RESULT_OK if result else manager.RESULT_FAILED)
             if len(resulttxt) > 0:
-                self.manager.log(loglevel, resulttxt)
+                self.manager.message("trace", resulttxt)
             if result and test.stats:
-                self.manager.log(manager.LOG_MEDIUM, "Total Time: %.3f secs" % (reqstats.totaltime,), indent=8)
-                self.manager.log(manager.LOG_MEDIUM, "Average Time: %.3f secs" % (reqstats.totaltime / reqstats.count,), indent=8)
+                self.manager.message("trace", "    Total Time: %.3f secs" % (reqstats.totaltime,), indent=8)
+                self.manager.message("trace", "    Average Time: %.3f secs" % (reqstats.totaltime / reqstats.count,), indent=8)
             self.postgresResult(postgresCount, indent=8)
             return ["f", "t"][result]
 
@@ -239,17 +225,15 @@
     def dorequests(self, description, list, doverify=True, forceverify=False, label="", count=1):
         if len(list) == 0:
             return True
-        description += " " * max(1, STATUSTXT_WIDTH - len(description))
-        self.manager.log(manager.LOG_HIGH, description, before=1, after=0)
+        self.manager.message("trace", "Start: " + description)
         for req_count, req in enumerate(list):
             result, resulttxt, _ignore_response, _ignore_respdata = self.dorequest(req, False, doverify, forceverify, label="%s | #%s" % (label, req_count + 1), count=count)
             if not result:
                 resulttxt += "\nFailure during multiple requests #%d out of %d, request=%s" % (req_count + 1, len(list), str(req))
                 break
-        loglevel = [manager.LOG_ERROR, manager.LOG_HIGH][result]
-        self.manager.log(loglevel, ["[FAILED]", "[OK]"][result])
+        self.manager.message("trace", "{name:<60}{value:>10}".format(name="End: " + description, value=["[FAILED]", "[OK]"][result]))
         if len(resulttxt) > 0:
-            self.manager.log(loglevel, resulttxt)
+            self.manager.message("trace", resulttxt)
         return result
 
 
@@ -466,8 +450,7 @@
     def doenddelete(self, description, label=""):
         if len(self.end_deletes) == 0:
             return True
-        description += " " * max(1, STATUSTXT_WIDTH - len(description))
-        self.manager.log(manager.LOG_HIGH, description, before=1, after=0)
+        self.manager.message("trace", "Start: " + description)
         for deleter in self.end_deletes:
             req = request(self.manager)
             req.method = "DELETE"
@@ -478,7 +461,7 @@
             if len(deleter[2]):
                 req.pswd = deleter[2]
             self.dorequest(req, False, False, label=label)
-        self.manager.log(manager.LOG_HIGH, "[DONE]")
+        self.manager.message("trace", "{name:<60}{value:>10}".format(name="End: " + description, value="[DONE]"))
 
 
     def dorequest(self, req, details=False, doverify=True, forceverify=False, stats=None, etags=None, label="", count=1):
@@ -492,12 +475,8 @@
             return True, "", None, None
 
         if len(req.missingFeatures()) != 0:
-            #self.manager.log(manager.LOG_HIGH, "[IGNORED]")
-            #self.manager.log(manager.LOG_HIGH, "      Missing features: %s" % (", ".join(sorted(req.missingFeatures())),))
             return True, "", None, None
         if len(req.excludedFeatures()) != 0:
-            #self.manager.log(manager.LOG_HIGH, "[IGNORED]")
-            #self.manager.log(manager.LOG_HIGH, "      Excluded features: %s" % (", ".join(sorted(req.excludedFeatures())),))
             return True, "", None, None
 
         # Special check for DELETEALL
@@ -594,19 +573,16 @@
             headers['User-Agent'] = label.encode("utf-8")
 
         try:
-            #self.manager.log(manager.LOG_LOW, "Sending request")
             puri = list(urlparse.urlparse(uri))
             puri[2] = urllib.quote(puri[2])
             quri = urlparse.urlunparse(puri)
 
             http.request(method, quri, data, headers)
-            #self.manager.log(manager.LOG_LOW, "Sent request")
 
             response = http.getresponse()
 
             respdata = None
             respdata = response.read()
-            #self.manager.log(manager.LOG_LOW, "Read response")
 
         finally:
             http.close()
@@ -983,4 +959,4 @@
                 newCount = int(commands.getoutput("grep \"LOG:  statement:\" %s | wc -l" % (self.manager.postgresLog,)))
             else:
                 newCount = 0
-            self.manager.log(manager.LOG_HIGH, "Postgres Statements: %d" % (newCount - startCount,), indent=indent)
+            self.manager.message("trace", "Postgres Statements: %d" % (newCount - startCount,))

Modified: CalDAVTester/trunk/src/manager.py
===================================================================
--- CalDAVTester/trunk/src/manager.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/src/manager.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -19,7 +19,7 @@
 """
 
 from src.serverinfo import serverinfo
-from xml.etree.ElementTree import ElementTree
+from xml.etree.cElementTree import ElementTree
 from xml.parsers.expat import ExpatError
 import getopt
 import httplib
@@ -40,20 +40,19 @@
     Main class that runs test suites defined in an XML config file.
     """
 
-    LOG_NONE = 0
-    LOG_ERROR = 1
-    LOG_LOW = 2
-    LOG_MEDIUM = 3
-    LOG_HIGH = 4
+    RESULT_OK = 0
+    RESULT_FAILED = 1
+    RESULT_ERROR = 2
+    RESULT_IGNORED = 3
 
-    def __init__(self, text=True, level=LOG_HIGH):
+
+    def __init__(self, text=True):
         self.server_info = serverinfo()
         self.tests = []
         self.textMode = text
         self.pid = 0
         self.memUsage = None
         self.randomSeed = None
-        self.logLevel = level
         self.logFile = None
         self.digestCache = {}
         self.postgresLog = ""
@@ -61,27 +60,67 @@
         self.print_response = False
         self.print_request_response_on_error = False
 
+        self.results = []
+        self.totals = {
+            self.RESULT_OK: 0,
+            self.RESULT_FAILED: 0,
+            self.RESULT_ERROR: 0,
+            self.RESULT_IGNORED: 0
+        }
+        self.observers = []
 
-    def log(self, level, str, indent=0, indentStr=" ", after=1, before=0):
-        if self.textMode and level <= self.logLevel:
-            if before:
-                self.logit("\n" * before)
-            if indent:
-                self.logit(indentStr * indent)
-            self.logit(str)
-            if after:
-                self.logit("\n" * after)
 
-
     def logit(self, str):
         if self.logFile:
-            self.logFile.write(str)
-        print str,
+            self.logFile.write(str + "\n")
+        print str
 
 
+    def loadObserver(self, observer_name):
+        module = __import__("observers." + observer_name, globals(), locals(), ["Observer", ])
+        cl = getattr(module, "Observer")
+        self.observers.append(cl(self))
+
+
+    def message(self, message, *args, **kwargs):
+        map(lambda x: x.message(message, *args, **kwargs), self.observers)
+
+
+    def testFile(self, name, details, result=None):
+        self.results.append({
+            "name": name,
+            "details": details,
+            "result": result,
+            "tests": []
+        })
+        self.message("testFile", self.results[-1])
+        return self.results[-1]["tests"]
+
+
+    def testSuite(self, testfile, name, details, result=None):
+        testfile.append({
+            "name": name,
+            "details": details,
+            "result": result,
+            "tests": []
+        })
+        self.message("testSuite", testfile[-1])
+        return testfile[-1]["tests"]
+
+
+    def testResult(self, testsuite, name, details, result,):
+        testsuite.append({
+            "name": name,
+            "result": result,
+            "details": details
+        })
+        self.totals[result] += 1
+        self.message("testResult", testsuite[-1])
+
+
     def readXML(self, serverfile, testfiles, ssl, all, moresubs={}):
 
-        self.log(manager.LOG_HIGH, "Reading Server Info from \"%s\"" % serverfile, after=2)
+        self.message("trace", "Reading Server Info from \"{}\"".format(serverfile))
 
         # Open and parse the server config file
         try:
@@ -118,7 +157,8 @@
 
         self.server_info.addsubs(moresubs)
 
-        for testfile in testfiles:
+        for ctr, testfile in enumerate(testfiles):
+            print "\rTest File {} of {}".format(ctr + 1, len(testfiles)),
             # Open and parse the config file
             try:
                 tree = ElementTree(file=testfile)
@@ -129,11 +169,11 @@
             from src.caldavtest import caldavtest
             caldavtest_node = tree.getroot()
             if caldavtest_node.tag != src.xmlDefs.ELEMENT_CALDAVTEST:
-                self.log(manager.LOG_HIGH, "Ignoring file \"%s\" because it is not a test file" % (testfile,), after=2)
+                self.message("trace", "Ignoring file \"{}\" because it is not a test file".format(testfile))
                 continue
             if not len(caldavtest_node):
                 raise EX_INVALID_CONFIG_FILE
-            self.log(manager.LOG_HIGH, "Reading Test Details from \"%s\"" % testfile, after=2)
+            self.message("Reading Test Details from \"{}\"".format(testfile))
 
             # parse all the config data
             test = caldavtest(self, testfile)
@@ -143,7 +183,9 @@
             if not all or not test.ignore_all:
                 self.tests.append(test)
 
+        print ""
 
+
     def readCommandLine(self):
         sname = "scripts/server/serverinfo.xml"
         dname = "scripts/tests"
@@ -155,6 +197,8 @@
         pidfile = "../CalendarServer/logs/caldavd.pid"
         random_order = False
         random_seed = str(random.randint(0, 1000000))
+        observer_names = []
+
         options, args = getopt.getopt(
             sys.argv[1:],
             "s:mo:x:",
@@ -163,6 +207,7 @@
                 "all",
                 "subdir=",
                 "exclude=",
+                "observer=",
                 "pid=",
                 "postgres-log=",
                 "random",
@@ -193,6 +238,8 @@
                 self.logFile = open(value, "w")
             elif option == "--pid":
                 pidfile = value
+            elif option == "--observer":
+                observer_names.append(value)
             elif option == "--postgres-log":
                 self.postgresLog = value
             elif option == "--print-details-onfail":
@@ -239,6 +286,9 @@
             random.shuffle(fnames)
             self.randomSeed = random_seed
 
+        # Load observers
+        map(lambda name: self.loadObserver(name), observer_names if observer_names else ["log", ])
+
         self.readXML(sname, fnames, ssl, all)
 
         if self.memUsage:
@@ -251,8 +301,7 @@
 
         startTime = time.time()
 
-        if self.randomSeed is not None:
-            self.log(manager.LOG_LOW, "Randomizing order using seed '%s'" % (self.randomSeed,))
+        self.message("start")
 
         ok = 0
         failed = 0
@@ -270,8 +319,8 @@
 
         endTime = time.time()
 
-        self.log(manager.LOG_LOW, "Overall Results: %d PASSED, %d FAILED, %d IGNORED" % (ok, failed, ignored), before=2, indent=4)
-        self.log(manager.LOG_LOW, "Total time: %.3f secs" % (endTime - startTime,))
+        self.timeDiff = endTime - startTime
+        self.message("finish")
 
         if self.logFile is not None:
             self.logFile.close()

Added: CalDAVTester/trunk/src/observers/__init__.py
===================================================================
--- CalDAVTester/trunk/src/observers/__init__.py	                        (rev 0)
+++ CalDAVTester/trunk/src/observers/__init__.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -0,0 +1,15 @@
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##

Added: CalDAVTester/trunk/src/observers/base.py
===================================================================
--- CalDAVTester/trunk/src/observers/base.py	                        (rev 0)
+++ CalDAVTester/trunk/src/observers/base.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -0,0 +1,48 @@
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+
+class BaseResultsObserver(object):
+    """
+    A base class for an observer that gets passed results of tests.
+
+    Supported messages:
+
+    trace - tracing tool activity
+    begin - beginning
+    load - loading a test
+    start - starting tests
+    testFile - add a test file
+    testSuite - add a test suite
+    testResult - add a test result
+    finish - tests completed
+    """
+
+    def __init__(self, manager):
+        self.updateCalls()
+        self.manager = manager
+
+
+    def updateCalls(self):
+        self._calls = {}
+
+
+    def message(self, message, *args, **kwargs):
+
+        callit = self._calls.get(message)
+
+        if callit is not None:
+            callit(*args, **kwargs)

Added: CalDAVTester/trunk/src/observers/jsondump.py
===================================================================
--- CalDAVTester/trunk/src/observers/jsondump.py	                        (rev 0)
+++ CalDAVTester/trunk/src/observers/jsondump.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -0,0 +1,34 @@
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from src.observers.base import BaseResultsObserver
+import json
+
+
+class Observer(BaseResultsObserver):
+    """
+    A results observer that prints results to standard output.
+    """
+
+    def updateCalls(self):
+        super(Observer, self).updateCalls()
+        self._calls.update({
+            "finish": self.finish,
+        })
+
+
+    def finish(self):
+        print json.dumps(self.manager.results)

Added: CalDAVTester/trunk/src/observers/log.py
===================================================================
--- CalDAVTester/trunk/src/observers/log.py	                        (rev 0)
+++ CalDAVTester/trunk/src/observers/log.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -0,0 +1,107 @@
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+from src.manager import manager
+from src.observers.base import BaseResultsObserver
+
+
+class Observer(BaseResultsObserver):
+    """
+    A results observer that prints results to standard output.
+    """
+
+    RESULT_STRINGS = {
+        manager.RESULT_OK: "[OK]",
+        manager.RESULT_FAILED: "[FAILED]",
+        manager.RESULT_ERROR: "[ERROR]",
+        manager.RESULT_IGNORED: "[IGNORED]",
+    }
+
+    _print_details = False
+
+    def __init__(self, manager):
+        super(Observer, self).__init__(manager)
+        self.loggedFailures = []
+        self.currentFile = None
+        self.currentSuite = None
+
+
+    def updateCalls(self):
+        super(Observer, self).updateCalls()
+        self._calls.update({
+            "start": self.start,
+            "testFile": self.testFile,
+            "testSuite": self.testSuite,
+            "testResult": self.testResult,
+            "finish": self.finish,
+        })
+
+
+    def start(self):
+        self.manager.logit("Starting tests")
+        if self.manager.randomSeed is not None:
+            self.manager.logit("Randomizing order using seed '{}'".format(self.randomSeed))
+
+
+    def testFile(self, result):
+        self.currentFile = result["name"].replace("/", ".")[:-4]
+        self.manager.logit("")
+        self._logResult(self.currentFile, result)
+
+
+    def testSuite(self, result):
+        self.currentSuite = result["name"]
+        result_name = "  Suite: " + result["name"]
+        self._logResult(result_name, result)
+
+
+    def testResult(self, result):
+        result_name = "    Test: " + result["name"]
+        self._logResult(result_name, result)
+        if result["result"] in (manager.RESULT_FAILED, manager.RESULT_ERROR):
+            failtxt = "{}/{}/{}\n{}".format(self.currentFile, self.currentSuite, result["name"], result["details"])
+            self.loggedFailures.append(failtxt)
+
+
+    def _logResult(self, name, result):
+        if result["result"] is not None:
+            result_value = self.RESULT_STRINGS[result["result"]]
+            self.manager.logit("{name:<60}{value:>10}".format(name=name, value=result_value))
+        else:
+            self.manager.logit("{name:<60}".format(name=name))
+        if self._print_details and result["details"]:
+            self.manager.logit(result["details"])
+
+
+    def finish(self):
+        self.manager.logit("")
+        if self.manager.totals[manager.RESULT_FAILED] + self.manager.totals[manager.RESULT_ERROR] != 0:
+            for failed in self.loggedFailures:
+                self.manager.logit("******")
+                self.manager.logit(failed)
+            overall = "******\n\nFAILED (ok={}, ignored={}, failed={}, errors={}) Time = {:.3f} secs".format(
+                self.manager.totals[manager.RESULT_OK],
+                self.manager.totals[manager.RESULT_IGNORED],
+                self.manager.totals[manager.RESULT_FAILED],
+                self.manager.totals[manager.RESULT_ERROR],
+                self.manager.timeDiff
+            )
+        else:
+            overall = "PASSED (ok={}, ignored={}) Time = {:.3f} secs".format(
+                self.manager.totals[manager.RESULT_OK],
+                self.manager.totals[manager.RESULT_IGNORED],
+                self.manager.timeDiff
+            )
+        self.manager.logit(overall)

Added: CalDAVTester/trunk/src/observers/trace.py
===================================================================
--- CalDAVTester/trunk/src/observers/trace.py	                        (rev 0)
+++ CalDAVTester/trunk/src/observers/trace.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -0,0 +1,35 @@
+##
+# Copyright (c) 2014 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from src.observers.log import Observer as LogObserver
+
+
+class Observer(LogObserver):
+    """
+    A results observer that prints results to standard output.
+    """
+
+    _print_details = True
+
+    def updateCalls(self):
+        super(Observer, self).updateCalls()
+        self._calls.update({
+            "trace": self.trace,
+        })
+
+
+    def trace(self, text):
+        self.manager.logit(text)

Modified: CalDAVTester/trunk/verifiers/aclItems.py
===================================================================
--- CalDAVTester/trunk/verifiers/aclItems.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/aclItems.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -20,7 +20,7 @@
 are available for the currently authenticated user.
 """
 
-from xml.etree.ElementTree import ElementTree
+from xml.etree.cElementTree import ElementTree
 from StringIO import StringIO
 
 class Verifier(object):

Modified: CalDAVTester/trunk/verifiers/dataMatch.py
===================================================================
--- CalDAVTester/trunk/verifiers/dataMatch.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/dataMatch.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -19,7 +19,7 @@
 Verifier that checks the response body for an exact match to data in a file.
 """
 
-from xml.etree.ElementTree import ElementTree, tostring
+from xml.etree.cElementTree import ElementTree, tostring
 from StringIO import StringIO
 
 class Verifier(object):

Modified: CalDAVTester/trunk/verifiers/multistatusItems.py
===================================================================
--- CalDAVTester/trunk/verifiers/multistatusItems.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/multistatusItems.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -20,7 +20,7 @@
 are returned with appropriate status codes.
 """
 
-from xml.etree.ElementTree import ElementTree
+from xml.etree.cElementTree import ElementTree
 from StringIO import StringIO
 
 class Verifier(object):

Modified: CalDAVTester/trunk/verifiers/postFreeBusy.py
===================================================================
--- CalDAVTester/trunk/verifiers/postFreeBusy.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/postFreeBusy.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -20,7 +20,7 @@
 
 from pycalendar.icalendar.calendar import Calendar
 from pycalendar.exceptions import InvalidData
-from xml.etree.ElementTree import ElementTree
+from xml.etree.cElementTree import ElementTree
 from xml.parsers.expat import ExpatError
 import StringIO
 

Modified: CalDAVTester/trunk/verifiers/prepostcondition.py
===================================================================
--- CalDAVTester/trunk/verifiers/prepostcondition.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/prepostcondition.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -18,7 +18,7 @@
 Verifier that checks the response for a pre/post-condition <DAV:error> result.
 """
 
-from xml.etree.ElementTree import ElementTree
+from xml.etree.cElementTree import ElementTree
 from StringIO import StringIO
 
 class Verifier(object):

Modified: CalDAVTester/trunk/verifiers/propfindItems.py
===================================================================
--- CalDAVTester/trunk/verifiers/propfindItems.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/propfindItems.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -20,7 +20,7 @@
 are returned with appropriate status codes.
 """
 
-from xml.etree.ElementTree import ElementTree, tostring
+from xml.etree.cElementTree import ElementTree, tostring
 from StringIO import StringIO
 
 class Verifier(object):

Modified: CalDAVTester/trunk/verifiers/propfindValues.py
===================================================================
--- CalDAVTester/trunk/verifiers/propfindValues.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/propfindValues.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -19,7 +19,7 @@
 Verifier that checks a propfind response for regex matches to property values.
 """
 
-from xml.etree.ElementTree import ElementTree, tostring
+from xml.etree.cElementTree import ElementTree, tostring
 from StringIO import StringIO
 import re
 

Modified: CalDAVTester/trunk/verifiers/xmlDataMatch.py
===================================================================
--- CalDAVTester/trunk/verifiers/xmlDataMatch.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/xmlDataMatch.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -19,7 +19,7 @@
 """
 
 from difflib import unified_diff
-from xml.etree.ElementTree import ElementTree, tostring
+from xml.etree.cElementTree import ElementTree, tostring
 import StringIO
 
 class Verifier(object):

Modified: CalDAVTester/trunk/verifiers/xmlElementMatch.py
===================================================================
--- CalDAVTester/trunk/verifiers/xmlElementMatch.py	2014-02-10 19:35:54 UTC (rev 12633)
+++ CalDAVTester/trunk/verifiers/xmlElementMatch.py	2014-02-10 19:57:08 UTC (rev 12634)
@@ -20,7 +20,7 @@
 """
 
 from pycalendar.icalendar.calendar import Calendar
-from xml.etree.ElementTree import ElementTree
+from xml.etree.cElementTree import ElementTree
 import json
 import StringIO
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/4f0e36fb/attachment.html>


More information about the calendarserver-changes mailing list