aboutsummaryrefslogtreecommitdiffstats
path: root/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/process/mtrlogobserver.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/process/mtrlogobserver.py')
-rw-r--r--lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/process/mtrlogobserver.py474
1 files changed, 0 insertions, 474 deletions
diff --git a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/process/mtrlogobserver.py b/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/process/mtrlogobserver.py
deleted file mode 100644
index a49b90e2..00000000
--- a/lib/python2.7/site-packages/buildbot-0.8.8-py2.7.egg/buildbot/process/mtrlogobserver.py
+++ /dev/null
@@ -1,474 +0,0 @@
-# This file is part of Buildbot. Buildbot is free software: you can
-# redistribute it and/or modify it under the terms of the GNU General Public
-# License as published by the Free Software Foundation, version 2.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program; if not, write to the Free Software Foundation, Inc., 51
-# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Copyright Buildbot Team Members
-
-import sys
-import re
-from twisted.python import log
-from twisted.internet import defer
-from twisted.enterprise import adbapi
-from buildbot.process.buildstep import LogLineObserver
-from buildbot.steps.shell import Test
-
-class EqConnectionPool(adbapi.ConnectionPool):
- """This class works the same way as
-twisted.enterprise.adbapi.ConnectionPool. But it adds the ability to
-compare connection pools for equality (by comparing the arguments
-passed to the constructor).
-
-This is useful when passing the ConnectionPool to a BuildStep, as
-otherwise Buildbot will consider the buildstep (and hence the
-containing buildfactory) to have changed every time the configuration
-is reloaded.
-
-It also sets some defaults differently from adbapi.ConnectionPool that
-are more suitable for use in MTR.
-"""
- def __init__(self, *args, **kwargs):
- self._eqKey = (args, kwargs)
- return adbapi.ConnectionPool.__init__(self,
- cp_reconnect=True, cp_min=1, cp_max=3,
- *args, **kwargs)
-
- def __eq__(self, other):
- if isinstance(other, EqConnectionPool):
- return self._eqKey == other._eqKey
- else:
- return False
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
-
-class MtrTestFailData:
- def __init__(self, testname, variant, result, info, text, callback):
- self.testname = testname
- self.variant = variant
- self.result = result
- self.info = info
- self.text = text
- self.callback = callback
-
- def add(self, line):
- self.text+= line
-
- def fireCallback(self):
- return self.callback(self.testname, self.variant, self.result, self.info, self.text)
-
-
-class MtrLogObserver(LogLineObserver):
- """
- Class implementing a log observer (can be passed to
- BuildStep.addLogObserver().
-
- It parses the output of mysql-test-run.pl as used in MySQL,
- MariaDB, Drizzle, etc.
-
- It counts number of tests run and uses it to provide more accurate
- completion estimates.
-
- It parses out test failures from the output and summarises the results on
- the Waterfall page. It also passes the information to methods that can be
- overridden in a subclass to do further processing on the information."""
-
- _line_re = re.compile(r"^([-._0-9a-zA-z]+)( '[-_ a-zA-Z]+')?\s+(w[0-9]+\s+)?\[ (fail|pass) \]\s*(.*)$")
- _line_re2 = re.compile(r"^[-._0-9a-zA-z]+( '[-_ a-zA-Z]+')?\s+(w[0-9]+\s+)?\[ [-a-z]+ \]")
- _line_re3 = re.compile(r"^\*\*\*Warnings generated in error logs during shutdown after running tests: (.*)")
- _line_re4 = re.compile(r"^The servers were restarted [0-9]+ times$")
- _line_re5 = re.compile(r"^Only\s+[0-9]+\s+of\s+[0-9]+\s+completed.$")
-
- def __init__(self, textLimit=5, testNameLimit=16, testType=None):
- self.textLimit = textLimit
- self.testNameLimit = testNameLimit
- self.testType = testType
- self.numTests = 0
- self.testFail = None
- self.failList = []
- self.warnList = []
- LogLineObserver.__init__(self)
-
- def setLog(self, loog):
- LogLineObserver.setLog(self, loog)
- d= loog.waitUntilFinished()
- d.addCallback(lambda l: self.closeTestFail())
-
- def outLineReceived(self, line):
- stripLine = line.strip("\r\n")
- m = self._line_re.search(stripLine)
- if m:
- testname, variant, worker, result, info = m.groups()
- self.closeTestFail()
- self.numTests += 1
- self.step.setProgress('tests', self.numTests)
-
- if result == "fail":
- if variant == None:
- variant = ""
- else:
- variant = variant[2:-1]
- self.openTestFail(testname, variant, result, info, stripLine + "\n")
-
- else:
- m = self._line_re3.search(stripLine)
- if m:
- stuff = m.group(1)
- self.closeTestFail()
- testList = stuff.split(" ")
- self.doCollectWarningTests(testList)
-
- elif (self._line_re2.search(stripLine) or
- self._line_re4.search(stripLine) or
- self._line_re5.search(stripLine) or
- stripLine == "Test suite timeout! Terminating..." or
- stripLine.startswith("mysql-test-run: *** ERROR: Not all tests completed") or
- (stripLine.startswith("------------------------------------------------------------")
- and self.testFail != None)):
- self.closeTestFail()
-
- else:
- self.addTestFailOutput(stripLine + "\n")
-
- def openTestFail(self, testname, variant, result, info, line):
- self.testFail = MtrTestFailData(testname, variant, result, info, line, self.doCollectTestFail)
-
- def addTestFailOutput(self, line):
- if self.testFail != None:
- self.testFail.add(line)
-
- def closeTestFail(self):
- if self.testFail != None:
- self.testFail.fireCallback()
- self.testFail = None
-
- def addToText(self, src, dst):
- lastOne = None
- count = 0
- for t in src:
- if t != lastOne:
- dst.append(t)
- count += 1
- if count >= self.textLimit:
- break
-
- def makeText(self, done):
- if done:
- text = ["test"]
- else:
- text = ["testing"]
- if self.testType:
- text.append(self.testType)
- fails = self.failList[:]
- fails.sort()
- self.addToText(fails, text)
- warns = self.warnList[:]
- warns.sort()
- self.addToText(warns, text)
- return text
-
- # Update waterfall status.
- def updateText(self):
- self.step.step_status.setText(self.makeText(False))
-
- strip_re = re.compile(r"^[a-z]+\.")
-
- def displayTestName(self, testname):
-
- displayTestName = self.strip_re.sub("", testname)
-
- if len(displayTestName) > self.testNameLimit:
- displayTestName = displayTestName[:(self.testNameLimit-2)] + "..."
- return displayTestName
-
- def doCollectTestFail(self, testname, variant, result, info, text):
- self.failList.append("F:" + self.displayTestName(testname))
- self.updateText()
- self.collectTestFail(testname, variant, result, info, text)
-
- def doCollectWarningTests(self, testList):
- for t in testList:
- self.warnList.append("W:" + self.displayTestName(t))
- self.updateText()
- self.collectWarningTests(testList)
-
- # These two methods are overridden to actually do something with the data.
- def collectTestFail(self, testname, variant, result, info, text):
- pass
- def collectWarningTests(self, testList):
- pass
-
-class MTR(Test):
- """
- Build step that runs mysql-test-run.pl, as used in MySQL, Drizzle,
- MariaDB, etc.
-
- It uses class MtrLogObserver to parse test results out from the
- output of mysql-test-run.pl, providing better completion time
- estimates and summarising test failures on the waterfall page.
-
- It also provides access to mysqld server error logs from the test
- run to help debugging any problems.
-
- Optionally, it can insert into a database data about the test run,
- including details of any test failures.
-
- Parameters:
-
- textLimit
- Maximum number of test failures to show on the waterfall page
- (to not flood the page in case of a large number of test
- failures. Defaults to 5.
-
- testNameLimit
- Maximum length of test names to show unabbreviated in the
- waterfall page, to avoid excessive column width. Defaults to 16.
-
- parallel
- Value of --parallel option used for mysql-test-run.pl (number
- of processes used to run the test suite in parallel). Defaults
- to 4. This is used to determine the number of server error log
- files to download from the slave. Specifying a too high value
- does not hurt (as nonexisting error logs will be ignored),
- however if using --parallel value greater than the default it
- needs to be specified, or some server error logs will be
- missing.
-
- dbpool
- An instance of twisted.enterprise.adbapi.ConnectionPool, or None.
- Defaults to None. If specified, results are inserted into the database
- using the ConnectionPool.
-
- The class process.mtrlogobserver.EqConnectionPool subclass of
- ConnectionPool can be useful to pass as value for dbpool, to
- avoid having config reloads think the Buildstep is changed
- just because it gets a new ConnectionPool instance (even
- though connection parameters are unchanged).
-
- autoCreateTables
- Boolean, defaults to False. If True (and dbpool is specified), the
- necessary database tables will be created automatically if they do
- not exist already. Alternatively, the tables can be created manually
- from the SQL statements found in the mtrlogobserver.py source file.
-
- test_type
- test_info
- Two descriptive strings that will be inserted in the database tables if
- dbpool is specified. The test_type string, if specified, will also
- appear on the waterfall page."""
-
- renderables = [ 'mtr_subdir' ]
-
- def __init__(self, dbpool=None, test_type=None, test_info="",
- description=None, descriptionDone=None,
- autoCreateTables=False, textLimit=5, testNameLimit=16,
- parallel=4, logfiles = {}, lazylogfiles = True,
- warningPattern="MTR's internal check of the test case '.*' failed",
- mtr_subdir="mysql-test", **kwargs):
-
- if description is None:
- description = ["testing"]
- if test_type:
- description.append(test_type)
- if descriptionDone is None:
- descriptionDone = ["test"]
- if test_type:
- descriptionDone.append(test_type)
- Test.__init__(self, logfiles=logfiles, lazylogfiles=lazylogfiles,
- description=description, descriptionDone=descriptionDone,
- warningPattern=warningPattern, **kwargs)
- self.dbpool = dbpool
- self.test_type = test_type
- self.test_info = test_info
- self.autoCreateTables = autoCreateTables
- self.textLimit = textLimit
- self.testNameLimit = testNameLimit
- self.parallel = parallel
- self.mtr_subdir = mtr_subdir
- self.progressMetrics += ('tests',)
-
- def start(self):
- # Add mysql server logfiles.
- for mtr in range(0, self.parallel+1):
- for mysqld in range(1, 4+1):
- if mtr == 0:
- logname = "mysqld.%d.err" % mysqld
- filename = "var/log/mysqld.%d.err" % mysqld
- else:
- logname = "mysqld.%d.err.%d" % (mysqld, mtr)
- filename = "var/%d/log/mysqld.%d.err" % (mtr, mysqld)
- self.addLogFile(logname, self.mtr_subdir + "/" + filename)
-
- self.myMtr = self.MyMtrLogObserver(textLimit=self.textLimit,
- testNameLimit=self.testNameLimit,
- testType=self.test_type)
- self.addLogObserver("stdio", self.myMtr)
- # Insert a row for this test run into the database and set up
- # build properties, then start the command proper.
- d = self.registerInDB()
- d.addCallback(self.afterRegisterInDB)
- d.addErrback(self.failed)
-
- def getText(self, command, results):
- return self.myMtr.makeText(True)
-
- def runInteractionWithRetry(self, actionFn, *args, **kw):
- """
- Run a database transaction with dbpool.runInteraction, but retry the
- transaction in case of a temporary error (like connection lost).
-
- This is needed to be robust against things like database connection
- idle timeouts.
-
- The passed callable that implements the transaction must be retryable,
- ie. it must not have any destructive side effects in the case where
- an exception is thrown and/or rollback occurs that would prevent it
- from functioning correctly when called again."""
-
- def runWithRetry(txn, *args, **kw):
- retryCount = 0
- while(True):
- try:
- return actionFn(txn, *args, **kw)
- except txn.OperationalError:
- retryCount += 1
- if retryCount >= 5:
- raise
- excType, excValue, excTraceback = sys.exc_info()
- log.msg("Database transaction failed (caught exception %s(%s)), retrying ..." % (excType, excValue))
- txn.close()
- txn.reconnect()
- txn.reopen()
-
- return self.dbpool.runInteraction(runWithRetry, *args, **kw)
-
- def runQueryWithRetry(self, *args, **kw):
- """
- Run a database query, like with dbpool.runQuery, but retry the query in
- case of a temporary error (like connection lost).
-
- This is needed to be robust against things like database connection
- idle timeouts."""
-
- def runQuery(txn, *args, **kw):
- txn.execute(*args, **kw)
- return txn.fetchall()
-
- return self.runInteractionWithRetry(runQuery, *args, **kw)
-
- def registerInDB(self):
- if self.dbpool:
- return self.runInteractionWithRetry(self.doRegisterInDB)
- else:
- return defer.succeed(0)
-
- # The real database work is done in a thread in a synchronous way.
- def doRegisterInDB(self, txn):
- # Auto create tables.
- # This is off by default, as it gives warnings in log file
- # about tables already existing (and I did not find the issue
- # important enough to find a better fix).
- if self.autoCreateTables:
- txn.execute("""
-CREATE TABLE IF NOT EXISTS test_run(
- id INT PRIMARY KEY AUTO_INCREMENT,
- branch VARCHAR(100),
- revision VARCHAR(32) NOT NULL,
- platform VARCHAR(100) NOT NULL,
- dt TIMESTAMP NOT NULL,
- bbnum INT NOT NULL,
- typ VARCHAR(32) NOT NULL,
- info VARCHAR(255),
- KEY (branch, revision),
- KEY (dt),
- KEY (platform, bbnum)
-) ENGINE=innodb
-""")
- txn.execute("""
-CREATE TABLE IF NOT EXISTS test_failure(
- test_run_id INT NOT NULL,
- test_name VARCHAR(100) NOT NULL,
- test_variant VARCHAR(16) NOT NULL,
- info_text VARCHAR(255),
- failure_text TEXT,
- PRIMARY KEY (test_run_id, test_name, test_variant)
-) ENGINE=innodb
-""")
- txn.execute("""
-CREATE TABLE IF NOT EXISTS test_warnings(
- test_run_id INT NOT NULL,
- list_id INT NOT NULL,
- list_idx INT NOT NULL,
- test_name VARCHAR(100) NOT NULL,
- PRIMARY KEY (test_run_id, list_id, list_idx)
-) ENGINE=innodb
-""")
-
- revision = self.getProperty("got_revision")
- if revision is None:
- revision = self.getProperty("revision")
- typ = "mtr"
- if self.test_type:
- typ = self.test_type
- txn.execute("""
-INSERT INTO test_run(branch, revision, platform, dt, bbnum, typ, info)
-VALUES (%s, %s, %s, CURRENT_TIMESTAMP(), %s, %s, %s)
-""", (self.getProperty("branch"), revision,
- self.getProperty("buildername"), self.getProperty("buildnumber"),
- typ, self.test_info))
-
- return txn.lastrowid
-
- def afterRegisterInDB(self, insert_id):
- self.setProperty("mtr_id", insert_id)
- self.setProperty("mtr_warn_id", 0)
-
- Test.start(self)
-
- def reportError(self, err):
- log.msg("Error in async insert into database: %s" % err)
-
- class MyMtrLogObserver(MtrLogObserver):
- def collectTestFail(self, testname, variant, result, info, text):
- # Insert asynchronously into database.
- dbpool = self.step.dbpool
- run_id = self.step.getProperty("mtr_id")
- if dbpool == None:
- return defer.succeed(None)
- if variant == None:
- variant = ""
- d = self.step.runQueryWithRetry("""
-INSERT INTO test_failure(test_run_id, test_name, test_variant, info_text, failure_text)
-VALUES (%s, %s, %s, %s, %s)
-""", (run_id, testname, variant, info, text))
-
- d.addErrback(self.step.reportError)
- return d
-
- def collectWarningTests(self, testList):
- # Insert asynchronously into database.
- dbpool = self.step.dbpool
- if dbpool == None:
- return defer.succeed(None)
- run_id = self.step.getProperty("mtr_id")
- warn_id = self.step.getProperty("mtr_warn_id")
- self.step.setProperty("mtr_warn_id", warn_id + 1)
- q = ("INSERT INTO test_warnings(test_run_id, list_id, list_idx, test_name) " +
- "VALUES " + ", ".join(map(lambda x: "(%s, %s, %s, %s)", testList)))
- v = []
- idx = 0
- for t in testList:
- v.extend([run_id, warn_id, idx, t])
- idx = idx + 1
- d = self.step.runQueryWithRetry(q, tuple(v))
- d.addErrback(self.step.reportError)
- return d