diff --git a/master/buildbot/status/logfile.py b/master/buildbot/status/logfile.py index 4c2264010e7..c67523174ae 100644 --- a/master/buildbot/status/logfile.py +++ b/master/buildbot/status/logfile.py @@ -16,6 +16,7 @@ import os from bz2 import BZ2File +from cStringIO import StringIO from gzip import GzipFile from buildbot import interfaces @@ -558,6 +559,7 @@ def finish(self): for w in watchers: w.callback(self) self.watchers = [] + return defer.succeed(None) def compressLog(self): logCompressionMethod = self.master.config.logCompressionMethod @@ -630,51 +632,27 @@ def __setstate__(self, d): self.finished = True -class HTMLLogFile: - filename = None +class HTMLLogFile(LogFile): def __init__(self, parent, name, logfilename, html): - self.step = parent - self.name = name - self.filename = logfilename - self.html = html - - def getName(self): - return self.name # set in BuildStepStatus.addLog - - def old_getStep(self): - return self.step - - def isFinished(self): - return True - - def waitUntilFinished(self): - return defer.succeed(self) + LogFile.__init__(self, parent, name, logfilename) + self.addStderr(html) + self.finish() def old_hasContents(self): return True - def old_getText(self): - return self.html # looks kinda like text - - def old_getChunks(self): - return [(STDERR, self.html)] - - def subscribe(self, receiver, catchup): - pass - - def unsubscribe(self, receiver): - pass - - def finish(self): - pass + def __setstate__(self, d): + self.__dict__ = d + self.watchers = [] + self.finishedWatchers = [] + self.finished = True - def __getstate__(self): - d = self.__dict__.copy() - del d['step'] - if "master" in d: - del d['master'] - return d + # buildbot <= 0.8.8 stored all html logs in the html property + if 'html' in self.__dict__: + buf = "%d:%d%s," % (len(self.html) + 1, STDERR, self.html) + self.openfile = StringIO(buf) + del self.__dict__['html'] def _tryremove(filename, timeout, retries): diff --git a/master/buildbot/test/unit/test_status_logfile.py b/master/buildbot/test/unit/test_status_logfile.py index aad9101758a..d96879490c7 100644 --- a/master/buildbot/test/unit/test_status_logfile.py +++ b/master/buildbot/test/unit/test_status_logfile.py @@ -15,6 +15,7 @@ from __future__ import with_statement +import cPickle import cStringIO import mock import os @@ -311,3 +312,101 @@ def test_compressLog_bz2(self): def test_compressLog_none(self): self.config.logCompressionMethod = None return self.do_test_compressLog('', expect_comp=False) + + +class TestHTMLLogFile(unittest.TestCase, dirs.DirsMixin): + + # The following script was used to pickle an 0.8.8 logfile. It was then + # base64 encoded to be safely embedded in this test class. + # + # from buildbot.status import logfile + # import pickle + # lf = logfile.HTMLLogFile('error.html', '123-error_html', 'You lost the game') + # with open('123-error_html', 'w') as f: + # pickle.dump(lf, f) + buildbot088pickle = ''' + KGlidWlsZGJvdC5zdGF0dXMubG9nZmlsZQpIVE1MTG9nRmlsZQpwMAooZHAyClMnaHRtbCcKcD + MKUyc8c3Bhbj5Zb3UgbG9zdCB0aGUgZ2FtZTwvc3Bhbj4nCnA0CnNTJ25hbWUnCnA1ClMnZXJy + b3IuaHRtbCcKcDYKc1MnZmlsZW5hbWUnCnA3ClMnMTIzLWVycm9yX2h0bWwnCnA4CnNiLg== + ''' + + def setUp(self): + step = self.build_step_status = mock.Mock(name='build_step_status') + self.basedir = step.build.builder.basedir = os.path.abspath('basedir') + self.setUpDirs(self.basedir) + self.logfile = logfile.HTMLLogFile(step, 'error.html', '123-error_html', 'You lost the game') + self.master = self.logfile.master = mock.Mock() + self.config = self.logfile.master.config = config.MasterConfig() + + def tearDown(self): + if self.logfile.openfile: + try: + self.logfile.openfile.close() + except: + pass # oh well, we tried + self.tearDownDirs() + + def pickle_and_restore(self): + pkl = cPickle.dumps(self.logfile) + self.logfile = cPickle.loads(pkl) + step = self.build_step_status + self.logfile.step = step + self.logfile.master = self.master + step.build.builder.basedir = self.basedir + + def test_unpickle(self): + self.pickle_and_restore() + + d = self.logfile.finish() + + def check(_): + fn = os.path.join('basedir', '123-error_html') + self.assertTrue(os.path.exists(fn)) + fp = open(fn, 'r') + self.assertEqual(fp.read(), '31:1You lost the game,') + fp.close() + + d.addCallback(check) + return d + + def test_unpickle_buildbot088pickle(self): + import base64 + s = base64.b64decode(self.buildbot088pickle) + self.logfile = cPickle.loads(s) + step = self.build_step_status + self.logfile.step = step + self.logfile.master = self.master + step.build.builder.basedir = self.basedir + + self.assertEqual(self.logfile.getName(), 'error.html') + self.assertEqual(self.logfile.old_getText(), 'You lost the game') + + def test_hasContents(self): + self.assertTrue(self.logfile.old_hasContents()) + + def test_getName(self): + self.assertEqual(self.logfile.getName(), 'error.html') + + def test_getStep(self): + self.assertEqual(self.logfile.old_getStep(), self.build_step_status) + + def test_isFinished(self): + self.assertTrue(self.logfile.isFinished()) + + def test_waitUntilFinished(self): + d = self.logfile.waitUntilFinished() + return d + + def test_getText(self): + self.assertEqual(self.logfile.old_getText(), 'You lost the game') + + def test_getFile(self): + fp = self.logfile.getFile() + fp.seek(0, 0) + self.assertEqual(fp.read(), '31:1You lost the game,') + + self.pickle_and_restore() + + fp = self.logfile.getFile() + fp.seek(0, 0) + self.assertEqual(fp.read(), '31:1You lost the game,')