blob: 3b2fb1063cf7c604c7a38bdf883faf6dd0fe5cdf [file] [log] [blame]
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Unittests for classes in package.py"""
import unittest
from bsp import package
from bsp import status
from bsp import subpackage
from core import user_config_stub
from core import util_stub
from test import stubs
class PackageTest(unittest.TestCase):
def setUp(self):
self.stub_os = stubs.StubOs()
self.stub_open = stubs.StubOpen(self.stub_os)
self.stub_hashlib = stubs.StubHashlib()
self.stub_shutil = stubs.StubShutil(self.stub_os)
self.stub_subprocess = stubs.StubSubprocess(stub_os=self.stub_os,
stub_open=self.stub_open)
self.stub_user_config = user_config_stub.StubUserConfig()
self.stub_util = util_stub.StubUtil()
package.os = self.stub_os
package.open = self.stub_open.open
package.shutil = self.stub_shutil
package.hashlib = self.stub_hashlib
package.tool.subprocess = self.stub_subprocess
package.user_config = self.stub_user_config
package.util = self.stub_util
self.subpackages = {
'subpackage1': subpackage.Subpackage('subpackage1', 'subdir1',
['license1', 'license2']),
'subpackage2': subpackage.Subpackage('subpackage2', 'sub/dir2',
['license2', 'license3'])
}
self.pkg = package.Package('name', 'remote', 'version',
self.subpackages)
# Special packages for downloading
self.tar_package = package.TarPackage('name', 'remote', 'hash',
self.subpackages)
self.git_package = package.GitPackage('name', 'remote', 'branch:hash',
self.subpackages)
# Helpers for subpackage status.
def setup_missing_subpackage(self):
# Package dir is there, but subpackage is not.
self.stub_os.path.should_be_dir.append(self.pkg.directory)
def setup_installed_subpackage(self, name):
# Like missing, but subpackage is present.
self.setup_missing_subpackage()
subdir_path = self.stub_os.path.join(self.pkg.directory,
self.pkg.subpackages[name].subdir)
self.stub_os.path.should_be_dir.append(subdir_path)
self.stub_os.path.should_exist.append(subdir_path)
def setup_unknown_subpackage(self, name, path):
# Like installed, but with a file at path.
self.setup_installed_subpackage(name)
self.stub_os.path.should_exist.append(path)
def setup_linked_subpackage(self, name, path):
# Like unknown, but with a link to the right place.
self.setup_unknown_subpackage(name, path)
self.stub_os.path.should_link[path] = self.stub_os.path.join(
self.pkg.directory, self.pkg.subpackages[name].subdir)
def setup_broken_link_subpackage(self, name, path):
# Like missing, but with a link to the missing dir.
self.setup_missing_subpackage()
self.stub_os.path.should_exist.append(path)
self.stub_os.path.should_link[path] = self.stub_os.path.join(
self.pkg.directory, self.pkg.subpackages[name].subdir)
def test_from_dict(self):
package_dict = {
'package_type': 'git',
'remote': 'github',
'version': 'branch:deadbeef',
'subpackages': {
'subpackage_1': {
'subdir': 'sub1',
'licenses': ['license.txt']
},
'subpackage_2': {
'subdir': 'sub2',
'licenses': []
}
}
}
pkg = package.Package.from_dict(package_dict, 'package_1')
self.assertIsInstance(pkg, package.Package)
self.assertEqual(len(pkg.subpackages), 2)
self.assertTrue('subpackage_1' in pkg.subpackages)
self.assertEqual(pkg.remote, 'github')
self.assertEqual(pkg.version, 'branch:deadbeef')
# Check git vs. tar packages.
self.assertIsInstance(pkg, package.GitPackage)
package_dict['package_type'] = 'tar'
pkg = package.Package.from_dict(package_dict, 'package_1')
self.assertIsInstance(pkg, package.TarPackage)
def test_from_dict_invalid(self):
package_dict = {
'package_type': 'not_git_or_tar',
'remote': 'github',
'version': 'branch:deadbeef',
'subpackages': {
'subpackage_1': {
'subdir': 'sub1',
'licenses': ['license.txt']
},
'subpackage_2': {
'subdir': 'sub2',
'licenses': []
}
}
}
# Type must be git or tar.
with self.assertRaises(ValueError):
package.Package.from_dict(package_dict, 'package_1')
def test_path(self):
self.assertEqual(
self.pkg.directory,
self.stub_os.path.join(self.stub_user_config.USER_CONFIG.bsp_dir,
self.pkg.name,
'source_versions',
self.pkg.version))
# If we update the user config, bsp dir should update accordingly;
# it shouldn't be cached.
self.stub_user_config.USER_CONFIG.bsp_dir = '/who/knows/where'
self.assertEqual(
self.pkg.directory,
self.stub_os.path.join(self.stub_user_config.USER_CONFIG.bsp_dir,
self.pkg.name,
'source_versions',
self.pkg.version))
def test_downloaded(self):
self.stub_os.path.should_be_dir = [self.pkg.directory]
self.assertTrue(self.pkg.is_downloaded())
def test_not_downloaded(self):
self.assertFalse(self.pkg.is_downloaded())
def test_subpackage_status_nonexistent_subpackage(self):
# Should get some sort of error about 'not_a_subpackage'
# not being a real subpackage of pkg.
with self.assertRaisesRegexp(package.NoSuchSubpackageError,
'not_a_subpackage'):
self.pkg.subpackage_status('not_a_subpackage', 'path')
def test_subpackage_status_not_installed(self):
self.assertEqual(self.pkg.subpackage_status('subpackage1', 'path')[0],
status.NOT_INSTALLED)
# Not installed with no path: still not installed.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.NOT_INSTALLED)
def test_subpackage_status_installed(self):
self.setup_installed_subpackage('subpackage1')
self.assertEqual(self.pkg.subpackage_status('subpackage1', 'path')[0],
status.INSTALLED)
# Installed with no path: still installed.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.INSTALLED)
def test_subpackage_status_linked(self):
self.setup_linked_subpackage('subpackage1', 'link')
self.assertEqual(self.pkg.subpackage_status('subpackage1', 'link')[0],
status.LINKED)
# Linked with no path: just installed.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.INSTALLED)
def test_subpackage_status_missing(self):
self.setup_missing_subpackage()
self.assertEqual(self.pkg.subpackage_status('subpackage1', 'path')[0],
status.MISSING)
# Missing with no path: still missing.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.MISSING)
def test_subpackage_status_broken_link(self):
self.setup_broken_link_subpackage('subpackage1', 'link')
self.assertEqual(self.pkg.subpackage_status('subpackage1', 'link')[0],
status.UNRECOGNIZED)
# Broken link because missing, with no path: just missing.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.MISSING)
def test_subpackage_status_unknown(self):
self.setup_unknown_subpackage('subpackage1', '???')
self.assertEqual(self.pkg.subpackage_status('subpackage1', '???')[0],
status.UNRECOGNIZED)
# Unknown but installed, with no path: just installed.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.INSTALLED)
def test_subpackage_status_incorrect_version(self):
self.setup_installed_subpackage('subpackage1')
self.pkg.version = 'different_version'
# Different versions are treated as completely different BSPs for now.
self.assertEqual(self.pkg.subpackage_status('subpackage1', 'path')[0],
status.NOT_INSTALLED)
# Still not installed with no path.
self.assertEqual(self.pkg.subpackage_status('subpackage1')[0],
status.NOT_INSTALLED)
def test_tarball_version(self):
file1 = stubs.StubFile('file1')
self.stub_os.path.should_exist = ['file1']
self.stub_open.files = {'file1': file1}
self.stub_hashlib.should_return = '<hash_of_file1>'
self.assertEqual(package.TarPackage.get_tarball_version('file1'),
'<hash_of_file1>')
def test_tarball_invalid_version(self):
self.stub_os.path.should_exist = []
with self.assertRaises(package.VersionError):
package.TarPackage.get_tarball_version('file1')
def test_already_downloaded(self):
self.stub_os.path.should_be_dir = [self.pkg.directory]
# Shouldn't do any downloading - no dirs should be made.
self.stub_os.should_makedirs = []
self.pkg.download()
def test_download_exception(self):
# pkg is the abstract Package class,
# download isn't fully implemented
self.stub_os.should_makedirs = [self.pkg.directory]
with self.assertRaises(NotImplementedError):
self.pkg.download()
# downloaded failed, should clean up the download directory.
self.assertFalse(self.stub_os.path.exists(self.pkg.directory))
def test_git_download_shallow(self):
self.stub_os.should_makedirs = [self.git_package.directory]
# Note: we don't test that tmp_dir/.git exists, because that's
# hard to write cleanly; so we assume it is created and exists
# as expected by clone/rev-parse.
ls_remote = self.stub_subprocess.AddCommand(ret_out='hash')
clone = self.stub_subprocess.AddFileCommand(pos_out=[-1])
rev_parse = self.stub_subprocess.AddCommand(ret_out='hash')
self.git_package.download()
self.assertTrue(self.stub_os.path.exists(self.git_package.directory))
ls_remote.AssertCallContained(['git', 'ls-remote'])
clone.AssertCallContained(['git', 'clone'])
rev_parse.AssertCallContained(['git', 'rev-parse'])
def test_git_download_full(self):
self.stub_os.should_makedirs = [self.git_package.directory]
# Note: we don't test that tmp_dir/.git exists, because that's
# hard to write cleanly; so we assume it is created and exists
# as expected by clone/rev-parse.
ls_remote = self.stub_subprocess.AddCommand(ret_out='wronghash')
clone = self.stub_subprocess.AddFileCommand(pos_out=[-1])
reset = self.stub_subprocess.AddCommand()
rev_parse = self.stub_subprocess.AddCommand(ret_out='hash')
self.git_package.download()
self.assertTrue(self.stub_os.path.exists(self.git_package.directory))
ls_remote.AssertCallContained(['git', 'ls-remote'])
clone.AssertCallContained(['git', 'clone'])
reset.AssertCallContained(['git', 'reset'])
rev_parse.AssertCallContained(['git', 'rev-parse'])
def test_git_failed_lsremote(self):
self.stub_os.should_makedirs = [self.git_package.directory]
ls_remote = self.stub_subprocess.AddCommand(ret_code=-1)
with self.assertRaises(package.RemoteError):
self.git_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.git_package.directory))
ls_remote.AssertCallContained(['git', 'ls-remote'])
def test_git_failed_download(self):
self.stub_os.should_makedirs = [self.git_package.directory]
ls_remote = self.stub_subprocess.AddCommand(ret_out='hash')
clone = self.stub_subprocess.AddFileCommand(pos_out=[-1],
ret_code=-1)
with self.assertRaises(package.DownloadError):
self.git_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.git_package.directory))
ls_remote.AssertCallContained(['git', 'ls-remote'])
clone.AssertCallContained(['git', 'clone'])
def test_git_failed_reset(self):
self.stub_os.should_makedirs = [self.git_package.directory]
# Note: we don't test that tmp_dir/.git exists, because that's
# hard to write cleanly; so we assume it is created and exists
# as expected by clone/rev-parse.
ls_remote = self.stub_subprocess.AddCommand(ret_out='wronghash')
clone = self.stub_subprocess.AddFileCommand(pos_out=[-1])
reset = self.stub_subprocess.AddCommand(ret_code=-1)
with self.assertRaises(package.VersionError):
self.git_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.git_package.directory))
ls_remote.AssertCallContained(['git', 'ls-remote'])
clone.AssertCallContained(['git', 'clone'])
reset.AssertCallContained(['git', 'reset'])
def test_git_wrong_version(self):
self.stub_os.should_makedirs = [self.git_package.directory]
# Note: we don't test that tmp_dir/.git exists, because that's
# hard to write cleanly; so we assume it is created and exists
# as expected by clone/rev-parse.
ls_remote = self.stub_subprocess.AddCommand(ret_out='hash')
clone = self.stub_subprocess.AddFileCommand(pos_out=[-1])
rev_parse = self.stub_subprocess.AddCommand(ret_out='wronghash')
with self.assertRaises(package.VersionError):
self.git_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.git_package.directory))
ls_remote.AssertCallContained(['git', 'ls-remote'])
clone.AssertCallContained(['git', 'clone'])
rev_parse.AssertCallContained(['git', 'rev-parse'])
def test_git_tarball(self):
self.stub_os.should_makedirs = [self.git_package.directory]
# Some sort of error about no tarballs for git packages
with self.assertRaisesRegexp(ValueError, 'tarball'):
self.git_package.download(tarball='file')
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.git_package.directory))
def test_tar_download(self):
self.stub_os.should_makedirs = [self.tar_package.directory]
curl = self.stub_subprocess.AddFileCommand(out_args=['-o'])
# Again, side effects of popen are hard to encode, so this doesn't
# reflect that the '-C' output directory will get populated by tar.
tar = self.stub_subprocess.AddFileCommand(in_args=['-C', '-xzf'])
self.stub_hashlib.should_return = self.tar_package.version
self.tar_package.download()
self.assertTrue(self.stub_os.path.exists(self.tar_package.directory))
curl.AssertCallContained(['curl', '-o'])
tar.AssertCallContained(['tar', '-C', '-xzf'])
def test_tar_failed_download(self):
self.stub_os.should_makedirs = [self.tar_package.directory]
curl = self.stub_subprocess.AddFileCommand(out_args=['-o'], ret_code=-1)
self.stub_hashlib.should_return = self.tar_package.version
with self.assertRaises(package.DownloadError):
self.tar_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.tar_package.directory))
curl.AssertCallContained(['curl', '-o'])
def test_tar_wrong_version(self):
self.stub_os.should_makedirs = [self.tar_package.directory]
curl = self.stub_subprocess.AddFileCommand(out_args=['-o'])
self.stub_hashlib.should_return = '0xWRONG'
with self.assertRaisesRegexp(package.VersionError,
'incorrect version 0xWRONG'):
self.tar_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.tar_package.directory))
curl.AssertCallContained(['curl', '-o'])
def test_tar_bad_unzip(self):
self.stub_os.should_makedirs = [self.tar_package.directory]
curl = self.stub_subprocess.AddFileCommand(out_args=['-o'])
tar = self.stub_subprocess.AddFileCommand(in_args=['-C', '-xzf'],
ret_code=-1)
self.stub_hashlib.should_return = self.tar_package.version
with self.assertRaises(package.UnzipError):
self.tar_package.download()
# Leave in a clean state
self.assertFalse(self.stub_os.path.exists(self.tar_package.directory))
curl.AssertCallContained(['curl', '-o'])
tar.AssertCallContained(['tar', '-C', '-xzf'])
def test_tar_tarball(self):
self.stub_os.path.should_exist = ['tar_file']
self.stub_open.files['tar_file'] = stubs.StubFile()
self.stub_os.should_makedirs = [self.tar_package.directory]
tar = self.stub_subprocess.AddFileCommand(in_args=['-C', '-xzf'])
self.stub_hashlib.should_return = self.tar_package.version
self.tar_package.download('tar_file')
self.assertTrue(self.stub_os.path.exists(self.tar_package.directory))
# Make sure the tar file is still where it was.
self.assertTrue(self.stub_os.path.exists('tar_file'))
tar.AssertCallContained(['tar', '-C', '-xzf'])
def test_uninstall(self):
dirs = [self.pkg.directory,
self.stub_os.path.join(self.pkg.directory, 'subdir1'),
self.stub_os.path.join(self.pkg.directory, 'subdir2'),
self.stub_os.path.join(self.pkg.directory, 'subdir2',
'deeper')]
self.stub_os.path.should_be_dir.extend(dirs)
self.stub_os.path.should_exist.extend(dirs)
files = [self.stub_os.path.join(dirname, 'a_file') for dirname in dirs]
self.stub_os.path.should_exist.extend(files)
self.assertTrue(self.pkg.is_downloaded())
self.pkg.uninstall()
for f in files:
self.assertFalse(self.stub_os.path.exists(f))
for d in dirs:
self.assertFalse(self.stub_os.path.exists(d))
self.assertFalse(self.stub_os.path.isdir(d))
self.assertFalse(self.pkg.is_downloaded())
def test_get_licenses(self):
licenses = self.pkg.get_licenses(['subpackage1', 'subpackage2'])
expected_names = ['name.subpackage1 - license1',
'name.subpackage1 - license2',
'name.subpackage2 - license2',
'name.subpackage2 - license3']
for l in licenses:
self.assertTrue(l.name in expected_names)
expected_names.remove(l.name)
self.assertEqual(expected_names, [])
def test_get_some_licenses(self):
licenses = self.pkg.get_licenses(['subpackage2'])
expected_names = ['name.subpackage2 - license2',
'name.subpackage2 - license3']
for l in licenses:
self.assertTrue(l.name in expected_names)
expected_names.remove(l.name)
self.assertEqual(expected_names, [])
def test_get_no_licenses(self):
licenses = self.pkg.get_licenses([])
self.assertEqual(licenses, [])
def test_link_subpackage(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist.append(subpackage_dir)
self.stub_os.should_create_link = [(subpackage_dir, 'link_origin')]
self.pkg.link_subpackage('subpackage1', 'link_origin')
# Correct link should be created.
self.assertTrue(self.stub_os.path.islink('link_origin'))
self.assertEqual(self.stub_os.readlink('link_origin'),
subpackage_dir)
def test_link_not_downloaded(self):
with self.assertRaises(package.NotDownloadedError):
self.pkg.link_subpackage('subpackage1', 'link_origin')
# No link should be created.
self.assertFalse(self.stub_os.path.exists('link_origin'))
def test_link_missing(self):
self.stub_os.path.should_be_dir = [self.pkg.directory]
with self.assertRaises(package.MissingDirectoryError):
self.pkg.link_subpackage('subpackage1', 'link_origin')
# No link should be created.
self.assertFalse(self.stub_os.path.exists('link_origin'))
def test_link_fail(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist.append(subpackage_dir)
with self.assertRaises(package.SubpackageLinkError):
self.pkg.link_subpackage('subpackage1', 'link_origin')
# No link should be created.
self.assertFalse(self.stub_os.path.exists('link_origin'))
def test_link_overwrite(self):
(_, subpackage_) = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist += [subpackage_dir, 'link_origin']
# Should still be a link error. The caller needs to check for this.
with self.assertRaises(package.SubpackageLinkError):
self.pkg.link_subpackage('subpackage1', 'link_origin')
# No link should be created, the file should still be there.
self.assertTrue(self.stub_os.path.exists('link_origin'))
self.assertFalse(self.stub_os.path.islink('link_origin'))
def test_unlink(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist += [subpackage_dir, 'link_origin']
self.stub_os.path.should_link['link_origin'] = subpackage_dir
self.pkg.unlink_subpackage('subpackage1', 'link_origin')
# Should be unlinked.
self.assertFalse(self.stub_os.path.islink('link_origin'))
self.assertFalse(self.stub_os.path.exists('link_origin'))
def test_unlink_wrong_link(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist += [subpackage_dir, 'link_origin',
'somewhere_else']
self.stub_os.path.should_link['link_origin'] = 'somewhere_else'
with self.assertRaises(package.SubpackageUnlinkError):
self.pkg.unlink_subpackage('subpackage1', 'link_origin')
# Original link should be in place.
self.assertTrue(self.stub_os.path.exists('link_origin'))
self.assertTrue(self.stub_os.path.islink('link_origin'))
self.assertEqual(self.stub_os.readlink('link_origin'),
'somewhere_else')
def test_unlink_no_link(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist += [subpackage_dir, 'link_origin']
with self.assertRaises(package.SubpackageUnlinkError):
self.pkg.unlink_subpackage('subpackage1', 'link_origin')
# Original lack of link should be in place.
self.assertTrue(self.stub_os.path.exists('link_origin'))
self.assertFalse(self.stub_os.path.islink('link_origin'))
def test_unlink_dead_link(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
# Dead links don't exist for os.path purposes.
self.stub_os.path.should_exist += [subpackage_dir]
self.stub_os.path.should_link['link_origin'] = 'somewhere_else'
with self.assertRaises(package.SubpackageUnlinkError):
self.pkg.unlink_subpackage('subpackage1', 'link_origin')
# Original dead link should be in place.
self.assertTrue(self.stub_os.path.islink('link_origin'))
self.assertEqual(self.stub_os.readlink('link_origin'),
'somewhere_else')
def test_unlink_nothing(self):
_, subpackage_ = self.pkg.subpackages.items()[0]
subpackage_dir = self.stub_os.path.join(self.pkg.directory,
subpackage_.subdir)
self.stub_os.path.should_be_dir = [self.pkg.directory, subpackage_dir]
self.stub_os.path.should_exist += [subpackage_dir]
with self.assertRaises(package.SubpackageUnlinkError):
self.pkg.unlink_subpackage('subpackage1', 'link_origin')
# Should still be nothing.
self.assertFalse(self.stub_os.path.islink('link_origin'))
self.assertFalse(self.stub_os.path.exists('link_origin'))