238
Representation of a directory tree that will be converted into an
241
TEST_REMOVED_FILE = 'removed'
242
TEST_SYSTEM_DIR = 'system/'
246
# Directory tree used to construct the tar file from.
247
# Also used to hold the TEST_REMOVED_FILE file.
248
self.dir = make_tmp_dir(tag='UpdateTree-tar-source')
250
self.removed_file = os.path.join(self.dir, self.TEST_REMOVED_FILE)
252
# Directory to place create/modify files into.
253
self.system_dir = os.path.join(self.dir, self.TEST_SYSTEM_DIR)
255
# Directory used to write the generated tarfile to.
256
# This directory should also be used to write the command file
258
self.tmp_dir = make_tmp_dir(tag='UpdateTree-cache')
261
if os.path.exists(self.dir):
262
shutil.rmtree(self.dir)
264
if os.path.exists(self.tmp_dir):
265
shutil.rmtree(self.tmp_dir)
267
def add_to_removed_file(self, removed_files):
269
Add the specified list of files to the removed file.
271
The 'removed' file is simply a file with a well-known name that
272
contains a list of files (one per line) to be removed from a
273
system before the rest of the update archive is unpacked.
275
:param removed_files: list of file names to add to the removed file.
278
# all files listed in the removed list must be system files
279
final = list(map(lambda a:
280
'{}{}'.format(self.TEST_SYSTEM_DIR, a), removed_files))
282
contents = "".join(final)
283
append_file(self.removed_file, contents)
285
def tar_filter(self, member):
287
Function to filter the tarinfo members before creating the
290
# members are created with relative paths (no leading slash)
291
path = os.sep + member.name
293
if member.name == '/.':
296
i = path.find(self.dir)
299
# remove the temporary directory elements
300
# (+1 for the os.sep we added above)
301
member.name = path[len(self.dir)+1:]
305
def create_archive(self, name):
307
Create an archive with the specified name from the UpdateTree
308
object. Also creates a fake signature file alongside the archive
309
file since this is currently required by the upgrader (although
310
it is not validated).
312
:param name: name of tarfile.
313
:param name: full path to xz archive to create.
314
:return full path to tar file with name @name.
317
self.tar_path = os.path.join(self.tmp_dir, name)
318
tar = tarfile.open(self.tar_path, 'w:xz')
320
# We can't just add recursively since that would attempt to add
321
# the parent directory. However, the real update tars don't
322
# include that, and attempting to ignore the parent directory
323
# results in an empty archive. So, walk the tree and add
325
for path, names, files in os.walk(self.dir):
327
full = os.path.join(path, file)
328
tar.add(full, recursive=False, filter=self.tar_filter)
329
if not files and not names:
330
# add (empty) directories
331
tar.add(path, recursive=False, filter=self.tar_filter)
335
signature = '{}.asc'.format(self.tar_path)
337
with open(signature, 'w') as fh:
338
fh.write('fake signature file')
343
class UbuntuCoreUpgraderTestCase(unittest.TestCase):
345
Base class for Upgrader tests.
347
Most of the tests follow a standard pattern:
349
1) Create an UpdateTree object:
351
update = UpdateTree()
353
This creates 2 temporary directories:
355
- self.system dir: Used as to generate an update archive from.
357
- self.tmp_dir: Used to write the generated archive file to. The
358
intention is that this directory should also be used to hold
361
2) Removal tests call update.add_to_removed_file(file) to add a
362
particular file to the removals file in the update archive.
364
3) Create/Modify tests create files below update.system_dir.
366
4) Create an update archive (which includes the removals file
367
and all files below update.system_dir):
369
archive = update.create_archive(self.TARFILE)
371
5) Create a command file (which tells the upgrader what to do
372
and which archive files to apply):
374
make_command_file(...)
376
6) Create a victim directory. This is a temporary directory where
377
the upgrade will happen.
379
7) Start the upgrade:
383
8) Perform checks on the victim directory to ensure that upgrade
384
did what was expected.
388
TARFILE = 'update.tar.xz'
390
# result of last test run. Hack to deal with fact that even if a
391
# test fails, unittest still calls .tearDown() (whomever thought
392
# that was a good idea...?)
399
# Create an object to hold the tree that will be converted into
400
# an upgrade archive.
401
self.update = UpdateTree()
403
# The directory which will have the update archive applied to
405
self.victim_dir = make_tmp_dir(tag='victim')
407
self.patch_get_root_partitions_by_label = \
408
patch('ubuntucoreupgrader.upgrader.get_root_partitions_by_label',
409
mock_get_root_partitions_by_label)
411
self.patch_make_mount_private = \
412
patch('ubuntucoreupgrader.upgrader.make_mount_private',
413
mock_make_mount_private)
415
self.mock_get_root_partitions_by_label = \
416
self.patch_get_root_partitions_by_label.start()
418
self.mock_make_mount_private = \
419
self.patch_make_mount_private.start()
426
if not self.currentResult.wasSuccessful():
427
# Do not clean up - the only sane option if a test fails.
430
self.update.destroy()
433
for d in (self.victim_dir,):
435
self.victim_dir = None
437
self.patch_get_root_partitions_by_label.stop()
438
self.patch_make_mount_private.stop()
440
def run(self, result=None):
441
self.currentResult = result
442
unittest.TestCase.run(self, result)
445
189
class UpgraderFileRemovalTestCase(UbuntuCoreUpgraderTestCase):
447
191
Test how the upgrader handles the removals file.