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,')