1
# Copyright 2009 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
6
This file contains the follwoing tests:
8
* Basic authentication handling (used to download private sources);
9
* Build log sanitization (removal of passwords from private buildlog);
10
* Build log(tail) mechanisms (limited output from the end of the buildlog).
16
__all__ = ['LaunchpadBuilddSlaveTests']
25
from canonical.buildd.tests.harness import (
26
BuilddSlaveTestSetup, BuilddTestCase)
30
"""Helper for reading the contents of a file."""
31
file_object = open(path)
33
return file_object.read()
38
class LaunchpadBuilddSlaveTests(BuilddTestCase):
39
"""Unit tests for scrubbing (removal of passwords) of buildlog files."""
41
def testBasicAuth(self):
42
"""Test that the auth handler is installed with the right details."""
43
url = "http://fakeurl/"
45
password = "fakepassword"
47
opener = self.slave.setupAuthHandler(url, user, password)
49
# Inspect the openers and ensure the wanted handler is installed.
50
basic_auth_handler = None
51
for handler in opener.handlers:
52
if isinstance(handler, urllib2.HTTPBasicAuthHandler):
53
basic_auth_handler = handler
56
basic_auth_handler is not None,
57
"No basic auth handler installed.")
59
password_mgr = basic_auth_handler.passwd
60
stored_user, stored_pass = password_mgr.find_user_password(None, url)
61
self.assertEqual(user, stored_user)
62
self.assertEqual(password, stored_pass)
64
def testBuildlogScrubbing(self):
65
"""Tests the buildlog scrubbing (removal of passwords from URLs)."""
66
# This is where the buildlog file lives.
67
log_path = self.slave.cachePath('buildlog')
69
# This is where the slave leaves the original/unsanitized
70
# buildlog file after scrubbing.
71
unsanitized_path = self.slave.cachePath('buildlog.unsanitized')
73
# Copy the fake buildlog file to the cache path.
74
shutil.copy(os.path.join(self.here, 'buildlog'), log_path)
76
# Invoke the slave's buildlog scrubbing method.
77
self.slave.sanitizeBuildlog(log_path)
79
# Read the unsanitized original content.
80
unsanitized = read_file(unsanitized_path).splitlines()
81
# Read the new, sanitized content.
82
clean = read_file(log_path).splitlines()
84
# Compare the scrubbed content with the unsanitized one.
85
differences = '\n'.join(difflib.unified_diff(unsanitized, clean))
87
# Read the expected differences from the prepared disk file.
88
expected = read_file(os.path.join(self.here, 'test_1.diff'))
90
# Make sure they match.
91
self.assertEqual(differences, expected)
93
def testLogtailScrubbing(self):
94
"""Test the scrubbing of the slave's getLogTail() output."""
96
# This is where the buildlog file lives.
97
log_path = self.slave.cachePath('buildlog')
99
# Copy the prepared, longer buildlog file so we can test lines
100
# that are chopped off in the middle.
101
shutil.copy(os.path.join(self.here, 'buildlog.long'), log_path)
103
# First get the unfiltered log tail output (which is the default
104
# behaviour because the BuildManager's 'is_archive_private'
105
# property is initialized to False).
106
self.slave.manager.is_archive_private = False
107
unsanitized = self.slave.getLogTail().splitlines()
109
# Make the slave believe we are building in a private archive to
110
# obtain the scrubbed log tail output.
111
self.slave.manager.is_archive_private = True
112
clean = self.slave.getLogTail().splitlines()
114
# Get the differences ..
115
differences = '\n'.join(difflib.unified_diff(unsanitized, clean))
117
# .. and the expected differences.
118
expected = read_file(os.path.join(self.here, 'test_2.diff'))
120
# Finally make sure what we got is what we expected.
121
self.assertEqual(differences, expected)
123
def testLogtail(self):
124
"""Tests the logtail mechanisms.
126
'getLogTail' return up to 2 KiB text from the current 'buildlog' file.
129
log_tail = self.slave.getLogTail()
130
self.assertEqual(len(log_tail), 0)
133
log_tail = self.slave.getLogTail()
134
self.assertEqual(len(log_tail), 1)
137
log_tail = self.slave.getLogTail()
138
self.assertEqual(len(log_tail), 2048)
141
log_tail = self.slave.getLogTail()
142
self.assertEqual(len(log_tail), 2048)
145
log_tail = self.slave.getLogTail()
146
self.assertEqual(len(log_tail), 2048)
148
def testLogtailWhenLogFileVanishes(self):
149
"""Slave.getLogTail doesn't get hurt if the logfile has vanished.
151
This is a common race-condition in our slaves, since they get
152
pooled all the time when they are building.
154
Sometimes the getLogTail calls coincides with the job
155
cleanup/sanitization, so there is no buildlog to inspect and thus
156
we expect an empty string to be returned instead of a explosion.
158
# Create some log content and read it.
160
log_tail = self.slave.getLogTail()
161
self.assertEqual(len(log_tail), 2048)
163
# Read it again for luck.
164
log_tail = self.slave.getLogTail()
165
self.assertEqual(len(log_tail), 2048)
167
# Remove the buildlog file
168
os.remove(self.slave.cachePath('buildlog'))
170
# Instead of shocking the getLogTail call, return an empty string.
171
log_tail = self.slave.getLogTail()
172
self.assertEqual(len(log_tail), 0)
175
class XMLRPCBuildDSlaveTests(unittest.TestCase):
178
super(XMLRPCBuildDSlaveTests, self).setUp()
179
self.slave = BuilddSlaveTestSetup()
181
self.server = xmlrpclib.Server('http://localhost:8221/rpc/')
184
self.slave.tearDown()
185
super(XMLRPCBuildDSlaveTests, self).tearDown()
187
def test_build_unknown_builder(self):
188
# If a bogus builder name is passed into build, it returns an
189
# appropriate error message and not just 'None'.
190
buildername = 'nonexistentbuilder'
191
status, info = self.server.build('foo', buildername, 'sha1', {}, {})
193
self.assertEqual('BuilderStatus.UNKNOWNBUILDER', status)
195
info is not None, "UNKNOWNBUILDER returns 'None' info.")
197
info.startswith("%s not in [" % buildername),
198
'UNKNOWNBUILDER info is "%s"' % info)