def testFill(self): f_dict1 = {'name': 'a.txt', 'path': '/usr/a.txt'} f_dict2 = {'name': 'b.txt', 'path': '/lib/b.txt'} fd1 = FileData(f_dict1['name'], f_dict1['path']) fd2 = FileData(f_dict2['name'], f_dict2['path']) fd1.fill(f_dict1) fd2.fill(f_dict2) p_dict = {'name': 'p1', 'version': '1.0', 'pkg_license': 'Apache 2.0', 'checksum': 'abcxyz', 'pkg_licenses': ['MIT', 'GPL'], 'files': '/usr/a.txt\n/lib/b.txt'} p = Package('p1') p.fill(p_dict) self.assertEqual(p.name, 'p1') self.assertEqual(p.version, '1.0') self.assertEqual(p.pkg_license, 'Apache 2.0') self.assertEqual(p.checksum, 'abcxyz') self.assertEqual(p.pkg_licenses, ['MIT', 'GPL']) self.assertTrue(fd1.is_equal(p.files[0])) self.assertTrue(fd2.is_equal(p.files[1])) self.assertFalse(p.copyright) self.assertFalse(p.proj_url) self.assertEqual(len(p.origins.origins), 1) self.assertEqual(p.origins.origins[0].origin_str, 'p1') self.assertEqual(len(p.origins.origins[0].notices), 3) self.assertEqual(p.origins.origins[0].notices[0].message, "No metadata for key: copyright") self.assertEqual(p.origins.origins[0].notices[0].level, 'warning') self.assertEqual(p.origins.origins[0].notices[1].message, "No metadata for key: proj_url") self.assertEqual(p.origins.origins[0].notices[2].message, "No metadata for key: download_url")
def testFill(self): p_dict = { 'name': 'p1', 'version': '1.0', 'pkg_license': 'Apache 2.0', 'checksum': 'abcxyz', 'pkg_licenses': ['MIT', 'GPL'] } p = Package('p1') p.fill(p_dict) self.assertEqual(p.name, 'p1') self.assertEqual(p.version, '1.0') self.assertEqual(p.pkg_license, 'Apache 2.0') self.assertEqual(p.checksum, 'abcxyz') self.assertEqual(p.pkg_licenses, ['MIT', 'GPL']) self.assertFalse(p.copyright) self.assertFalse(p.proj_url) self.assertEqual(len(p.origins.origins), 1) self.assertEqual(p.origins.origins[0].origin_str, 'p1') self.assertEqual(len(p.origins.origins[0].notices), 4) self.assertEqual(p.origins.origins[0].notices[0].message, "No metadata for key: copyright") self.assertEqual(p.origins.origins[0].notices[0].level, 'warning') self.assertEqual(p.origins.origins[0].notices[1].message, "No metadata for key: proj_url") self.assertEqual(p.origins.origins[0].notices[2].message, "No metadata for key: download_url")
def testRemovePackage(self): p1 = Package('x') p2 = Package('y') self.layer.add_package(p1) self.layer.add_package(p2) self.assertTrue(self.layer.remove_package('y')) self.assertFalse(self.layer.remove_package('y'))
def testFill(self): p_dict = {'name': 'p1', 'version': '1.0', 'pkg_license': 'Apache 2.0', 'checksum': 'abcxyz', 'pkg_licenses': ['MIT', 'GPL'], 'files': [{'name': 'a.txt', 'path': '/usr/a.txt'}, {'name': 'b.txt', 'path': '/lib/b.txt'}], 'pkg_format': 'rpm', 'src_name': 'p1src', 'src_version': '1.0' } p = Package('p1') p.fill(p_dict) self.assertEqual(p.name, 'p1') self.assertEqual(p.version, '1.0') self.assertEqual(p.pkg_license, 'Apache 2.0') self.assertEqual(p.checksum, 'abcxyz') self.assertEqual(p.pkg_licenses, ['MIT', 'GPL']) self.assertEqual(len(p.files), 2) self.assertEqual(p.files[0].name, 'a.txt') self.assertEqual(p.files[0].path, '/usr/a.txt') self.assertFalse(p.copyright) self.assertFalse(p.proj_url) self.assertEqual(len(p.origins.origins), 1) self.assertEqual(p.origins.origins[0].origin_str, 'p1') self.assertEqual(len(p.origins.origins[0].notices), 3) self.assertEqual(p.origins.origins[0].notices[0].message, "No metadata for key: copyright") self.assertEqual(p.origins.origins[0].notices[0].level, 'warning') self.assertEqual(p.origins.origins[0].notices[1].message, "No metadata for key: proj_url") self.assertEqual(p.origins.origins[0].notices[2].message, "No metadata for key: download_url")
def setUp(self): self.p1 = Package('p1') self.p1.version = '1.0' self.p1.pkg_license = 'Apache 2.0' self.p1.src_url = 'github.com' self.p2 = Package('p2')
def load_image(self): l1 = ImageLayer('123abc', 'path/to/tar') self.name = 'testimage' self.tag = 'testtag' l1.add_package(Package('p1')) l1.add_package(Package('p2')) self._layers.append(l1)
def create_image_layer(report): """Given a report file, create an ImageLayer object with the metadata""" # expect a json input, raise an error if it is not content = {} try: with open(os.path.abspath(report), encoding='utf-8') as f: content = json.load(f) except OSError as err: logger.critical("Cannot access file %s: %s", report, err) raise ConsumerError(f"Error with given report file: {report}") from err except json.JSONDecodeError as err: logger.critical("Cannot parse JSON in file %s: %s", report, err) raise ConsumerError(f"Error with given report file: {report}") from err # we should have some content but it may be empty if not content: raise ConsumerError("No content consumed from given report file") # instantiate a layer and fill it layer = ImageLayer("") try: layer.os_guess = content['os_guess'] for pkg in content['packages']: pkg_obj = Package(pkg['name']) pkg_obj.fill(pkg) layer.add_package(pkg_obj) for filedict in content['files']: file_obj = FileData(filedict['name'], filedict['path']) file_obj.fill(filedict) layer.add_file(file_obj) return layer except ValueError as err: logger.critical("Cannot find required data in report: %s", err) return None
def load_from_cache(layer, redo=False): '''Given a layer object, check against cache to see if that layer id exists if yes then get the package list and load it in the layer and return true. If it doesn't exist return false. Default operation is to not redo the cache. Add notices to the layer's origins matching the origin_str''' loaded = False origin_layer = 'Layer: ' + layer.fs_hash[:10] if not layer.packages and not redo: # there are no packages in this layer and we are not repopulating the # cache, try to get it from the cache raw_pkg_list = cache.get_packages(layer.fs_hash) if raw_pkg_list: logger.debug('Loaded from cache: layer {}'.format( layer.fs_hash[:10])) message = formats.loading_from_cache.format( layer_id=layer.fs_hash[:10]) # add notice to the origin layer.origins.add_notice_to_origins(origin_layer, Notice(message, 'info')) for pkg_dict in raw_pkg_list: pkg = Package(pkg_dict['name']) pkg.fill(pkg_dict) layer.add_package(pkg) loaded = True return loaded
def testGetFilePaths(self): p1 = Package('package') f1 = FileData('test.java', 'abc/pqr/test.java') f2 = FileData('test.c', 'abc/pqr/test.c') p1.add_file(f1) p1.add_file(f2) self.assertEqual(p1.get_file_paths(), ['abc/pqr/test.java', 'abc/pqr/test.c'])
def setUp(self): self.p1 = Package('p1') self.p1.version = '1.0' self.p1.pkg_license = 'Apache 2.0' self.p1.copyright = 'All Rights Reserved' self.p1.proj_url = 'github.com' self.p1.download_url = 'https://github.com' self.p2 = Package('p2')
def fill_pkg_results(image_layer, pkg_list_dict): """Fill results from collecting package information into the image layer object""" if 'names' in pkg_list_dict and len(pkg_list_dict['names']) > 1: pkg_list = convert_to_pkg_dicts(pkg_list_dict) for pkg_dict in pkg_list: pkg = Package(pkg_dict['name']) pkg.fill(pkg_dict) image_layer.add_package(pkg)
def add_base_packages(image_layer, binary, shell): # pylint: disable=too-many-locals '''Given the image layer, the binary to invoke and shell: 1. get the listing from the base.yml 2. Invoke any commands against the base layer 3. Make a list of packages and add them to the layer''' origin_layer = 'Layer: ' + image_layer.fs_hash[:10] if image_layer.created_by: image_layer.origins.add_notice_to_origins( origin_layer, Notice( formats.layer_created_by.format( created_by=image_layer.created_by), 'info')) else: image_layer.origins.add_notice_to_origins( origin_layer, Notice(formats.no_created_by, 'warning')) origin_command_lib = formats.invoking_base_commands # find the binary listing = command_lib.get_base_listing(binary) if listing: # put info notice about what is going to be invoked snippet_msg = formats.invoke_for_base + '\n' + \ content.print_base_invoke(binary) image_layer.origins.add_notice_to_origins(origin_layer, Notice(snippet_msg, 'info')) shell, _ = command_lib.get_image_shell(listing) if not shell: shell = constants.shell # get all the packages in the base layer names, n_msg = command_lib.get_pkg_attr_list(shell, listing['names']) versions, v_msg = command_lib.get_pkg_attr_list( shell, listing['versions']) licenses, l_msg = command_lib.get_pkg_attr_list( shell, listing['licenses']) src_urls, u_msg = command_lib.get_pkg_attr_list( shell, listing['src_urls']) # add a notice to the image if something went wrong invoke_msg = n_msg + v_msg + l_msg + u_msg if invoke_msg: image_layer.origins.add_notice_to_origins( origin_layer, Notice(invoke_msg, 'error')) if names and len(names) > 1: for index, name in enumerate(names): pkg = Package(name) if len(versions) == len(names): pkg.version = versions[index] if len(licenses) == len(names): pkg.license = licenses[index] if len(src_urls) == len(names): pkg.src_url = src_urls[index] image_layer.add_package(pkg) # if there is no listing add a notice else: image_layer.origins.add_notice_to_origins( origin_command_lib, Notice(errors.no_listing_for_base_key.format(listing_key=binary), 'error'))
def get_package_from_dict(pkg_dict): """The SPDX JSON format contains a list of dictionaries, each containing the package metadata. For one package dictionary, return a Package object""" pkg_obj = Package(pkg_dict['name']) pkg_obj.version = ("" if pkg_dict['versionInfo'] == 'NOASSERTION' else pkg_dict['versionInfo']) pkg_obj.proj_url = ("" if pkg_dict['downloadLocation'] == 'NONE' else pkg_dict['downloadLocation']) pkg_obj.copyright = ("" if pkg_dict['copyrightText'] == 'NONE' else pkg_dict['copyrightText']) return pkg_obj
class TestClassPackage(unittest.TestCase): def setUp(self): self.package = Package('x') def tearDown(self): del self.package def testInstance(self): self.assertEqual(self.package.name, 'x') self.assertEqual(self.package.version, '') self.assertFalse(self.package.src_url) self.assertFalse(self.package.license) def testSetters(self): self.assertRaises(AttributeError, setattr, self.package, 'name', 'y') self.package.version = '1.0' self.assertEqual(self.package.version, '1.0') self.package.license = 'Apache 2.0' self.assertEqual(self.package.license, 'Apache 2.0') self.package.src_url = 'github.com' self.assertEqual(self.package.src_url, 'github.com') def testGetters(self): self.package.version = '1.0' self.package.license = 'Apache 2.0' self.package.src_url = 'github.com' self.assertEqual(self.package.name, 'x') self.assertEqual(self.package.version, '1.0') self.assertEqual(self.package.license, 'Apache 2.0') self.assertEqual(self.package.src_url, 'github.com') def testToDict(self): self.package.version = '1.0' self.package.license = 'Apache 2.0' self.package.src_url = 'github.com' a_dict = self.package.to_dict() self.assertEqual(a_dict['name'], 'x') self.assertEqual(a_dict['version'], '1.0') self.assertEqual(a_dict['license'], 'Apache 2.0') self.assertEqual(a_dict['src_url'], 'github.com') def testIsEqual(self): p = Package('x') p.license = 'TestLicense' p.version = '1.0' p.src_url = 'TestUrl' self.package.license = 'TestLicense' self.package.version = '2.0' self.package.src_url = 'TestUrl' self.assertFalse(self.package.is_equal(p)) p.version = '2.0' self.assertTrue(self.package.is_equal(p))
def add_base_packages(image_layer, binary, shell, work_dir=None, envs=None): '''Given the image layer, the binary to invoke and shell: 1. get the listing from the base.yml 2. Invoke any commands against the base layer 3. Make a list of packages and add them to the layer''' origin_layer = 'Layer {}'.format(image_layer.layer_index) if image_layer.created_by: image_layer.origins.add_notice_to_origins( origin_layer, Notice( formats.layer_created_by.format( created_by=image_layer.created_by), 'info')) else: image_layer.origins.add_notice_to_origins( origin_layer, Notice(formats.no_created_by, 'warning')) origin_command_lib = formats.invoking_base_commands # find the binary listing = command_lib.get_base_listing(binary) if listing: # put info notice about what is going to be invoked snippet_msg = (formats.invoke_for_base + '\n' + content.print_base_invoke(binary)) image_layer.origins.add_notice_to_origins(origin_layer, Notice(snippet_msg, 'info')) # get all the packages in the base layer pkg_dict, invoke_msg, warnings = collate_list_metadata( shell, listing, work_dir, envs) if listing.get("pkg_format") == "deb": pkg_dict["pkg_licenses"] = get_deb_package_licenses( pkg_dict["copyrights"]) if invoke_msg: image_layer.origins.add_notice_to_origins( origin_layer, Notice(invoke_msg, 'error')) if warnings: image_layer.origins.add_notice_to_origins( origin_command_lib, Notice(warnings, 'warning')) if 'names' in pkg_dict and len(pkg_dict['names']) > 1: pkg_list = convert_to_pkg_dicts(pkg_dict) for pkg_dict in pkg_list: pkg = Package(pkg_dict['name']) pkg.fill(pkg_dict) image_layer.add_package(pkg) remove_duplicate_layer_files(image_layer) # if there is no listing add a notice else: image_layer.origins.add_notice_to_origins( origin_command_lib, Notice(errors.no_listing_for_base_key.format(listing_key=binary), 'error'))
def setUp(self): self.p1 = Package('p1') self.p1.version = '1.0' self.p1.pkg_license = 'Apache 2.0' self.p1.copyright = 'All Rights Reserved' self.p1.proj_url = 'github.com' self.p1.download_url = 'https://github.com' self.p1.checksum = '123abc456' self.p1.pkg_licenses = ['MIT', 'GPL'] self.p1.pkg_format = 'deb' self.p1.src_name = 'p1src' self.p1.src_version = '1.0' self.p2 = Package('p2')
def testAddPackage(self): err = "Object type String, should be Package" p1 = Package('x') self.layer.add_package(p1) self.assertEqual(len(self.layer.packages), 1) with self.assertRaises(TypeError, msg=err): self.layer.add_package("not_a_package")
def execute_snippets(layer_obj, command_obj, prereqs): """Given in ImageLayer object, shell and binary to look up, find packages installed in the layer using the default method: For snippets, we will get the packages installed by the command""" # set up a notice origin for the layer origin_layer = 'Layer {}'.format(layer_obj.layer_index) # find packages for the command cmd_msg = (formats.invoke_for_snippets + '\n' + content.print_package_invoke(command_obj.name)) layer_obj.origins.add_notice_to_origins(origin_layer, Notice(cmd_msg, 'info')) pkg_list = filter.get_installed_package_names(command_obj) # collect all the dependencies for each package name all_pkgs = [] for pkg_name in pkg_list: pkg_invoke = command_lib.check_for_unique_package( prereqs.listing, pkg_name) deps, deps_msg = com.get_package_dependencies(pkg_invoke, pkg_name, prereqs.shell) if deps_msg: logger.warning(deps_msg) layer_obj.origins.add_notice_to_origins(origin_layer, Notice(deps_msg, 'error')) all_pkgs.append(pkg_name) all_pkgs.extend(deps) unique_pkgs = list(set(all_pkgs)) # get package metadata for each package name for pkg_name in unique_pkgs: pkg = Package(pkg_name) dcom.fill_package_metadata(pkg, pkg_invoke, prereqs.shell, layer_obj.get_layer_workdir(), prereqs.envs) layer_obj.add_package(pkg)
def add_snippet_packages(image_layer, command, pkg_listing, shell): '''Given an image layer object, a command object, the package listing and the shell used to invoke commands, add package metadata to the layer object. We assume the filesystem is already mounted and ready 1. Get the packages installed by the command 3. For each package get the dependencies 4. For each unique package name, find the metadata and add to the layer''' # set up a notice origin for the layer origin_layer = 'Layer: ' + image_layer.fs_hash[:10] # find packages for the command cmd_msg = formats.invoke_for_snippets + '\n' + \ content.print_package_invoke(command.name) image_layer.origins.add_notice_to_origins(origin_layer, Notice(cmd_msg, 'info')) pkg_list = get_installed_package_names(command) # collect all the dependencies for each package name all_pkgs = [] for pkg_name in pkg_list: pkg_invoke = command_lib.check_for_unique_package( pkg_listing, pkg_name) deps, deps_msg = get_package_dependencies(pkg_invoke, pkg_name, shell) if deps_msg: logger.warning(deps_msg) image_layer.origins.add_notice_to_origins( origin_layer, Notice(deps_msg, 'error')) all_pkgs.append(pkg_name) all_pkgs.extend(deps) unique_pkgs = list(set(all_pkgs)) # get package metadata for each package name for pkg_name in unique_pkgs: pkg = Package(pkg_name) fill_package_metadata(pkg, pkg_invoke, shell) image_layer.add_package(pkg)
def get_dockerfile_packages(): '''Given a Dockerfile return an approximate image object. This is mosty guess work and shouldn't be relied on for accurate information. Add Notice messages indicating as such: 1. Create an image with a placeholder repotag 2. For each RUN command, create a package list 3. Create layer objects with incremental integers and add the package list to that layer with a Notice about parsing 4. Return stub image''' stub_image = Image('easteregg:cookie') layer_count = 0 for cmd in dhelper.docker_commands: if cmd['instruction'] == 'RUN': layer_count = layer_count + 1 layer = ImageLayer(layer_count) install_commands, msg = \ common.filter_install_commands(cmd['value']) if msg: layer.origins.add_notice_to_origins(cmd['value'], Notice(msg, 'info')) pkg_names = [] for command in install_commands: pkg_names.append(common.get_installed_package_names(command)) for pkg_name in pkg_names: pkg = Package(pkg_name) # shell parser does not parse version pins yet # when that is enabled, Notices for no versions need to be # added here layer.add_package(pkg) return stub_image
def add_base_packages(image_layer, binary, shell): '''Given the image layer, the binary to invoke and shell: 1. get the listing from the base.yml 2. Invoke any commands against the base layer 3. Make a list of packages and add them to the layer''' origin_layer = 'Layer: ' + image_layer.fs_hash[:10] if image_layer.created_by: image_layer.origins.add_notice_to_origins( origin_layer, Notice( formats.layer_created_by.format( created_by=image_layer.created_by), 'info')) else: image_layer.origins.add_notice_to_origins( origin_layer, Notice(formats.no_created_by, 'warning')) origin_command_lib = formats.invoking_base_commands # find the binary listing = command_lib.get_base_listing(binary) if listing: # put info notice about what is going to be invoked snippet_msg = formats.invoke_for_base + '\n' + \ content.print_base_invoke(binary) image_layer.origins.add_notice_to_origins(origin_layer, Notice(snippet_msg, 'info')) shell, _ = command_lib.get_image_shell(listing) if not shell: shell = constants.shell # get all the packages in the base layer pkg_dict, invoke_msg, warnings = collate_list_metadata(shell, listing) if invoke_msg: image_layer.origins.add_notice_to_origins( origin_layer, Notice(invoke_msg, 'error')) if warnings: image_layer.origins.add_notice_to_origins( origin_command_lib, Notice(warnings, 'warning')) if 'names' in pkg_dict and len(pkg_dict['names']) > 1: pkg_list = convert_to_pkg_dicts(pkg_dict) for pkg_dict in pkg_list: pkg = Package(pkg_dict['name']) pkg.fill(pkg_dict) image_layer.add_package(pkg) # if there is no listing add a notice else: image_layer.origins.add_notice_to_origins( origin_command_lib, Notice(errors.no_listing_for_base_key.format(listing_key=binary), 'error'))
def testToDict(self): p1 = Package('x') self.layer.add_package(p1) a_dict = self.layer.to_dict() self.assertTrue(a_dict['']) self.assertEqual(len(a_dict['']['packages']), 1) self.assertEqual(a_dict['']['packages'][0]['name'], 'x') self.assertEqual(a_dict['']['tar_file'], 'path/to/tar')
def load_from_cache(layer, redo=False): '''Given a layer object, check against cache to see if that layer id exists if yes then get the package list and load it in the layer and return true. If it doesn't exist return false. Default operation is to not redo the cache. Add notices to the layer's origins matching the origin_str''' loaded = False if not layer.packages and not redo: # there are no packages in this layer and we are not repopulating the # cache, try to get it from the cache raw_pkg_list = cache.get_packages(layer.fs_hash) if raw_pkg_list: logger.debug('Loaded from cache: layer \"%s\"', layer.fs_hash[:10]) for pkg_dict in raw_pkg_list: pkg = Package(pkg_dict['name']) pkg.fill(pkg_dict) layer.add_package(pkg) load_notices_from_cache(layer) loaded = True return loaded
def get_scancode_package(package_dict): '''Given a package dictionary from the scancode results, return a Package object with the results''' package = Package(package_dict['name']) package.version = package_dict['version'] package.pkg_license = package_dict['declared_license'] package.copyright = package_dict['copyright'] package.proj_url = package_dict['repository_homepage_url'] package.download_url = package_dict['download_url'] package.licenses = [package_dict['declared_license'], package_dict['license_expression']] return package
def load_packages_from_cache(layer): '''Given a layer object, populate package level information''' loaded = False raw_pkg_list = cache.get_packages(layer.fs_hash) if raw_pkg_list: logger.debug('Loading packages from cache: layer \"%s\"', layer.fs_hash[:10]) for pkg_dict in raw_pkg_list: pkg = Package(pkg_dict['name']) pkg.fill(pkg_dict) # collect package origins if 'origins' in pkg_dict.keys(): for origin_dict in pkg_dict['origins']: for notice in origin_dict['notices']: pkg.origins.add_notice_to_origins( origin_dict['origin_str'], Notice(notice['message'], notice['level'])) layer.add_package(pkg) loaded = True return loaded
def testRemoveFile(self): p1 = Package('package') f1 = FileData('test.java', 'abc/pqr/test.java') f2 = FileData('test.c', 'abc/pqr/test.c') p1.add_file(f1) p1.add_file(f2) self.assertTrue(p1.remove_file('abc/pqr/test.java')) self.assertEqual(len(p1.files), 1) self.assertTrue(p1.remove_file('abc/pqr/test.c')) self.assertEqual(len(p1.files), 0) self.assertFalse(p1.remove_file('abc'))
def testToDict(self): p1 = Package('x') f1 = FileData('file1', 'path/to/file1') self.layer.add_package(p1) self.layer.add_file(f1) a_dict = self.layer.to_dict() self.assertEqual(a_dict['diff_id'], '123abc') self.assertEqual(len(a_dict['packages']), 1) self.assertEqual(a_dict['packages'][0]['name'], 'x') self.assertEqual(len(a_dict['files']), 1) self.assertEqual(a_dict['files'][0]['name'], 'file1') self.assertEqual(a_dict['files'][0]['path'], 'path/to/file1') self.assertEqual(a_dict['tar_file'], 'path/to/tar')
def testIsEqual(self): p = Package('p2') p.pkg_license = 'Testpkg_license' p.version = '1.0' p.pkg_format = 'rpm' p.proj_url = 'TestUrl' p.checksum = 'abcdef' self.p2.pkg_license = 'Testpkg_license' self.p2.version = '2.0' self.p2.pkg_format = 'rpm' self.p2.proj_url = 'TestUrl' self.p2.checksum = 'abcdef' self.assertFalse(self.p2.is_equal(p)) p.version = '2.0' self.assertTrue(self.p2.is_equal(p))
def testToDictTemplate(self): template1 = TestTemplate1() template2 = TestTemplate2() p1 = Package('x') self.layer.add_package(p1) dict1 = self.layer.to_dict(template1) dict2 = self.layer.to_dict(template2) self.assertEqual(len(dict1.keys()), 3) self.assertEqual(dict1['layer.diff'], '123abc') self.assertEqual(dict1['layer.tarfile'], 'path/to/tar') self.assertEqual(len(dict1['layer.packages']), 1) self.assertEqual(len(dict2.keys()), 4) self.assertFalse(dict2['notes']) self.assertFalse(dict2['layer.packages'][0]['notes'])
def testAddFile(self): p1 = Package('package') f1 = FileData('test.java', 'abc/pqr/test.java') f2 = FileData('test.c', 'abc/pqr/test.c') p1.add_file(f1) p1.add_file(f2) self.assertEqual(len(p1.files), 2) with self.assertRaises(TypeError): p1.add_file('sample.cpp')