diff --git a/.gitignore b/.gitignore index da23f500..7b5906d7 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ docs/_build /.cache/ /.settings/ /tcl/ +/.python-version +/.tox/ diff --git a/.travis.yml b/.travis.yml index 73b875e0..91d9131d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ install: - ./configure etc/conf/dev script: - - bin/py.test -vvs tests + - bin/py.test -vvs notifications: irc: diff --git a/about.cfg b/about.cfg deleted file mode 100644 index cbcdfd61..00000000 --- a/about.cfg +++ /dev/null @@ -1,35 +0,0 @@ -# AboutCode configuration file - -[mappings] -# When you generate ABOUT files from a CSV inventory, use this section to map -# your column headers to standard ABOUT fields. -# For example if you have these columns: -# "Resource", "Component", "file_name", "file_version" -# ... you can define this mapping to ABOUT field names: -# about_resource= Resource -# name= Component -# version= file_version -# -# In addition, if there are fields that you want to put in the ABOUT -# files, please use the 'Custom Fields' to include these fields. -# -# Note: All the Custom Field's keys will be converted to lower case and -# all the spaces will be replaced by '_' -# -# -# See http://www.dejacode.org/about_spec_v0.8.1.html for more information -# - -# Essential Fields -about_file: Directory/Filename - -# Mandatory Fields -name: Component -version: Confirmed Version - -# Optional Fields -copyright: Confirmed Copyright - - -# Custom Fields -confirmed_license: Confirmed License diff --git a/etc/scripts/irc-notify.py.ABOUT b/etc/scripts/irc-notify.py.ABOUT index 5637531e..f0fc4c74 100644 --- a/etc/scripts/irc-notify.py.ABOUT +++ b/etc/scripts/irc-notify.py.ABOUT @@ -1,3 +1,4 @@ +about_resource: irc-notify.py name: irc-notify.py version: def54f8166089b733d166665fdabcad4cdc526d8 download_url: https://raw.githubusercontent.com/gridsync/gridsync/def54f8166089b733d166665fdabcad4cdc526d8/misc/irc-notify.py @@ -5,7 +6,7 @@ description: Quick and dirty IRC notification script. homepage_url: https://github.com/gridsync/gridsync owner: Christopher R. Wood copyright: Copyright (C) 2015-2016 Christopher R. Wood -dje_license_key: gpl-2.0-plus +license_expression: gpl-2.0-plus license_text_file: gpl-2.0.LICENSE notice_text: | This program is free software; you can redistribute it and/or modify it under the diff --git a/setup.cfg b/setup.cfg index b19142d9..c29a174d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,24 +10,23 @@ release = clean --all sdist --formats=bztar,zip bdist_wheel [tool:pytest] norecursedirs = .git + .cache + .settings bin dist + dist build _build - dist - local - ci docs man share - samples - .cache - .settings + example etc Include include Lib lib + local Scripts thirdparty tmp diff --git a/setup.py b/setup.py index 33edfaa7..93d87dc0 100644 --- a/setup.py +++ b/setup.py @@ -68,9 +68,15 @@ def read(*names, **kwargs): ], install_requires=[ 'jinja2 >= 2.9, < 3.0', + 'click >= 6.7, < 7.0', + "backports.csv ; python_version<'3.6'", - 'PyYAML >= 3.0, < 4.0', + + # required by saneyaml + 'PyYAML >= 3.11, <=3.13', + 'saneyaml', + 'boolean.py >= 3.5, < 4.0', 'license_expression >= 0.94, < 1.0', ], @@ -79,7 +85,7 @@ def read(*names, **kwargs): }, entry_points={ 'console_scripts': [ - 'about=attributecode.cmd:cli', + 'about=attributecode.cmd:about', ] }, ) diff --git a/src/attributecode/__init__.py b/src/attributecode/__init__.py index b567b74d..3f0033f0 100644 --- a/src/attributecode/__init__.py +++ b/src/attributecode/__init__.py @@ -20,16 +20,19 @@ from collections import namedtuple import logging +import os try: - unicode # Python 2 -except NameError: - unicode = str # Python 3 #NOQA + # Python 2 + unicode # NOQA +except NameError: # pragma: nocover + # Python 3 + unicode = str # NOQA +import saneyaml __version__ = '3.3.0' - __about_spec_version__ = '3.1' __copyright__ = """ @@ -53,9 +56,9 @@ class Error(namedtuple('Error', ['severity', 'message'])): def __new__(self, severity, message): if message: if isinstance(message, unicode): - message = clean_string(message) + message = self._clean_string(message) else: - message = clean_string(unicode(repr(message), encoding='utf-8')) + message = self._clean_string(unicode(repr(message), encoding='utf-8')) message = message.strip('"') return super(Error, self).__new__( @@ -63,29 +66,36 @@ def __new__(self, severity, message): def __repr__(self, *args, **kwargs): sev = severities[self.severity] - msg = clean_string(repr(self.message)) + msg = self._clean_string(repr(self.message)) return 'Error(%(sev)s, %(msg)s)' % locals() - -def clean_string(s): - """ - Return a cleaned string for `s`, stripping eventual "u" prefixes - from unicode representations. - """ - if not s: + def to_dict(self, *args, **kwargs): + """ + Return an ordered mapping of self. + """ + return self._asdict() + + @staticmethod + def _clean_string(s): + """ + Return a cleaned string for `s`, stripping eventual "u" prefixes + from unicode representations. + """ + if not s: + return s + if s.startswith(('u"', "u'")): + s = s.lstrip('u') + s = s.replace('[u"', '["') + s = s.replace("[u'", "['") + s = s.replace("(u'", "('") + s = s.replace("(u'", "('") + s = s.replace("{u'", "{'") + s = s.replace("{u'", "{'") + s = s.replace(" u'", " '") + s = s.replace(" u'", " '") + s = s.replace("\\\\", "\\") return s - if s.startswith(('u"', "u'")): - s = s.lstrip('u') - s = s.replace('[u"', '["') - s = s.replace("[u'", "['") - s = s.replace("(u'", "('") - s = s.replace("(u'", "('") - s = s.replace("{u'", "{'") - s = s.replace("{u'", "{'") - s = s.replace(" u'", " '") - s = s.replace(" u'", " '") - s = s.replace("\\\\", "\\") - return s + # modeled after the logging levels CRITICAL = 50 @@ -97,10 +107,14 @@ def clean_string(s): severities = { - CRITICAL : u'CRITICAL', - ERROR : u'ERROR', - WARNING : u'WARNING', - INFO : u'INFO', - DEBUG : u'DEBUG', - NOTSET : u'NOTSET' - } + CRITICAL : 'CRITICAL', + ERROR : 'ERROR', + WARNING : 'WARNING', + INFO : 'INFO', + DEBUG : 'DEBUG', + NOTSET : 'NOTSET' +} + + +DEFAULT_MAPPING = os.path.join(os.path.abspath( + os.path.dirname(__file__)), 'mapping.config') diff --git a/src/attributecode/__main__.py b/src/attributecode/__main__.py index 977d906f..0c0ecadf 100644 --- a/src/attributecode/__main__.py +++ b/src/attributecode/__main__.py @@ -19,6 +19,6 @@ from __future__ import unicode_literals -if __name__ == '__main__': +if __name__ == '__main__': # pragma: nocover from attributecode import cmd cmd.cli() diff --git a/src/attributecode/api.py b/src/attributecode/api.py index 62f60949..5eb4cef6 100644 --- a/src/attributecode/api.py +++ b/src/attributecode/api.py @@ -20,16 +20,23 @@ import json -try: # Python 2 - from urllib import urlencode, quote - from urllib2 import urlopen, Request, HTTPError -except ImportError: # Python 3 - from urllib.parse import urlencode, quote - from urllib.request import urlopen, Request - from urllib.error import HTTPError - from attributecode import ERROR from attributecode import Error +from attributecode.util import python2 + + +if python2: # pragma: nocover + from urllib import quote # NOQA + from urllib import urlencode # NOQA + from urllib2 import HTTPError # NOQA + from urllib2 import Request # NOQA + from urllib2 import urlopen # NOQA +else: # pragma: nocover + from urllib.parse import quote # NOQA + from urllib.parse import urlencode # NOQA + from urllib.request import Request # NOQA + from urllib.request import urlopen # NOQA + from urllib.error import HTTPError # NOQA """ @@ -37,11 +44,11 @@ """ -def request_license_data(url, api_key, license_key): +# FIXME: args should start with license_key +def request_license_data(api_url, api_key, license_key): """ - Return a dictionary of license data. - Send a request to a given API URL to gather license data for - license_key, authenticating through an api_key. + Return a tuple of (dictionary of license data, list of errors) given a + `license_key`. Send a request to `api_url` authenticating with `api_key`. """ headers = { 'Authorization': 'Token %s' % api_key, @@ -52,9 +59,10 @@ def request_license_data(url, api_key, license_key): 'format': 'json' } - url = url.rstrip('/') - encoded_payload = urlencode(payload) - full_url = '%(url)s/?%(encoded_payload)s' % locals() + api_url = api_url.rstrip('/') + payload = urlencode(payload) + + full_url = '%(api_url)s/?%(payload)s' % locals() # handle special characters in URL such as space etc. quoted_url = quote(full_url, safe="%/:=&?~#+!$,;'@()*[]") @@ -64,10 +72,12 @@ def request_license_data(url, api_key, license_key): request = Request(quoted_url, headers=headers) response = urlopen(request) response_content = response.read().decode('utf-8') + # FIXME: this should be an ordered dict license_data = json.loads(response_content) if not license_data['results']: msg = u"Invalid 'license': %s" % license_key errors.append(Error(ERROR, msg)) + except HTTPError as http_e: # some auth problem if http_e.code == 403: @@ -80,20 +90,29 @@ def request_license_data(url, api_key, license_key): # this exception. msg = u"Invalid 'license': %s" % license_key errors.append(Error(ERROR, msg)) + except Exception as e: errors.append(Error(ERROR, str(e))) + finally: - license_data = license_data.get('results')[0] if license_data.get('count') == 1 else {} + if license_data.get('count') == 1: + license_data = license_data.get('results')[0] + else: + license_data = {} return license_data, errors -def get_license_details_from_api(url, api_key, license_key): +# FIXME: args should start with license_key +def get_license_details_from_api(api_url, api_key, license_key): """ - Return the license_text of a given license_key using an API request. - Return an empty string if the text is not available. + Return a tuple of license data given a `license_key` using the `api_url` + authenticating with `api_key`. + The details are a tuple of (license_name, license_key, license_text, errors) + where errors is a list of strings. + Missing values are provided as empty strings. """ - license_data, errors = request_license_data(url, api_key, license_key) + license_data, errors = request_license_data(api_url, api_key, license_key) license_name = license_data.get('name', '') license_text = license_data.get('full_text', '') license_key = license_data.get('key', '') diff --git a/src/attributecode/attrib.py b/src/attributecode/attrib.py index 820123ad..8a078f0e 100644 --- a/src/attributecode/attrib.py +++ b/src/attributecode/attrib.py @@ -18,35 +18,48 @@ from __future__ import print_function from __future__ import unicode_literals -import codecs import collections import datetime +import io import os -from posixpath import basename -from posixpath import dirname -from posixpath import exists -from posixpath import join import jinja2 -import attributecode +from attributecode import CRITICAL from attributecode import ERROR from attributecode import Error from attributecode.licenses import COMMON_LICENSES from attributecode.model import parse_license_expression from attributecode.util import add_unc +from attributecode.util import get_about_file_path -def generate(abouts, template_string=None, vartext_dict=None): +# FIXME: the template dir should be outside the code tree +DEFAULT_TEMPLATE_FILE = os.path.join( + os.path.dirname(os.path.realpath(__file__)), 'templates', 'default_html.template') + + +def generate(abouts, template=None, variables=None): """ - Generate and return attribution text from a list of About objects and a - template string. - The returned rendered text may contain template processing error messages. + Generate an attribution text from an `abouts` list of About objects, a + `template` template text and a `variables` optional mapping of extra + variables. + + Return a tuple of (error, attribution text) where error is an Error object + or None and attribution text is the generated text or None. """ - syntax_error = check_template(template_string) - if syntax_error: - return 'Template validation error at line: %r: %r' % (syntax_error) - template = jinja2.Template(template_string) + rendered = None + error = None + template_error = check_template(template) + if template_error: + lineno, message = template_error + error = Error( + CRITICAL, + 'Template validation error at line: {lineno}: "{message}"'.format(**locals()) + ) + return error, None + + template = jinja2.Template(template) try: captured_license = [] @@ -105,18 +118,25 @@ def generate(abouts, template_string=None, vartext_dict=None): # Get the current UTC time utcnow = datetime.datetime.utcnow() - rendered = template.render(abouts=abouts, common_licenses=COMMON_LICENSES, - license_key_and_context=sorted_license_key_and_context, - license_file_name_and_key=license_file_name_and_key, - license_key_to_license_name=license_key_to_license_name, - license_name_to_license_key=license_name_to_license_key, - utcnow=utcnow, vartext_dict=vartext_dict) + rendered = template.render( + abouts=abouts, common_licenses=COMMON_LICENSES, + license_key_and_context=sorted_license_key_and_context, + license_file_name_and_key=license_file_name_and_key, + license_key_to_license_name=license_key_to_license_name, + license_name_to_license_key=license_name_to_license_key, + utcnow=utcnow, + variables=variables + ) except Exception as e: - line = getattr(e, 'lineno', None) - ln_msg = ' at line: %r' % line if line else '' - err = getattr(e, 'message', '') - return 'Template processing error%(ln_msg)s: %(err)r' % locals() - return rendered + lineno = getattr(e, 'lineno', '') or '' + if lineno: + lineno = ' at line: {}'.format(lineno) + err = getattr(e, 'message', '') or '' + error = Error( + CRITICAL, + 'Template processing error {lineno}: {err}'.format(**locals()), + ) + return error, rendered def check_template(template_string): @@ -130,62 +150,60 @@ def check_template(template_string): return e.lineno, e.message -# FIXME: the template dir should be outside the code tree -default_template = join(os.path.dirname(os.path.realpath(__file__)), - 'templates', 'default_html.template') - -def generate_from_file(abouts, template_loc=None, vartext_dict=None): +def generate_from_file(abouts, template_loc=DEFAULT_TEMPLATE_FILE, variables=None): """ - Generate and return attribution string from a list of About objects and a - template location. + Generate an attribution text from an `abouts` list of About objects, a + `template_loc` template file location and a `variables` optional + mapping of extra variables. + + Return a tuple of (error, attribution text) where error is an Error object + or None and attribution text is the generated text or None. """ - if not template_loc: - template_loc = default_template + template_loc = add_unc(template_loc) - with codecs.open(template_loc, 'rb', encoding='utf-8') as tplf: + with io.open(template_loc, encoding='utf-8') as tplf: tpls = tplf.read() - return generate(abouts, template_string=tpls, vartext_dict=vartext_dict) + return generate(abouts, template=tpls, variables=variables) -def generate_and_save(abouts, output_location, use_mapping=False, mapping_file=None, - template_loc=None, inventory_location=None, vartext=None): +def generate_and_save(abouts, output_location, template_loc=None, variables=None, + mapping_file=None, inventory_location=None): """ - Generate attribution file using the `abouts` list of About object - at `output_location`. - - Optionally use the mapping.config file if `use_mapping` is True. + Generate an attribution text from an `abouts` list of About objects, a + `template_loc` template file location and a `variables` optional + mapping of extra variables. Save the generated attribution text in the + `output_location` file. + Return a list of Error objects if any. - Optionally use the custom mapping file if mapping_file is set. + FIXME: these three argument are too complex: - Use the optional `template_loc` custom temaplte or a default template. - - Optionally filter `abouts` object based on the inventory JSON or - CSV at `inventory_location`. + Optionally use the `mapping_file` mapping config if provided. + Optionally filter `abouts` object based on the inventory JSON or CSV at `inventory_location`. """ updated_abouts = [] lstrip_afp = [] afp_list = [] not_match_path = [] errors = [] - vartext_dict = {} if not inventory_location: updated_abouts = abouts - # Do the following if an filter list (inventory_location) is provided + + # FIXME: this is too complex + # Do the following if a filter list (inventory_location) is provided else: - if not exists(inventory_location): + if not os.path.exists(inventory_location): # FIXME: this message does not make sense msg = (u'"INVENTORY_LOCATION" does not exist. Generation halted.') errors.append(Error(ERROR, msg)) return errors if inventory_location.endswith('.csv') or inventory_location.endswith('.json'): - # FIXME: we should use the same inventory lodaing that we use everywhere!!!! + # FIXME: we should use the same inventory loading that we use everywhere try: # Return a list which contains only the about file path - about_list = attributecode.util.get_about_file_path( - inventory_location, use_mapping=use_mapping, mapping_file=mapping_file) + about_list = get_about_file_path(inventory_location, mapping_file=mapping_file) # FIXME: why catching all exceptions? except Exception: # 'about_file_path' key/column doesn't exist @@ -231,37 +249,42 @@ def generate_and_save(abouts, output_location, use_mapping=False, mapping_file=N # Parse license_expression and save to the license list for about in updated_abouts: - if about.license_expression.value: - special_char_in_expression, lic_list = parse_license_expression(about.license_expression.value) - if special_char_in_expression: - msg = (u"The following character(s) cannot be in the licesne_expression: " + - str(special_char_in_expression)) - errors.append(Error(ERROR, msg)) - else: - about.license_key.value = lic_list + if not about.license_expression.value: + continue + special_char_in_expression, lic_list = parse_license_expression(about.license_expression.value) + if special_char_in_expression: + msg = (u"The following character(s) cannot be in the licesne_expression: " + + str(special_char_in_expression)) + errors.append(Error(ERROR, msg)) + else: + about.license_key.value = lic_list - # Parse the vartext and save to the vartext dictionary - if vartext: - for var in vartext: - key = var.partition('=')[0] - value = var.partition('=')[2] - vartext_dict[key] = value + rendering_error, rendered = generate_from_file( + updated_abouts, + template_loc=template_loc, + variables=variables + ) - rendered = generate_from_file(updated_abouts, template_loc=template_loc, vartext_dict=vartext_dict) + if rendering_error: + errors.append(rendering_error) - if rendered: + if rendered: output_location = add_unc(output_location) - with codecs.open(output_location, 'wb', encoding='utf-8') as of: + with io.open(output_location, 'w', encoding='utf-8') as of: of.write(rendered) return errors +# FIXME: this function purpose needs to be explained. def as_about_paths(paths): """ Return a list of paths to .ABOUT files from a list of `paths` strings. """ + from posixpath import basename + from posixpath import dirname + about_paths = [] for path in paths: if path.endswith('.ABOUT'): diff --git a/src/attributecode/cmd.py b/src/attributecode/cmd.py index 04384a17..7f2eda2a 100644 --- a/src/attributecode/cmd.py +++ b/src/attributecode/cmd.py @@ -18,29 +18,36 @@ from __future__ import print_function from __future__ import unicode_literals -import errno +from collections import defaultdict +from functools import partial +import io import logging import os -from os.path import exists, join import sys import click # silence unicode literals warnings click.disable_unicode_literals_warning = True +from attributecode import WARNING +from attributecode.util import unique + from attributecode import __about_spec_version__ from attributecode import __version__ -from attributecode.attrib import generate_and_save as attrib_generate_and_save -from attributecode.gen import generate as gen_generate -from attributecode import model +from attributecode import DEFAULT_MAPPING from attributecode import severities +from attributecode.attrib import check_template +from attributecode.attrib import DEFAULT_TEMPLATE_FILE +from attributecode.attrib import generate_and_save as generate_attribution_doc +from attributecode.gen import generate as generate_about_files +from attributecode.model import collect_inventory +from attributecode.model import write_output from attributecode.util import extract_zip -from attributecode.util import to_posix from attributecode.util import inventory_filter __copyright__ = """ - Copyright (c) 2013-2017 nexB Inc. All rights reserved. + Copyright (c) 2013-2018 nexB Inc and others. All rights reserved. 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 @@ -62,18 +69,19 @@ ''' % locals() -problematic_errors = [u'CRITICAL', u'ERROR', u'WARNING'] - def print_version(): click.echo('Running aboutcode-toolkit version ' + __version__) class AboutCommand(click.Command): + """ + An enhanced click Command working around some Click quirk. + """ def main(self, args=None, prog_name=None, complete_var=None, standalone_mode=True, **extra): """ - Workaround click 4.0 bug https://github.com/mitsuhiko/click/issues/365 + Workaround click bug https://github.com/mitsuhiko/click/issues/365 """ return click.Command.main( self, args=args, prog_name=self.name, @@ -84,7 +92,7 @@ def main(self, args=None, prog_name=None, complete_var=None, @click.group(name='about') @click.version_option(version=__version__, prog_name=prog_name, message=intro) @click.help_option('-h', '--help') -def cli(): +def about(): """ Generate licensing attribution and credit notices from .ABOUT files and inventories. @@ -95,93 +103,134 @@ def cli(): ###################################################################### -# inventory subcommand +# option validators ###################################################################### -@cli.command(cls=AboutCommand, - short_help='Collect .ABOUT files and write an inventory as CSV or JSON.') +def validate_key_values(ctx, param, value): + """ + Return the a mapping of {key: [values,...] if valid or raise a UsageError + otherwise. + """ + if not value: + return -@click.argument('location', nargs=1, required=True, - type=click.Path( - exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) + kvals, errors = parse_key_values(value) + if errors: + ive = '\n'.join(sorted(' ' + x for x in errors)) + msg = ('Invalid {param} option(s):\n' + '{ive}'.format(**locals())) + raise click.UsageError(msg) + return kvals -@click.argument('output', nargs=1, required=True, - type=click.Path(exists=False, dir_okay=False, resolve_path=True)) -@click.option('--filter', nargs=1, multiple=True, - help='Filter for the output inventory. e.g. "license_expression=gpl-2.0') +def validate_mapping(mapping, mapping_file): + """ + Return a mapping_file or None. + Raise a UsageError on errors. + """ + if mapping and mapping_file: + raise click.UsageError( + 'Invalid options combination: ' + '--mapping and --mapping-file are mutually exclusive.') + if mapping: + return DEFAULT_MAPPING + return mapping_file or None -@click.option('-f', '--format', is_flag=False, default='csv', show_default=True, - type=click.Choice(['json', 'csv']), - help='Set OUTPUT inventory file format.') -@click.option('--mapping', is_flag=True, - help='Use the default file mapping.config (./attributecode/mapping.config) with mapping between input keys and ABOUT field names.') +def validate_extensions(ctx, param, value, extensions=tuple(('.csv', '.json',))): + if not value: + return + if not value.endswith(extensions): + msg = ' '.join(extensions) + raise click.UsageError( + 'Invalid {param} file extension: must be one of: {msg}'.format(**locals())) + return value -@click.option('--mapping-file', metavar='FILE', nargs=1, - type=click.Path(exists=True, dir_okay=True, readable=True, resolve_path=True), - help='Use a custom mapping file with mapping between input keys and ABOUT field names.') -@click.option('--mapping-output', metavar='FILE', nargs=1, - type=click.Path(exists=True, dir_okay=True, readable=True, resolve_path=True), - help='Use a custom mapping file with mapping between ABOUT field names and output keys') +###################################################################### +# inventory subcommand +###################################################################### -@click.option('--verbose', is_flag=True, default=False, - help='Show all errors and warnings. ' - 'By default, the tool only prints these ' - 'error levels: CRITICAL, ERROR, and WARNING. ' - 'Use this option to print all errors and warning ' - 'for any level.' -) +@about.command(cls=AboutCommand, + short_help='Collect the inventory of .ABOUT files to a CSV or JSON file.') + +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) -@click.option('-q', '--quiet', is_flag=True, +@click.argument('output', + required=True, + metavar='OUTPUT', + type=click.Path(exists=False, dir_okay=False, writable=True, resolve_path=True)) + +# fIXME: this is too complex and should be removed +@click.option('--filter', + multiple=True, + metavar='=', + callback=validate_key_values, + help='Filter the inventory to ABOUT matching these key=value e.g. "license_expression=gpl-2.0') + +@click.option('-f', '--format', + is_flag=False, + default='csv', + show_default=True, + type=click.Choice(['json', 'csv']), + help='Set OUTPUT inventory file format.') + +@click.option('--mapping', + is_flag=True, + help='Use the default built-in "mapping.config" file ' + 'with mapping between input keys and .ABOUT field names.' + 'Cannot be combined with the --mapping-file option.') + +@click.option('--mapping-file', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True), + help='Path to an optional custom mapping FILE ' + 'with mapping between input keys and .ABOUT field names. ' + 'Cannot be combined with the --mapping option.') + +@click.option('-q', '--quiet', + is_flag=True, help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') + @click.help_option('-h', '--help') -def inventory(location, output, mapping, mapping_file, mapping_output, filter, quiet, format, verbose): # NOQA +def inventory(location, output, mapping, mapping_file, + format, filter, quiet, verbose): # NOQA """ -Collect a JSON or CSV inventory of components from .ABOUT files. +Collect the inventory of .ABOUT file data as CSV or JSON. LOCATION: Path to an .ABOUT file or a directory with .ABOUT files. OUTPUT: Path to the JSON or CSV inventory file to create. """ - print_version() - - if not exists(os.path.dirname(output)): - # FIXME: there is likely a better way to return an error - click.echo('ERROR: path does not exists.') - # FIXME: return error code? - return - - click.echo('Collecting inventory from: %(location)s and writing output to: %(output)s' % locals()) + if not quiet: + print_version() + click.echo('Collecting inventory from ABOUT files...') # FIXME: do we really want to continue support zip as an input? if location.lower().endswith('.zip'): # accept zipped ABOUT files as input location = extract_zip(location) - errors, abouts = model.collect_inventory(location, use_mapping=mapping, mapping_file=mapping_file) + mapping_file = validate_mapping(mapping, mapping_file) - updated_abouts = [] + errors, abouts = collect_inventory(location, mapping_file=mapping_file) + + # FIXME: this is too complex if filter: - filter_dict = {} - # Parse the filter and save to the filter dictionary with a list of value - for element in filter: - key = element.partition('=')[0] - value = element.partition('=')[2] - if key in filter_dict: - filter_dict[key].append(value) - else: - value_list = [value] - filter_dict[key] = value_list - updated_abouts = inventory_filter(abouts, filter_dict) - else: - updated_abouts = abouts + abouts = inventory_filter(abouts, filter) - # Do not write the output if one of the ABOUT files has duplicated key names - dup_error_msg = u'Duplicated key name(s)' + # Do not write the output if one of the ABOUT files has duplicated keys + # TODO: why do this check here?? Also if this is the place, we should list what the errors are. + dup_error_msg = u'Duplicated keys' halt_output = False for err in errors: if dup_error_msg in err.message: @@ -189,300 +238,434 @@ def inventory(location, output, mapping, mapping_file, mapping_output, filter, q break if not halt_output: - write_errors = model.write_output(updated_abouts, output, format, mapping_output) + write_errors = write_output(abouts=abouts, location=output, format=format) for err in write_errors: errors.append(err) else: - msg = u'Duplicated key names are not supported.\n' + \ - 'Please correct and re-run.' - print(msg) - - error_count = 0 - - for e in errors: - # Only count as warning/error if CRITICAL, ERROR and WARNING - if e.severity > 20: - error_count = error_count + 1 + if not quiet: + msg = u'Duplicated keys are not supported.\nPlease correct and re-run.' + click.echo(msg) - log_errors(errors, error_count, quiet, verbose, os.path.dirname(output)) - click.echo(' %(error_count)d errors or warnings detected.' % locals()) - sys.exit(0) + errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log') + if not quiet: + msg = 'Inventory collected in {output}.'.format(**locals()) + click.echo(msg) + sys.exit(errors_count) ###################################################################### # gen subcommand ###################################################################### -@cli.command(cls=AboutCommand, +@about.command(cls=AboutCommand, short_help='Generate .ABOUT files from an inventory as CSV or JSON.') -@click.argument('location', nargs=1, required=True, - type=click.Path(exists=True, file_okay=True, readable=True, resolve_path=True)) - -@click.argument('output', nargs=1, required=True, - type=click.Path(exists=True, writable=True, dir_okay=True, resolve_path=True)) - -@click.option('--fetch-license', type=str, nargs=2, metavar='KEY', - help=('Fetch licenses text from a DejaCode API. and create .LICENSE side-by-side ' - 'with the generated .ABOUT file using data fetched from a DejaCode License Library. ' - 'The "license" key is needed in the input. ' - 'The following additional options are required:\n\n' - 'api_url - URL to the DejaCode License Library API endpoint\n\n' - 'api_key - DejaCode API key' - '\nExample syntax:\n\n' - "about gen --fetch-license 'api_url' 'api_key'") - ) - -# TODO: this option help and long name is obscure and would need to be refactored -@click.option('--license-notice-text-location', nargs=1, - type=click.Path(exists=True, dir_okay=True, readable=True, resolve_path=True), - help="Copy the 'license_file' from the directory to the generated location.") - -@click.option('--mapping', is_flag=True, - help='Use the default file mapping.config (./attributecode/mapping.config) with mapping between input keys and ABOUT field names.') - -@click.option('--mapping-file', metavar='FILE', nargs=1, - type=click.Path(exists=True, dir_okay=True, readable=True, resolve_path=True), - help='Use a custom mapping file with mapping between input keys and ABOUT field names.') - -@click.option('--verbose', is_flag=True, default=False, - help='Show all errors and warnings. ' - 'By default, the tool only prints these ' - 'error levels: CRITICAL, ERROR, and WARNING. ' - 'Use this option to print all errors and warning ' - 'for any level.' -) +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) -@click.option('-q', '--quiet', is_flag=True, +@click.argument('output', + required=True, + metavar='OUTPUT', + type=click.Path(exists=True, file_okay=False, writable=True, resolve_path=True)) + +# FIXME: the CLI UX should be improved with two separate options for API key and URL +@click.option('--fetch-license', + nargs=2, + type=str, + metavar='URL KEY', + help='Fetch license data and text files from a DejaCode License Library ' + 'API URL using the API KEY.') + +@click.option('--reference', + metavar='DIR', + type=click.Path(exists=True, file_okay=False, readable=True, resolve_path=True), + help='Path to a directory with reference license data and text files.') + +@click.option('--mapping', + is_flag=True, + help='Use the default built-in "mapping.config" file ' + 'with mapping between input keys and .ABOUT field names.' + 'Cannot be combined with the --mapping-file option.') + +@click.option('--mapping-file', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True), + help='Path to an optional custom mapping FILE ' + 'with mapping between input keys and .ABOUT field names. ' + 'Cannot be combined with the --mapping option.') + +@click.option('-q', '--quiet', + is_flag=True, help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') + @click.help_option('-h', '--help') -def gen(location, output, mapping, mapping_file, license_notice_text_location, fetch_license, +def gen(location, output, + fetch_license, + reference, + mapping, mapping_file, quiet, verbose): """ -Generate .ABOUT files in OUTPUT directory from a JSON or CSV inventory of .ABOUT files at LOCATION. +Generate .ABOUT files in OUTPUT from an inventory of .ABOUT files at LOCATION. LOCATION: Path to a JSON or CSV inventory file. OUTPUT: Path to a directory where ABOUT files are generated. """ - print_version() - - if not location.endswith('.csv') and not location.endswith('.json'): - click.echo('ERROR: Invalid input file format: must be .csv or .json.') - sys.exit(errno.EIO) + if not quiet: + print_version() + click.echo('Generating .ABOUT files...') - click.echo('Generating .ABOUT files...') + mapping_file = validate_mapping(mapping, mapping_file) - errors, abouts = gen_generate( - location=location, base_dir=output, license_notice_text_location=license_notice_text_location, - fetch_license=fetch_license, use_mapping=mapping, mapping_file=mapping_file) + if not location.endswith(('.csv', '.json',)): + raise click.UsageError('ERROR: Invalid input file extension: must be one .csv or .json.') - about_count = len(abouts) - error_count = 0 + errors, abouts = generate_about_files( + location=location, + base_dir=output, + reference_dir=reference, + fetch_license=fetch_license, + mapping_file=mapping_file + ) - for e in errors: - # Only count as warning/error if CRITICAL, ERROR and WARNING - if e.severity > 20: - error_count = error_count + 1 - log_errors(errors, error_count, quiet, verbose, output) - click.echo('Generated %(about_count)d .ABOUT files with %(error_count)d errors or warnings' % locals()) - sys.exit(0) + errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log') + if not quiet: + abouts_count = len(abouts) + msg = '{abouts_count} .ABOUT files generated in {output}.'.format(**locals()) + click.echo(msg) + sys.exit(errors_count) ###################################################################### # attrib subcommand ###################################################################### -@cli.command(cls=AboutCommand, - short_help='Generate an attribution document from .ABOUT files.') +def validate_template(ctx, param, value): + if not value: + return DEFAULT_TEMPLATE_FILE -@click.argument('location', nargs=1, required=True, - type=click.Path(exists=True, readable=True, resolve_path=True)) + with io.open(value, encoding='utf-8') as templatef: + template_error = check_template(templatef.read()) -@click.argument('output', nargs=1, required=True, - type=click.Path(exists=False, writable=True, resolve_path=True)) + if template_error: + lineno, message = template_error + raise click.UsageError( + 'Template syntax error at line: ' + '{lineno}: "{message}"'.format(**locals())) + return value -@click.option('--inventory', required=False, - type=click.Path(exists=True, file_okay=True, resolve_path=True), - help='Path to an optional JSON or CSV inventory file listing the ' - 'subset of .ABOUT files path to consider when generating attribution.' - ) -@click.option('--mapping', is_flag=True, - help='Use the default file mapping.config (./attributecode/mapping.config) with mapping between input keys and ABOUT field names.') - -@click.option('--mapping-file', metavar='FILE', nargs=1, - type=click.Path(exists=True, dir_okay=True, readable=True, resolve_path=True), - help='Use a custom mapping file with mapping between input keys and ABOUT field names.') - -@click.option('--template', type=click.Path(exists=True), nargs=1, - help='Path to an optional custom attribution template used for generation.') - -@click.option('--vartext', nargs=1, multiple=True, - help='Variable texts to the attribution template.') +@about.command(cls=AboutCommand, + short_help='Generate an attribution document from .ABOUT files.') -@click.option('--verbose', is_flag=True, default=False, - help='Show all errors and warnings. ' - 'By default, the tool only prints these ' - 'error levels: CRITICAL, ERROR, and WARNING. ' - 'Use this option to print all errors and warning ' - 'for any level.' -) +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) -@click.option('-q', '--quiet', is_flag=True, +@click.argument('output', + required=True, + metavar='OUTPUT', + type=click.Path(exists=False, dir_okay=False, writable=True, resolve_path=True)) + +@click.option('--template', + metavar='FILE', + callback=validate_template, + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True), + help='Path to an optional custom attribution template to generate the ' + 'attribution document. If not provided the default built-in template is used.') + +@click.option('--vartext', + multiple=True, + callback=validate_key_values, + metavar='=', + help='Add variable text as key=value for use in a custom attribution template.') + +@click.option('--inventory', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + help='Path to an optional JSON or CSV inventory FILE listing the ' + 'subset of .ABOUT files paths to consider when generating the attribution document.') + +@click.option('--mapping', + is_flag=True, + help='Use the default built-in "mapping.config" file ' + 'with mapping between input keys and .ABOUT field names.' + 'Cannot be combined with the --mapping-file option.') + +@click.option('--mapping-file', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True), + help='Path to an optional custom mapping FILE ' + 'with mapping between input keys and .ABOUT field names. ' + 'Cannot be combined with the --mapping option.') + +@click.option('-q', '--quiet', + is_flag=True, help='Do not print error or warning messages.') +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') + @click.help_option('-h', '--help') -def attrib(location, output, template, mapping, mapping_file, inventory, vartext, quiet, verbose): +def attrib(location, output, template, vartext, + inventory, mapping, mapping_file, + quiet, verbose): """ Generate an attribution document at OUTPUT using .ABOUT files at LOCATION. -LOCATION: Path to an .ABOUT file, a directory containing .ABOUT files or a .zip archive containing .ABOUT files. +LOCATION: Path to a file, directory or .zip archive containing .ABOUT files. -OUTPUT: Path to output file to write the attribution to. +OUTPUT: Path where to write the attribution document. """ - print_version() - click.echo('Generating attribution...') + if not quiet: + print_version() + click.echo('Generating attribution...') + + mapping_file = validate_mapping(mapping, mapping_file) # accept zipped ABOUT files as input if location.lower().endswith('.zip'): location = extract_zip(location) - inv_errors, abouts = model.collect_inventory(location, use_mapping=mapping, mapping_file=mapping_file) - no_match_errors = attrib_generate_and_save( - abouts=abouts, output_location=output, - use_mapping=mapping, mapping_file=mapping_file, template_loc=template, - inventory_location=inventory, vartext=vartext) - - if not no_match_errors: - # Check for template error - with open(output, 'r') as output_file: - first_line = output_file.readline() - if first_line.startswith('Template'): - click.echo(first_line) - sys.exit(errno.ENOEXEC) + errors, abouts = collect_inventory(location, mapping_file=mapping_file) - for no_match_error in no_match_errors: - inv_errors.append(no_match_error) - - error_count = 0 + attrib_errors = generate_attribution_doc( + abouts=abouts, + output_location=output, + template_loc=template, + variables=vartext, + mapping_file=mapping_file, + inventory_location=inventory, + ) + errors.extend(attrib_errors) - for e in inv_errors: - # Only count as warning/error if CRITICAL, ERROR and WARNING - if e.severity > 20: - error_count = error_count + 1 + errors_count = report_errors(errors, quiet, verbose, log_file_loc=output + '-error.log') - log_errors(inv_errors, error_count, quiet, verbose, os.path.dirname(output)) - click.echo(' %(error_count)d errors or warnings detected.' % locals()) - click.echo('Finished.') - sys.exit(0) + if not quiet: + msg = 'Attribution generated in: {output}'.format(**locals()) + click.echo(msg) + sys.exit(errors_count) ###################################################################### # check subcommand ###################################################################### -@cli.command(cls=AboutCommand, short_help='Validate that the format of .ABOUT files is correct.') +# FIXME: This is really only a dupe of the Inventory command -@click.argument('location', nargs=1, required=True, - type=click.Path(exists=True, readable=True, resolve_path=True)) +@about.command(cls=AboutCommand, + short_help='Validate that the format of .ABOUT files is correct and report ' + 'errors and warnings.') -@click.option('--verbose', is_flag=True, default=False, - help='Show all errors and warnings. ' - 'By default, the tool only prints these ' - 'error levels: CRITICAL, ERROR, and WARNING. ' - 'Use this option to print all errors and warning ' - 'for any level.' -) +@click.argument('location', + required=True, + metavar='LOCATION', + type=click.Path( + exists=True, file_okay=True, dir_okay=True, readable=True, resolve_path=True)) + +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') @click.help_option('-h', '--help') def check(location, verbose): """ -Check and validate .ABOUT file(s) at LOCATION for errors and -print error messages on the terminal. +Check .ABOUT file(s) at LOCATION for validity and print error messages. -LOCATION: Path to a .ABOUT file or a directory containing .ABOUT files. +LOCATION: Path to a file or directory containing .ABOUT files. """ - click.echo('Running aboutcode-toolkit version ' + __version__) + print_version() click.echo('Checking ABOUT files...') + errors, _abouts = collect_inventory(location) + severe_errors_count = report_errors(errors, quiet=False, verbose=verbose) + sys.exit(severe_errors_count) - errors, abouts = model.collect_inventory(location) - msg_format = '%(sever)s: %(message)s' - print_errors = [] - number_of_errors = 0 - for severity, message in errors: - sever = severities[severity] - # Only problematic_errors should be counted. - # Others such as INFO should not be counted as error. - if sever in problematic_errors: - number_of_errors = number_of_errors + 1 - if verbose: - print_errors.append(msg_format % locals()) - elif sever in problematic_errors: - print_errors.append(msg_format % locals()) - - for err in print_errors: - print(err) - - if print_errors: - click.echo('Found {} errors.'.format(number_of_errors)) - # FIXME: not sure this is the right way to exit with a return code - sys.exit(1) +###################################################################### +# transform subcommand +###################################################################### + +def print_config_help(ctx, param, value): + if not value or ctx.resilient_parsing: + return + from attributecode.transform import tranformer_config_help + click.echo(tranformer_config_help) + ctx.exit() + + +@about.command(cls=AboutCommand, + short_help='Transform a CSV by applying renamings, filters and checks.') + +@click.argument('location', + required=True, + callback=partial(validate_extensions, extensions=('.csv',)), + metavar='LOCATION', + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True)) + +@click.argument('output', + required=True, + callback=partial(validate_extensions, extensions=('.csv',)), + metavar='OUTPUT', + type=click.Path(exists=False, dir_okay=False, writable=True, resolve_path=True)) + +@click.option('-c', '--configuration', + metavar='FILE', + type=click.Path(exists=True, dir_okay=False, readable=True, resolve_path=True), + help='Path to an optional YAML configuration file. See --help-format for ' + 'format help.') + +@click.option('--help-format', + is_flag=True, is_eager=True, expose_value=False, + callback=print_config_help, + help='Show configuration file format help and exit.') + +@click.option('-q', '--quiet', + is_flag=True, + help='Do not print error or warning messages.') + +@click.option('--verbose', + is_flag=True, + help='Show all error and warning messages.') + +@click.help_option('-h', '--help') + +def transform(location, output, configuration, quiet, verbose): # NOQA + """ +Transform the CSV file at LOCATION by applying renamings, filters and checks +and write a new CSV to OUTPUT. + +LOCATION: Path to a CSV file. + +OUTPUT: Path to CSV inventory file to create. + """ + from attributecode.transform import transform_csv_to_csv + from attributecode.transform import Transformer + + if not quiet: + print_version() + click.echo('Transforming CSV...') + + if not configuration: + transformer = Transformer.default() else: - click.echo('No error found.') - sys.exit(0) + transformer = Transformer.from_file(configuration) + + errors = transform_csv_to_csv(location, output, transformer) + errors_count = report_errors(errors, quiet, verbose) + if not quiet: + msg = 'Transformed CSV written to {output}.'.format(**locals()) + click.echo(msg) + sys.exit(errors_count) -def log_errors(errors, err_count, quiet, verbose, base_dir=False): + +###################################################################### +# Error management +###################################################################### + +def report_errors(errors, quiet, verbose, log_file_loc=None): """ - Iterate of sequence of Error objects and print and log errors with - a severity superior or equal to level. + Report the `errors` list of Error objects to screen based on the `quiet` and + `verbose` flags. + + If `log_file_loc` file location is provided also write a verbose log to this + file. + Return True if there were severe error reported. """ - logger = logging.getLogger(__name__) - handler = logging.StreamHandler() - handler.setLevel(logging.CRITICAL) - handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) - logger.addHandler(handler) - file_logger = logging.getLogger(__name__ + '_file') - - msg_format = '%(sever)s: %(message)s' - - # Create error.log if problematic_error detected - if base_dir and have_problematic_error(errors): - bdir = to_posix(base_dir) - LOG_FILENAME = 'error.log' - log_path = join(bdir, LOG_FILENAME) - if exists(log_path): - os.remove(log_path) - f = open(log_path, "a") - error_msg = str(err_count) + u" errors or warnings detected." - f.write(error_msg) - file_handler = logging.FileHandler(log_path) - file_logger.addHandler(file_handler) + errors = unique(errors) + messages, severe_errors_count = get_error_messages(errors, quiet, verbose) + for msg in messages: + click.echo(msg) + if log_file_loc: + log_msgs, _ = get_error_messages(errors, quiet=False, verbose=True) + with io.open(log_file_loc, 'w', encoding='utf-8') as lf: + lf.write('\n'.join(log_msgs)) + return severe_errors_count + + +def get_error_messages(errors, quiet=False, verbose=False): + """ + Return a tuple of (list of error message strings to report, + severe_errors_count) given an `errors` list of Error objects and using the + `quiet` and `verbose` flags. + """ + errors = unique(errors) + severe_errors = filter_errors(errors, WARNING) + severe_errors_count = len(severe_errors) + + messages = [] + + if severe_errors and not quiet: + error_msg = 'Command completed with {} errors or warnings.'.format(severe_errors_count) + messages.append(error_msg) for severity, message in errors: - sever = severities[severity] + sevcode = severities.get(severity) or 'UNKNOWN' + msg = '{sevcode}: {message}'.format(**locals()) if not quiet: if verbose: - print(msg_format % locals()) - elif sever in problematic_errors: - print(msg_format % locals()) - if base_dir: - # The logger will only log error for severity >= 30 - file_logger.log(severity, msg_format % locals()) + messages .append(msg) + elif severity >= WARNING: + messages .append(msg) + return messages, severe_errors_count + + +def filter_errors(errors, minimum_severity=WARNING): + """ + Return a list of unique `errors` Error object filtering errors that have a + severity below `minimum_severity`. + """ + return unique([e for e in errors if e.severity >= minimum_severity]) + + +###################################################################### +# Misc +###################################################################### + +def parse_key_values(key_values): + """ + Given a list of "key=value" strings, return: + - a mapping {key: [value, value, ...]} + - a sorted list of unique error messages for invalid entries where there is + a missing a key or value. + """ + if not key_values: + return {}, [] + + errors = set() + parsed_key_values = defaultdict(list) + for key_value in key_values: + key, _, value = key_value.partition('=') + + key = key.strip().lower() + if not key: + errors.add('missing in "{key_value}".'.format(**locals())) + continue + + value = value.strip() + if not value: + errors.add('missing in "{key_value}".'.format(**locals())) + continue + + values = parsed_key_values[key] + if value not in values: + parsed_key_values[key].append(value) + return dict(parsed_key_values), sorted(errors) -def have_problematic_error(errors): - for severity, message in errors: # NOQA - sever = severities[severity] - if sever in problematic_errors: - return True - return False if __name__ == '__main__': - cli() + about() diff --git a/src/attributecode/gen.py b/src/attributecode/gen.py index b5dda2d4..9549beef 100644 --- a/src/attributecode/gen.py +++ b/src/attributecode/gen.py @@ -20,16 +20,13 @@ import codecs from collections import OrderedDict -import logging -from posixpath import basename, dirname, exists, join, normpath -import sys -if sys.version_info[0] < 3: - # Python 2 - import backports.csv as csv #NOQA -else: - # Python 3 - import csv #NOQA +# FIXME: why posipath??? +from posixpath import basename +from posixpath import dirname +from posixpath import exists +from posixpath import join +from posixpath import normpath from attributecode import ERROR from attributecode import CRITICAL @@ -38,18 +35,10 @@ from attributecode import model from attributecode import util from attributecode.util import add_unc +from attributecode.util import csv from attributecode.util import to_posix from attributecode.util import UNC_PREFIX_POSIX - - -LOG_FILENAME = 'error.log' - -logger = logging.getLogger(__name__) -handler = logging.StreamHandler() -handler.setLevel(logging.CRITICAL) -handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s')) -logger.addHandler(handler) -file_logger = logging.getLogger(__name__ + '_file') +from attributecode.util import unique def check_duplicated_columns(location): @@ -58,7 +47,7 @@ def check_duplicated_columns(location): at location. """ location = add_unc(location) - # FIXME: why ignore errors? + # FIXME: why errors=ignore? with codecs.open(location, 'rb', encoding='utf-8', errors='ignore') as csvfile: reader = csv.reader(csvfile) columns = next(reader) @@ -86,7 +75,7 @@ def check_duplicated_columns(location): msg = ('Duplicated column name(s): %(dup_msg)s\n' % locals() + 'Please correct the input and re-run.') errors.append(Error(ERROR, msg)) - return errors + return unique(errors) def check_duplicated_about_file_path(inventory_dict): @@ -105,31 +94,36 @@ def check_duplicated_about_file_path(inventory_dict): afp_list.append(component['about_file_path']) return errors - -def load_inventory(location, base_dir, license_notice_text_location=None, - use_mapping=False, mapping_file=None): +# TODO: this should be either the CSV or the ABOUT files but not both??? +def load_inventory(location, base_dir, reference_dir=None, + mapping_file=None): """ Load the inventory file at `location` for ABOUT and LICENSE files stored in the `base_dir`. Return a list of errors and a list of - About objects validated against the base_dir. - Optionally use `license_notice_text_location` as the location of - license and notice texts. - Optionally use mappings for field names if `use_mapping` is True - or a custom mapping_file if provided. + About objects validated against the `base_dir`. + + Optionally use `reference_dir` as the directory location of + reference license and notice files to reuse. + + Optionally use mappings for field names if `mapping_file` is provided for + the CSV format. """ errors = [] abouts = [] base_dir = util.to_posix(base_dir) + # FIXME: do not mix up CSV and JSON if location.endswith('.csv'): + # FIXME: this should not be done here. dup_cols_err = check_duplicated_columns(location) if dup_cols_err: errors.extend(dup_cols_err) return errors, abouts - inventory = util.load_csv(location, use_mapping, mapping_file) + inventory = util.load_csv(location, mapping_file) else: - inventory = util.load_json(location, use_mapping, mapping_file) + inventory = util.load_json(location) try: + # FIXME: this should not be done here. dup_about_paths_err = check_duplicated_about_file_path(inventory) if dup_about_paths_err: errors.extend(dup_about_paths_err) @@ -161,6 +155,7 @@ def load_inventory(location, base_dir, license_notice_text_location=None, return errors, abouts afp = fields.get(model.About.about_file_path_attr) + # FIXME: this should not be a failure condition if not afp or not afp.strip(): msg = 'Empty column: %(afp)r. Cannot generate .ABOUT file.' % locals() errors.append(Error(ERROR, msg)) @@ -171,9 +166,14 @@ def load_inventory(location, base_dir, license_notice_text_location=None, about = model.About(about_file_path=afp) about.location = loc running_inventory = False - ld_errors = about.load_dict(fields, base_dir, running_inventory, - use_mapping, mapping_file, license_notice_text_location, - with_empty=False) + ld_errors = about.load_dict( + fields, + base_dir, + running_inventory, + mapping_file, + reference_dir, + with_empty=False + ) # 'about_resource' field will be generated during the process. # No error need to be raise for the missing 'about_resource'. for e in ld_errors: @@ -183,22 +183,21 @@ def load_inventory(location, base_dir, license_notice_text_location=None, if not e in errors: errors.extend(ld_errors) abouts.append(about) - return errors, abouts + + return unique(errors), abouts -def generate(location, base_dir, license_notice_text_location=None, - fetch_license=False, policy=None, conf_location=None, - with_empty=False, with_absent=False, use_mapping=False, mapping_file=None): +def generate(location, base_dir, reference_dir=None, fetch_license=False, + with_empty=False, with_absent=False, mapping_file=None): """ Load ABOUT data from a CSV inventory at `location`. Write ABOUT files to - base_dir using policy flags and configuration file at conf_location. - Policy defines which action to take for merging or overwriting fields and - files. Return errors and about objects. + base_dir. Return errors and about objects. """ not_exist_errors = [] api_url = '' api_key = '' gen_license = False + # FIXME: use two different arguments: key and url # Check if the fetch_license contains valid argument if fetch_license: # Strip the ' and " for api_url, and api_key from input @@ -206,12 +205,12 @@ def generate(location, base_dir, license_notice_text_location=None, api_key = fetch_license[1].strip("'").strip('"') gen_license = True + # TODO: WHY? bdir = to_posix(base_dir) errors, abouts = load_inventory( location=location, base_dir=bdir, - license_notice_text_location=license_notice_text_location, - use_mapping=use_mapping, + reference_dir=reference_dir, mapping_file=mapping_file) if gen_license: @@ -292,7 +291,12 @@ def generate(location, base_dir, license_notice_text_location=None, about.license_name.present = True # Write the ABOUT files - about.dump(dump_loc, use_mapping=use_mapping, mapping_file=mapping_file, with_empty=with_empty, with_absent=with_absent) + about.dump( + dump_loc, + mapping_file=mapping_file, + with_empty=with_empty, + with_absent=with_absent + ) for e in not_exist_errors: errors.append(Error(INFO, e)) except Exception as e: @@ -302,17 +306,4 @@ def generate(location, base_dir, license_notice_text_location=None, u'%(dump_loc)s ' u'with error: %(emsg)s' % locals()) errors.append(Error(ERROR, msg)) - dedup_errors = deduplicate(errors) - return dedup_errors, abouts - - -def deduplicate(sequence): - """ - Return a list of unique items found in sequence. Preserve the original - sequence order. - """ - deduped = [] - for item in sequence: - if item not in deduped: - deduped.append(item) - return deduped + return unique(errors), abouts diff --git a/src/attributecode/model.py b/src/attributecode/model.py index 0e6ee4ed..de6d0ed4 100644 --- a/src/attributecode/model.py +++ b/src/attributecode/model.py @@ -32,21 +32,18 @@ import codecs import json import os +# FIXME: why posixpath??? import posixpath -from posixpath import dirname +import traceback -import yaml -import re -import sys +from attributecode.util import python2 -if sys.version_info[0] < 3: # Python 2 - import backports.csv as csv # NOQA +if python2: # pragma: nocover from itertools import izip_longest as zip_longest # NOQA from urlparse import urljoin, urlparse # NOQA from urllib2 import urlopen, Request, HTTPError # NOQA -else: # Python 3 +else: # pragma: nocover basestring = str # NOQA - import csv # NOQA from itertools import zip_longest # NOQA from urllib.parse import urljoin, urlparse # NOQA from urllib.request import urlopen, Request # NOQA @@ -63,10 +60,12 @@ from attributecode import saneyaml from attributecode import util from attributecode.util import add_unc +from attributecode.util import csv from attributecode.util import copy_license_notice_files from attributecode.util import on_windows -from attributecode.util import ungroup_licenses from attributecode.util import UNC_PREFIX +from attributecode.util import ungroup_licenses +from attributecode.util import unique class Field(object): @@ -177,7 +176,7 @@ def serialize(self): # insert 4 spaces for newline values value = u' '.join(value) else: - # See https://github.com/nexB/aboutcode-toolkit/issues/323 + # FIXME: See https://github.com/nexB/aboutcode-toolkit/issues/323 # The yaml.load() will throw error if the parsed value # contains ': ' character. A work around is to put a pipe, '|' # to indicate the whole value as a string @@ -417,7 +416,7 @@ def _validate(self, *args, **kwargs): self.about_file_path = kwargs.get('about_file_path') self.running_inventory = kwargs.get('running_inventory') self.base_dir = kwargs.get('base_dir') - self.license_notice_text_location = kwargs.get('license_notice_text_location') + self.reference_dir = kwargs.get('reference_dir') if self.base_dir: self.base_dir = util.to_posix(self.base_dir) @@ -444,7 +443,7 @@ def _validate(self, *args, **kwargs): # the license files, if need to be copied, are located under the path # set from the 'license-text-location' option, so the tool should check # at the 'license-text-location' instead of the 'base_dir' - if not (self.base_dir or self.license_notice_text_location): + if not (self.base_dir or self.reference_dir): msg = (u'Field %(name)s: Unable to verify path: %(path)s:' u' No base directory provided' % locals()) errors.append(Error(ERROR, msg)) @@ -452,8 +451,8 @@ def _validate(self, *args, **kwargs): paths[path] = location continue - if self.license_notice_text_location: - location = posixpath.join(self.license_notice_text_location, path) + if self.reference_dir: + location = posixpath.join(self.reference_dir, path) else: # The 'about_resource' should be a joined path with # the 'about_file_path' and the 'base_dir @@ -506,22 +505,6 @@ def _validate(self, *args, **kwargs): errors = super(AboutResourceField, self)._validate(*args, ** kwargs) return errors - def resolve(self, about_file_path): - """ - Resolve resource paths relative to an ABOUT file path. - Set a list attribute on self called resolved_paths - """ - self.resolved_paths = [] - if not about_file_path: - # FIXME: should we return an info or warning? - # The existence of about_file_path has been checked in the load_inventory() - return - base_dir = posixpath.dirname(about_file_path).strip(posixpath.sep) - for path in self.value.keys(): - resolved = posixpath.join(base_dir, path) - resolved = posixpath.normpath(resolved) - self.resolved_paths.append(resolved) - class FileTextField(PathField): """ @@ -666,7 +649,7 @@ def __eq__(self, other): def validate_fields(fields, about_file_path, running_inventory, base_dir, - license_notice_text_location=None): + reference_dir=None): """ Validate a sequence of Field objects. Return a list of errors. Validation may update the Field objects as needed as a side effect. @@ -677,7 +660,7 @@ def validate_fields(fields, about_file_path, running_inventory, base_dir, base_dir=base_dir, about_file_path=about_file_path, running_inventory=running_inventory, - license_notice_text_location=license_notice_text_location, + reference_dir=reference_dir, ) errors.extend(val_err) return errors @@ -706,8 +689,6 @@ def create_fields(self): is simpler. """ self.fields = OrderedDict([ - # ('about_resource', ListField(required=True)), - # ('about_resource', AboutResourceField(required=True)), ('about_resource', AboutResourceField(required=True)), ('name', SingleLineField(required=True)), @@ -759,7 +740,7 @@ def create_fields(self): field.name = name setattr(self, name, field) - def __init__(self, location=None, about_file_path=None, use_mapping=False, mapping_file=None): + def __init__(self, location=None, about_file_path=None, mapping_file=None): self.create_fields() self.custom_fields = OrderedDict() @@ -773,7 +754,7 @@ def __init__(self, location=None, about_file_path=None, use_mapping=False, mappi self.base_dir = None if self.location: self.base_dir = os.path.dirname(location) - self.load(location, use_mapping, mapping_file) + self.load(location, mapping_file) def __repr__(self): return repr(self.all_fields()) @@ -786,46 +767,6 @@ def __eq__(self, other): and self.fields == other.fields and self.custom_fields == other.custom_fields) - @staticmethod - def attribution_fields(fields): - """ - Return attrib-only fields - """ - attrib_fields = ['name', - 'version', - 'license_key', - 'license_name', - 'license_file', - 'license_url', - 'copyright', - 'notice_file', - 'notice_url', - 'redistribute', - 'attribute', - 'track_changes', - 'modified', - 'changelog_file', - 'owner', - 'author'] - - return OrderedDict([(n, o) for n, o in fields.items() - if n in attrib_fields]) - - def same_attribution(self, other): - """ - Equality based on attribution-related fields. - """ - return (isinstance(other, self.__class__) - and self.attribution_fields(self.fields) == self.attribution_fields(other.fields)) - - def resolved_resources_paths(self): - """ - Return a serialized string of resolved resource paths, one per line. - """ - abrf = self.about_resource_path - abrf.resolve(self.about_file_path) - return u'\n'.join(abrf.resolved_paths) - def all_fields(self, with_absent=True, with_empty=True): """ Return the list of all Field objects. @@ -880,7 +821,7 @@ def as_dict(self, with_paths=False, with_absent=True, with_empty=True): as_dict[field.name] = field.serialized_value() return as_dict - def hydrate(self, fields, use_mapping=False, mapping_file=None): + def hydrate(self, fields, mapping_file=None): """ Process an iterable of field (name, value) tuples. Update or create Fields attributes and the fields and custom fields dictionaries. @@ -889,9 +830,8 @@ def hydrate(self, fields, use_mapping=False, mapping_file=None): errors = [] seen_fields = OrderedDict() - mapping = {} - if use_mapping or mapping_file: - mapping = util.get_mapping(mapping_file) + mapping = util.get_mapping(mapping_file) + for name, value in fields: orig_name = name name = name.lower() @@ -916,7 +856,7 @@ def hydrate(self, fields, use_mapping=False, mapping_file=None): standard_field.present = True continue - if not use_mapping and not mapping_file: + if not mapping_file: if not name == self.about_file_path_attr: msg = (u'Field %(orig_name)s is not a supported field and is ignored.') errors.append(Error(INFO, msg % locals())) @@ -972,37 +912,34 @@ def hydrate(self, fields, use_mapping=False, mapping_file=None): return errors def process(self, fields, about_file_path, running_inventory=False, - base_dir=None, license_notice_text_location=None, - use_mapping=False, mapping_file=None): + base_dir=None, reference_dir=None, + mapping_file=None): """ - Hydrate and validate a sequence of field name/value tuples from an - ABOUT file. Return a list of errors. + Validate and set as attributes on this About object a sequence of + `fields` name/value tuples. Return a list of errors. """ self.base_dir = base_dir - self.license_notice_text_location = license_notice_text_location + self.reference_dir = reference_dir afp = self.about_file_path errors = [] - hydratation_errors = self.hydrate(fields, use_mapping=use_mapping, mapping_file=mapping_file) + hydratation_errors = self.hydrate(fields, mapping_file=mapping_file) errors.extend(hydratation_errors) # We want to copy the license_files before the validation - if license_notice_text_location: + if reference_dir: copy_license_notice_files( - fields, base_dir, license_notice_text_location, afp) + fields, base_dir, reference_dir, afp) + # we validate all fields, not only these hydrated all_fields = self.all_fields() validation_errors = validate_fields( all_fields, about_file_path, running_inventory, - self.base_dir, self.license_notice_text_location) + self.base_dir, self.reference_dir) errors.extend(validation_errors) - # do not forget to resolve about resource paths The - # 'about_resource' field is now a ListField and those do not - # need to resolve - # self.about_resource.resolve(self.about_file_path) return errors - def load(self, location, use_mapping=False, mapping_file=None): + def load(self, location, mapping_file=None): """ Read, parse and process the ABOUT file at location. Return a list of errors and update self with errors. @@ -1015,8 +952,6 @@ def load(self, location, use_mapping=False, mapping_file=None): loc = add_unc(loc) with codecs.open(loc, encoding='utf-8') as txt: input_text = txt.read() - # Check for duplicated key - yaml.load(input_text, Loader=util.NoDuplicateLoader) """ The running_inventory defines if the current process is 'inventory' or not. This is used for the validation of the path of the 'about_resource'. @@ -1027,26 +962,25 @@ def load(self, location, use_mapping=False, mapping_file=None): and then join with the 'about_resource' """ running_inventory = True - # wrap the value of the boolean field in quote to avoid - # automatically conversion from yaml.load - input = util.wrap_boolean_value(input_text) # NOQA - errs = self.load_dict(saneyaml.load(input), base_dir, running_inventory, use_mapping, mapping_file) + data = saneyaml.load(input_text, allow_duplicate_keys=False) + errs = self.load_dict(data, base_dir, running_inventory, mapping_file) errors.extend(errs) except Exception as e: - msg = 'Cannot load invalid ABOUT file: %(location)r: %(e)r\n' + str(e) + trace = traceback.format_exc() + msg = 'Cannot load invalid ABOUT file: %(location)r: %(e)r\n%(trace)s' errors.append(Error(CRITICAL, msg % locals())) self.errors = errors return errors + # FIXME: should be a from_dict class factory instead + # FIXME: an About object should not know about mappings def load_dict(self, fields_dict, base_dir, running_inventory=False, - use_mapping=False, mapping_file=None, - license_notice_text_location=None, with_empty=True): + mapping_file=None, + reference_dir=None, with_empty=True): """ - Load the ABOUT file from a fields name/value mapping. - If with_empty, create fields with no value for empty fields. - Return a list of - errors. + Load this About object file from a `fields_dict` name/value mapping. + Return a list of errors. """ fields = list(fields_dict.items()) about_file_path = self.about_file_path @@ -1054,6 +988,7 @@ def load_dict(self, fields_dict, base_dir, running_inventory=False, fields = [(n, v) for n, v in fields_dict.items() if v] for key, value in fields: if key == u'licenses': + # FIXME: use a license object instead lic_key, lic_name, lic_file, lic_url = ungroup_licenses(value) if lic_key: fields.append(('license_key', lic_key)) @@ -1069,13 +1004,16 @@ def load_dict(self, fields_dict, base_dir, running_inventory=False, licenses_field = (key, value) fields.remove(licenses_field) errors = self.process( - fields, about_file_path, running_inventory, base_dir, - license_notice_text_location, use_mapping, mapping_file) + fields=fields, + about_file_path=about_file_path, + running_inventory=running_inventory, + base_dir=base_dir, + reference_dir=reference_dir, + mapping_file=mapping_file) self.errors = errors return errors - - def dumps(self, use_mapping=False, mapping_file=False, with_absent=False, with_empty=True): + def dumps(self, mapping_file=False, with_absent=False, with_empty=True): """ Return self as a formatted ABOUT string. If with_absent, include absent (not present) fields. @@ -1110,7 +1048,7 @@ def dumps(self, use_mapping=False, mapping_file=False, with_absent=False, with_e # Group the same license information in a list license_group = list(zip_longest(license_key, license_name, license_file, license_url)) for lic_group in license_group: - lic_dict = {} + lic_dict = OrderedDict() if lic_group[0]: lic_dict['key'] = lic_group[0] if lic_group[1]: @@ -1120,10 +1058,12 @@ def dumps(self, use_mapping=False, mapping_file=False, with_absent=False, with_e if lic_group[3]: lic_dict['url'] = lic_group[3] about_data.setdefault('licenses', []).append(lic_dict) - formatted_about_data = util.format_output(about_data, use_mapping, mapping_file) + + formatted_about_data = util.format_output(about_data, mapping_file) + return saneyaml.dump(formatted_about_data) - def dump(self, location, use_mapping=False, mapping_file=False, with_absent=False, with_empty=True): + def dump(self, location, mapping_file=False, with_absent=False, with_empty=True): """ Write formatted ABOUT representation of self to location. If with_absent, include absent (not present) fields. @@ -1137,13 +1077,16 @@ def dump(self, location, use_mapping=False, mapping_file=False, with_absent=Fals about_file_path = loc if not about_file_path.endswith('.ABOUT'): + # FIXME: we should not infer some location. if about_file_path.endswith('/'): about_file_path = util.to_posix(os.path.join(parent, os.path.basename(parent))) about_file_path += '.ABOUT' + if on_windows: about_file_path = add_unc(about_file_path) + with codecs.open(about_file_path, mode='wb', encoding='utf-8') as dumped: - dumped.write(self.dumps(use_mapping, mapping_file, with_absent, with_empty)) + dumped.write(self.dumps(mapping_file, with_absent, with_empty)) def dump_lic(self, location, license_dict): """ @@ -1178,101 +1121,12 @@ def dump_lic(self, location, license_dict): pass return license_key_name_context_url - -# valid field name -field_name = r'(?P[a-z][0-9a-z_]*)' - -valid_field_name = re.compile(field_name, re.UNICODE | re.IGNORECASE).match - -# line in the form of "name: value" -field_declaration = re.compile( - r'^' - +field_name + - r'\s*:\s*' - r'(?P.*)' - r'\s*$' - , re.UNICODE | re.IGNORECASE - ).match - - -# continuation line in the form of " value" -continuation = re.compile( - r'^' - r' ' - r'(?P.*)' - r'\s*$' - , re.UNICODE | re.IGNORECASE - ).match - - -def parse(lines): - """ - Parse a list of unicode lines from an ABOUT file. Return a - list of errors found during parsing and a list of tuples of (name, - value) strings. - """ - errors = [] - fields = [] - - # track current name and value to accumulate possible continuations - name = None - value = [] - - for num, line in enumerate(lines): - has_content = line.strip() - new_field = field_declaration(line) - cont = continuation(line) - - if cont: - if name: - # name is set, so the continuation is for the field name - # append the value - value.append(cont.group('value')) - else: - # name is not set and the line is not empty - if has_content: - msg = 'Invalid continuation line: %(num)d: %(line)r' - errors.append(Error(CRITICAL, msg % locals())) - - elif not line or not has_content: - # an empty line: append current name/value if any and reset - if name: - fields.append((name, value,)) - # reset - name = None - - elif new_field: - # new field line: yield current name/value if any - if name: - fields.append((name, value,)) - # start new field - name = new_field.group('name') - # values are always stored in a list - # even simple single string values - value = [new_field.group('value')] - - else: - # neither empty, nor new field nor continuation - # this is an error - msg = 'Invalid line: %(num)d: %(line)r' - errors.append(Error(CRITICAL, msg % locals())) - - # append if any name/value was left over on last iteration - if name: - fields.append((name, value,)) - - # rejoin eventual multi-line string values - fields = [(name, u'\n'.join(value),) for name, value in fields] - return errors, fields - - -def collect_inventory(location, use_mapping=False, mapping_file=None): +def collect_inventory(location, mapping_file=None): """ Collect ABOUT files at location and return a list of errors and a list of About objects. """ errors = [] - dedup_errors = [] input_location = util.get_absolute(location) about_locations = list(util.get_about_locations(input_location)) @@ -1281,18 +1135,14 @@ def collect_inventory(location, use_mapping=False, mapping_file=None): abouts = [] for about_loc in about_locations: about_file_path = util.get_relative_path(input_location, about_loc) - about = About(about_loc, about_file_path, use_mapping, mapping_file) + about = About(about_loc, about_file_path, mapping_file) # Insert about_file_path reference to the error for severity, message in about.errors: msg = (about_file_path + ": " + message) errors.append(Error(severity, msg)) abouts.append(about) - # Avoid logging duplicated/same errors multiple times - for about_error in errors: - if not about_error in dedup_errors: - dedup_errors.append(about_error) - return dedup_errors, abouts + return unique(errors), abouts def field_names(abouts, with_paths=True, with_absent=True, with_empty=True): @@ -1359,25 +1209,19 @@ def about_object_to_list_of_dictionary(abouts, with_absent=False, with_empty=Tru return abouts_dictionary_list -def write_output(abouts, location, format, mapping_output=None, with_absent=False, with_empty=True): # NOQA +def write_output(abouts, location, format, with_absent=False, with_empty=True): # NOQA """ - Write a CSV/JSON file at location given a list of About objects + Write a CSV/JSON file at location given a list of About objects. + Return a list of Error objects. """ errors = [] about_dictionary_list = about_object_to_list_of_dictionary(abouts, with_absent, with_empty) - if mapping_output: - updated_dictionary_list = util.update_about_dictionary_keys(about_dictionary_list, mapping_output) - else: - updated_dictionary_list = about_dictionary_list + updated_dictionary_list = about_dictionary_list location = add_unc(location) with codecs.open(location, mode='wb', encoding='utf-8') as output_file: if format == 'csv': fieldnames = field_names(abouts) - if mapping_output: - updated_fieldnames = util.update_fieldnames(fieldnames, mapping_output) - else: - updated_fieldnames = fieldnames - writer = csv.DictWriter(output_file, updated_fieldnames) + writer = csv.DictWriter(output_file, fieldnames) writer.writeheader() csv_formatted_list = util.format_about_dict_for_csv_output(updated_dictionary_list) for row in csv_formatted_list: @@ -1393,81 +1237,6 @@ def write_output(abouts, location, format, mapping_output=None, with_absent=Fals return errors -def by_license(abouts): - """ - Return an ordered dict sorted by key of About objects grouped by license - """ - grouped = {} - grouped[''] = [] - no_license = grouped[''] - for about in abouts: - if about.license_expression.value: - special_char_in_expression, lic_list = parse_license_expression(about.license_expression.value) - if not special_char_in_expression: - for lic in lic_list: - if lic in grouped: - grouped[lic].append(about) - else: - grouped[lic] = [about] - else: - no_license.append(about) - return OrderedDict(sorted(grouped.items())) - - -def by_name(abouts): - """ - Return an ordered dict sorted by key of About objects grouped by component - name. - """ - grouped = {} - grouped[''] = [] - no_name = grouped[''] - for about in abouts: - name = about.name.value - if name: - if name in grouped: - grouped[name].append(about) - else: - grouped[name] = [about] - else: - no_name.append(about) - return OrderedDict(sorted(grouped.items())) - - -def unique(abouts): - """ - Return a list of unique About objects. - """ - uniques = [] - for about in abouts: - if any(about == x for x in uniques): - continue - uniques.append(about) - return uniques - - -def by_license_content(abouts): - """ - Return an ordered dict sorted by key of About objects grouped by license - content. - """ - grouped = {} - grouped[''] = [] - no_license = grouped[''] - for about in abouts: - if about.license_key.value: - special_char_in_expression, lic_list = parse_license_expression(about.license_key.value) - if not special_char_in_expression: - for lic in lic_list: - if lic in grouped: - grouped[lic].append(about) - else: - grouped[lic] = [about] - else: - no_license.append(about) - return OrderedDict(sorted(grouped.items())) - - def pre_process_and_fetch_license_dict(abouts, api_url, api_key): """ Modify a list of About data dictionaries by adding license information @@ -1552,31 +1321,3 @@ def valid_api_url(api_url): # All other exceptions yield to invalid api_url pass return False - - -def verify_license_files_in_location(about, lic_location): - """ - Check the existence of the license file provided in the license_field from the - license_text_location. - Return a dictionary of the path of where the license should be copied to as - the key and the path of where the license should be copied from as the value. - """ - license_location_dict = {} - errors = [] - # The license_file field is filled if the input has license value and - # the 'fetch_license' option is used. - if about.license_file.value: - for lic in about.license_file.value: - lic_path = util.to_posix(posixpath.join(lic_location, lic)) - if posixpath.exists(lic_path): - copy_to = dirname(about.about_file_path) - license_location_dict[copy_to] = lic_path - else: - msg = (u'The license file : ' - u'%(lic)s ' - u'does not exist in ' - u'%(lic_path)s and therefore cannot be copied' % locals()) - errors.append(Error(ERROR, msg)) - return license_location_dict, errors - - diff --git a/src/attributecode/saneyaml.py b/src/attributecode/saneyaml.py deleted file mode 100644 index cbe5db37..00000000 --- a/src/attributecode/saneyaml.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf8 -*- - -# ============================================================================ -# Copyright (c) 2015-2017 nexB Inc. http://www.nexb.com/ - All rights reserved. -# 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. -# ============================================================================ - -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals - -from collections import OrderedDict -from functools import partial - -import yaml -import sys - -try: - from yaml import CSafeLoader as SafeLoader - from yaml import CSafeDumper as SafeDumper -except ImportError: - from yaml import SafeLoader - from yaml import SafeDumper - -try: - unicode # Python 2 -except NameError: - unicode = str # Python 3 #NOQA - -try: - basestring # Python 2 -except NameError: - basestring = str # Python 3 #NOQA - - -""" -Wrapper around PyYAML to provide sane defaults ensuring that dump/load does -not damage content, keeps ordering, use always block-style and use four -spaces indents to get readable YAML and quotes and folds texts in a sane way. - -Use the `load` function to get a primitive type from a YAML string and the -`dump` function to get a YAML string from a primitive type. - -Load and dump rely on subclasses of SafeLoader and SafeDumper respectively -doing all the dirty bidding to get PyYAML straight. -""" - -# Check: -# https://github.com/ralienpp/reyaml/blob/master/reyaml/__init__.py -# https://pypi.python.org/pypi/PyYAML.Yandex/3.11.1 -# https://pypi.python.org/pypi/ruamel.yaml/0.9.1 -# https://pypi.python.org/pypi/yaml2rst/0.2 - -def load(s): - """ - Return an object safely loaded from YAML string `s`. `s` must be unicode - or be a string that converts to unicode without errors. - """ - return yaml.load(s, Loader=SaneLoader) - - -def dump(obj): - """ - Return a safe YAML unicode string representation from `obj`. - """ - kwargs = dict( - Dumper=SaneDumper, - default_flow_style=False, - default_style=None, - canonical=False, - allow_unicode=True, - # do not encode Unicode - encoding=None, - indent=4, - width=80, - line_break='\n', - explicit_start=False, - explicit_end=False, - ) - return yaml.dump(obj, **kwargs) - - -class SaneLoader(SafeLoader): - pass - - -def string_loader(loader, node): - """ - Ensure that a scalar type (a value) is returned as a plain unicode string. - """ - return loader.construct_scalar(node) - - -SaneLoader.add_constructor(u'tag:yaml.org,2002:str', string_loader) - - -# Load as strings most scalar types: nulls, booleans, ints, (such as in -# version 01) floats (such version 2.20) and timestamps conversion (in -# versions too) are all emitted as unicode strings. This avoid unwanted type -# conversions for unquoted strings and the resulting content damaging. This -# overrides the implicit resolvers. Callers must handle type conversion -# explicitly from unicode to other types in the loaded objects. - -SaneLoader.add_constructor(u'tag:yaml.org,2002:null', string_loader) -SaneLoader.add_constructor(u'tag:yaml.org,2002:boolean', string_loader) -SaneLoader.add_constructor(u'tag:yaml.org,2002:timestamp', string_loader) -SaneLoader.add_constructor(u'tag:yaml.org,2002:float', string_loader) -SaneLoader.add_constructor(u'tag:yaml.org,2002:int', string_loader) -SaneLoader.add_constructor(u'tag:yaml.org,2002:null', string_loader) - - -def ordered_loader(loader, node): - """ - Ensure that YAML maps ordered is preserved and loaded in an OrderedDict. - """ - assert isinstance(node, yaml.MappingNode) - omap = OrderedDict() - yield omap - - for key, value in node.value: - key = loader.construct_object(key) - value = loader.construct_object(value) - omap[key] = value - -SaneLoader.add_constructor(u'tag:yaml.org,2002:map', ordered_loader) -SaneLoader.add_constructor(u'tag:yaml.org,2002:omap', ordered_loader) - - -class SaneDumper(SafeDumper): - """ - Ensure that lists items are always indented. - """ - def increase_indent(self, flow=False, indentless=False): # @UnusedVariable - return super(SaneDumper, self).increase_indent(flow, indentless=False) - - -def ordered_dumper(dumper, data): - """ - Ensure that maps are always dumped in the items order. - """ - return dumper.represent_mapping(u'tag:yaml.org,2002:map', data.items()) - -SaneDumper.add_representer(OrderedDict, ordered_dumper) - - -def null_dumper(dumper, value): # @UnusedVariable - """ - Always dump nulls as empty string. - """ - return dumper.represent_scalar(u'tag:yaml.org,2002:null', u'') - -SafeDumper.add_representer(type(None), null_dumper) - - -def string_dumper(dumper, value, _tag=u'tag:yaml.org,2002:str'): - """ - Ensure that all scalars are dumped as UTF-8 unicode, folded and quoted in - the sanest and most readable way. - """ - style = None - - if not isinstance(value, basestring): - value = repr(value) - - if isinstance(value, str): - if sys.version_info[0] < 3: # Python 2 - value = value.decode('utf-8') - - folded_style = '>' - verbatim_style = '|' -# single_style = "'" -# double_style = '"' - - long_lines = any(len(line) > 40 for line in value.splitlines(False)) and ' ' in value - multilines = '\n' in value -# single_quote = "'" in value -# double_quote = '"' in value -# colon_space = ': ' in value -# hash_space = '# ' in value - - if multilines: # or colon_space or hash_space or (single_quote and double_quote) or double_quote: - style = verbatim_style - elif long_lines: - style = folded_style -# elif single_quote and double_quote: -# style = folded_style -# elif single_quote: -# style = double_style -# elif double_quote: -# style = single_style - - return dumper.represent_scalar(_tag, value, style=style) - -SaneDumper.add_representer(str, string_dumper) -SaneDumper.add_representer(unicode, string_dumper) -SaneDumper.add_representer(int, partial(string_dumper, _tag=u'tag:yaml.org,2002:int')) -SaneDumper.add_representer(float, partial(string_dumper, _tag=u'tag:yaml.org,2002:float')) - - -def boolean_dumper(dumper, value): - """ - Dump booleans as yes or no. - """ - value = u'yes' if value else u'no' - style = None - return dumper.represent_scalar(u'tag:yaml.org,2002:bool', value, style=style) - -SaneDumper.add_representer(bool, boolean_dumper) diff --git a/src/attributecode/transform.py b/src/attributecode/transform.py new file mode 100644 index 00000000..8c9365e3 --- /dev/null +++ b/src/attributecode/transform.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- +# ============================================================================ +# Copyright (c) 2013-2018 nexB Inc. http://www.nexb.com/ - All rights reserved. +# 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. +# ============================================================================ + +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals + +from collections import Counter +from collections import OrderedDict +import io + +import attr + +from attributecode import CRITICAL +from attributecode import Error +from attributecode import saneyaml +from attributecode.util import csv +from attributecode.util import python2 + + +if python2: # pragma: nocover + from itertools import izip_longest as zip_longest # NOQA +else: # pragma: nocover + from itertools import zip_longest # NOQA + + +def transform_csv_to_csv(location, output, transformer): + """ + Read a CSV file at `location` and write a new CSV file at `output`. Apply + transformations using the `transformer` Tranformer. + Return a list of Error objects. + """ + if not transformer: + raise ValueError('Cannot transform without Transformer') + + rows = read_csv_rows(location) + + column_names, data, errors = transform_data(rows, transformer) + + if errors: + return errors + else: + write_csv(output, data, column_names) + return [] + + +def transform_data(rows, transformer): + """ + Read a list of list of CSV-like data `rows` and apply transformations using the + `transformer` Tranformer. + Return a tuple of: + ([column names...], [transformed ordered mappings...], [Error objects..]) + """ + + if not transformer: + return rows + + errors = [] + rows = iter(rows) + column_names = next(rows) + column_names = transformer.clean_columns(column_names) + + dupes = check_duplicate_columns(column_names) + + if dupes: + msg = 'Duplicated column name: {name}' + errors.extend(Error(CRITICAL, msg.format(name)) for name in dupes) + return column_names, [], errors + + column_names = transformer.apply_renamings(column_names) + + # convert to mappings using the renamed columns + data = [OrderedDict(zip_longest(column_names, row)) for row in rows] + + if transformer.column_filters: + data = list(transformer.filter_columns(data)) + column_names = [c for c in column_names if c in transformer.column_filters] + + errors = transformer.check_required_columns(data) + if errors: + return column_names, data, errors + + if transformer.row_filters: + data = list(transformer.filter_rows(data)) + + return column_names, data, errors + + +tranformer_config_help = ''' +A transform configuration file is used to describe which transformations and +validations to apply to a source CSV file. This is a simple text file using YAML +format, using the same format as an .ABOUT file. + +The attributes that can be set in a configuration file are: + +* column_renamings: +An optional mapping of source CSV column name to target CSV new column name that +is used to rename CSV columns. + +For instance with this configuration the columns "Directory/Location" will be +renamed to "about_resource" and "foo" to "bar": + renamings: + 'Directory/Location' : about_resource + foo : bar + +The renaming is always applied first before other transforms and checks. All +other column names referenced below are these that exist AFTER the renamings +have been applied to the existing column names. + +* required_columns: +An optional list of required column names that must have a value, beyond the +standard columns names. If a source CSV does not have such a column or a row is +missing a value for a required column, an error is reported. + +For instance with this configuration an error will be reported if the columns +"name" and "version" are missing or if any row does not have a value set for +these columns: + required_columns: + - name + - version + +* column_filters: +An optional list of column names that should be kept in the transformed CSV. If +this list is provided, all the columns from the source CSV that should be kept +in the target CSV must be listed be even if they are standard or required +columns. If this list is not provided, all source CSV columns are kept in the +transformed target CSV. + +For instance with this configuration the target CSV will only contains the "name" +and "version" columns and no other column: + column_filters: + - name + - version + +* row_filters: +An optional list of mappings of : that a source CSV row +should match to be added to the transformed target CSV. If any column value of a +row matches any such filter it is kept. Otherwise it is skipped. Filters are +applied last after all renamings, checks and tranforms and can therefore onlu +use remaining column names. + +For instance with this configuration the target CSV will only contain rows that +have a "path" equal to "/root/user/lib": + row_filters: + path : /root/user/lib +''' + + +@attr.attributes +class Transformer(object): + __doc__ = tranformer_config_help + + column_renamings = attr.attrib(default=attr.Factory(dict)) + required_columns = attr.attrib(default=attr.Factory(list)) + column_filters = attr.attrib(default=attr.Factory(list)) + row_filters = attr.attrib(default=attr.Factory(list)) + + # TODO: populate these! + # a list of all the standard columns from AboutCode toolkit + standard_columns = attr.attrib(default=attr.Factory(list), init=False) + # a list of the subset of standard columns that are essential and MUST be + # present for AboutCode toolkit to work + essential_columns = attr.attrib(default=attr.Factory(list), init=False) + + @classmethod + def default(cls): + """ + Return a default Transformer with built-in transforms. + """ + return cls( + column_renamings={}, + required_columns=[], + column_filters=[], + row_filters=[], + ) + + @classmethod + def from_file(cls, location): + """ + Load and return a Transformer instance from a YAML configuration file at + `location`. + """ + with io.open(location, encoding='utf-8') as conf: + data = saneyaml.load(conf.read()) + return cls( + column_renamings=data.get('column_renamings', {}), + required_columns=data.get('required_columns', []), + column_filters=data.get('column_filters', []), + row_filters=data.get('row_filters', []), + ) + + def check_required_columns(self, data): + """ + Return a list of Error for a `data` list of ordered mappings where a + mapping is missing a value for a required column name. + """ + errors = [] + required = set(self.essential_columns + self.required_columns) + if not required: + return [] + + for rn, item in enumerate(data): + missings = [rk for rk in required if not item.get(rk)] + if not missings: + continue + + missings = ', '.join(missings) + msg = 'Row {rn} is missing required values for columns: {missings}' + errors.append(Error(CRITICAL, msg.format(**locals()))) + return errors + + def apply_renamings(self, column_names): + """ + Return a tranformed list of `column_names` where columns are renamed + based on this Transformer configuration. + """ + renamings = self.column_renamings + if not renamings: + return column_names + renamings = {n.lower(): rn.lower() for n, rn in renamings.items()} + + renamed = [] + for name in column_names: + name = name.lower() + new_name = renamings.get(name, name) + renamed.append(new_name) + return renamed + + def clean_columns(self, column_names): + """ + Apply standard cleanups to a list of columns and return these. + """ + if not column_names: + return column_names + return [c.strip().lower() for c in column_names] + + def filter_columns(self, data): + """ + Yield transformed mappings from a `data` list of mappings keeping only + columns with a name in the `column_filters`of this Transformer. + Return the data unchanged if no `column_filters` exists. + """ + column_filters = set(self.clean_columns(self.column_filters)) + for entry in data: + items = ((k, v) for k, v in entry.items() if k in column_filters) + yield OrderedDict(items) + + def filter_rows(self, data): + """ + Yield a filtered list of mappings from a `data` list of mappings keeping + only items that match any one of the `row_filters` of this Transformer. + Return the data unchanged if no `row_filters` is avilable in this + Transformer. + """ + filters = self.row_filters + for entry in data: + for filt in filters: + for filtered_column_name, filtered_column_value in filt.items(): + if entry.get(filtered_column_name) == filtered_column_value: + yield entry + + +def check_duplicate_columns(column_names): + """ + Check that there are no duplicate in the `column_names` list of column name + strings, ignoring case. Return a list of unique duplicated column names. + """ + counted = Counter(c.lower() for c in column_names) + return [column for column, count in sorted(counted.items()) if count > 1] + + +def read_csv_rows(location): + """ + Yield rows (as a list of values) from a CSV file at `location`. + """ + with io.open(location, encoding='utf-8') as csvfile: + reader = csv.reader(csvfile) + for row in reader: + yield row + + +def write_csv(location, data, column_names): # NOQA + """ + Write a CSV file at `location` the `data` list of ordered mappings using the + `column_names`. + """ + with io.open(location, 'w', encoding='utf-8') as csvfile: + writer = csv.DictWriter(csvfile, fieldnames=column_names) + writer.writeheader() + writer.writerows(data) diff --git a/src/attributecode/util.py b/src/attributecode/util.py index e7d58164..1b44f105 100644 --- a/src/attributecode/util.py +++ b/src/attributecode/util.py @@ -17,52 +17,35 @@ from __future__ import print_function from __future__ import unicode_literals -import collections -from collections import OrderedDict import codecs -import errno +from collections import OrderedDict import json import ntpath import os -from os.path import abspath -from os.path import dirname -from os.path import join import posixpath import shutil -import socket import string import sys -if sys.version_info[0] < 3: # Python 2 - from itertools import izip_longest as zip_longest # NOQA -else: # Python 3 - from itertools import zip_longest # NOQA - +from attributecode import CRITICAL +from attributecode import Error +from attributecode import DEFAULT_MAPPING -from yaml.reader import Reader -from yaml.scanner import Scanner -from yaml.parser import Parser -from yaml.composer import Composer -from yaml.constructor import Constructor, ConstructorError -from yaml.resolver import Resolver -from yaml.nodes import MappingNode -if sys.version_info[0] < 3: - # Python 2 - import backports.csv as csv # NOQA -else: - # Python 3 - import csv # NOQA +python2 = sys.version_info[0] < 3 -try: - # Python 2 - import httplib -except ImportError: - # Python 3 - import http.client as httplib +if python2: # pragma: nocover + from itertools import izip_longest as zip_longest # NOQA +else: # pragma: nocover + from itertools import zip_longest # NOQA -from attributecode import CRITICAL -from attributecode import Error +if python2: # pragma: nocover + from backports import csv # NOQA + # monkey patch backports.csv until bug is fixed + # https://github.com/ryanhiebert/backports.csv/issues/30 + csv.dict = OrderedDict +else: # pragma: nocover + import csv # NOQA on_windows = 'win32' in sys.platform @@ -71,9 +54,9 @@ def to_posix(path): """ Return a path using the posix path separator given a path that may contain - posix or windows separators, converting \ to /. NB: this path will still - be valid in the windows explorer (except if UNC or share name). It will be - a valid path everywhere in Python. It will not be valid for windows + posix or windows separators, converting "\\" to "/". NB: this path will + still be valid in the windows explorer (except for a UNC or share name). It + will be a valid path everywhere in Python. It will not be valid for windows command line operations. """ return path.replace(ntpath.sep, posixpath.sep) @@ -88,7 +71,7 @@ def to_posix(path): def invalid_chars(path): """ - Return a list of invalid characters in the file name of path + Return a list of invalid characters in the file name of `path`. """ path = to_posix(path) rname = resource_name(path) @@ -112,6 +95,7 @@ def check_file_names(paths): systems (such as Linux), a tool must raise an error if two ABOUT files stored in the same directory have the same lowercase file name. """ + # FIXME: this should be a defaultdicts that accumulates all duplicated paths seen = {} errors = [] for orig_path in paths: @@ -139,38 +123,7 @@ def check_file_names(paths): return errors -def check_duplicate_keys_about_file(context): - keys = [] - dup_keys = [] - for line in context.splitlines(): - """ - Ignore all the continuation string, string block and empty line - """ - if not line.startswith(' ') and not len(line.strip()) == 0 : - # Get the key name - key = line.partition(':')[0] - if key in keys: - dup_keys.append(key) - else: - keys.append(key) - return dup_keys - - -def wrap_boolean_value(context): - bool_fields = ['redistribute', 'attribute', 'track_changes', 'modified'] - input = [] # NOQA - for line in context.splitlines(): - key = line.partition(':')[0] - if key in bool_fields: - value = "'" + line.partition(':')[2].strip() + "'" - updated_line = key + ': ' + value - input.append(updated_line) - else: - input.append(line) - updated_context = '\n'.join(input) - return updated_context - - +# TODO: rename to normalize_path def get_absolute(location): """ Return an absolute normalized location. @@ -184,7 +137,7 @@ def get_absolute(location): def get_locations(location): """ - Return a list of locations of files given the location of a + Return a list of locations of files given the `location` of a a file or a directory tree containing ABOUT files. File locations are normalized using posix path separators. """ @@ -203,7 +156,7 @@ def get_locations(location): def get_about_locations(location): """ - Return a list of locations of ABOUT files given the location of a + Return a list of locations of ABOUT files given the `location` of a a file or a directory tree containing ABOUT files. File locations are normalized using posix path separators. """ @@ -256,8 +209,8 @@ def norm(p): def to_native(path): """ Return a path using the current OS path separator given a path that may - contain posix or windows separators, converting / to \ on windows and \ to - / on posix OSes. + contain posix or windows separators, converting "/" to "\\" on windows + and "\\" to "/" on posix OSes. """ path = path.replace(ntpath.sep, os.path.sep) path = path.replace(posixpath.sep, os.path.sep) @@ -268,7 +221,9 @@ def is_about_file(path): """ Return True if the path represents a valid ABOUT file name. """ - return path and path.lower().endswith('.about') + if path: + path = path.lower() + return path.endswith('.about') and path != '.about' def resource_name(path): @@ -282,106 +237,44 @@ def resource_name(path): return right.strip() -# Python 3 -OrderedDictReader = csv.DictReader - -if sys.version_info[0] < 3: - # Python 2 - class OrderedDictReader(csv.DictReader): - """ - A DictReader that return OrderedDicts - Copied from csv.DictReader itself backported from Python 3 - license: python - """ - def __next__(self): - if self.line_num == 0: - # Used only for its side effect. - self.fieldnames - row = next(self.reader) - self.line_num = self.reader.line_num - - # unlike the basic reader, we prefer not to return blanks, - # because we will typically wind up with a dict full of None - # values - while row == []: - row = next(self.reader) - d = OrderedDict(zip(self.fieldnames, row)) - lf = len(self.fieldnames) - lr = len(row) - if lf < lr: - d[self.restkey] = row[lf:] - elif lf > lr: - for key in self.fieldnames[lr:]: - d[key] = self.restval - return d - - next = __next__ - - -def get_mapping(location=None): +# FIXME: we should use a proper YAML file for this instead +def load_mapping(location, lowercase=True): """ - Return a mapping of user key names to About key names by reading the - mapping.config file from location or the directory of this source file if - location was not provided. + Return a mapping loaded from a mapping configuration file at `location`. + If `lowercase` is True, the keys are lowercased. + Raise Exception on errors including empty of non existing location. + Return an empty mapping if the location is empty or does not exists. """ if not location: - location = join(abspath(dirname(__file__)), 'mapping.config') - if not os.path.exists(location): return {} - - mapping = collections.OrderedDict() - try: - with open(location) as mapping_file: - for line in mapping_file: - if not line or not line.strip() or line.strip().startswith('#'): - continue - - if ':' in line: - line = line.lower() - key, sep, value = line.partition(':') - about_key = key.strip().replace(' ', '_') - user_key = value.strip() - mapping[about_key] = user_key - - except Exception as e: - print(repr(e)) - print('Cannot open or process mapping.config file at %(location)r.' % locals()) - # FIXME: this is rather brutal - sys.exit(errno.EACCES) + mapping = OrderedDict() + with open(location) as mapping_file: + for line in mapping_file: + line = line.strip() + if not line or ':' not in line or line.startswith('#'): + continue + if lowercase: + line = line.lower() + + about_key, _, user_key = line.partition(':') + # FIXME: why do we allow spaces in ABOUT keys and converts these to _???? + # FIXME: this should be an error instead + about_key = about_key.strip().replace(' ', '_') + user_key = user_key.strip() + mapping[about_key] = user_key return mapping -def get_output_mapping(location): +def get_mapping(location=DEFAULT_MAPPING, lowercase=True): """ - Return a mapping of About key names to user key names by reading the - user's input file from location. The format of the user key names will - NOT be formatted (i.e. keys will NOT be forced to convert to lower case) + Return a mapping of user key names to About key names by reading the + mapping.config file from `location` or the directory of this source file if + location was not provided. """ - if not os.path.exists(location): - return {} + return load_mapping(location, lowercase) - mapping = {} - try: - with open(location) as mapping_file: - for line in mapping_file: - if not line or not line.strip() or line.strip().startswith('#'): - continue - - if ':' in line: - key, sep, value = line.partition(':') - user_key = key.strip() - about_key = value.strip() - mapping[about_key] = user_key - - except Exception as e: - print(repr(e)) - print('Cannot open or process file at %(location)r.' % locals()) - # FIXME: this is rather brutal - sys.exit(errno.EACCES) - return mapping - -def apply_mapping(abouts, alternate_mapping=None): +def apply_mapping(abouts, mapping_file=None): """ Given a list of About data dictionaries and a dictionary of mapping, return a new About data dictionaries list where the keys @@ -389,10 +282,11 @@ def apply_mapping(abouts, alternate_mapping=None): the mapping from the default mnapping.config if an alternate mapping dict is not provided. """ - if alternate_mapping: - mapping = get_mapping(alternate_mapping) - else: - mapping = get_mapping() + + if not mapping_file: + return abouts + + mapping = get_mapping(mapping_file) if not mapping: return abouts @@ -412,18 +306,8 @@ def apply_mapping(abouts, alternate_mapping=None): mapped_abouts.append(mapped_about) return mapped_abouts -def get_mapping_key_order(mapping_file): - """ - Get the mapping key order and return as a list - """ - if mapping_file: - mapping = get_mapping(mapping_file) - else: - mapping = get_mapping() - return mapping.keys() - -def format_output(about_data, use_mapping, mapping_file): +def format_output(about_data, mapping_file=None): """ Convert the about_data dictionary to an ordered dictionary for saneyaml.dump() The ordering should be: @@ -434,19 +318,23 @@ def format_output(about_data, use_mapping, mapping_file): and the rest is the order from the mapping.config file (if any); otherwise alphabetical order. """ mapping_key_order = [] - if use_mapping or mapping_file: - mapping_key_order = get_mapping_key_order(mapping_file) - priority_keys = [u'about_resource', u'name', u'version'] + if mapping_file: + mapping_key_order = get_mapping(mapping_file).keys() + + priority_keys = ['about_resource', 'name', 'version'] about_data_keys = [] - order_dict = collections.OrderedDict() + order_dict = OrderedDict() for key in about_data: about_data_keys.append(key) - if u'about_resource' in about_data_keys: + if 'about_resource' in about_data_keys: order_dict['about_resource'] = about_data['about_resource'] - if u'name' in about_data_keys: + + if 'name' in about_data_keys: order_dict['name'] = about_data['name'] - if u'version' in about_data_keys: + + if 'version' in about_data_keys: order_dict['version'] = about_data['version'] + if not mapping_key_order: for other_key in sorted(about_data_keys): if not other_key in priority_keys: @@ -460,50 +348,51 @@ def format_output(about_data, use_mapping, mapping_file): order_dict[other_key] = about_data[other_key] return order_dict - -def get_about_file_path(location, use_mapping=False, mapping_file=None): +# FIXME: why is this used for +def get_about_file_path(location, mapping_file=None): """ Read file at location, return a list of about_file_path. """ afp_list = [] if location.endswith('.csv'): - about_data = load_csv(location, use_mapping=use_mapping, mapping_file=mapping_file) + about_data = load_csv(location, mapping_file=mapping_file) else: - about_data = load_json(location, use_mapping=use_mapping, mapping_file=mapping_file) + about_data = load_json(location) for about in about_data: afp_list.append(about['about_file_path']) return afp_list -def load_csv(location, use_mapping=False, mapping_file=None): +def load_csv(location, mapping_file=None): """ - Read CSV at location, return a list of ordered dictionaries, one + Read CSV at `location`, return a list of ordered dictionaries, one for each row. + Use `mapping_file` if provided. """ results = [] # FIXME: why ignore encoding errors here? with codecs.open(location, mode='rb', encoding='utf-8', errors='ignore') as csvfile: - for row in OrderedDictReader(csvfile): + for row in csv.DictReader(csvfile): # convert all the column keys to lower case as the same # behavior as when user use the --mapping updated_row = OrderedDict( [(key.lower(), value) for key, value in row.items()] ) results.append(updated_row) - if use_mapping or mapping_file: + if mapping_file: results = apply_mapping(results, mapping_file) return results -def load_json(location, use_mapping=False, mapping_file=None): +def load_json(location): """ Read JSON file at `location` and return a list of ordered mappings, one for each entry. """ # FIXME: IMHO we should know where the JSON is from and its shape - # TODO use: object_pairs_hook=OrderedDict + # FIXME use: object_pairs_hook=OrderedDict with open(location) as json_file: results = json.load(json_file) @@ -548,30 +437,32 @@ def load_json(location, use_mapping=False, mapping_file=None): # "name": "test", # ... # } + # FIXME: this is too clever and complex... IMHO we should not try to guess the format. + # instead a command line option should be provided explictly to say what is the format if isinstance(results, list): - updated_results = sorted(results) + results = sorted(results) else: if u'aboutcode_manager_notice' in results: - updated_results = results['components'] + results = results['components'] elif u'scancode_notice' in results: - updated_results = results['files'] + results = results['files'] else: - updated_results = [results] - - about_ordered_list = updated_results - - # FIXME: why this double test? either have a mapping file and we use mapping or we do not. - # FIXME: IMHO only one argument is needed - if use_mapping or mapping_file: - about_ordered_list = apply_mapping(updated_results, mapping_file) - return about_ordered_list + results = [results] + return results +# FIXME: rename to is_online: BUT do we really need this at all???? def have_network_connection(): """ Return True if an HTTP connection to some public web site is possible. """ - http_connection = httplib.HTTPConnection('dejacode.org', timeout=10) + import socket + if python2: + import httplib # NOQA + else: + import http.client as httplib # NOQA + + http_connection = httplib.HTTPConnection('dejacode.org', timeout=10) # NOQA try: http_connection.connect() except socket.error: @@ -579,6 +470,7 @@ def have_network_connection(): else: return True + def extract_zip(location): """ Extract a zip file at location in a temp directory and return the temporary @@ -586,11 +478,12 @@ def extract_zip(location): """ import zipfile import tempfile + if not zipfile.is_zipfile(location): raise Exception('Incorrect zip file %(location)r' % locals()) archive_base_name = os.path.basename(location).replace('.zip', '') - base_dir = tempfile.mkdtemp() + base_dir = tempfile.mkdtemp(prefix='aboutcode-toolkit-extract-') target_dir = os.path.join(base_dir, archive_base_name) target_dir = add_unc(target_dir) os.makedirs(target_dir) @@ -623,7 +516,7 @@ def extract_zip(location): def add_unc(location): """ - Convert a location to an absolute Window UNC path to support long paths on + Convert a `location` to an absolute Window UNC path to support long paths on Windows. Return the location unchanged if not on Windows. See https://msdn.microsoft.com/en-us/library/aa365247.aspx """ @@ -634,14 +527,23 @@ def add_unc(location): return location -def copy_license_notice_files(fields, base_dir, license_notice_text_location, afp): - lic_name = u'' +# FIXME: add docstring +def copy_license_notice_files(fields, base_dir, reference_dir, afp): + """ + Given a list of (key, value) `fields` tuples and a `base_dir` where ABOUT + files and their companion LICENSe are store, and an extra `reference_dir` + where reference license an notice files are stored and the `afp` + about_file_path value, this function will copy to the base_dir the + license_file or notice_file if found in the reference_dir + + """ + lic_name = '' for key, value in fields: - if key == u'license_file' or key == u'notice_file': + if key == 'license_file' or key == 'notice_file': lic_name = value - from_lic_path = posixpath.join(to_posix(license_notice_text_location), lic_name) - about_file_dir = dirname(to_posix(afp)).lstrip('/') + from_lic_path = posixpath.join(to_posix(reference_dir), lic_name) + about_file_dir = os.path.dirname(to_posix(afp)).lstrip('/') to_lic_path = posixpath.join(to_posix(base_dir), about_file_dir) if on_windows: @@ -664,52 +566,36 @@ def copy_license_notice_files(fields, base_dir, license_notice_text_location, af print(repr(e)) print('Cannot copy file at %(from_lic_path)r.' % locals()) -def inventory_filter(abouts, filter_dict): - updated_abouts = [] - for key in filter_dict: - for about in abouts: - try: - # Check if the about object has the filtered attribute and if the - # attributed value is the same as the defined in the filter - for value in filter_dict[key]: - if vars(about)[key].value == value: - if not about in updated_abouts: - updated_abouts.append(about) - except: - # The current about object does not have the defined attribute - continue - return updated_abouts +# FIXME: this is NOT a util but something to move with inventories or a method +# from About objects +def inventory_filter(abouts, filters): + """ + Return a list of filtered About objects from an `abouts` list of About + object using the `filters` mapping of: + {field_name: [acceptable_values, ....]} + + ... such that only the About object that have a field_name with a value that + matches one of the acceptable values is returned. Other About object are + filtered out. + """ + matching_abouts = [] + for about in abouts: + for field_name, acceptable_values in filters.items(): + # Check if the about object has the filtered attribute and if the + # attributed value is the same as the defined in the filter + actual_value = getattr(about, field_name, None) + if actual_value in acceptable_values and not about in matching_abouts: + matching_abouts.append(about) + # FIXME: if it matches once it matches always which is probably not right + break -def update_fieldnames(fieldnames, mapping_output): - mapping = get_output_mapping(mapping_output) - updated_header = [] - for name in fieldnames: - try: - updated_header.append(mapping[name]) - except: - updated_header.append(name) - return updated_header + return matching_abouts -def update_about_dictionary_keys(about_dictionary_list, mapping_output): - output_map = get_output_mapping(mapping_output) - updated_dict_list = [] - for element in about_dictionary_list: - updated_ordered_dict = OrderedDict() - for about_key, value in element.items(): - update_key = False - for custom_key in output_map: - if about_key == custom_key: - update_key = True - updated_ordered_dict[output_map[custom_key]] = value - break - if not update_key: - updated_ordered_dict[about_key] = value - updated_dict_list.append(updated_ordered_dict) - return updated_dict_list +# FIXME: we should use a license object instead def ungroup_licenses(licenses): """ Ungroup multiple licenses information @@ -730,6 +616,7 @@ def ungroup_licenses(licenses): return lic_key, lic_name, lic_file, lic_url +# FIXME: add docstring def format_about_dict_for_csv_output(about_dictionary_list): csv_formatted_list = [] file_fields = ['license_file', 'notice_file', 'changelog_file', 'author_file'] @@ -747,24 +634,23 @@ def format_about_dict_for_csv_output(about_dictionary_list): return csv_formatted_list +# FIXME: add docstring def format_about_dict_for_json_output(about_dictionary_list): licenses = ['license_key', 'license_name', 'license_file', 'license_url'] file_fields = ['notice_file', 'changelog_file', 'author_file'] json_formatted_list = [] for element in about_dictionary_list: row_list = OrderedDict() + # FIXME: aboid using parallel list... use an object instead license_key = [] license_name = [] license_file = [] license_url = [] + for key in element: if element[key]: - """ - if key == u'about_resource': - row_list[key] = element[key][0] - """ # The 'about_resource' is an ordered dict - if key == u'about_resource': + if key == 'about_resource': row_list[key] = list(element[key].keys())[0] elif key in licenses: if key == 'license_key': @@ -799,51 +685,17 @@ def format_about_dict_for_json_output(about_dictionary_list): json_formatted_list.append(row_list) return json_formatted_list -class NoDuplicateConstructor(Constructor): - def construct_mapping(self, node, deep=False): - if not isinstance(node, MappingNode): - raise ConstructorError( - None, None, - "expected a mapping node, but found %s" % node.id, - node.start_mark) - mapping = {} - for key_node, value_node in node.value: - # keys can be list -> deep - key = self.construct_object(key_node, deep=True) - # lists are not hashable, but tuples are - if not isinstance(key, collections.Hashable): - if isinstance(key, list): - key = tuple(key) - - if sys.version_info.major == 2: - try: - hash(key) - except TypeError as exc: - raise ConstructorError( - "while constructing a mapping", node.start_mark, - "found unacceptable key (%s)" % - exc, key_node.start_mark) - else: - if not isinstance(key, collections.Hashable): - raise ConstructorError( - "while constructing a mapping", node.start_mark, - "found unhashable key", key_node.start_mark) - - value = self.construct_object(value_node, deep=deep) - - # Actually do the check. - if key in mapping: - raise KeyError("Got duplicate key: {!r}".format(key)) - - mapping[key] = value - return mapping - - -class NoDuplicateLoader(Reader, Scanner, Parser, Composer, NoDuplicateConstructor, Resolver): - def __init__(self, stream): - Reader.__init__(self, stream) - Scanner.__init__(self) - Parser.__init__(self) - Composer.__init__(self) - NoDuplicateConstructor.__init__(self) - Resolver.__init__(self) + +def unique(sequence): + """ + Return a list of unique items found in sequence. Preserve the original + sequence order. + For example: + >>> unique([1, 5, 3, 5]) + [1, 5, 3] + """ + deduped = [] + for item in sequence: + if item not in deduped: + deduped.append(item) + return deduped diff --git a/tests/test_api.py b/tests/test_api.py index 502170b3..d502017a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -25,10 +25,20 @@ from attributecode import api from attributecode import ERROR from attributecode import Error -from testing_utils import FakeResponse + + +class FakeResponse(object): + response_content = None + + def __init__(self, response_content): + self.response_content = response_content + + def read(self): + return self.response_content class ApiTest(unittest.TestCase): + @mock.patch.object(api, 'request_license_data') def test_api_get_license_details_from_api(self, request_license_data): license_data = { @@ -39,22 +49,34 @@ def test_api_get_license_details_from_api(self, request_license_data): errors = [] request_license_data.return_value = license_data, errors - expected = ('Apache License 2.0', 'apache-2.0', 'Apache License Version 2.0 ...', []) - result = api.get_license_details_from_api('url', 'api_key', 'license_key') + expected = ( + 'Apache License 2.0', + 'apache-2.0', + 'Apache License Version 2.0 ...', + []) + result = api.get_license_details_from_api( + api_url='api_url', api_key='api_key', license_key='license_key') assert expected == result @mock.patch.object(api, 'urlopen') - def test_api_request_license_data(self, mock_data): + def test_api_request_license_data_with_result(self, mock_data): response_content = ( b'{"count":1,"results":[{"name":"Apache 2.0","key":"apache-2.0","text":"Text"}]}' ) mock_data.return_value = FakeResponse(response_content) - license_data = api.request_license_data('http://fake.url/', 'api_key', 'apache-2.0') - expected = ({'name': 'Apache 2.0', 'key': 'apache-2.0', 'text': 'Text'}, []) + license_data = api.request_license_data( + api_url='http://fake.url/', api_key='api_key', license_key='apache-2.0') + expected = ( + {'name': 'Apache 2.0', 'key': 'apache-2.0', 'text': 'Text'}, + [] + ) assert expected == license_data + @mock.patch.object(api, 'urlopen') + def test_api_request_license_data_without_result(self, mock_data): response_content = b'{"count":0,"results":[]}' mock_data.return_value = FakeResponse(response_content) - license_data = api.request_license_data('http://fake.url/', 'api_key', 'apache-2.0') + license_data = api.request_license_data( + api_url='http://fake.url/', api_key='api_key', license_key='apache-2.0') expected = ({}, [Error(ERROR, "Invalid 'license': apache-2.0")]) assert expected == license_data diff --git a/tests/test_attrib.py b/tests/test_attrib.py index 0252281f..9742c317 100644 --- a/tests/test_attrib.py +++ b/tests/test_attrib.py @@ -18,6 +18,8 @@ from __future__ import print_function from __future__ import unicode_literals +import io +import os import unittest from testing_utils import get_test_loc @@ -26,37 +28,91 @@ from attributecode import model -class AttribTest(unittest.TestCase): +class TemplateTest(unittest.TestCase): - def test_check_template(self): - assert attrib.check_template('template_string') == None - assert attrib.check_template('{{template_string') == (1, - "unexpected end of template, expected 'end of print statement'.",) - with open(get_test_loc('attrib_gen/test.template')) as tmpl: - template = tmpl.read() - assert attrib.check_template(template) == None + def test_check_template_simple_valid_returns_None(self): + expected = None + assert expected == attrib.check_template('template_string') - def test_check_template_default_is_valid(self): - with open(attrib.default_template) as tmpl: - template = tmpl.read() - assert attrib.check_template(template) == None + def test_check_template_complex_valid_returns_None(self): + template = ''' + {% for about in abouts -%} + {{ about.name.value }}: {{ about.version.value }} + {% for res in about.about_resource.value -%} + resource: {{ res }} + {% endfor -%} + {% endfor -%}''' + expected = None + assert expected == attrib.check_template(template) + + def test_check_template_complex_invalid_returns_error(self): + template = ''' + {% for about in abouts -%} + {{ about.name.value }}: {{ about.version.value }} + {% for res in about.about_ressdsdsdsdsdsdource.value -%} + resource: {{] res }} + {% endfor -%} + {% endfor -%}''' + expected = (5, "unexpected ']'") + assert expected == attrib.check_template(template) + + def test_check_template_invalid_return_error_lineno_and_message(self): + expected = 1, "unexpected end of template, expected 'end of print statement'." + assert expected == attrib.check_template('{{template_string') + + def test_check_template_all_builtin_templates_are_valid(self): + builtin_templates_dir = os.path.dirname(attrib.DEFAULT_TEMPLATE_FILE) + for template in os.listdir(builtin_templates_dir): + template_loc = os.path.join(builtin_templates_dir, template) + with io.open(template_loc, 'r', encoding='utf-8') as tmpl: + template = tmpl.read() + try: + assert None == attrib.check_template(template) + except: + raise Exception(template_loc) + + +class GenerateTest(unittest.TestCase): + + def test_generate_from_collected_inventory_wih_custom_temaplte(self): + test_file = get_test_loc('test_attrib/gen_simple/attrib.ABOUT') + errors, abouts = model.collect_inventory(test_file) + assert not errors - def test_generate(self): - expected = (u'Apache HTTP Server: 2.4.3\n' - u'resource: httpd-2.4.3.tar.gz\n') - test_file = get_test_loc('attrib_gen/attrib.ABOUT') - with open(get_test_loc('attrib_gen/test.template')) as tmpl: + test_template = get_test_loc('test_attrib/gen_simple/test.template') + with open(test_template) as tmpl: template = tmpl.read() - _errors, abouts = model.collect_inventory(test_file) - result = attrib.generate(abouts, template) - self.assertEqual(expected, result) - - def test_generate_from_file_with_default_template(self): - test_file = get_test_loc('attrib_gen/attrib.ABOUT') - _errors, abouts = model.collect_inventory(test_file) - result = attrib.generate_from_file(abouts) - with open(get_test_loc('attrib_gen/expected_default_attrib.html')) as exp: + + expected = ( + 'Apache HTTP Server: 2.4.3\n' + 'resource: httpd-2.4.3.tar.gz\n') + + error, result = attrib.generate(abouts, template) + assert expected == result + assert not error + + def test_generate_with_default_template(self): + test_file = get_test_loc('test_attrib/gen_default_template/attrib.ABOUT') + errors, abouts = model.collect_inventory(test_file) + assert not errors + + error, result = attrib.generate_from_file(abouts) + assert not error + + expected_file = get_test_loc( + 'test_attrib/gen_default_template/expected_default_attrib.html') + with open(expected_file) as exp: expected = exp.read() + # strip the timestamp: the timestamp is wrapped in italic block - self.assertEqual([x.rstrip() for x in expected.splitlines()], - [x.rstrip() for x in result.splitlines() if not '' in x]) + result = remove_timestamp(result) + expected = remove_timestamp(expected) + assert expected == result + + +def remove_timestamp(html_text): + """ + Return the `html_text` generated attribution stripped from timestamps: the + timestamp is wrapped in italic block in the default template. + """ + return '\n'.join(x for x in html_text.splitlines() if not '' in x) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 5aa562c0..36ae8c88 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -18,6 +18,9 @@ from __future__ import print_function from __future__ import unicode_literals +import io +import unittest + from attributecode import CRITICAL from attributecode import DEBUG from attributecode import ERROR @@ -27,81 +30,399 @@ from attributecode import cmd from attributecode import Error +from testing_utils import run_about_command_test_click +from testing_utils import get_test_loc +from testing_utils import get_temp_dir +from testing_utils import get_temp_file + + +# NB: the test_report_errors* tests depend on py.test stdout/err capture capabilities -# NB: these tests depends on py.test stdout/err capture capabilities -def test_log_errors(capsys): - quiet = False - show_all = True - errors = [Error(CRITICAL, 'msg1'), - Error(ERROR, 'msg2'), - Error(INFO, 'msg3'), - Error(WARNING, 'msg4'), - Error(DEBUG, 'msg4'), - Error(NOTSET, 'msg4'), - ] - error_count = len(errors) - cmd.log_errors(errors, error_count, quiet, show_all, base_dir='') +def test_report_errors(capsys): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + ec = cmd.report_errors(errors, quiet=False, verbose=True, log_file_loc=None) + assert 3 == ec out, err = capsys.readouterr() - expected_out = '''CRITICAL: msg1 -ERROR: msg2 -INFO: msg3 -WARNING: msg4 -DEBUG: msg4 -NOTSET: msg4 -''' + expected_out = [ + 'Command completed with 3 errors or warnings.', + 'CRITICAL: msg1', + 'ERROR: msg2', + 'INFO: msg3', + 'WARNING: msg4', + 'DEBUG: msg4', + 'NOTSET: msg4'] assert '' == err - assert expected_out == out - - -def test_log_errors_without_show_all(capsys): - quiet = False - show_all = False - errors = [Error(CRITICAL, 'msg1'), - Error(ERROR, 'msg2'), - Error(INFO, 'msg3'), - Error(WARNING, 'msg4'), - Error(DEBUG, 'msg4'), - Error(NOTSET, 'msg4'), - ] - error_count = len(errors) - cmd.log_errors(errors, error_count, quiet, show_all, base_dir='') + assert expected_out == out.splitlines(False) + + +def test_report_errors_without_verbose(capsys): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + ec = cmd.report_errors(errors, quiet=False, verbose=False, log_file_loc=None) + assert 3 == ec + out, err = capsys.readouterr() + expected_out = [ + 'Command completed with 3 errors or warnings.', + 'CRITICAL: msg1', + 'ERROR: msg2', + 'WARNING: msg4', + ] + assert '' == err + assert expected_out == out.splitlines(False) + + +def test_report_errors_with_quiet_ignores_verbose_flag(capsys): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + Error(WARNING, 'msg4'), + ] + severe_errors_count = cmd.report_errors(errors, quiet=True, verbose=True) + assert severe_errors_count == 3 out, err = capsys.readouterr() - expected_out = '''CRITICAL: msg1 -ERROR: msg2 -WARNING: msg4 -''' + assert '' == out assert '' == err - assert expected_out == out - - -def test_log_errors_with_quiet(capsys): - quiet = True - show_all = True - errors = [Error(CRITICAL, 'msg1'), - Error(ERROR, 'msg2'), - Error(INFO, 'msg3'), - Error(WARNING, 'msg4'), - Error(DEBUG, 'msg4'), - Error(NOTSET, 'msg4'), - ] - error_count = len(errors) - cmd.log_errors(errors, error_count, quiet, show_all, base_dir='') + + +def test_report_errors_with_quiet_ignores_verbose_flag2(capsys): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + Error(WARNING, 'msg4'), + ] + severe_errors_count = cmd.report_errors(errors, quiet=True, verbose=False) + assert severe_errors_count == 3 out, err = capsys.readouterr() assert '' == out assert '' == err +def test_report_errors_with_verbose_flag(capsys): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + Error(WARNING, 'msg4'), + ] + severe_errors_count = cmd.report_errors(errors, quiet=False, verbose=True) + assert severe_errors_count == 3 + out, err = capsys.readouterr() + expected_out = [ + 'Command completed with 3 errors or warnings.', + 'CRITICAL: msg1', + 'ERROR: msg2', + 'INFO: msg3', + 'WARNING: msg4', + 'DEBUG: msg4', + 'NOTSET: msg4' + ] + assert expected_out == out.splitlines(False) + assert '' == err + + +def test_report_errors_can_write_to_logfile(): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + Error(WARNING, 'msg4'), + ] + + result_file = get_temp_file() + _ec = cmd.report_errors(errors, quiet=False, verbose=True, + log_file_loc=result_file) + with io.open(result_file, 'r', encoding='utf-8') as rf: + result = rf.read() + expected = [ + 'Command completed with 3 errors or warnings.', + 'CRITICAL: msg1', + 'ERROR: msg2', + 'INFO: msg3', + 'WARNING: msg4', + 'DEBUG: msg4', + 'NOTSET: msg4' + ] + assert expected == result.splitlines(False) + + +def test_report_errors_does_not_report_duplicate_errors(capsys): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + # dupes + Error(WARNING, 'msg4'), + Error(CRITICAL, 'msg1'), + ] + severe_errors_count = cmd.report_errors(errors, quiet=True, verbose=True) + assert severe_errors_count == 3 + + +def test_get_error_messages(): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + + emsgs, ec = cmd.get_error_messages(errors) + assert 3 == ec + expected = [ + 'Command completed with 3 errors or warnings.', + 'CRITICAL: msg1', + 'ERROR: msg2', + 'WARNING: msg4', + ] + assert expected == emsgs + + +def test_get_error_messages_quiet(): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + + emsgs, ec = cmd.get_error_messages(errors, quiet=True) + assert 3 == ec + expected = [] + assert expected == emsgs + + +def test_get_error_messages_verbose(): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + + emsgs, ec = cmd.get_error_messages(errors, verbose=True) + assert 3 == ec + expected = [ + 'Command completed with 3 errors or warnings.', + 'CRITICAL: msg1', + 'ERROR: msg2', + 'INFO: msg3', + 'WARNING: msg4', + 'DEBUG: msg4', + 'NOTSET: msg4'] + assert expected == emsgs + + +class TestFilterError(unittest.TestCase): + def test_filter_errors_default(self): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + expected = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(WARNING, 'msg4'), + ] + assert expected == cmd.filter_errors(errors) + + + def test_filter_errors_with_min(self): + errors = [ + Error(CRITICAL, 'msg1'), + Error(ERROR, 'msg2'), + Error(INFO, 'msg3'), + Error(WARNING, 'msg4'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + expected = [ + Error(CRITICAL, 'msg1'), + ] + assert expected == cmd.filter_errors(errors, CRITICAL) + + + def test_filter_errors_no_errors(self): + errors = [ + Error(INFO, 'msg3'), + Error(DEBUG, 'msg4'), + Error(NOTSET, 'msg4'), + ] + assert [] == cmd.filter_errors(errors) + + + def test_filter_errors_none(self): + assert [] == cmd.filter_errors([]) + + +class TestParseKeyValues(unittest.TestCase): + + def test_parse_key_values_empty(self): + assert ({}, []) == cmd.parse_key_values([]) + assert ({}, []) == cmd.parse_key_values(None) + + + def test_parse_key_values_simple(self): + test = [ + 'key=value', + 'This=THat', + 'keY=bar', + ] + expected = { + 'key': ['value', 'bar'], + 'this': ['THat'] + } + keyvals, errors = cmd.parse_key_values(test) + assert expected == keyvals + assert not errors + + + def test_parse_key_values_with_errors(self): + test = [ + 'key', + '=THat', + 'keY=', + 'FOO=bar' + ] + expected = { + 'foo': ['bar'], + } + keyvals, errors = cmd.parse_key_values(test) + assert expected == keyvals + expected = [ + 'missing in "=THat".', + 'missing in "keY=".', + 'missing in "key".' + ] + assert expected == errors + + +############################################################################### +# Run full cli command +############################################################################### + +def check_about_stdout(options, expected_loc, regen=False): + """ + Run the about command with the `options` list of options. Assert that + command success and that the stdout is equal to the `expected_loc` test file + content. + """ + result = run_about_command_test_click(options) + if regen: + expected_file = get_test_loc(expected_loc, must_exists=False) + with open(expected_file, 'wb') as ef: + ef.write(result.output_bytes) + + expected_file = get_test_loc(expected_loc, must_exists=True) + with open(expected_file, 'rb') as ef: + expected = ef.read() + + assert expected.splitlines(False) == result.output_bytes.splitlines(False) + + +def test_about_help_text(): + check_about_stdout(['--help'], 'test_cmd/help/about_help.txt', regen=False) + + +def test_about_inventory_help_text(): + check_about_stdout( + ['inventory', '--help'], + 'test_cmd/help/about_inventory_help.txt', regen=False) + + +def test_about_gen_help_text(): + check_about_stdout( + ['gen', '--help'], + 'test_cmd/help/about_gen_help.txt', regen=False) + + +def test_about_check_help_text(): + check_about_stdout( + ['check', '--help'], + 'test_cmd/help/about_check_help.txt', regen=False) + + +def test_about_attrib_help_text(): + check_about_stdout( + ['attrib', '--help'], + 'test_cmd/help/about_attrib_help.txt', regen=False) + + +def test_about_command_fails_with_an_unknown_subcommand(): + test_dir = get_temp_dir() + result = run_about_command_test_click(['foo', test_dir], expected_rc=2) + assert b'Error: No such command "foo".' in result.output_bytes + + +def test_about_inventory_command_can_run_minimally_without_error(): + test_dir = get_test_loc('test_cmd/repository-mini') + result = get_temp_file() + run_about_command_test_click(['inventory', test_dir, result]) + + +def test_about_gen_command_can_run_minimally_without_error(): + test_inv = get_test_loc('test_cmd/geninventory.csv') + gen_dir = get_temp_dir() + run_about_command_test_click(['gen', test_inv, gen_dir]) + + +def test_about_attrib_command_can_run_minimally_without_error(): + test_dir = get_test_loc('test_cmd/repository-mini') + result = get_temp_file() + run_about_command_test_click(['attrib', test_dir, result]) + + +def test_about_transform_command_can_run_minimally_without_error(): + test_file = get_test_loc('test_cmd/transform.csv') + result = get_temp_file('file_name.csv') + run_about_command_test_click(['transform', test_file, result]) + + +def test_about_transform_help_text(): + check_about_stdout( + ['transform', '--help'], + 'test_cmd/help/about_transform_help.txt', regen=False) + -def test_have_problematic_error(): - have_problematic_errors = [Error(CRITICAL, 'msg1'), - Error(ERROR, 'msg2'), - Error(INFO, 'msg3'), - Error(WARNING, 'msg4'), - Error(DEBUG, 'msg4'), - Error(NOTSET, 'msg4'), - ] - no_problematic_errors = [Error(INFO, 'msg3'), - Error(DEBUG, 'msg4'), - Error(NOTSET, 'msg4'), - ] - assert cmd.have_problematic_error(have_problematic_errors) - assert cmd.have_problematic_error(no_problematic_errors) == False +def test_about_transform_expanded_help_text(): + check_about_stdout( + ['transform', '--help-format'], + 'test_cmd/help/about_transform_config_help.txt', regen=False) diff --git a/tests/test_gen.py b/tests/test_gen.py index 5ece6d76..6dec5f6d 100644 --- a/tests/test_gen.py +++ b/tests/test_gen.py @@ -29,93 +29,89 @@ from attributecode import CRITICAL from attributecode import Error from attributecode import gen +from attributecode import DEFAULT_MAPPING +from unittest.case import skip class GenTest(unittest.TestCase): def test_check_duplicated_columns(self): - test_file = get_test_loc('gen/dup_keys.csv') - expected = [Error(ERROR, u'Duplicated column name(s): copyright with copyright\nPlease correct the input and re-run.')] + test_file = get_test_loc('test_gen/dup_keys.csv') + expected = [Error(ERROR, 'Duplicated column name(s): copyright with copyright\nPlease correct the input and re-run.')] result = gen.check_duplicated_columns(test_file) assert expected == result def test_check_duplicated_columns_handles_lower_upper_case(self): - test_file = get_test_loc('gen/dup_keys_with_diff_case.csv') - expected = [Error(ERROR, u'Duplicated column name(s): copyright with Copyright\nPlease correct the input and re-run.')] + test_file = get_test_loc('test_gen/dup_keys_with_diff_case.csv') + expected = [Error(ERROR, 'Duplicated column name(s): copyright with Copyright\nPlease correct the input and re-run.')] result = gen.check_duplicated_columns(test_file) assert expected == result def test_check_duplicated_about_file_path(self): - test_dict = [{'about_file_path': u'/test/test.c', u'version': u'1.03', u'name': u'test.c'}, - {'about_file_path': u'/test/abc/', u'version': u'1.0', u'name': u'abc'}, - {'about_file_path': u'/test/test.c', u'version': u'1.04', u'name': u'test1.c'}] - expected = [Error(CRITICAL, u'The input has duplicated values in \'about_file_path\' field: /test/test.c')] + test_dict = [ + {'about_file_path': '/test/test.c', 'version': '1.03', 'name': 'test.c'}, + {'about_file_path': '/test/abc/', 'version': '1.0', 'name': 'abc'}, + {'about_file_path': '/test/test.c', 'version': '1.04', 'name': 'test1.c'}] + expected = [ + Error(CRITICAL, + "The input has duplicated values in 'about_file_path' field: /test/test.c")] result = gen.check_duplicated_about_file_path(test_dict) assert expected == result def test_load_inventory(self): - location = get_test_loc('gen/inv.csv') - base_dir = get_test_loc('inv') + location = get_test_loc('test_gen/inv.csv') + base_dir = get_temp_dir() errors, abouts = gen.load_inventory(location, base_dir) - expected_error_messages = ['Field about_resource', - 'Field custom1 is not a supported field and is ignored.'] - # FIXME: this is not used expected_errors = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.')] - assert len(errors) == 2 - for e in errors: - # we don't want to check the path value - if e.message.startswith('Field about_resource'): - continue - else: - assert e.message in expected_error_messages - - expected = [u'about_resource: .\n' - u'name: AboutCode\n' - u'version: 0.11.0\n' - u'description: |-\n' - u' multi\n' - u' line\n'] - result = [a.dumps(use_mapping=False, mapping_file=False, with_absent=False, with_empty=False) + Error(INFO, 'Field custom1 is not a supported field and is ignored.'), + Error(INFO, 'Field about_resource: Path') + ] + for exp, err in zip(expected_errors, errors): + assert exp.severity == err.severity + assert err.message.startswith(exp.message) + + expected = ( + 'about_resource: .\n' + 'name: AboutCode\n' + 'version: 0.11.0\n' + 'description: |\n' + ' multi\n' + ' line\n' + ) + result = [a.dumps(mapping_file=False, with_absent=False, with_empty=False) for a in abouts] - assert expected == result + assert expected == result[0] def test_load_inventory_with_mapping(self): - location = get_test_loc('gen/inv4.csv') - base_dir = get_test_loc('inv') - license_notice_text_location = None - use_mapping = True - errors, abouts = gen.load_inventory(location, - base_dir, - license_notice_text_location, - use_mapping) - expected_error_messages = ['Field about_resource', - 'Field test is not a supported field and is not defined in the mapping file. This field is ignored.', - 'Field resource is a custom field'] - - assert len(errors) == 3 - for e in errors: - # we don't want to check the path value - if e.message.startswith('Field about_resource'): - continue - else: - assert e.message in expected_error_messages - - expected = [u'about_resource: .\n' - u'name: AboutCode\n' - u'version: 0.11.0\n' - u'copyright: Copyright (c) nexB, Inc.\n' - u'resource: this.ABOUT\n' - u'description: |-\n' - u' multi\n' - u' line\n' - ] - result = [a.dumps(use_mapping, mapping_file=False, with_absent=False, with_empty=False) - for a in abouts] - assert expected == result + location = get_test_loc('test_gen/inv4.csv') + base_dir = get_temp_dir() + errors, abouts = gen.load_inventory(location, base_dir, mapping_file=DEFAULT_MAPPING) + + expected_errors = [ + Error(INFO, 'Field resource is a custom field'), + Error(INFO, 'Field test is not a supported field and is not defined in the mapping file. This field is ignored.'), + Error(INFO, 'Field about_resource: Path ') + ] + + for exp, err in zip(expected_errors, errors): + assert exp.severity == err.severity + assert err.message.startswith(exp.message) + + expected = ( + 'about_resource: .\n' + 'name: AboutCode\n' + 'version: 0.11.0\n' + 'copyright: Copyright (c) nexB, Inc.\n' + 'description: |\n' + ' multi\n' + ' line\n' + 'resource: this.ABOUT\n' + ) + result = [a.dumps(with_empty=False) for a in abouts] + assert expected == result[0] def test_generation_dir_endswith_space(self): - location = get_test_loc('inventory/complex/about_file_path_dir_endswith_space.csv') + location = get_test_loc('test_gen/inventory/complex/about_file_path_dir_endswith_space.csv') base_dir = get_temp_dir() errors, _abouts = gen.generate(location, base_dir) expected_errors_msg1 = 'contains directory name ends with spaces which is not allowed. Generation skipped.' @@ -126,75 +122,72 @@ def test_generation_dir_endswith_space(self): assert expected_errors_msg2 in errors[0].message or expected_errors_msg2 in errors[1].message def test_generation_with_no_about_resource(self): - location = get_test_loc('gen/inv2.csv') + location = get_test_loc('test_gen/inv2.csv') base_dir = get_temp_dir() errors, abouts = gen.generate(location, base_dir) - expected = OrderedDict([(u'.', None)]) + expected = OrderedDict([('.', None)]) assert abouts[0].about_resource.value == expected assert len(errors) == 1 def test_generation_with_no_about_resource_reference(self): - location = get_test_loc('gen/inv3.csv') + location = get_test_loc('test_gen/inv3.csv') base_dir = get_temp_dir() errors, abouts = gen.generate(location, base_dir) - expected = OrderedDict([(u'test.tar.gz', None)]) + expected = OrderedDict([('test.tar.gz', None)]) assert abouts[0].about_resource.value == expected assert len(errors) == 1 - msg = u'Field about_resource' + msg = 'Field about_resource' assert msg in errors[0].message def test_generation_with_no_about_resource_reference_no_resource_validation(self): - location = get_test_loc('gen/inv3.csv') + location = get_test_loc('test_gen/inv3.csv') base_dir = get_temp_dir() errors, abouts = gen.generate(location, base_dir) - expected = OrderedDict([(u'test.tar.gz', None)]) + expected = OrderedDict([('test.tar.gz', None)]) assert abouts[0].about_resource.value == expected assert len(errors) == 1 def test_generate(self): - location = get_test_loc('gen/inv.csv') + location = get_test_loc('test_gen/inv.csv') base_dir = get_temp_dir() errors, abouts = gen.generate(location, base_dir) - msg1 = u'Field custom1 is not a supported field and is ignored.' - msg2 = u'Field about_resource' + msg1 = 'Field custom1 is not a supported field and is ignored.' + msg2 = 'Field about_resource' assert msg1 in errors[0].message assert msg2 in errors[1].message - in_mem_result = [a.dumps(use_mapping=False, mapping_file=False, with_absent=False, with_empty=False) + result = [a.dumps(mapping_file=False, with_absent=False, with_empty=False) for a in abouts][0] - expected = (u'about_resource: .\n' - u'name: AboutCode\n' - u'version: 0.11.0\n' - u'description: |-\n' - u' multi\n' - u' line\n') - assert expected == in_mem_result + expected = ( + 'about_resource: .\n' + 'name: AboutCode\n' + 'version: 0.11.0\n' + 'description: |\n' + ' multi\n' + ' line\n') + assert expected == result + @skip('FIXME: this test is making a failed, live API call') def test_generate_not_overwrite_original_license_file(self): - location = get_test_loc('gen/inv5.csv') + location = get_test_loc('test_gen/inv5.csv') base_dir = get_temp_dir() - license_notice_text_location = None + reference_dir = None fetch_license = ['url', 'lic_key'] - _errors, abouts = gen.generate(location, base_dir, license_notice_text_location, fetch_license) + _errors, abouts = gen.generate( + location, base_dir, reference_dir, fetch_license) - in_mem_result = [a.dumps(use_mapping=False, mapping_file=False, with_absent=False, with_empty=False) - for a in abouts][0] - expected = (u'about_resource: .\n' - u'name: AboutCode\n' - u'version: 0.11.0\n' - u'licenses:\n' - u' - file: this.LICENSE\n') - assert expected == in_mem_result - - def test_deduplicate(self): - items = ['a', 'b', 'd', 'b', 'c', 'a'] - expected = ['a', 'b', 'd', 'c'] - results = gen.deduplicate(items) - assert expected == results + result = [a.dumps(with_empty=False)for a in abouts][0] + expected = ( + 'about_resource: .\n' + 'name: AboutCode\n' + 'version: 0.11.0\n' + 'licenses:\n' + ' - file: this.LICENSE\n') + assert expected == result diff --git a/tests/test_model.py b/tests/test_model.py index daf15738..e66fc2f5 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -19,36 +19,38 @@ from __future__ import unicode_literals from collections import OrderedDict +import io import json import posixpath -import sys +import shutil import unittest -from unittest.case import expectedFailure import mock -import attributecode from attributecode import CRITICAL from attributecode import ERROR from attributecode import INFO from attributecode import WARNING +from attributecode import DEFAULT_MAPPING from attributecode import Error from attributecode import model -from attributecode import util from attributecode.util import add_unc from attributecode.util import load_csv +from attributecode.util import to_posix + from testing_utils import extract_test_loc from testing_utils import get_temp_file from testing_utils import get_test_loc -from testing_utils import get_test_lines -from testing_utils import get_unicode_content +import saneyaml -def check_csv(expected, result): +def check_csv(expected, result, regen=False): """ Assert that the contents of two CSV files locations `expected` and `result` are equal. """ + if regen: + shutil.copyfile(result, expected) expected = sorted([sorted(d.items()) for d in load_csv(expected)]) result = sorted([sorted(d.items()) for d in load_csv(result)]) assert expected == result @@ -65,6 +67,21 @@ def check_json(expected, result): assert expected == result +def get_test_content(test_location): + """ + Read file at test_location and return a unicode string. + """ + return get_unicode_content(get_test_loc(test_location)) + + +def get_unicode_content(location): + """ + Read file at location and return a unicode string. + """ + with io.open(location, encoding='utf-8') as doc: + return doc.read() + + class FieldTest(unittest.TestCase): def test_Field_init(self): model.Field() @@ -86,24 +103,24 @@ def test_empty_Field_has_default_value(self): def test_PathField_check_location(self): test_file = 'license.LICENSE' field = model.PathField(name='f', value=test_file, present=True) - base_dir = get_test_loc('fields') + base_dir = get_test_loc('test_model/fields') errors = field.validate(base_dir=base_dir) expected_errrors = [] assert expected_errrors == errors result = field.value[test_file] - expected = add_unc(posixpath.join(util.to_posix(base_dir), test_file)) + expected = add_unc(posixpath.join(to_posix(base_dir), test_file)) assert expected == result def test_PathField_check_missing_location(self): test_file = 'does.not.exist' field = model.PathField(name='f', value=test_file, present=True) - base_dir = get_test_loc('fields') + base_dir = get_test_loc('test_model/fields') errors = field.validate(base_dir=base_dir) file_path = posixpath.join(base_dir, test_file) - err_msg = u'Field f: Path %s not found' % file_path + err_msg = 'Field f: Path %s not found' % file_path expected_errors = [ Error(CRITICAL, err_msg)] @@ -116,12 +133,11 @@ def test_TextField_loads_file(self): field = model.FileTextField( name='f', value='license.LICENSE', present=True) - - base_dir = get_test_loc('fields') + base_dir = get_test_loc('test_model/fields') errors = field.validate(base_dir=base_dir) assert [] == errors - expected = {'license.LICENSE': u'some license text'} + expected = {'license.LICENSE': 'some license text'} assert expected == field.value def test_UrlField_is_valid_url(self): @@ -189,188 +205,135 @@ def test_PathField_contains_dict_after_validate(self): field_class = model.PathField expected = OrderedDict([('string', None)]) expected_errors = [ - Error(ERROR, u'Field s: Unable to verify path: string: No base directory provided') + Error(ERROR, 'Field s: Unable to verify path: string: No base directory provided') ] self.check_validate(field_class, value, expected, expected_errors) - """ - UrlField no longer become a list. - If a list is wanted, use UrlListField instead. - def test_UrlField_contains_list_after_validate(self): - value = 'http://some.com/url' - field_class = model.UrlField - expected = [value] - self.check_validate(field_class, value, expected, expected_errors=[]) - """ def test_SingleLineField_has_errors_if_multiline(self): value = '''line1 line2''' field_class = model.SingleLineField expected = value - expected_errors = [Error(ERROR, u'Field s: Cannot span multiple lines: line1\n line2')] + expected_errors = [Error(ERROR, 'Field s: Cannot span multiple lines: line1\n line2')] self.check_validate(field_class, value, expected, expected_errors) - def test_AboutResourceField_can_resolve_single_value(self): - about_file_path = 'some/dir/me.ABOUT' - field = model.AboutResourceField(name='s', value='.', present=True) - field.validate() - expected = ['some/dir'] - field.resolve(about_file_path) - result = field.resolved_paths - assert expected == result - - def check_AboutResourceField_can_resolve_paths_list(self): - about_file_path = 'some/dir/me.ABOUT' - value = '''. - ../path1 - path2/path3/ - /path2/path3/ - ''' - field = model.AboutResourceField(name='s', value=value, present=True) - field.validate() - expected = ['some/dir', - 'some/path1', - 'some/dir/path2/path3'] - field.resolve(about_file_path) - result = field.resolved_paths - assert expected == result - - def test_AboutResourceField_can_resolve_paths_list_multiple_times(self): - for _ in range(3): - self.check_AboutResourceField_can_resolve_paths_list() - -class ParseTest(unittest.TestCase): +class YamlParseTest(unittest.TestCase): maxDiff = None - def test_parse_can_parse_simple_fields(self): - test = get_test_lines('parse/basic.about') - errors, result = list(model.parse(test)) + def test_saneyaml_load_can_parse_simple_fields(self): + test = get_test_content('test_model/parse/basic.about') + result = saneyaml.load(test) - assert [] == errors - - expected = [(u'single_line', u'optional'), - (u'other_field', u'value'), - ] - assert expected == result - - def test_parse_can_parse_continuations(self): - test = get_test_lines('parse/continuation.about') - errors, result = model.parse(test) - - assert [] == errors + expected = [ + ('single_line', 'optional'), + ('other_field', 'value'), + ] - expected = [(u'single_line', u'optional'), - (u'other_field', u'value'), - (u'multi_line', u'some value\n' - u'and more\n' - u' and yet more')] - assert expected == result + assert expected == list(result.items()) - def test_parse_can_handle_complex_continuations(self): - test = get_test_lines('parse/complex.about') - errors, result = model.parse(test) - assert [] == errors + def test_saneyaml_load_can_parse_continuations(self): + test = get_test_content('test_model/parse/continuation.about') + result = saneyaml.load(test) - expected = [(u'single_line', u'optional'), - (u'other_field', u'value\n'), - (u'multi_line', u'some value\n' - u'and more\n' - u' and yet more\n' - u' '), - (u'yetanother', u'\nsdasd')] - assert expected == result + expected = [ + ('single_line', 'optional'), + ('other_field', 'value'), + (u'multi_line', u'some value and more and yet more') + ] - def test_parse_error_for_invalid_field_name(self): - test = get_test_lines('parse/invalid_names.about') - errors, result = model.parse(test) - expected = [(u'val3_id_', u'some:value'), - (u'VALE3_ID_', u'some:value')] - assert expected == result + assert expected == list(result.items()) - expected_errors = [ - Error(CRITICAL, "Invalid line: 0: 'invalid space:value\\n'"), - Error(CRITICAL, "Invalid line: 1: 'other-field: value\\n'"), - Error(CRITICAL, "Invalid line: 4: '_invalid_dash: value\\n'"), - Error(CRITICAL, "Invalid line: 5: '3invalid_number: value\\n'"), - Error(CRITICAL, "Invalid line: 6: 'invalid.dot: value'") - ] - assert expected_errors == errors - - def test_parse_error_for_invalid_continuation(self): - test = get_test_lines('parse/invalid_continuation.about') - errors, result = model.parse(test) - expected = [(u'single_line', u'optional'), - (u'other_field', u'value'), - (u'multi_line', u'some value\n' u'and more')] - assert expected == result - expected_errors = [ - Error(CRITICAL, "Invalid continuation line: 0:" - " u' invalid continuation1\\n'"), - Error(CRITICAL, "Invalid continuation line: 7:" - " u' invalid continuation2\\n'")] - assert expected_errors == errors + def test_saneyaml_load_can_handle_multiline_texts_and_strips_text_fields(self): + test = get_test_content('test_model/parse/complex.about') + result = saneyaml.load(test) - def test_parse_rejects_non_ascii_names_and_accepts_unicode_values(self): - test = get_test_lines('parse/non_ascii_field_name_value.about') - errors, result = model.parse(test) - expected = [(u'name', u'name'), - (u'about_resource', u'.'), - (u'owner', 'Matías Aguirre')] - assert expected == result + expected = [ + ('single_line', 'optional'), + ('other_field', 'value'), + ('multi_line', 'some value and more and yet more'), + ('yetanother', 'sdasd')] - expected_msg = "Invalid line: 3: 'Matías: unicode field name\\n'" - if sys.version_info[0] < 3: # Python 2 - expected_msg = "Invalid line: 3: 'Mat\\xedas: unicode field name\\n'" + assert expected == list(result.items()) - expected_errors = [ - Error(CRITICAL, expected_msg)] - assert expected_errors == errors + def test_saneyaml_load_can_parse_verbatim_text_unstripped(self): + test = get_test_content('test_model/parse/continuation_verbatim.about') + result = saneyaml.load(test) - def test_parse_handles_blank_lines_and_spaces_in_field_names(self): + expected = [ + (u'single_line', u'optional'), + (u'other_field', u'value'), + (u'multi_line', u'some value \n and more \n and yet more \n \n') + ] + + assert expected == list(result.items()) + + def test_saneyaml_load_report_error_for_invalid_field_name(self): + test = get_test_content('test_model/parse/invalid_names.about') + try: + saneyaml.load(test) + self.fail('Exception not raised') + except Exception: + pass + + def test_saneyaml_dangling_text_is_not_an_invalid_continuation(self): + test = get_test_content('test_model/parse/invalid_continuation.about') + result = saneyaml.load(test) + expected = [ + (u'single_line', u'optional'), + (u'other_field', u'value'), + (u'multi_line', u'some value and more\ninvalid continuation2') + ] + assert expected == list(result.items()) + + def test_saneyaml_load_accepts_unicode_keys_and_values(self): + test = get_test_content('test_model/parse/non_ascii_field_name_value.about') + result = saneyaml.load(test) + expected = [ + ('name', 'name'), + ('about_resource', '.'), + ('owner', 'Matías Aguirre'), + (u'Matías', u'unicode field name') + ] + assert expected == list(result.items()) + + def test_saneyaml_load_accepts_blank_lines_and_spaces_in_field_names(self): test = ''' name: test space version: 0.7.0 about_resource: about.py field with spaces: This is a test case for field with spaces -'''.splitlines(True) +''' - errors, result = model.parse(test) + result = saneyaml.load(test) - expected = [('name', 'test space'), - ('version', '0.7.0'), - ('about_resource', 'about.py')] - assert expected == result + expected = [ + ('name', 'test space'), + ('version', '0.7.0'), + ('about_resource', 'about.py'), + (u'field with spaces', u'This is a test case for field with spaces'), + ] - expected_errors = [ - Error(CRITICAL, "Invalid line: 4: 'field with spaces: This is a test case for field with spaces\\n'")] - assert expected_errors == errors + assert expected == list(result.items()) - def test_parse_ignore_blank_lines_and_lines_without_no_colon(self): + def test_saneyaml_loads_blank_lines_and_lines_without_no_colon(self): test = ''' name: no colon test test version: 0.7.0 about_resource: about.py test with no colon -'''.splitlines(True) - errors, result = model.parse(test) - - expected = [('name', 'no colon test'), - ('version', '0.7.0'), - ('about_resource', 'about.py')] - assert expected == result - - expected_errors = [ - Error(CRITICAL, "Invalid line: 2: 'test\\n'"), - Error(CRITICAL, "Invalid line: 5: 'test with no colon\\n'")] - assert expected_errors == errors - +''' + try: + saneyaml.load(test) + self.fail('Exception not raised') + except Exception: + pass class AboutTest(unittest.TestCase): def test_About_load_ignores_original_field_order_and_uses_standard_predefined_order(self): # fields in this file are not in the standard order - test_file = get_test_loc('parse/ordered_fields.ABOUT') + test_file = get_test_loc('test_model/parse/ordered_fields.ABOUT') a = model.About(test_file) assert [] == a.errors @@ -382,15 +345,15 @@ def test_About_duplicate_field_names_are_detected_with_different_case(self): # This test is failing because the YAML does not keep the order when # loads the test files. For instance, it treat the 'About_Resource' as the # first element and therefore the dup key is 'about_resource'. - test_file = get_test_loc('parse/dupe_field_name.ABOUT') + test_file = get_test_loc('test_model/parse/dupe_field_name.ABOUT') a = model.About(test_file) expected = [ - Error(WARNING, u'Field Name is a duplicate. Original value: "old" replaced with: "new"'), - Error(INFO, u'Field About_Resource is a duplicate with the same value as before.')] + Error(WARNING, 'Field Name is a duplicate. Original value: "old" replaced with: "new"'), + Error(INFO, 'Field About_Resource is a duplicate with the same value as before.')] result = a.errors assert sorted(expected) == sorted(result) - def check_About_hydrate(self, about, fields, errors): + def check_About_hydrate(self, about, fields): expected = set([ 'name', 'homepage_url', @@ -401,9 +364,9 @@ def check_About_hydrate(self, about, fields, errors): 'about_resource']) expected_errors = [ - Error(INFO, u'Field date is not a supported field and is ignored.'), - Error(INFO, u'Field license_spdx is not a supported field and is ignored.'), - Error(INFO, u'Field license_text_file is not a supported field and is ignored.')] + Error(INFO, 'Field date is not a supported field and is ignored.'), + Error(INFO, 'Field license_spdx is not a supported field and is ignored.'), + Error(INFO, 'Field license_text_file is not a supported field and is ignored.')] errors = about.hydrate(fields) @@ -413,22 +376,14 @@ def check_About_hydrate(self, about, fields, errors): assert expected == result def test_About_hydrate_normalize_field_names_to_lowercase(self): - test_file = get_test_lines('parser_tests/upper_field_names.ABOUT') - errors, fields = model.parse(test_file) - assert [] == errors - a = model.About() - self.check_About_hydrate(a, fields, errors) - - def test_About_hydrate_can_be_called_multiple_times(self): - test_file = get_test_lines('parser_tests/upper_field_names.ABOUT') - errors, fields = model.parse(test_file) - assert [] == errors + test_content = get_test_content('test_gen/parser_tests/upper_field_names.ABOUT') + fields = saneyaml.load(test_content).items() a = model.About() for _ in range(3): - self.check_About_hydrate(a, fields, errors) + self.check_About_hydrate(a, fields) def test_About_with_existing_about_resource_has_no_error(self): - test_file = get_test_loc('parser_tests/about_resource_field.ABOUT') + test_file = get_test_loc('test_gen/parser_tests/about_resource_field.ABOUT') a = model.About(test_file) assert [] == a.errors result = a.about_resource.value['about_resource.c'] @@ -436,86 +391,75 @@ def test_About_with_existing_about_resource_has_no_error(self): self.assertNotEqual([], result) def test_About_has_errors_when_about_resource_is_missing(self): - test_file = get_test_loc('parser_tests/.ABOUT') + test_file = get_test_loc('test_gen/parser_tests/.ABOUT') a = model.About(test_file) - expected = [ - Error(CRITICAL, u'Field about_resource is required') - ] + expected = [Error(CRITICAL, 'Field about_resource is required')] result = a.errors assert expected == result def test_About_has_errors_when_about_resource_does_not_exist(self): - test_file = get_test_loc('parser_tests/missing_about_ref.ABOUT') + test_file = get_test_loc('test_gen/parser_tests/missing_about_ref.ABOUT') file_path = posixpath.join(posixpath.dirname(test_file), 'about_file_missing.c') a = model.About(test_file) - err_msg = u'Field about_resource: Path %s not found' % file_path - expected = [ - Error(INFO, err_msg)] + err_msg = 'Field about_resource: Path %s not found' % file_path + expected = [Error(INFO, err_msg)] result = a.errors assert expected == result def test_About_has_errors_when_missing_required_fields_are_missing(self): - test_file = get_test_loc('parse/missing_required.ABOUT') + test_file = get_test_loc('test_model/parse/missing_required.ABOUT') a = model.About(test_file) expected = [ - Error(CRITICAL, u'Field about_resource is required'), + Error(CRITICAL, 'Field about_resource is required'), Error(CRITICAL, 'Field name is required'), - ] + ] result = a.errors assert expected == result def test_About_has_errors_when_required_fields_are_empty(self): - test_file = get_test_loc('parse/empty_required.ABOUT') + test_file = get_test_loc('test_model/parse/empty_required.ABOUT') a = model.About(test_file) expected = [ - Error(CRITICAL, u'Field about_resource is required and empty'), + Error(CRITICAL, 'Field about_resource is required and empty'), Error(CRITICAL, 'Field name is required and empty'), - ] + ] result = a.errors assert expected == result def test_About_has_errors_with_empty_notice_file_field(self): - test_file = get_test_loc('parse/empty_notice_field.about') + test_file = get_test_loc('test_model/parse/empty_notice_field.about') a = model.About(test_file) expected = [ - Error(WARNING, u'Field notice_file is present but empty')] + Error(WARNING, 'Field notice_file is present but empty')] result = a.errors assert expected == result - @expectedFailure - # This test need to be updated as the custom field will be ignore if no - # mapping is set - def test_About_custom_fields_are_collected_correctly(self): - test_file = get_test_loc('parse/custom_fields.about') + def test_About_custom_fields_are_ignored_if_not_in_mapping(self): + test_file = get_test_loc('test_model/custom_fields/custom_fields.about') a = model.About(test_file) result = [(n, f.value) for n, f in a.custom_fields.items()] - expected = [ - (u'single_line', u'README STUFF'), - (u'multi_line', u'line1\nline2'), - (u'empty', '')] - assert sorted(expected) == sorted(result) + assert not result - @expectedFailure - # This test need to be updated as the custom field will be ignore if no - # mapping is set - def test_About_custom_fields_are_collected_correctly_as_multiline_scalar(self): - test_file = get_test_loc('parse/custom_fields.about') - a = model.About(test_file) + def test_About_custom_fields_are_not_ignored_if_in_mapping(self): + test_file = get_test_loc('test_model/custom_fields/custom_fields.about') + test_mapping = get_test_loc('test_model/custom_fields/mapping.config') + a = model.About(test_file, mapping_file=test_mapping) result = [(n, f.value) for n, f in a.custom_fields.items()] expected = [ - (u'single_line', u'README STUFF'), - (u'multi_line', u'line1\nline2'), - (u'empty', '')] - assert expected == result + ('empty', ''), + ('single_line', 'README STUFF'), + ('multi_line', 'line1\nline2'), + ] + assert sorted(expected) == sorted(result) def test_About_has_errors_for_illegal_custom_field_name(self): - test_file = get_test_loc('parse/illegal_custom_field.about') + test_file = get_test_loc('test_model/parse/illegal_custom_field.about') a = model.About(test_file) result = a.custom_fields assert {} == result def test_About_file_fields_are_empty_if_present_and_path_missing(self): - test_file = get_test_loc('parse/missing_notice_license_files.ABOUT') + test_file = get_test_loc('test_model/parse/missing_notice_license_files.ABOUT') a = model.About(test_file) file_path1 = posixpath.join(posixpath.dirname(test_file), 'test.LICENSE') @@ -531,7 +475,7 @@ def test_About_file_fields_are_empty_if_present_and_path_missing(self): assert {'test.NOTICE': None} == a.notice_file.value def test_About_notice_and_license_text_are_loaded_from_file(self): - test_file = get_test_loc('parse/license_file_notice_file.ABOUT') + test_file = get_test_loc('test_model/parse/license_file_notice_file.ABOUT') a = model.About(test_file) expected = '''Tester holds the copyright for test component. Tester relinquishes copyright of @@ -547,7 +491,7 @@ def test_About_notice_and_license_text_are_loaded_from_file(self): assert expected == result def test_About_license_and_notice_text_are_empty_if_field_missing(self): - test_file = get_test_loc('parse/no_file_fields.ABOUT') + test_file = get_test_loc('test_model/parse/no_file_fields.ABOUT') a = model.About(test_file) expected_errors = [] @@ -560,21 +504,21 @@ def test_About_license_and_notice_text_are_empty_if_field_missing(self): assert {} == result def test_About_rejects_non_ascii_names_and_accepts_unicode_values(self): - test_file = get_test_loc('parse/non_ascii_field_name_value.about') + test_file = get_test_loc('test_model/parse/non_ascii_field_name_value.about') a = model.About(test_file) result = a.errors expected = [ - Error(INFO, u'Field Mat\xedas is not a supported field and is ignored.')] + Error(INFO, 'Field Mat\xedas is not a supported field and is ignored.')] assert expected == result def test_About_invalid_boolean_value(self): - test_file = get_test_loc('parse/invalid_boolean.about') + test_file = get_test_loc('test_model/parse/invalid_boolean.about') a = model.About(test_file) expected_msg = "Field modified: Invalid flag value: 'blah'" assert expected_msg in a.errors[0].message def test_About_contains_about_file_path(self): - test_file = get_test_loc('parse/complete/about.ABOUT') + test_file = get_test_loc('test_model/parse/complete/about.ABOUT') a = model.About(test_file, about_file_path='complete/about.ABOUT') assert [] == a.errors expected = 'complete/about.ABOUT' @@ -582,45 +526,19 @@ def test_About_contains_about_file_path(self): assert expected == result def test_About_equals(self): - test_file = get_test_loc('equal/complete/about.ABOUT') + test_file = get_test_loc('test_model/equal/complete/about.ABOUT') a = model.About(test_file, about_file_path='complete/about.ABOUT') b = model.About(test_file, about_file_path='complete/about.ABOUT') assert a == b - def FAILING_test_About_equals_with_small_text_differences(self): - test_file = get_test_loc('equal/complete2/about.ABOUT') + def test_About_are_not_equal_with_small_text_differences(self): + test_file = get_test_loc('test_model/equal/complete2/about.ABOUT') a = model.About(test_file, about_file_path='complete2/about.ABOUT') - test_file2 = get_test_loc('equal/complete/about.ABOUT') + test_file2 = get_test_loc('test_model/equal/complete/about.ABOUT') b = model.About(test_file2, about_file_path='complete/about.ABOUT') - assert a.dumps(True) == b.dumps(True) + assert a.dumps() != b.dumps() assert a == b - - def test_About_same_attribution(self): - base_dir = 'some_dir' - a = model.About() - a.load_dict({'name': u'apache', 'version': u'1.1' }, base_dir) - b = model.About() - b.load_dict({'name': u'apache', 'version': u'1.1' }, base_dir) - assert a.same_attribution(b) - - def test_About_same_attribution_with_different_resource(self): - base_dir = 'some_dir' - a = model.About() - a.load_dict({'about_resource': u'resource', 'name': u'apache', 'version': u'1.1' }, base_dir) - b = model.About() - b.load_dict({'about_resource': u'other', 'name': u'apache', 'version': u'1.1' }, base_dir) - assert a.same_attribution(b) - - def test_About_same_attribution_different_data(self): - base_dir = 'some_dir' - a = model.About() - a.load_dict({'about_resource': u'resource', 'name': u'apache', 'version': u'1.1' }, base_dir) - b = model.About() - b.load_dict({'about_resource': u'other', 'name': u'apache', 'version': u'1.2' }, base_dir) - assert not a.same_attribution(b) - assert not b.same_attribution(a) - def test_field_names(self): a = model.About() a.custom_fields['f'] = model.StringField(name='f', value='1', @@ -675,7 +593,6 @@ def test_field_names(self): result = model.field_names(abouts) assert expected == result - @expectedFailure def test_field_names_does_not_return_duplicates_custom_fields(self): a = model.About() a.custom_fields['f'] = model.StringField(name='f', value='1', @@ -690,107 +607,60 @@ def test_field_names_does_not_return_duplicates_custom_fields(self): abouts = [a, b] # ensure that custom fields and about file path are collected # and that all fields are in the correct order + # FIXME: this is not USED expected = [ 'about_resource', + 'name', 'cf', 'f', 'g', ] - model.field_names(abouts, with_paths=False, - with_absent=False, - with_empty=False) - # FIXME: missing test!!! - assert True == False + result = model.field_names(abouts, with_paths=False, with_absent=False, with_empty=False) + assert expected == result class SerializationTest(unittest.TestCase): def test_About_dumps(self): - test_file = get_test_loc('parse/complete/about.ABOUT') + test_file = get_test_loc('test_model/parse/complete/about.ABOUT') a = model.About(test_file) assert [] == a.errors - expected = u'''about_resource: . + expected = '''about_resource: . name: AboutCode version: 0.11.0 copyright: Copyright (c) 2013-2014 nexB Inc. license_expression: apache-2.0 author: - - Jillian Daguil - - Chin Yeung Li - - Philippe Ombredanne - - Thomas Druez -description: |- - AboutCode is a tool - to process ABOUT files. - An ABOUT file is a file. + - Jillian Daguil + - Chin Yeung Li + - Philippe Ombredanne + - Thomas Druez +description: | + AboutCode is a tool + to process ABOUT files. + An ABOUT file is a file. homepage_url: http://dejacode.org licenses: - - file: apache-2.0.LICENSE - key: apache-2.0 + - key: apache-2.0 + file: apache-2.0.LICENSE notice_file: NOTICE owner: nexB Inc. vcs_repository: https://github.com/dejacode/about-code-tool.git vcs_tool: git ''' - result = a.dumps(use_mapping=True) + result = a.dumps(mapping_file=DEFAULT_MAPPING) assert expected == result - # We do not support with_absent and with_empty staring in version 3.2.0. - def FAILING_test_About_dumps_all_fields_if_not_present_with_absent_True(self): - test_file = get_test_loc('parse/complete2/about.ABOUT') - a = model.About(test_file) - expected_error = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.'), - Error(INFO, u'Field custom2 is not a supported field and is ignored.')] - assert sorted(expected_error) == sorted(a.errors) - - expected = u'''about_resource: . -name: AboutCode -version: 0.11.0 -download_url: -description: -homepage_url: -notes: -license: -license_expression: -license_name: -license_file: -license_url: -copyright: -notice_file: -notice_url: -redistribute: -attribute: -track_changes: -modified: -changelog_file: -owner: -owner_url: -contact: -author: -vcs_tool: -vcs_repository: -vcs_path: -vcs_tag: -vcs_branch: -vcs_revision: -checksum_md5: -checksum_sha1: -checksum_sha256: -spec_version: -''' - result = a.dumps(with_absent=True) - assert set(expected) == set(result) def test_About_dumps_does_not_dump_not_present_with_absent_False(self): - test_file = get_test_loc('parse/complete2/about.ABOUT') + test_file = get_test_loc('test_model/parse/complete2/about.ABOUT') a = model.About(test_file) expected_error = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.'), - Error(INFO, u'Field custom2 is not a supported field and is ignored.')] + Error(INFO, 'Field custom1 is not a supported field and is ignored.'), + Error(INFO, 'Field custom2 is not a supported field and is ignored.')] assert sorted(expected_error) == sorted(a.errors) - expected = u'''about_resource: . + expected = '''about_resource: . name: AboutCode version: 0.11.0 ''' @@ -798,13 +668,13 @@ def test_About_dumps_does_not_dump_not_present_with_absent_False(self): assert set(expected) == set(result) def test_About_dumps_with_different_boolean_value(self): - test_file = get_test_loc('parse/complete2/about2.ABOUT') + test_file = get_test_loc('test_model/parse/complete2/about2.ABOUT') a = model.About(test_file) expected_error_msg = "Field track_changes: Invalid flag value: 'blah' is not one of" assert len(a.errors) == 1 assert expected_error_msg in a.errors[0].message - expected = u'''about_resource: . + expected = '''about_resource: . name: AboutCode version: 0.11.0 @@ -814,27 +684,26 @@ def test_About_dumps_with_different_boolean_value(self): modified: yes ''' - result = a.dumps(use_mapping=False, mapping_file=False) + result = a.dumps(mapping_file=False) assert set(expected) == set(result) - def test_About_dumps_does_not_dump_present__empty_with_absent_False(self): - test_file = get_test_loc('parse/complete2/about.ABOUT') + test_file = get_test_loc('test_model/parse/complete2/about.ABOUT') a = model.About(test_file) expected_error = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.'), - Error(INFO, u'Field custom2 is not a supported field and is ignored.')] + Error(INFO, 'Field custom1 is not a supported field and is ignored.'), + Error(INFO, 'Field custom2 is not a supported field and is ignored.')] assert sorted(expected_error) == sorted(a.errors) - expected = u'''about_resource: . + expected = '''about_resource: . name: AboutCode version: 0.11.0 ''' - result = a.dumps(use_mapping=False, mapping_file=False, with_absent=False, with_empty=False) + result = a.dumps(mapping_file=False, with_absent=False, with_empty=False) assert expected == result def test_About_as_dict_contains_special_paths(self): - test_file = get_test_loc('parse/complete/about.ABOUT') + test_file = get_test_loc('test_model/parse/complete/about.ABOUT') a = model.About(test_file, about_file_path='complete/about.ABOUT') expected_errors = [] assert expected_errors == a.errors @@ -843,132 +712,36 @@ def test_About_as_dict_contains_special_paths(self): result = as_dict[model.About.about_file_path_attr] assert expected == result - # The with_present and empty is no longer supported. - def FAILING_test_About_as_dict_with_empty(self): - test_file = get_test_loc('as_dict/about.ABOUT') - a = model.About(test_file, about_file_path='complete/about.ABOUT') - expected_errors = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.'), - Error(INFO, u'Field custom_empty is not a supported field and is ignored.'), - Error(WARNING, u'Field author is present but empty')] - assert expected_errors == a.errors - expected = {'about_resource': u'.', - 'author': u'', - 'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - 'description': u'AboutCode is a tool\nfor files.', - 'license_key': u'apache-2.0', - 'license_expression': u'apache-2.0', - 'name': u'AboutCode', - 'owner': u'nexB Inc.'} - result = a.as_dict(with_paths=False, - with_empty=True, - with_absent=False) - # FIXME: why converting back to dict? - assert expected == dict(result) - - # The with_present and empty is no longer supported. - def FAILING_test_About_as_dict_with_present(self): - test_file = get_test_loc('as_dict/about.ABOUT') - a = model.About(test_file, about_file_path='complete/about.ABOUT') - expected_errors = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.'), - Error(INFO, u'Field custom_empty is not a supported field and is ignored.'), - Error(WARNING, u'Field author is present but empty')] - assert expected_errors == a.errors - expected = {'about_resource': u'.', - 'about_resource_path': u'', - 'author': u'', - 'author_file': u'', - 'attribute': u'', - 'changelog_file': u'', - 'checksum_md5': u'', - 'checksum_sha1': u'', - 'checksum_sha256': u'', - 'contact': u'', - 'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - 'description': u'AboutCode is a tool\nfor files.', - 'download_url': u'', - 'homepage_url': u'', - 'license_key': u'apache-2.0', - 'license_expression': u'apache-2.0', - 'license_file': u'', - 'license_name': u'', - 'license_url': u'', - 'modified': u'', - 'name': u'AboutCode', - 'notes': u'', - 'notice_file': u'', - 'notice_url': u'', - 'owner': u'nexB Inc.', - 'owner_url': u'', - 'redistribute': u'', - 'spec_version': u'', - 'track_changes': u'', - 'vcs_branch': u'', - 'vcs_path': u'', - 'vcs_repository': u'', - 'vcs_revision': u'', - 'vcs_tag': u'', - 'vcs_tool': u'', - 'version': u''} - result = a.as_dict(with_paths=False, - with_empty=False, - with_absent=True) - # FIXME: why converting back to dict? - assert expected == dict(result) - - # FIXME: Need revisit - def FAILING_test_About_as_dict_with_nothing(self): - test_file = get_test_loc('as_dict/about.ABOUT') - a = model.About(test_file, about_file_path='complete/about.ABOUT') - expected_errors = [ - Error(INFO, u'Field custom1 is not a supported field and is ignored.'), - Error(INFO, u'Field custom_empty is not a supported field and is ignored.'), - Error(WARNING, u'Field author is present but empty')] - assert expected_errors == a.errors - expected = {'about_resource': [u'.'], - 'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - 'description': u'AboutCode is a tool\nfor files.', - 'license_key': [u'apache-2.0'], - 'license_expression': u'apache-2.0', - 'name': u'AboutCode', - 'owner': u'nexB Inc.'} - result = a.as_dict(with_paths=False, - with_empty=False, - with_absent=False) - # FIXME: why converting back to dict? - assert expected == dict(result) - def test_load_dump_is_idempotent(self): - test_file = get_test_loc('load/this.ABOUT') + test_file = get_test_loc('test_model/this.ABOUT') a = model.About() a.load(test_file) dumped_file = get_temp_file('that.ABOUT') - a.dump(dumped_file, use_mapping=False, mapping_file=False, with_absent=False, with_empty=False) + a.dump(dumped_file, mapping_file=False, with_absent=False, with_empty=False) expected = get_unicode_content(test_file).splitlines() result = get_unicode_content(dumped_file).splitlines() assert expected == result def test_load_can_load_unicode(self): - test_file = get_test_loc('unicode/nose-selecttests.ABOUT') + test_file = get_test_loc('test_model/unicode/nose-selecttests.ABOUT') a = model.About() a.load(test_file) file_path = posixpath.join(posixpath.dirname(test_file), 'nose-selecttests-0.3.zip') - err_msg = u'Field about_resource: Path %s not found' % file_path + err_msg = 'Field about_resource: Path %s not found' % file_path errors = [ - Error(INFO, u'Field dje_license is not a supported field and is ignored.'), - Error(INFO, u'Field license_text_file is not a supported field and is ignored.'), - Error(INFO, u'Field scm_tool is not a supported field and is ignored.'), - Error(INFO, u'Field scm_repository is not a supported field and is ignored.'), - Error(INFO, u'Field test is not a supported field and is ignored.'), + Error(INFO, 'Field dje_license is not a supported field and is ignored.'), + Error(INFO, 'Field license_text_file is not a supported field and is ignored.'), + Error(INFO, 'Field scm_tool is not a supported field and is ignored.'), + Error(INFO, 'Field scm_repository is not a supported field and is ignored.'), + Error(INFO, 'Field test is not a supported field and is ignored.'), Error(INFO, err_msg)] assert errors == a.errors - assert u'Copyright (c) 2012, Domen Kožar' == a.copyright.value + assert 'Copyright (c) 2012, Domen Kožar' == a.copyright.value def test_load_has_errors_for_non_unicode(self): - test_file = get_test_loc('unicode/not-unicode.ABOUT') + test_file = get_test_loc('test_model/unicode/not-unicode.ABOUT') a = model.About() a.load(test_file) err = a.errors[0] @@ -977,23 +750,25 @@ def test_load_has_errors_for_non_unicode(self): assert 'UnicodeDecodeError' in err.message def test_as_dict_load_dict_is_idempotent(self): - test = {'about_resource': u'.', - 'author': u'', - 'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - u'custom1': u'some custom', - u'custom_empty': u'', - 'description': u'AboutCode is a tool\nfor files.', - 'license_expression': u'apache-2.0', - 'name': u'AboutCode', - 'owner': u'nexB Inc.'} - - expected = {'about_resource': OrderedDict([(u'.', None)]), - 'author': u'', - 'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - 'description': u'AboutCode is a tool\nfor files.', - 'license_expression': u'apache-2.0', - 'name': u'AboutCode', - 'owner': u'nexB Inc.'} + test = { + 'about_resource': '.', + 'author': '', + 'copyright': 'Copyright (c) 2013-2014 nexB Inc.', + 'custom1': 'some custom', + 'custom_empty': '', + 'description': 'AboutCode is a tool\nfor files.', + 'license_expression': 'apache-2.0', + 'name': 'AboutCode', + 'owner': 'nexB Inc.'} + + expected = { + 'about_resource': OrderedDict([('.', None)]), + 'author': '', + 'copyright': 'Copyright (c) 2013-2014 nexB Inc.', + 'description': 'AboutCode is a tool\nfor files.', + 'license_expression': 'apache-2.0', + 'name': 'AboutCode', + 'owner': 'nexB Inc.'} a = model.About() base_dir = 'some_dir' @@ -1002,63 +777,83 @@ def test_as_dict_load_dict_is_idempotent(self): # FIXME: why converting back to dict? assert expected == dict(as_dict) - # FIXME: Need to revisit to determine what is this test for. - def FAILING_test_load_dict_handles_field_validation_correctly(self): - test = {u'about_resource': [u'.'], - u'attribute': u'yes', - u'author': [u'Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez'], - u'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - u'description': u'AboutCode is a tool to process ABOUT files. An ABOUT file is a file.', - u'homepage_url': u'http://dejacode.org', - u'license_expression': u'apache-2.0', - u'name': u'AboutCode', - u'owner': u'nexB Inc.', - u'vcs_repository': u'https://github.com/dejacode/about-code-tool.git', - u'vcs_tool': u'git', - u'version': u'0.11.0'} + def test_load_dict_as_dict_is_idempotent(self): + test = { + 'about_resource': ['.'], + 'attribute': 'yes', + 'author': ['Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez'], + 'copyright': 'Copyright (c) 2013-2014 nexB Inc.', + 'description': 'AboutCode is a tool to process ABOUT files. An ABOUT file is a file.', + 'homepage_url': 'http://dejacode.org', + 'license_expression': 'apache-2.0', + 'name': 'AboutCode', + 'owner': 'nexB Inc.', + 'vcs_repository': 'https://github.com/dejacode/about-code-tool.git', + 'vcs_tool': 'git', + 'version': '0.11.0'} a = model.About() base_dir = 'some_dir' a.load_dict(test, base_dir) as_dict = a.as_dict(with_paths=False, with_absent=False, with_empty=True) - # FIXME: why converting back to dict? - assert test == dict(as_dict) + + expected = { + 'about_resource': OrderedDict([('.', None)]), + 'attribute': 'yes', + 'author': ['Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez'], + 'copyright': 'Copyright (c) 2013-2014 nexB Inc.', + 'description': 'AboutCode is a tool to process ABOUT files. An ABOUT file is a file.', + 'homepage_url': 'http://dejacode.org', + 'license_expression': 'apache-2.0', + 'name': 'AboutCode', + 'owner': 'nexB Inc.', + 'vcs_repository': 'https://github.com/dejacode/about-code-tool.git', + 'vcs_tool': 'git', + 'version': '0.11.0'} + + assert expected == dict(as_dict) def test_write_output_csv(self): - path = 'load/this.ABOUT' + path = 'test_model/this.ABOUT' test_file = get_test_loc(path) abouts = model.About(location=test_file, about_file_path=path) result = get_temp_file() model.write_output([abouts], result, format='csv') - expected = get_test_loc('load/expected.csv') + expected = get_test_loc('test_model/expected.csv') check_csv(expected, result) def test_write_output_json(self): - path = 'load/this.ABOUT' + path = 'test_model/this.ABOUT' test_file = get_test_loc(path) abouts = model.About(location=test_file, about_file_path=path) result = get_temp_file() model.write_output([abouts], result, format='json') - expected = get_test_loc('load/expected.json') + expected = get_test_loc('test_model/expected.json') check_json(expected, result) + class CollectorTest(unittest.TestCase): - def test_collect_inventory_in_directory_with_correct_about_file_path(self): - test_loc = get_test_loc('collect-inventory-errors') - _errors, abouts = model.collect_inventory(test_loc) - assert 2 == len(abouts) + def test_collect_inventory_return_errors(self): + test_loc = get_test_loc('test_model/collect_inventory_errors') + errors, _abouts = model.collect_inventory(test_loc) + file_path1 = posixpath.join(test_loc, 'distribute_setup.py') + file_path2 = posixpath.join(test_loc, 'date_test.py') - expected = ['non-supported_date_format.ABOUT', - 'supported_date_format.ABOUT'] - result = [a.about_file_path for a in abouts] - assert sorted(expected) == sorted(result) + err_msg1 = 'non-supported_date_format.ABOUT: Field about_resource: Path %s not found' % file_path1 + err_msg2 = 'supported_date_format.ABOUT: Field about_resource: Path %s not found' % file_path2 + expected_errors = [ + Error(INFO, 'non-supported_date_format.ABOUT: Field date is not a supported field and is ignored.'), + Error(INFO, 'supported_date_format.ABOUT: Field date is not a supported field and is ignored.'), + Error(INFO, err_msg1), + Error(INFO, err_msg2)] + assert sorted(expected_errors) == sorted(errors) def test_collect_inventory_with_long_path(self): - test_loc = extract_test_loc('longpath.zip') + test_loc = extract_test_loc('test_model/longpath.zip') _errors, abouts = model.collect_inventory(test_loc) assert 2 == len(abouts) @@ -1081,38 +876,23 @@ def test_collect_inventory_with_long_path(self): result_name = [a.name.value for a in abouts] assert sorted(expected_name) == sorted(result_name) - def test_collect_inventory_return_errors(self): - test_loc = get_test_loc('collect-inventory-errors') - errors, _abouts = model.collect_inventory(test_loc) - file_path1 = posixpath.join(test_loc, 'distribute_setup.py') - file_path2 = posixpath.join(test_loc, 'date_test.py') - - err_msg1 = u'non-supported_date_format.ABOUT: Field about_resource: Path %s not found' % file_path1 - err_msg2 = u'supported_date_format.ABOUT: Field about_resource: Path %s not found' % file_path2 - expected_errors = [ - Error(INFO, u'non-supported_date_format.ABOUT: Field date is not a supported field and is ignored.'), - Error(INFO, u'supported_date_format.ABOUT: Field date is not a supported field and is ignored.'), - Error(INFO, err_msg1), - Error(INFO, err_msg2)] - assert sorted(expected_errors) == sorted(errors) - def test_collect_inventory_can_collect_a_single_file(self): - test_loc = get_test_loc('thirdparty/django_snippets_2413.ABOUT') + test_loc = get_test_loc('test_model/single_file/django_snippets_2413.ABOUT') _errors, abouts = model.collect_inventory(test_loc) assert 1 == len(abouts) - expected = ['thirdparty/django_snippets_2413.ABOUT'] + expected = ['single_file/django_snippets_2413.ABOUT'] result = [a.about_file_path for a in abouts] assert expected == result - def test_collect_inventory_return_no_warnings(self): - test_loc = get_test_loc('allAboutInOneDir') + def test_collect_inventory_return_no_warnings_and_model_can_uuse_relative_paths(self): + test_loc = get_test_loc('test_model/rel/allAboutInOneDir') errors, _abouts = model.collect_inventory(test_loc) expected_errors = [] - result = [(level, e) for level, e in errors if level > attributecode.INFO] + result = [(level, e) for level, e in errors if level > INFO] assert expected_errors == result def test_collect_inventory_populate_about_file_path(self): - test_loc = get_test_loc('parse/complete') + test_loc = get_test_loc('test_model/parse/complete') errors, abouts = model.collect_inventory(test_loc) assert [] == errors expected = 'about.ABOUT' @@ -1120,25 +900,27 @@ def test_collect_inventory_populate_about_file_path(self): assert expected == result def test_collect_inventory_with_multi_line(self): - test_loc = get_test_loc('parse/multi_line_license_expresion.ABOUT') + test_loc = get_test_loc('test_model/parse/multi_line_license_expresion.ABOUT') errors, abouts = model.collect_inventory(test_loc) assert [] == errors - expected_lic_url = [u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:mit', u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:apache-2.0'] + expected_lic_url = [ + 'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:mit', + 'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:apache-2.0'] returned_lic_url = abouts[0].license_url.value assert expected_lic_url == returned_lic_url def test_collect_inventory_with_license_expression(self): - test_loc = get_test_loc('parse/multi_line_license_expresion.ABOUT') + test_loc = get_test_loc('test_model/parse/multi_line_license_expresion.ABOUT') errors, abouts = model.collect_inventory(test_loc) assert [] == errors - expected_lic = u'mit or apache-2.0' + expected_lic = 'mit or apache-2.0' returned_lic = abouts[0].license_expression.value assert expected_lic == returned_lic def test_collect_inventory_with_mapping(self): - test_loc = get_test_loc('parse/name_mapping_test.ABOUT') - mapping = True - errors, abouts = model.collect_inventory(test_loc, mapping) + test_loc = get_test_loc('test_model/parse/name_mapping_test.ABOUT') + mapping_file = DEFAULT_MAPPING + errors, abouts = model.collect_inventory(test_loc, mapping_file) expected_msg1 = 'Field resource is a custom field' expected_msg2 = 'Field custom_mapping is not a supported field and is not defined in the mapping file. This field is ignored.' assert len(errors) == 2 @@ -1148,10 +930,9 @@ def test_collect_inventory_with_mapping(self): assert abouts[0].resource.value def test_collect_inventory_with_custom_mapping(self): - test_loc = get_test_loc('parse/name_mapping_test.ABOUT') - mapping = False - mapping_file = get_test_loc('custom-mapping-file/mapping.config') - errors, abouts = model.collect_inventory(test_loc, mapping, mapping_file) + test_loc = get_test_loc('test_model/parse/name_mapping_test.ABOUT') + mapping_file = get_test_loc('test_model/custom_mapping/mapping.config') + errors, abouts = model.collect_inventory(test_loc, mapping_file) expected_msg1 = 'Field resource is a custom field' expected_msg2 = 'Field custom_mapping is a custom field' assert len(errors) == 2 @@ -1162,7 +943,7 @@ def test_collect_inventory_with_custom_mapping(self): assert abouts[0].custom_mapping.value def test_collect_inventory_without_mapping(self): - test_loc = get_test_loc('parse/name_mapping_test.ABOUT') + test_loc = get_test_loc('test_model/parse/name_mapping_test.ABOUT') errors, _abouts = model.collect_inventory(test_loc) expected_msg1 = 'Field resource is not a supported field and is ignored.' expected_msg2 = 'Field custom_mapping is not a supported field and is ignored.' @@ -1170,15 +951,15 @@ def test_collect_inventory_without_mapping(self): assert expected_msg1 in errors[0].message assert expected_msg2 in errors[1].message - def test_parse_license_expression(self): - spec_char, returned_lic = model.parse_license_expression(u'mit or apache-2.0') - expected_lic = [u'mit', u'apache-2.0'] + def test_saneyaml_load_license_expression(self): + spec_char, returned_lic = model.parse_license_expression('mit or apache-2.0') + expected_lic = ['mit', 'apache-2.0'] expected_spec_char = [] assert expected_lic == returned_lic assert expected_spec_char == spec_char - def test_parse_license_expression_with_special_chara(self): - spec_char, returned_lic = model.parse_license_expression(u'mit, apache-2.0') + def test_saneyaml_load_license_expression_with_special_chara(self): + spec_char, returned_lic = model.parse_license_expression('mit, apache-2.0') expected_lic = [] expected_spec_char = [','] assert expected_lic == returned_lic @@ -1188,7 +969,7 @@ def test_collect_inventory_works_with_relative_paths(self): # FIXME: This test need to be run under src/attributecode/ # or otherwise it will fail as the test depends on the launching # location - test_loc = get_test_loc('parse/complete') + test_loc = get_test_loc('test_model/parse/complete') # Use '.' as the indication of the current directory test_loc1 = test_loc + '/./' # Use '..' to go back to the parent directory @@ -1204,7 +985,7 @@ def test_collect_inventory_works_with_relative_paths(self): assert expected == result2 def test_collect_inventory_basic_from_directory(self): - location = get_test_loc('inventory/basic') + location = get_test_loc('test_model/inventory/basic') result = get_temp_file() errors, abouts = model.collect_inventory(location) @@ -1213,11 +994,11 @@ def test_collect_inventory_basic_from_directory(self): expected_errors = [] assert expected_errors == errors - expected = get_test_loc('inventory/basic/expected.csv') + expected = get_test_loc('test_model/inventory/basic/expected.csv') check_csv(expected, result) def test_collect_inventory_with_about_resource_path_from_directory(self): - location = get_test_loc('inventory/basic_with_about_resource_path') + location = get_test_loc('test_model/inventory/basic_with_about_resource_path') result = get_temp_file() errors, abouts = model.collect_inventory(location) @@ -1226,30 +1007,24 @@ def test_collect_inventory_with_about_resource_path_from_directory(self): expected_errors = [] assert expected_errors == errors - expected = get_test_loc('inventory/basic_with_about_resource_path/expected.csv') + expected = get_test_loc('test_model/inventory/basic_with_about_resource_path/expected.csv') check_csv(expected, result) def test_collect_inventory_with_no_about_resource_from_directory(self): - location = get_test_loc('inventory/no_about_resource_key') + location = get_test_loc('test_model/inventory/no_about_resource_key') result = get_temp_file() errors, abouts = model.collect_inventory(location) model.write_output(abouts, result, format='csv') - expected_errors = [Error(CRITICAL, u'about/about.ABOUT: Field about_resource is required')] + expected_errors = [Error(CRITICAL, 'about/about.ABOUT: Field about_resource is required')] assert expected_errors == errors - expected = get_test_loc('inventory/no_about_resource_key/expected.csv') + expected = get_test_loc('test_model/inventory/no_about_resource_key/expected.csv') check_csv(expected, result) - @expectedFailure def test_collect_inventory_complex_from_directory(self): - # FIXME: check_csv is failing because there are many keys in - # the ABOUT files that are not supported. Instead of removing - # all the non-supported keys in the output and do the - # comparison, it may be best to apply the mapping to include - # theses keys - location = get_test_loc('inventory/complex') + location = get_test_loc('test_model/inventory/complex') result = get_temp_file() errors, abouts = model.collect_inventory(location) @@ -1257,75 +1032,10 @@ def test_collect_inventory_complex_from_directory(self): assert all(e.severity == INFO for e in errors) - expected = get_test_loc('inventory/complex/expected.csv') + expected = get_test_loc('test_model/inventory/complex/expected.csv') check_csv(expected, result) -class GroupingsTest(unittest.TestCase): - - def test_unique(self): - base_dir = 'some_dir' - test = {'about_resource': u'.', - 'author': u'', - 'copyright': u'Copyright (c) 2013-2014 nexB Inc.', - u'custom1': u'some custom', - u'custom_empty': u'', - 'description': u'AboutCode is a tool\nfor files.', - 'license': u'apache-2.0', - 'name': u'AboutCode', - 'owner': u'nexB Inc.'} - - a = model.About() - a.load_dict(test, base_dir) - - b = model.About() - b.load_dict(test, base_dir) - abouts = [a, b] - results = model.unique(abouts) - assert [a] == results - - def test_by_license(self): - base_dir = 'some_dir' - a = model.About() - a.load_dict({'license_expression': u'apache-2.0 and cddl-1.0', }, base_dir) - b = model.About() - b.load_dict({'license_expression': u'apache-2.0', }, base_dir) - c = model.About() - c.load_dict({}, base_dir) - d = model.About() - d.load_dict({'license_expression': u'bsd', }, base_dir) - - abouts = [a, b, c, d] - results = model.by_license(abouts) - expected = OrderedDict([ - ('', [c]), - ('apache-2.0', [a, b]), - ('bsd', [d]), - ('cddl-1.0', [a]), - ]) - assert expected == results - - def test_by_name(self): - base_dir = 'some_dir' - a = model.About() - a.load_dict({'name': u'apache', 'version': u'1.1' }, base_dir) - b = model.About() - b.load_dict({'name': u'apache', 'version': u'1.2' }, base_dir) - c = model.About() - c.load_dict({}, base_dir) - d = model.About() - d.load_dict({'name': u'eclipse', 'version': u'1.1' }, base_dir) - - abouts = [a, b, c, d] - results = model.by_name(abouts) - expected = OrderedDict([ - ('', [c]), - ('apache', [a, b]), - ('eclipse', [d]), - ]) - assert expected == results - - class FetchLicenseTest(unittest.TestCase): @mock.patch.object(model, 'urlopen') def test_valid_api_url(self, mock_data): @@ -1338,8 +1048,9 @@ def test_pre_process_and_fetch_license_dict(self, have_network_connection, valid have_network_connection.return_value = True valid_api_url.return_value = False - error_msg = ('Network problem. Please check your Internet connection. ' - 'License generation is skipped.') + error_msg = ( + 'Network problem. Please check your Internet connection. ' + 'License generation is skipped.') expected = ({}, [Error(ERROR, error_msg)]) assert model.pre_process_and_fetch_license_dict([], '', '') == expected diff --git a/tests/test_util.py b/tests/test_util.py index 770e80ab..07ee2762 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -21,7 +21,8 @@ from collections import OrderedDict import string import unittest -from unittest.case import expectedFailure + +import saneyaml from testing_utils import extract_test_loc from testing_utils import get_test_loc @@ -32,9 +33,10 @@ from attributecode import Error from attributecode import model from attributecode import util +from attributecode import DEFAULT_MAPPING -class UtilsTest(unittest.TestCase): +class TestResourcePaths(unittest.TestCase): def test_resource_name(self): expected = 'first' @@ -131,40 +133,6 @@ def test_to_native_from_mixed(self): result = util.to_native(test) assert expected == result - def test_get_locations(self): - test_dir = get_test_loc('locations') - expected = sorted([ - 'locations/file with_spaces', - 'locations/file1', - 'locations/file2', - 'locations/dir1/file2', - 'locations/dir1/dir2/file1', - 'locations/dir2/file1']) - - result = sorted(util.get_locations(test_dir)) - for i, res in enumerate(result): - expect = expected[i] - assert res.endswith(expect) - - def test_get_about_locations_with_no_ABOUT_files(self): - test_dir = get_test_loc('locations') - expected = [] - result = list(util.get_about_locations(test_dir)) - assert expected == result - - def test_get_about_locations_with_ABOUT_files(self): - test_dir = get_test_loc('about_locations') - expected = sorted([ - 'locations/file with_spaces.ABOUT', - 'locations/dir1/file2.aBout', - 'locations/dir1/dir2/file1.about', - ]) - - result = sorted(util.get_about_locations(test_dir)) - for i, res in enumerate(result): - expect = expected[i] - assert res.endswith(expect) - def test_invalid_chars_with_valid_chars(self): name = string.digits + string.ascii_letters + '_-.' result = util.invalid_chars(name) @@ -195,7 +163,11 @@ def test_invalid_chars_with_space_is_valid(self): def test_check_file_names_with_dupes_return_errors(self): paths = ['some/path', 'some/PAth'] result = util.check_file_names(paths) - expected = [Error(CRITICAL, "Duplicate files: 'some/PAth' and 'some/path' have the same case-insensitive file name")] + expected = [ + Error( + CRITICAL, + "Duplicate files: 'some/PAth' and 'some/path' have the same case-insensitive file name") + ] assert expected == result def test_check_file_names_without_dupes_return_no_error(self): @@ -227,7 +199,7 @@ def test_check_file_names_with_invalid_chars_return_errors(self): 'Accessibilité/ périmètre' ] import sys - if sys.version_info[0] < 3: #python2 + if sys.version_info[0] < 3: # python2 expected = [Error(CRITICAL, b"Invalid characters '\xe9\xe8' in file name at: 'Accessibilit\xe9/ p\xe9rim\xe8tre'")] else: expected = [Error(CRITICAL, "Invalid characters 'éè' in file name at: 'Accessibilité/ périmètre'")] @@ -236,16 +208,15 @@ def test_check_file_names_with_invalid_chars_return_errors(self): assert expected[0].message == result[0].message assert expected == result - def test_get_about_locations(self): - location = get_test_loc('parse/complete') - result = list(util.get_about_locations(location)) - expected = 'testdata/parse/complete/about.ABOUT' - assert result[0].endswith(expected) - def test_is_about_file(self): assert util.is_about_file('test.About') assert util.is_about_file('test2.aboUT') assert not util.is_about_file('no_about_ext.something') + assert not util.is_about_file('about') + assert not util.is_about_file('about.txt') + + def test_is_about_file_is_false_if_only_bare_extension(self): + assert not util.is_about_file('.ABOUT') def test_get_relative_path(self): test = [('/some/path', '/some/path/file', 'file'), @@ -274,35 +245,138 @@ def test_get_relative_path_with_same_path_twice(self): result = util.get_relative_path(loc, loc) assert expected == result - def test_get_mapping_key_order(self): - import collections - expected = collections.OrderedDict() - expected['about_file_path'] = '' - expected['name'] = '' - expected['version'] = '' - expected['copyright'] = '' - expected['license_expression'] = '' - expected['resource'] = '' - expected_keys = expected.keys() - result = util.get_mapping_key_order(mapping_file=False) - assert expected_keys == result - - def test_get_mapping_key_order_with_mapping_file(self): - import collections - expected = collections.OrderedDict() - expected['about_file_path'] = '' - expected['name'] = '' - expected['version'] = '' - expected['description'] = '' - expected['license_expression'] = '' - expected['copyright'] = '' - expected_keys = expected.keys() - test_mapping_file = get_test_loc('mapping_config/mapping.config') - result = util.get_mapping_key_order(test_mapping_file) - assert expected_keys == result + +class TestGetLocations(unittest.TestCase): + + def test_get_locations(self): + test_dir = get_test_loc('test_util/about_locations') + expected = sorted([ + 'file with_spaces.ABOUT', + 'file1', + 'file2', + 'dir1/file2', + 'dir1/file2.aBout', + 'dir1/dir2/file1.about', + 'dir2/file1']) + + result = sorted(util.get_locations(test_dir)) + result = [l.partition('/about_locations/')[-1] for l in result] + assert expected == result + + def test_get_about_locations(self): + test_dir = get_test_loc('test_util/about_locations') + expected = sorted([ + 'file with_spaces.ABOUT', + 'dir1/file2.aBout', + 'dir1/dir2/file1.about', + ]) + + result = sorted(util.get_about_locations(test_dir)) + result = [l.partition('/about_locations/')[-1] for l in result] + assert expected == result + + def test_get_locations_can_yield_a_single_file(self): + test_file = get_test_loc('test_util/about_locations/file with_spaces.ABOUT') + result = list(util.get_locations(test_file)) + assert 1 == len(result) + + def test_get_about_locations_for_about(self): + location = get_test_loc('test_util/get_about_locations') + result = list(util.get_about_locations(location)) + expected = 'get_about_locations/about.ABOUT' + assert result[0].endswith(expected) + + # FIXME: these are not very long/deep paths + def test_get_locations_with_very_long_path(self): + longpath = ( + 'longpath' + '/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' + '/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' + '/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' + '/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' + ) + test_loc = extract_test_loc('test_util/longpath.zip') + result = list(util.get_locations(test_loc)) + assert any(longpath in r for r in result) + + +class TestMapping(unittest.TestCase): + + def test_apply_mapping(self): + about = [OrderedDict([ + ('about_resource', '.'), + ('name', 'test'), + ('confirmed version', '1'), + ('confirmed copyright', 'Copyright (c) 2013-2017 nexB Inc.') + ])] + expected = [OrderedDict([ + ('about_resource', '.'), + ('name', 'test'), + ('version', '1'), + ('copyright', 'Copyright (c) 2013-2017 nexB Inc.') + ])] + mapping_file = DEFAULT_MAPPING + assert expected == util.apply_mapping(about, mapping_file) + + def test_load_mapping(self): + test_file = get_test_loc('test_util/mapping/mapping.config') + result = util.load_mapping(test_file) + expected = OrderedDict([ + ('about_file_path', 'about_file'), + ('name', 'component'), + ('version', 'confirmed version'), + ('description', 'description'), + ('license_expression', 'dje_license_key'), + ('copyright', 'confirmed copyright') + ]) + assert expected == result + + def test_load_mapping_can_preserve_keys_case(self): + test_file = get_test_loc('test_util/mapping/case_mapping.config') + result = util.load_mapping(test_file, lowercase=False) + expected = OrderedDict([ + (u'about_file_path', u'about_file'), + (u'name', u'Component'), + (u'version', u'Confirmed Version'), + (u'description', u'description'), + (u'licEnse_expression', u'dje_license_key'), + (u'copYright', u'Confirmed Copyright')]) + assert expected == result + + def test_load_mapping_replace_space_with_underscore_in_about_keys(self): + # FIXME: this is a really weird behaviour ... but at least it is tested now + test_file = get_test_loc('test_util/mapping/space_mapping.config') + result = util.load_mapping(test_file) + expected = OrderedDict([ + (u'des__cription', u'description'), + (u'copy_right', u'confirmed copyright')]) + assert expected == result + + def test_load_mapping_from_invalid_location_raise_exception(self): + try: + util.load_mapping('this does not exists') + self.fail('Exception not raised') + except: + pass + + def test_load_mapping_from_None_location_raise_exception(self): + try: + util.load_mapping(None) + self.fail('Exception not raised') + except: + pass + + def test_load_mapping_only_load_last_instance_of_duplicated_keys(self): + test_file = get_test_loc('test_util/mapping/dupe_keys_mapping.config') + result = util.load_mapping(test_file) + expected = OrderedDict([(u'descr', u'description3'), (u'other', u'bar')]) + assert expected == result + + +class TestCsv(unittest.TestCase): def test_load_csv_without_mapping(self): - test_file = get_test_loc('util/about.csv') + test_file = get_test_loc('test_util/csv/about.csv') expected = [OrderedDict([ ('about_file', 'about.ABOUT'), ('about_resource', '.'), @@ -312,8 +386,60 @@ def test_load_csv_without_mapping(self): result = util.load_csv(test_file) assert expected == result + def test_load_csv_with_mapping(self): + test_file = get_test_loc('test_util/csv/about.csv') + expected = [OrderedDict([ + ('about_file_path', 'about.ABOUT'), + ('about_resource', '.'), + ('name', 'ABOUT tool'), + ('version', '0.8.1')]) + ] + result = util.load_csv(test_file, mapping_file=DEFAULT_MAPPING) + assert expected == result + + def test_get_about_file_path_from_csv_using_mapping(self): + test_file = get_test_loc('test_util/csv/about.csv') + expected = ['about.ABOUT'] + result = util.get_about_file_path( + test_file, mapping_file=DEFAULT_MAPPING) + assert expected == result + + def test_load_csv_does_convert_column_names_to_lowercase(self): + test_file = get_test_loc('test_util/csv/about_key_with_upper_case.csv') + expected = [OrderedDict( + [('about_file', 'about.ABOUT'), + ('about_resource', '.'), + ('name', 'ABOUT tool'), + ('version', '0.8.1')]) + ] + result = util.load_csv(test_file) + assert expected == result + + def test_format_about_dict_for_csv_output(self): + about = [OrderedDict([ + (u'about_file_path', u'/input/about1.ABOUT'), + (u'about_resource', [u'test.c']), + (u'name', u'AboutCode-toolkit'), + (u'license_expression', u'mit AND bsd-new'), + (u'license_key', [u'mit', u'bsd-new'])])] + + expected = [OrderedDict([ + (u'about_file_path', u'/input/about1.ABOUT'), + (u'about_resource', u'test.c'), + (u'name', u'AboutCode-toolkit'), + (u'license_expression', u'mit AND bsd-new'), + (u'license_key', u'mit\nbsd-new')])] + + output = util.format_about_dict_for_csv_output(about) + assert output == expected + + +class TestJson(unittest.TestCase): + + # FIXME: mappings are a CSV-only feature!!!!! + def test_load_json_without_mapping(self): - test_file = get_test_loc('load/expected.json') + test_file = get_test_loc('test_util/json/expected.json') expected = [OrderedDict([ ('about_file_path', '/load/this.ABOUT'), ('about_resource', '.'), @@ -323,33 +449,40 @@ def test_load_json_without_mapping(self): result = util.load_json(test_file) assert expected == result - def test_load_json_with_mapping(self): - test_file = get_test_loc('load/expected_need_mapping.json') + def test_load_json(self): + test_file = get_test_loc('test_util/json/expected_need_mapping.json') expected = [dict(OrderedDict([ - ('about_file_path', '/load/this.ABOUT'), + ('about_file', '/load/this.ABOUT'), ('about_resource', '.'), ('version', '0.11.0'), ('name', 'AboutCode'), ]) )] - result = util.load_json(test_file, use_mapping=True) + result = util.load_json(test_file) assert expected == result - def test_load_non_list_json_with_mapping(self): - test_file = get_test_loc('load/not_a_list_need_mapping.json') - mapping_file = get_test_loc('custom-mapping-file/mapping.config') + def test_load_non_list_json(self): + test_file = get_test_loc('test_util/json/not_a_list_need_mapping.json') + # FIXME: why this dict nesting?? expected = [dict(OrderedDict([ - ('about_file_path', '/load/this.ABOUT'), ('about_resource', '.'), ('name', 'AboutCode'), + ('path', '/load/this.ABOUT'), ('version', '0.11.0'), ]) )] - result = util.load_json(test_file, use_mapping=False, mapping_file=mapping_file) + result = util.load_json(test_file) assert expected == result - def test_load_non_list_json(self): - test_file = get_test_loc('load/not_a_list.json') + # FIXME: mappings are a CSV-only feature!!!!! + def test_get_about_file_path_from_json_using_mapping(self): + test_file = get_test_loc('test_util/json/expected.json') + expected = ['/load/this.ABOUT'] + result = util.get_about_file_path(test_file, mapping_file=DEFAULT_MAPPING) + assert expected == result + + def test_load_non_list_json2(self): + test_file = get_test_loc('test_util/json/not_a_list.json') expected = [OrderedDict([ ('about_file_path', '/load/this.ABOUT'), ('version', '0.11.0'), @@ -361,150 +494,126 @@ def test_load_non_list_json(self): assert expected == result def test_load_json_from_abc_mgr(self): - test_file = get_test_loc('load/aboutcode_manager_exported.json') - mapping_file = get_test_loc('custom-mapping-file/mapping.config') - expected = [dict(OrderedDict( - [('license_expression', 'apache-2.0'), - ('copyright', 'Copyright (c) 2017 nexB Inc.'), - ('licenses', [{'key':'apache-2.0'}]), - ('copyrights', [{'statements':['Copyright (c) 2017 nexB Inc.']}]), - ('about_file_path', 'ScanCode'), - ('review_status', 'Analyzed'), - ('name', 'ScanCode'), - ('version', '2.2.1'), - ('owner', 'nexB Inc.'), - ('code_type', 'Source'), - ('is_modified', False), - ('is_deployed', False), - ('feature', ''), - ('purpose', ''), - ('homepage_url', None), - ('download_url', None), - ('license_url', None), - ('notice_url', None), - ('programming_language', 'Python'), - ('notes', ''), - ('fileId', 8458), - ] - ))] - result = util.load_json(test_file, use_mapping=False, mapping_file=mapping_file) + test_file = get_test_loc('test_util/json/aboutcode_manager_exported.json') + expected = [dict(OrderedDict([ + ('license_expression', 'apache-2.0'), + ('copyright', 'Copyright (c) 2017 nexB Inc.'), + ('licenses', [{'key':'apache-2.0'}]), + ('copyrights', [{'statements':['Copyright (c) 2017 nexB Inc.']}]), + ('path', 'ScanCode'), + ('review_status', 'Analyzed'), + ('name', 'ScanCode'), + ('version', '2.2.1'), + ('owner', 'nexB Inc.'), + ('code_type', 'Source'), + ('is_modified', False), + ('is_deployed', False), + ('feature', ''), + ('purpose', ''), + ('homepage_url', None), + ('download_url', None), + ('license_url', None), + ('notice_url', None), + ('programming_language', 'Python'), + ('notes', ''), + ('fileId', 8458), + ]))] + result = util.load_json(test_file) assert expected == result def test_load_json_from_scancode(self): - test_file = get_test_loc('load/scancode_info.json') - mapping_file = get_test_loc('custom-mapping-file/mapping.config') - expected = [dict(OrderedDict( - [('about_file_path', 'Api.java'), - ('type', 'file'), - ('name', 'Api.java'), - ('base_name', 'Api'), - ('extension', '.java'), - ('size', 5074), - ('date', '2017-07-15'), - ('sha1', 'c3a48ec7e684a35417241dd59507ec61702c508c'), - ('md5', '326fb262bbb9c2ce32179f0450e24601'), - ('mime_type', 'text/plain'), - ('file_type', 'ASCII text'), - ('programming_language', 'Java'), - ('is_binary', False), - ('is_text', True), - ('is_archive', False), - ('is_media', False), - ('is_source', True), - ('is_script', False), - ('files_count', 0), - ('dirs_count', 0), - ('size_count', 0), - ('scan_errors', []), - ] - ))] - result = util.load_json(test_file, use_mapping=False, mapping_file=mapping_file) + test_file = get_test_loc('test_util/json/scancode_info.json') + expected = [dict(OrderedDict([ + ('type', 'file'), + ('name', 'Api.java'), + ('path', 'Api.java'), + ('base_name', 'Api'), + ('extension', '.java'), + ('size', 5074), + ('date', '2017-07-15'), + ('sha1', 'c3a48ec7e684a35417241dd59507ec61702c508c'), + ('md5', '326fb262bbb9c2ce32179f0450e24601'), + ('mime_type', 'text/plain'), + ('file_type', 'ASCII text'), + ('programming_language', 'Java'), + ('is_binary', False), + ('is_text', True), + ('is_archive', False), + ('is_media', False), + ('is_source', True), + ('is_script', False), + ('files_count', 0), + ('dirs_count', 0), + ('size_count', 0), + ('scan_errors', []), + ]))] + result = util.load_json(test_file) assert expected == result - def test_get_about_file_path_from_csv_using_mapping(self): - test_file = get_test_loc('util/about.csv') - expected = ['about.ABOUT'] - result = util.get_about_file_path(test_file, use_mapping=True) - assert expected == result + def test_format_about_dict_for_json_output(self): + about = [OrderedDict([ + (u'about_file_path', u'/input/about1.ABOUT'), + (u'about_resource', OrderedDict([(u'test.c', None)])), + (u'name', u'AboutCode-toolkit'), + (u'license_key', [u'mit', u'bsd-new'])])] - def test_get_about_file_path_from_json_using_mapping(self): - test_file = get_test_loc('load/expected.json') - expected = ['/load/this.ABOUT'] - result = util.get_about_file_path(test_file, use_mapping=True) - assert expected == result + expected = [OrderedDict([ + (u'about_file_path', u'/input/about1.ABOUT'), + (u'about_resource', u'test.c'), + (u'name', u'AboutCode-toolkit'), + (u'licenses', [ + OrderedDict([(u'key', u'mit')]), + OrderedDict([(u'key', u'bsd-new')])])])] - # The column names should be converted to lowercase as the same behavior as - # when user use the mapping.config - @expectedFailure - def test_load_csv_does_not_convert_column_names_to_lowercase(self): - test_file = get_test_loc('util/about_key_with_upper_case.csv') - expected = [OrderedDict( - [('about_file', 'about.ABOUT'), - ('about_resource', '.'), - ('nAme', 'ABOUT tool'), - ('Version', '0.8.1')]) - ] - result = util.load_csv(test_file) - assert expected == result + output = util.format_about_dict_for_json_output(about) + assert output == expected - def test_get_locations_with_very_long_path(self): - longpath = ( - u'longpath' - u'/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' - u'/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' - u'/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' - u'/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1/longpath1' - ) - test_loc = extract_test_loc('longpath.zip') - result = list(util.get_locations(test_loc)) - assert any(longpath in r for r in result) - def test_apply_mapping(self): - about = [OrderedDict([ - ('about_resource', '.'), - ('name', 'test'), - ('confirmed version', '1'), - ('confirmed copyright', 'Copyright (c) 2013-2017 nexB Inc.') - ])] - expected = [OrderedDict([ - ('about_resource', '.'), - ('name', 'test'), - ('version', '1'), - ('copyright', 'Copyright (c) 2013-2017 nexB Inc.') - ])] - assert util.apply_mapping(about) == expected +class TestMiscUtils(unittest.TestCase): - def test_check_duplicate_keys_about_file(self): + def test_load_yaml_about_file_with_no_dupe(self): test = ''' name: test -notes: some notes license_expression: mit notes: dup key here ''' - expected = ['notes'] - assert expected == util.check_duplicate_keys_about_file(test) + saneyaml.load(test, allow_duplicate_keys=False) - def test_wrap_boolean_value(self): + def test_load_yaml_about_file_raise_exception_on__duplicate(self): test = ''' name: test notes: some notes +notes: dup key here +notes: dup key here license_expression: mit -modified: yes -track_changes: no +notes: dup key here ''' - expected = ''' + try: + saneyaml.load(test, allow_duplicate_keys=False) + self.fail('Exception not raised') + except saneyaml.UnsupportedYamlFeatureError as e : + assert 'Duplicate key in YAML source: notes' == str(e) + + def test_load_yaml_about_file_raise_exception_on_invalid_yaml_ignore_non_key_line(self): + test = ''' name: test -notes: some notes +- notes: some notes + - notes: dup key here +# some +notes: dup key here license_expression: mit -modified: 'yes' -track_changes: 'no' +notes dup key here ''' - assert expected == util.wrap_boolean_value(test) + try: + saneyaml.load(test, allow_duplicate_keys=False) + self.fail('Exception not raised') + except Exception: + pass - def test_check_duplicate_keys_about_file_with_multiline(self): + def test_load_yaml_about_file_with_multiline(self): test = ''' name: test owner: test @@ -516,11 +625,15 @@ def test_check_duplicate_keys_about_file_with_multiline(self): line description: sample ''' - expected = ['owner', 'notes'] - assert expected == util.check_duplicate_keys_about_file(test) + try: + saneyaml.load(test, allow_duplicate_keys=False) + self.fail('Exception not raised') + except saneyaml.UnsupportedYamlFeatureError as e : + # notes: exceptio is rasied only for the first dupe + assert 'Duplicate key in YAML source: owner' == str(e) def test_inventory_filter(self): - test_loc = get_test_loc('basic') + test_loc = get_test_loc('test_util/inventory_filter') _errors, abouts = model.collect_inventory(test_loc) filter_dict = {'name': ['simple']} @@ -530,76 +643,61 @@ def test_inventory_filter(self): for about in updated_abouts: assert about.name.value == 'simple' - def test_update_fieldnames(self): - mapping_output = get_test_loc('util/mapping_output') - fieldnames = ['about_file_path', 'name', 'version'] - expexted_fieldnames = ['about_file_path', 'Component', 'version'] - result = util.update_fieldnames(fieldnames, mapping_output) - assert expexted_fieldnames == result - - def test_update_about_dictionary_keys(self): - mapping_output = get_test_loc('util/mapping_output') - about_ordered_dict = OrderedDict() - about_ordered_dict['name'] = 'test.c' - about_dict_list = [about_ordered_dict] - expected_output_dict = OrderedDict() - expected_output_dict['Component'] = 'test.c' - expected_dict_list = [expected_output_dict] - result = util.update_about_dictionary_keys(about_dict_list, mapping_output) - assert expected_dict_list == result - def test_ungroup_licenses(self): - about = [OrderedDict([(u'key', u'mit'), - (u'name', u'MIT License'), - (u'file', u'mit.LICENSE'), - (u'url', u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:mit')]), - OrderedDict([(u'key', u'bsd-new'), - (u'name', u'BSD-3-Clause'), - (u'file', u'bsd-new.LICENSE'), - (u'url', u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:bsd-new')])] + about = [ + OrderedDict([ + (u'key', u'mit'), + (u'name', u'MIT License'), + (u'file', u'mit.LICENSE'), + (u'url', u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:mit')]), + OrderedDict([ + (u'key', u'bsd-new'), + (u'name', u'BSD-3-Clause'), + (u'file', u'bsd-new.LICENSE'), + (u'url', u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:bsd-new')]) + ] expected_lic_key = [u'mit', u'bsd-new'] expected_lic_name = [u'MIT License', u'BSD-3-Clause'] expected_lic_file = [u'mit.LICENSE', u'bsd-new.LICENSE'] - expected_lic_url = [u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:mit', - u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:bsd-new'] + expected_lic_url = [ + u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:mit', + u'https://enterprise.dejacode.com/urn/?urn=urn:dje:license:bsd-new'] lic_key, lic_name, lic_file, lic_url = util.ungroup_licenses(about) assert expected_lic_key == lic_key assert expected_lic_name == lic_name assert expected_lic_file == lic_file assert expected_lic_url == lic_url - def test_format_about_dict_for_csv_output(self): - about = [OrderedDict([ - (u'about_file_path', u'/input/about1.ABOUT'), - (u'about_resource', [u'test.c']), - (u'name', u'AboutCode-toolkit'), - (u'license_expression', u'mit AND bsd-new'), - (u'license_key', [u'mit', u'bsd-new'])])] - - expected = [OrderedDict([ - (u'about_file_path', u'/input/about1.ABOUT'), - (u'about_resource', u'test.c'), - (u'name', u'AboutCode-toolkit'), - (u'license_expression', u'mit AND bsd-new'), - (u'license_key', u'mit\nbsd-new')])] - - output = util.format_about_dict_for_csv_output(about) - assert output == expected - - def test_format_about_dict_for_json_output(self): - about = [OrderedDict([ - (u'about_file_path', u'/input/about1.ABOUT'), - (u'about_resource', OrderedDict([(u'test.c', None)])), - (u'name', u'AboutCode-toolkit'), - (u'license_key', [u'mit', u'bsd-new'])])] - - expected = [OrderedDict([ - (u'about_file_path', u'/input/about1.ABOUT'), - (u'about_resource', u'test.c'), - (u'name', u'AboutCode-toolkit'), - (u'licenses', [ - OrderedDict([(u'key', u'mit')]), - OrderedDict([(u'key', u'bsd-new')])])])] - - output = util.format_about_dict_for_json_output(about) - assert output == expected + def test_unique_does_deduplicate_and_keep_ordering(self): + items = ['a', 'b', 'd', 'b', 'c', 'a'] + expected = ['a', 'b', 'd', 'c'] + results = util.unique(items) + assert expected == results + + def test_unique_can_handle_About_object(self): + base_dir = 'some_dir' + test = { + 'about_resource': '.', + 'author': '', + 'copyright': 'Copyright (c) 2013-2014 nexB Inc.', + 'custom1': 'some custom', + 'custom_empty': '', + 'description': 'AboutCode is a tool\nfor files.', + 'license': 'apache-2.0', + 'name': 'AboutCode', + 'owner': 'nexB Inc.' + } + + a = model.About() + a.load_dict(test, base_dir) + + c = model.About() + c.load_dict(test, base_dir) + + b = model.About() + test.update(dict(about_resource='asdasdasd')) + b.load_dict(test, base_dir) + + abouts = [a, b] + results = util.unique(abouts) + assert [a] == results diff --git a/tests/test_validate.py b/tests/test_validate.py new file mode 100644 index 00000000..a0a50f9b --- /dev/null +++ b/tests/test_validate.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf8 -*- + +# ============================================================================ +# Copyright (c) 2018 nexB Inc. http://www.nexb.com/ - All rights reserved. +# 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. +# ============================================================================ + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os + +from testing_utils import run_about_command_test + +""" +Common and global checks such as codestyle and check own ABOUT files. +""" + + +root_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + + +def disabled_test_codestyle(): + + # TODO: enable me + import subprocess + args = [ + os.path.join(root_dir, 'bin', 'pycodestyle'), + '--ignore', + 'E501,W503,W504,W605', + '--exclude=lib,lib64,tests,thirdparty,docs,bin,man,settings,local,tmp', + '.', + ] + + subprocess.check_output(args=args, cwd=root_dir) + + +def test_about_thirdparty(): + run_about_command_test(['check', 'thirdparty']) + + +def test_about_src(): + run_about_command_test(['check', 'src']) + + +def test_about_etc(): + run_about_command_test(['check', 'etc']) + + +def test_about_myself(): + run_about_command_test(['check', 'about.ABOUT']) diff --git a/tests/testdata/DateTest/non-supported_date_format.ABOUT b/tests/testdata/DateTest/non-supported_date_format.ABOUT deleted file mode 100644 index 11211ca2..00000000 --- a/tests/testdata/DateTest/non-supported_date_format.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: distribute -version: 1.1 -about_resource: distribute_setup.py -date:01/08/2013 \ No newline at end of file diff --git a/tests/testdata/DateTest/supported_date_format.ABOUT b/tests/testdata/DateTest/supported_date_format.ABOUT deleted file mode 100644 index bff24c47..00000000 --- a/tests/testdata/DateTest/supported_date_format.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: date_test -version: 1.1 -about_resource: date_test.py -date:2013-01-08 diff --git a/tests/testdata/FieldContainsSpaces/about.py b/tests/testdata/FieldContainsSpaces/about.py deleted file mode 100644 index 22ae0c5f..00000000 --- a/tests/testdata/FieldContainsSpaces/about.py +++ /dev/null @@ -1 +0,0 @@ -# Empty file \ No newline at end of file diff --git a/tests/testdata/FieldContainsSpaces/about.py.ABOUT b/tests/testdata/FieldContainsSpaces/about.py.ABOUT deleted file mode 100644 index a1a24783..00000000 --- a/tests/testdata/FieldContainsSpaces/about.py.ABOUT +++ /dev/null @@ -1,7 +0,0 @@ -name: test space -version: 0.7.0 -about_resource: about.py -field with spaces: This is a test case for field with spaces - - -date: 2012-05-29 diff --git a/tests/testdata/SCMTests/invalid_git_repo_elasticsearch.ABOUT b/tests/testdata/SCMTests/invalid_git_repo_elasticsearch.ABOUT deleted file mode 100644 index 04a31db2..00000000 --- a/tests/testdata/SCMTests/invalid_git_repo_elasticsearch.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: elasticsearch-0.19.8.zip -version: 0.19.8 -name: ElasticSearch - -vcs_tool: git -vcs_repository: https://code.google.com/p/selenium/ \ No newline at end of file diff --git a/tests/testdata/SCMTests/valid_git_repo_elasticsearch.ABOUT b/tests/testdata/SCMTests/valid_git_repo_elasticsearch.ABOUT deleted file mode 100644 index 39eee689..00000000 --- a/tests/testdata/SCMTests/valid_git_repo_elasticsearch.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: elasticsearch-0.19.8.zip -version: 0.19.8 -name: ElasticSearch - -vcs_tool: git -vcs_repository: https://github.com/elasticsearch/elasticsearch.git \ No newline at end of file diff --git a/tests/testdata/as_dict/about.ABOUT b/tests/testdata/as_dict/about.ABOUT deleted file mode 100644 index c932ce44..00000000 --- a/tests/testdata/as_dict/about.ABOUT +++ /dev/null @@ -1,14 +0,0 @@ -about_resource: . -name: AboutCode - -owner: nexB Inc. -author: -description: | - AboutCode is a tool - for files. -license_key: apache-2.0 -license_expression: apache-2.0 -copyright: Copyright (c) 2013-2014 nexB Inc. -custom1: some custom -custom_empty: - diff --git a/tests/testdata/attrib/attrib.html b/tests/testdata/attrib/attrib.html deleted file mode 100644 index 03b82f66..00000000 --- a/tests/testdata/attrib/attrib.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - Open Source Software Information - - - -

OPEN SOURCE SOFTWARE INFORMATION

-
- -

For instructions on how to obtain a copy of any source code - being made publicly available by Starship Technology related to - software used in this product you may send your request in - writing to:

- - Starship Technology LLC.
- OSS Management
- 7829 Montague Expressway
- Santa Clara, CA 95031
- USA
- -

The Starship Technology website www.dejacode.com also - contains information regarding Starship Technology's use of open - source. Starship Technology has created the www.dejacode.org to - serve as a portal for interaction with the software - community-at-large.

- -

This document contains additional information regarding - licenses, acknowledgments and required copyright notices for - open source packages used in this Starship Technology product. - This Starship Technology product contains the following open - source software components

- -
- - - -
- - -
-

Apache HTTP Server - 2.4.3 -

- - - - - - -
- -
- -

Common Licenses Used in This Product

- - - - -

End

- - diff --git a/tests/testdata/attrib/license_text.ABOUT b/tests/testdata/attrib/license_text.ABOUT deleted file mode 100644 index ab156c37..00000000 --- a/tests/testdata/attrib/license_text.ABOUT +++ /dev/null @@ -1,7 +0,0 @@ -about_resource:license_text_test -name: license extraction -version: beta -notes: this is a test file to see if the license_test() correctly returns the - the text of the file in license_text_file field -license_text_file: license_text.LICENSE -notice_file: notice_text.NOTICE \ No newline at end of file diff --git a/tests/testdata/attrib/missing_notice_license_files.ABOUT b/tests/testdata/attrib/missing_notice_license_files.ABOUT deleted file mode 100644 index 3469c86f..00000000 --- a/tests/testdata/attrib/missing_notice_license_files.ABOUT +++ /dev/null @@ -1,8 +0,0 @@ -about_resource:license_text_test -name: license extraction -version: beta -notes: this is a test file to see if the license_test() correctly returns an - of empty string when the license_text_file and notice_file have file values - that do not exist -license_text_file: test.LICENSE -notice_file: test.NOTICE \ No newline at end of file diff --git a/tests/testdata/attrib/no_text_file_field.ABOUT b/tests/testdata/attrib/no_text_file_field.ABOUT deleted file mode 100644 index 08e6f075..00000000 --- a/tests/testdata/attrib/no_text_file_field.ABOUT +++ /dev/null @@ -1,5 +0,0 @@ -about_resource: no_license_text_field_field -name: license extraction -version: beta -notes: this is a test file to see if the license_test() correctly returns the - the text of the file in license_text_file field \ No newline at end of file diff --git a/tests/testdata/attrib/test-about-code-tempfile b/tests/testdata/attrib/test-about-code-tempfile deleted file mode 100644 index b00fe55a..00000000 --- a/tests/testdata/attrib/test-about-code-tempfile +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Open Source Software Information - - - -

OPEN SOURCE SOFTWARE INFORMATION

-
- -

For instructions on how to obtain a copy of any source code - being made publicly available by Starship Technology related to - software used in this product you may send your request in - writing to:

- - Starship Technology LLC.
- OSS Management
- 7829 Montague Expressway
- Santa Clara, CA 95031
- USA
- -

The Starship Technology website www.dejacode.com also - contains information regarding Starship Technology's use of open - source. Starship Technology has created the www.dejacode.org to - serve as a portal for interaction with the software - community-at-large.

- -

This document contains additional information regarding - licenses, acknowledgments and required copyright notices for - open source packages used in this Starship Technology product. - This Starship Technology product contains the following open - source software components

- -
- -
- -
- -
- - -
- -

End

- - \ No newline at end of file diff --git a/tests/testdata/attrib/test.template b/tests/testdata/attrib/test.template deleted file mode 100644 index 4ba27652..00000000 --- a/tests/testdata/attrib/test.template +++ /dev/null @@ -1,5 +0,0 @@ -{% for about_object in about_objects -%} - {% for key, value in about_object.iteritems() -%} - {{ key }}:{{ value }} - {%- endfor %} -{%- endfor %} \ No newline at end of file diff --git a/tests/testdata/attrib_gen/license_text.ABOUT b/tests/testdata/attrib_gen/license_text.ABOUT deleted file mode 100644 index ab156c37..00000000 --- a/tests/testdata/attrib_gen/license_text.ABOUT +++ /dev/null @@ -1,7 +0,0 @@ -about_resource:license_text_test -name: license extraction -version: beta -notes: this is a test file to see if the license_test() correctly returns the - the text of the file in license_text_file field -license_text_file: license_text.LICENSE -notice_file: notice_text.NOTICE \ No newline at end of file diff --git a/tests/testdata/attrib_gen/license_text.LICENSE b/tests/testdata/attrib_gen/license_text.LICENSE deleted file mode 100644 index 53087fd6..00000000 --- a/tests/testdata/attrib_gen/license_text.LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -Tester holds the copyright for test component. Tester relinquishes copyright of -this software and releases the component to Public Domain. - -* Email Test@tester.com for any questions \ No newline at end of file diff --git a/tests/testdata/attrib_gen/missing_notice_license_files.ABOUT b/tests/testdata/attrib_gen/missing_notice_license_files.ABOUT deleted file mode 100644 index 3469c86f..00000000 --- a/tests/testdata/attrib_gen/missing_notice_license_files.ABOUT +++ /dev/null @@ -1,8 +0,0 @@ -about_resource:license_text_test -name: license extraction -version: beta -notes: this is a test file to see if the license_test() correctly returns an - of empty string when the license_text_file and notice_file have file values - that do not exist -license_text_file: test.LICENSE -notice_file: test.NOTICE \ No newline at end of file diff --git a/tests/testdata/attrib_gen/no_text_file_field.ABOUT b/tests/testdata/attrib_gen/no_text_file_field.ABOUT deleted file mode 100644 index 08e6f075..00000000 --- a/tests/testdata/attrib_gen/no_text_file_field.ABOUT +++ /dev/null @@ -1,5 +0,0 @@ -about_resource: no_license_text_field_field -name: license extraction -version: beta -notes: this is a test file to see if the license_test() correctly returns the - the text of the file in license_text_file field \ No newline at end of file diff --git a/tests/testdata/attrib_gen/notice_text.NOTICE b/tests/testdata/attrib_gen/notice_text.NOTICE deleted file mode 100644 index fce3b8d5..00000000 --- a/tests/testdata/attrib_gen/notice_text.NOTICE +++ /dev/null @@ -1 +0,0 @@ -Test component is released to Public Domain. \ No newline at end of file diff --git a/tests/testdata/attrib_gen/test-about-code-tempfile b/tests/testdata/attrib_gen/test-about-code-tempfile deleted file mode 100644 index b00fe55a..00000000 --- a/tests/testdata/attrib_gen/test-about-code-tempfile +++ /dev/null @@ -1,53 +0,0 @@ - - - - - Open Source Software Information - - - -

OPEN SOURCE SOFTWARE INFORMATION

-
- -

For instructions on how to obtain a copy of any source code - being made publicly available by Starship Technology related to - software used in this product you may send your request in - writing to:

- - Starship Technology LLC.
- OSS Management
- 7829 Montague Expressway
- Santa Clara, CA 95031
- USA
- -

The Starship Technology website www.dejacode.com also - contains information regarding Starship Technology's use of open - source. Starship Technology has created the www.dejacode.org to - serve as a portal for interaction with the software - community-at-large.

- -

This document contains additional information regarding - licenses, acknowledgments and required copyright notices for - open source packages used in this Starship Technology product. - This Starship Technology product contains the following open - source software components

- -
- -
- -
- -
- - -
- -

End

- - \ No newline at end of file diff --git a/tests/testdata/check/dupe_field_name.ABOUT b/tests/testdata/check/dupe_field_name.ABOUT deleted file mode 100644 index 70b43f7a..00000000 --- a/tests/testdata/check/dupe_field_name.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: Apache HTTP Server -version: 2.4.3 -name: Apache HTTP Server dupe -about_resource: about_file_ref.c \ No newline at end of file diff --git a/tests/testdata/default_entries/invalid-defaults.ABOUT b/tests/testdata/default_entries/invalid-defaults.ABOUT deleted file mode 100644 index f41e02c1..00000000 --- a/tests/testdata/default_entries/invalid-defaults.ABOUT +++ /dev/null @@ -1,7 +0,0 @@ -name: distribute -version: 1.1 -extend_file: distribute.ABOUT -about_resource: distribute_setup.py -date:2013-01-08 16:18:48+01:00 -download_url:http://python-distribute.org/distribute_setup.py -badkey: somevalue \ No newline at end of file diff --git a/tests/testdata/dumps/complete2/about.ABOUT b/tests/testdata/dumps/complete2/about.ABOUT deleted file mode 100644 index a12bfb6e..00000000 --- a/tests/testdata/dumps/complete2/about.ABOUT +++ /dev/null @@ -1,30 +0,0 @@ -about_resource: . - -name: AboutCode -version: 0.11.0 - -owner: nexB Inc. -author: - - Jillian Daguil, - - Chin Yeung Li, - - Philippe Ombredanne, - - Thomas Druez - -homepage_url: http://dejacode.org - -vcs_tool: git -vcs_repository: https://github.com/dejacode/about-code-tool.git - -description: |- - AboutCode is a tool to process ABOUT files. - An ABOUT file is a file. - -license_expression: apache-2.0 -licenses: - - key: apache-2.0 - file: apache-2.0.LICENSE - -copyright: Copyright (c) 2013-2014 nexB Inc. - -notice_file: NOTICE -attribute: Yes diff --git a/tests/testdata/equal/complete2/NOTICE b/tests/testdata/equal/complete2/NOTICE deleted file mode 100644 index df32cdb5..00000000 --- a/tests/testdata/equal/complete2/NOTICE +++ /dev/null @@ -1,3 +0,0 @@ - Copyright (c) 2013-2014 nexB Inc. - - http://www.nexb.com/ - All rights reserved. diff --git a/tests/testdata/equal/complete2/apache-2.0.LICENSE b/tests/testdata/equal/complete2/apache-2.0.LICENSE deleted file mode 100644 index f6b06388..00000000 --- a/tests/testdata/equal/complete2/apache-2.0.LICENSE +++ /dev/null @@ -1,3 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ diff --git a/tests/testdata/file_expansion/distribute.ABOUT b/tests/testdata/file_expansion/distribute.ABOUT deleted file mode 100644 index 35ca8ca7..00000000 --- a/tests/testdata/file_expansion/distribute.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -name: distribute -version: 1.1 -about_resource: distribute_setup.py -date:2013-01-08 16:18:48+01:00 -download_url:http://python-distribute.org/distribute_setup.py -badkey: somevalue \ No newline at end of file diff --git a/tests/testdata/filesfields/django_snippets_2413.ABOUT b/tests/testdata/filesfields/django_snippets_2413.ABOUT deleted file mode 100644 index 8b941e0b..00000000 --- a/tests/testdata/filesfields/django_snippets_2413.ABOUT +++ /dev/null @@ -1,13 +0,0 @@ -about_resource: django_snippets_2413.py -version: 2011-04-12 -download_url: http://djangosnippets.org/snippets/2413/download/ - -name: Yet another query string template tag -homepage_url: http://djangosnippets.org/snippets/2413/ - -license_url:http://djangosnippets.org/about/tos/ -license_text_file: django_snippets.LICENSE -notes: This file was modified to include the line "register = Library()" - without which the template tag is not registered. - - \ No newline at end of file diff --git a/tests/testdata/filesfields/django_snippets_2413.py b/tests/testdata/filesfields/django_snippets_2413.py deleted file mode 100644 index d0fa7726..00000000 --- a/tests/testdata/filesfields/django_snippets_2413.py +++ /dev/null @@ -1,155 +0,0 @@ -# This was taken from http://djangosnippets.org/snippets/2413/ -# It is stored in thirdparty as django_snippets_2413.py - -import re -from django.template import Library, Node, TemplateSyntaxError -from django.http import QueryDict -from django.utils.encoding import smart_str - -register = Library() - - -@register.tag -def query_string(parser, token): - """ - Template tag for creating and modifying query strings. - - Syntax: - {% query_string [] [modifier]* [as ] %} - - modifier is where op in {=, +, -} - - Parameters: - - base_querystring: literal query string, e.g. '?tag=python&tag=django&year=2011', - or context variable bound to either - - a literal query string, - - a python dict with potentially lists as values, or - - a django QueryDict object - May be '' or None or missing altogether. - - modifiers may be repeated and have the form . - They are processed in the order they appear. - name is taken as is for a parameter name. - op is one of {=, +, -}. - = replace all existing values of name with value(s) - + add value(s) to existing values for name - - remove value(s) from existing values if present - value is either a literal parameter value - or a context variable. If it is a context variable - it may also be bound to a list. - - as : bind result to context variable instead of injecting in output - (same as in url tag). - - Examples: - 1. {% query_string '?tag=a&m=1&m=3&tag=b' tag+'c' m=2 tag-'b' as myqs %} - - Result: myqs == '?m=2&tag=a&tag=c' - - 2. context = {'qs': {'tag': ['a', 'b'], 'year': 2011, 'month': 2}, - 'tags': ['c', 'd'], - 'm': 4,} - - {% query_string qs tag+tags month=m %} - - Result: '?tag=a&tag=b&tag=c&tag=d&year=2011&month=4 - """ - # matches 'tagname1+val1' or 'tagname1=val1' but not 'anyoldvalue' - mod_re = re.compile(r"^(\w+)(=|\+|-)(.*)$") - bits = token.split_contents() - qdict = None - mods = [] - asvar = None - bits = bits[1:] - if len(bits) >= 2 and bits[-2] == 'as': - asvar = bits[-1] - bits = bits[:-2] - if len(bits) >= 1: - first = bits[0] - if not mod_re.match(first): - qdict = parser.compile_filter(first) - bits = bits[1:] - for bit in bits: - match = mod_re.match(bit) - if not match: - raise TemplateSyntaxError("Malformed arguments to query_string tag") - name, op, value = match.groups() - mods.append((name, op, parser.compile_filter(value))) - return QueryStringNode(qdict, mods, asvar) - -class QueryStringNode(Node): - def __init__(self, qdict, mods, asvar): - self.qdict = qdict - self.mods = mods - self.asvar = asvar - def render(self, context): - mods = [(smart_str(k, 'ascii'), op, v.resolve(context)) - for k, op, v in self.mods] - if self.qdict: - qdict = self.qdict.resolve(context) - else: - qdict = None - # Internally work only with QueryDict - qdict = self._get_initial_query_dict(qdict) - #assert isinstance(qdict, QueryDict) - for k, op, v in mods: - qdict.setlist(k, self._process_list(qdict.getlist(k), op, v)) - qstring = qdict.urlencode() - if qstring: - qstring = '?' + qstring - if self.asvar: - context[self.asvar] = qstring - return '' - else: - return qstring - def _get_initial_query_dict(self, qdict): - if not qdict: - qdict = QueryDict(None, mutable=True) - elif isinstance(qdict, QueryDict): - qdict = qdict.copy() - elif isinstance(qdict, basestring): - if qdict.startswith('?'): - qdict = qdict[1:] - qdict = QueryDict(qdict, mutable=True) - else: - # Accept any old dict or list of pairs. - try: - pairs = qdict.items() - except: - pairs = qdict - qdict = QueryDict(None, mutable=True) - # Enter each pair into QueryDict object: - try: - for k, v in pairs: - # Convert values to unicode so that detecting - # membership works for numbers. - if isinstance(v, (list, tuple)): - for e in v: - qdict.appendlist(k,unicode(e)) - else: - qdict.appendlist(k, unicode(v)) - except: - # Wrong data structure, qdict remains empty. - pass - return qdict - def _process_list(self, current_list, op, val): - if not val: - if op == '=': - return [] - else: - return current_list - # Deal with lists only. - if not isinstance(val, (list, tuple)): - val = [val] - val = [unicode(v) for v in val] - # Remove - if op == '-': - for v in val: - while v in current_list: - current_list.remove(v) - # Replace - elif op == '=': - current_list = val - # Add - elif op == '+': - for v in val: - current_list.append(v) - return current_list diff --git a/tests/testdata/filesfields/non_ascii_field.about b/tests/testdata/filesfields/non_ascii_field.about deleted file mode 100644 index 046bcbdf..00000000 --- a/tests/testdata/filesfields/non_ascii_field.about +++ /dev/null @@ -1,4 +0,0 @@ -name: this is a test -about_resource: django_snippets_2413.py -version: test1 -owner: Matías Aguirre diff --git a/tests/testdata/filesfields/sym-link-django_snippets_2413.ABOUT b/tests/testdata/filesfields/sym-link-django_snippets_2413.ABOUT deleted file mode 100644 index 19c8da5c..00000000 Binary files a/tests/testdata/filesfields/sym-link-django_snippets_2413.ABOUT and /dev/null differ diff --git a/tests/testdata/filesfields/test.about b/tests/testdata/filesfields/test.about deleted file mode 100644 index 8524b2a5..00000000 --- a/tests/testdata/filesfields/test.about +++ /dev/null @@ -1,8 +0,0 @@ -about_resource: test_folder/test.py -version: 2011-04-12 -download_url: - -name: - -license_text_file: test.LICENSE - diff --git a/tests/testdata/filesfields/test_folder/django_snippets_2413.ABOUT b/tests/testdata/filesfields/test_folder/django_snippets_2413.ABOUT deleted file mode 100644 index 016d24a1..00000000 --- a/tests/testdata/filesfields/test_folder/django_snippets_2413.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: django_snippets_2413.py -version: 2011-04-12 - -name: Yet another query string template tag - -license_text_file: ../django_snippets.LICENSE diff --git a/tests/testdata/filesfields/test_folder/test.About b/tests/testdata/filesfields/test_folder/test.About deleted file mode 100644 index bcef3670..00000000 --- a/tests/testdata/filesfields/test_folder/test.About +++ /dev/null @@ -1,4 +0,0 @@ -name: test -version: unknown - -install_file: \ No newline at end of file diff --git a/tests/testdata/filesfields/test_folder/test1.About b/tests/testdata/filesfields/test_folder/test1.About deleted file mode 100644 index 14f1fdb5..00000000 --- a/tests/testdata/filesfields/test_folder/test1.About +++ /dev/null @@ -1,4 +0,0 @@ -name: test1 -version: unknown - -install_file: \ No newline at end of file diff --git a/tests/testdata/filesfields/windows-shortcut-django_snippets_2413.ABOUT b/tests/testdata/filesfields/windows-shortcut-django_snippets_2413.ABOUT deleted file mode 100644 index db434efc..00000000 Binary files a/tests/testdata/filesfields/windows-shortcut-django_snippets_2413.ABOUT and /dev/null differ diff --git a/tests/testdata/genattrib/default2.html b/tests/testdata/genattrib/default2.html deleted file mode 100644 index 8d8c59b3..00000000 --- a/tests/testdata/genattrib/default2.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - Open Source Software Information - - - -

OPEN SOURCE SOFTWARE INFORMATION

- -
- {% for about in abouts %} -

name: {{ about.name.value }} version: {{ about.version.value }}

- {% endfor %} -
- - diff --git a/tests/testdata/haveAboutFile/basic.about b/tests/testdata/haveAboutFile/basic.about deleted file mode 100644 index 3e52ce77..00000000 --- a/tests/testdata/haveAboutFile/basic.about +++ /dev/null @@ -1,33 +0,0 @@ -name: optional -version: 2.2 -about_format: 2.0 -date: 2013-1-1 -description: A description for this component. -homepage_url: http://msn.com -download_url: http://msn.com/download -readme: README STUFF -install: HOW TO INSTALLInstallation information for this component. You may use install_file when this is a long text or a pre-existing file as INSTALL files are commonly found in code archives. -changelog: Changelog text for this component. You may use changelog_file when this is a long text or a pre-existing file as CHANGELOG files are commonly found in code archives. -news: News text for this component. You may use news_file when this is a long text or a pre-existing file as NEWS files are commonly found in code archives. You may use news_url to point to an internet news feed for this component. -notes: SOME NOTES -usage: Describe the intended usage -contact: Davide Berti dberti@nexb.com -organization: NexB -copyright: Copyright Davide Berti 2013 statement for this component. You may use copyright_file when this is a long text or to reference a pre-existing file. For instance COPYRIGHT files are commonly found in code archives. -notice: Text containing a legal notice for this component. You may use notice_file when this is a long text or to reference a pre-existing file. For instance NOTICE files are commonly found in code archives. -notice_url: http://msn.com/notice -license_text: License text for this component. You may use license_text_file when this is a long text or to reference a pre-existing file. For instance LICENSE or COPYING files are commonly found in code archives. -license_url: http://msn.com/license -license_spdx: The SPDX license short form identifiers for the license of this component. See http://spdx.org/licenses/ for details. You can separate each identifier with an " or " and " and " as defined in the SPDX specification 1.1 to document the relationship between multiple license identifiers, such as a choice of license. -redistribute_sources: yes -scm_tool: SCM tool such as git, svn, cvs, etc. -scm_repository: Typically a URL or some other identifier used by the tool to point to a repository, folder or file, such as an SVN or Git repository URL. -scm_path: Path to a file, folder or module used by certain SCM pointing inside a repository. -scm_tag: tag name or path used by certain SCM. -scm_branch: branch name or path used by certain SCM. -scm_rev: revision identifier such as a revision hash or version number. -signature_gpg_file: linux-3.1.7.tar.sign -checksum_sha1: 87aaf7bc7e8715f0455997bb8c6791aa -dje_component: The DejaCode Enterprise URN for this component. -dje_license: The DejaCode Enterprise URNs of the licenses for this component. -dje_organization: The DejaCode Enterprise URN for this component organization. \ No newline at end of file diff --git a/tests/testdata/haveAboutFile/duplicate_key_names.AbOuT b/tests/testdata/haveAboutFile/duplicate_key_names.AbOuT deleted file mode 100644 index 9687b7a1..00000000 --- a/tests/testdata/haveAboutFile/duplicate_key_names.AbOuT +++ /dev/null @@ -1,9 +0,0 @@ -name: dup_name -version: 2.4.3 -date: 2012-08-21 - - -date: 2012-08-22 -copyright: Copyright 2012 The Apache Software Foundation. -date: 2010-08-01 -download_url: This is not a URL. \ No newline at end of file diff --git a/tests/testdata/haveAboutFile/duplicate_key_names_2.aBout b/tests/testdata/haveAboutFile/duplicate_key_names_2.aBout deleted file mode 100644 index 953a4dc5..00000000 --- a/tests/testdata/haveAboutFile/duplicate_key_names_2.aBout +++ /dev/null @@ -1,9 +0,0 @@ -name: dup_name -version: 1.1.2.4.3 -date: 2012-08-21 -copyright: Copyright 2012 The testing dup. -date: 2010-08-01 - -date: 2012-08-22 -copyright: Copyright 2012 LIFO -notice_url: \ No newline at end of file diff --git a/tests/testdata/haveAboutFile/httpd-2.4.3.tar.gz.ABOUT b/tests/testdata/haveAboutFile/httpd-2.4.3.tar.gz.ABOUT deleted file mode 100644 index 33951c77..00000000 --- a/tests/testdata/haveAboutFile/httpd-2.4.3.tar.gz.ABOUT +++ /dev/null @@ -1,9 +0,0 @@ -name: Apache HTTP Server -homepage_url: http://httpd.apache.org -download_url: http://archive.apache.org/dist/httpd/httpd-2.4.3.tar.gz -version: 2.4.3 -date: 2012-08-21 -license_spdx: Apache-2.0 -license_text_file: httpd.LICENSE -copyright: Copyright 2012 The Apache Software Foundation. -notice_file: httpd.NOTICE \ No newline at end of file diff --git a/tests/testdata/haveAboutFile/invalid_url.About b/tests/testdata/haveAboutFile/invalid_url.About deleted file mode 100644 index c2a98a33..00000000 --- a/tests/testdata/haveAboutFile/invalid_url.About +++ /dev/null @@ -1,3 +0,0 @@ -name: invalid_url - -download_url: http: \ No newline at end of file diff --git a/tests/testdata/haveAboutFile/optional.About b/tests/testdata/haveAboutFile/optional.About deleted file mode 100644 index be61c669..00000000 --- a/tests/testdata/haveAboutFile/optional.About +++ /dev/null @@ -1,32 +0,0 @@ -name: optional -about_format: 2.0 -date: 2013-1-1 -description: A description for this component. -homepage_url: http://msn.com -download_url: http://msn.com/download -readme: README STUFF -install: HOW TO INSTALLInstallation information for this component. You may use install_file when this is a long text or a pre-existing file as INSTALL files are commonly found in code archives. -changelog: Changelog text for this component. You may use changelog_file when this is a long text or a pre-existing file as CHANGELOG files are commonly found in code archives. -news: News text for this component. You may use news_file when this is a long text or a pre-existing file as NEWS files are commonly found in code archives. You may use news_url to point to an internet news feed for this component. -notes: SOME NOTES -usage: Describe the intended usage -contact: Davide Berti dberti@nexb.com -organization: NexB -copyright: Copyright Davide Berti 2013 statement for this component. You may use copyright_file when this is a long text or to reference a pre-existing file. For instance COPYRIGHT files are commonly found in code archives. -notice: Text containing a legal notice for this component. You may use notice_file when this is a long text or to reference a pre-existing file. For instance NOTICE files are commonly found in code archives. -notice_url: http://msn.com/notice -license_text: License text for this component. You may use license_text_file when this is a long text or to reference a pre-existing file. For instance LICENSE or COPYING files are commonly found in code archives. -license_url: http://msn.com/license -license_spdx: The SPDX license short form identifiers for the license of this component. See http://spdx.org/licenses/ for details. You can separate each identifier with an " or " and " and " as defined in the SPDX specification 1.1 to document the relationship between multiple license identifiers, such as a choice of license. -redistribute_sources: yes -scm_tool: SCM tool such as git, svn, cvs, etc. -scm_repository: Typically a URL or some other identifier used by the tool to point to a repository, folder or file, such as an SVN or Git repository URL. -scm_path: Path to a file, folder or module used by certain SCM pointing inside a repository. -scm_tag: tag name or path used by certain SCM. -scm_branch: branch name or path used by certain SCM. -scm_rev: revision identifier such as a revision hash or version number. -signature_gpg_file: linux-3.1.7.tar.sign -checksum_sha1: 87aaf7bc7e8715f0455997bb8c6791aa -dje_component: The DejaCode Enterprise URN for this component. -dje_license: The DejaCode Enterprise URNs of the licenses for this component. -dje_organization: The DejaCode Enterprise URN for this component organization. \ No newline at end of file diff --git a/tests/testdata/haveAboutFile/test1.about b/tests/testdata/haveAboutFile/test1.about deleted file mode 100644 index 4e31ad27..00000000 --- a/tests/testdata/haveAboutFile/test1.about +++ /dev/null @@ -1,3 +0,0 @@ -name: test_name1 -version: 1 -about_resource: test_file diff --git a/tests/testdata/haveAboutFile/test_file b/tests/testdata/haveAboutFile/test_file deleted file mode 100644 index af27ff49..00000000 --- a/tests/testdata/haveAboutFile/test_file +++ /dev/null @@ -1 +0,0 @@ -This is a test file. \ No newline at end of file diff --git a/tests/testdata/mandatory_fields/missing-name-and-version.about b/tests/testdata/mandatory_fields/missing-name-and-version.about deleted file mode 100644 index 738109ed..00000000 --- a/tests/testdata/mandatory_fields/missing-name-and-version.about +++ /dev/null @@ -1 +0,0 @@ -date:2013-01-08 16:18:48+01:00 diff --git a/tests/testdata/mandatory_fields/missing-name.about b/tests/testdata/mandatory_fields/missing-name.about deleted file mode 100644 index ca139257..00000000 --- a/tests/testdata/mandatory_fields/missing-name.about +++ /dev/null @@ -1 +0,0 @@ -version: 1.0 diff --git a/tests/testdata/mandatory_fields/missing-version.about b/tests/testdata/mandatory_fields/missing-version.about deleted file mode 100644 index d825b6fd..00000000 --- a/tests/testdata/mandatory_fields/missing-version.about +++ /dev/null @@ -1 +0,0 @@ -name: something.file diff --git a/tests/testdata/noAboutFile/test_file b/tests/testdata/noAboutFile/test_file deleted file mode 100644 index af27ff49..00000000 --- a/tests/testdata/noAboutFile/test_file +++ /dev/null @@ -1 +0,0 @@ -This is a test file. \ No newline at end of file diff --git a/tests/testdata/no_colon/about_no_colon.py b/tests/testdata/no_colon/about_no_colon.py deleted file mode 100644 index 22ae0c5f..00000000 --- a/tests/testdata/no_colon/about_no_colon.py +++ /dev/null @@ -1 +0,0 @@ -# Empty file \ No newline at end of file diff --git a/tests/testdata/no_colon/about_no_colon.py.ABOUT b/tests/testdata/no_colon/about_no_colon.py.ABOUT deleted file mode 100644 index 99416405..00000000 --- a/tests/testdata/no_colon/about_no_colon.py.ABOUT +++ /dev/null @@ -1,10 +0,0 @@ -name: no colon test -version 0.7.0 -about_resource: about_no_colon.py - -test -test with no colon -test with colon: -test with colon1: test - -date: 2013-05-10 \ No newline at end of file diff --git a/tests/testdata/output/test.csv b/tests/testdata/output/test.csv deleted file mode 100644 index e60ee2ad..00000000 --- a/tests/testdata/output/test.csv +++ /dev/null @@ -1 +0,0 @@ -Resource,name,version,about_resource,about_format,date,description,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,usage,contact,organization,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute_sources,scm_tool,scm_repository,scm_path,scm_tag,scm_branch,scm_rev,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization,warnings,errors diff --git a/tests/testdata/output/testoutput-old.csv b/tests/testdata/output/testoutput-old.csv deleted file mode 100644 index a0e095c7..00000000 --- a/tests/testdata/output/testoutput-old.csv +++ /dev/null @@ -1,4 +0,0 @@ -Resource,name,version,about_file,about_format,date,description,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,usage,contact,organization,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute_sources,scm_tool,scm_repository,scm_path,scm_tag,scm_branch,scm_rev,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization,warnings,errors -testdata/haveAboutFile/test1.about,test_name1,1,,,,,,,,,,,,,,,,,,,,,,,,,,license,,,,,,,,,,,,,,,,, -testdata/haveAboutFile/test2.About,test_name2,2,,,,,http://www.test.com/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, -testdata/haveAboutFile/test3.about,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/tests/testdata/output/testoutput.csv b/tests/testdata/output/testoutput.csv deleted file mode 100644 index b233c070..00000000 --- a/tests/testdata/output/testoutput.csv +++ /dev/null @@ -1 +0,0 @@ -testdata/haveAboutFile/,test_name1,1,test_file,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/tests/testdata/output/testparser.csv b/tests/testdata/output/testparser.csv deleted file mode 100644 index 24c24313..00000000 --- a/tests/testdata/output/testparser.csv +++ /dev/null @@ -1,13 +0,0 @@ -Resource,name,version,about_format,date,description,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,usage,contact,organization,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute_sources,warnings -C:\Users\CYL\nexBWorkspace\about\testdata\basic\basic.about,optional,2.2,2.0,2013-1-1,A description for this component.,http://msn.com,http://msn.com/download,README STUFF,,HOW TO INSTALLInstallation information for this component. You may use install_file when this is a long text or a pre-existing file as INSTALL files are commonly found in code archives.,,Changelog text for this component. You may use changelog_file when this is a long text or a pre-existing file as CHANGELOG files are commonly found in code archives.,,News text for this component. You may use news_file when this is a long text or a pre-existing file as NEWS files are commonly found in code archives. You may use news_url to point to an internet news feed for this component.,,,SOME NOTES,Describe the intended usage,Davide Berti dberti@nexb.com,NexB,Copyright Davide Berti 2013 statement for this component. You may use copyright_file when this is a long text or to reference a pre-existing file. For instance COPYRIGHT files are commonly found in code archives.,,Text containing a legal notice for this component. You may use notice_file when this is a long text or to reference a pre-existing file. For instance NOTICE files are commonly found in code archives.,,http://msn.com/notice,License text for this component. You may use license_text_file when this is a long text or to reference a pre-existing file. For instance LICENSE or COPYING files are commonly found in code archives.,,http://msn.com/license,"The SPDX license short form identifiers for the license of this component. See http://spdx.org/licenses/ for details. You can separate each identifier with an "" or "" and "" and "" as defined in the SPDX specification 1.1 to document the relationship between multiple license identifiers, such as a choice of license.",yes,"{('about_file', 'basic'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\default_entries\invalid-defaults.ABOUT,distribute,1.1,,2013-01-08 16:18:48+01:00,,,http://python-distribute.org/distribute_setup.py,,,,,,,,,,,,,,,,,,,,,,,,"{('about_file', 'distribute_setup.py'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\file_expansion\distribute.ABOUT,distribute,1.1,,2013-01-08 16:18:48+01:00,,,http://python-distribute.org/distribute_setup.py,,,,,,,,,,,,,,,,,,,,,,,,"{('about_file', 'distribute_setup.py'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\haveAboutFile\basic.about,optional,2.2,2.0,2013-1-1,A description for this component.,http://msn.com,http://msn.com/download,README STUFF,,HOW TO INSTALLInstallation information for this component. You may use install_file when this is a long text or a pre-existing file as INSTALL files are commonly found in code archives.,,Changelog text for this component. You may use changelog_file when this is a long text or a pre-existing file as CHANGELOG files are commonly found in code archives.,,News text for this component. You may use news_file when this is a long text or a pre-existing file as NEWS files are commonly found in code archives. You may use news_url to point to an internet news feed for this component.,,,SOME NOTES,Describe the intended usage,Davide Berti dberti@nexb.com,NexB,Copyright Davide Berti 2013 statement for this component. You may use copyright_file when this is a long text or to reference a pre-existing file. For instance COPYRIGHT files are commonly found in code archives.,,Text containing a legal notice for this component. You may use notice_file when this is a long text or to reference a pre-existing file. For instance NOTICE files are commonly found in code archives.,,http://msn.com/notice,License text for this component. You may use license_text_file when this is a long text or to reference a pre-existing file. For instance LICENSE or COPYING files are commonly found in code archives.,,http://msn.com/license,"The SPDX license short form identifiers for the license of this component. See http://spdx.org/licenses/ for details. You can separate each identifier with an "" or "" and "" and "" as defined in the SPDX specification 1.1 to document the relationship between multiple license identifiers, such as a choice of license.",yes,"{('about_file', 'basic'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\haveAboutFile\duplicate_key_names.AbOuT,dup_name,2.4.3,,2010-08-01,,,,,,,,,,,,,,,,,Copyright 2012 The Apache Software Foundation.,,,,,,,,,,"{('date', '2012-08-22'): 'Duplicate field names found. This value will be overwritten', ('date', '2012-08-21'): 'Duplicate field names found. This value will be overwritten', ('about_file', 'duplicate_key_names'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\haveAboutFile\duplicate_key_names_2.aBout,dup_name,1.1.2.4.3,,2012-08-22,,,,,,,,,,,,,,,,,Copyright 2012 LIFO,,,,,,,,,,"{('date', '2012-08-21'): 'Duplicate field names found. This value will be overwritten', ('about_file', 'duplicate_key_names_2'): 'The file that is being referenced by about file does not exist in the directory', ('copyright', 'Copyright 2012 The testing dup.'): 'Duplicate field names found. This value will be overwritten', ('date', '2010-08-01'): 'Duplicate field names found. This value will be overwritten'}" -C:\Users\CYL\nexBWorkspace\about\testdata\haveAboutFile\httpd-2.4.3.tar.gz.ABOUT,Apache HTTP Server,2.4.3,,2012-08-21,,http://httpd.apache.org,http://archive.apache.org/dist/httpd/httpd-2.4.3.tar.gz,,,,,,,,,,,,,,Copyright 2012 The Apache Software Foundation.,,,httpd.NOTICE,,,httpd.LICENSE,,Apache-2.0,,"{('about_file', 'httpd-2.4.3.tar.gz'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\haveAboutFile\optional.About,optional,,2.0,2013-1-1,A description for this component.,http://msn.com,http://msn.com/download,README STUFF,,HOW TO INSTALLInstallation information for this component. You may use install_file when this is a long text or a pre-existing file as INSTALL files are commonly found in code archives.,,Changelog text for this component. You may use changelog_file when this is a long text or a pre-existing file as CHANGELOG files are commonly found in code archives.,,News text for this component. You may use news_file when this is a long text or a pre-existing file as NEWS files are commonly found in code archives. You may use news_url to point to an internet news feed for this component.,,,SOME NOTES,Describe the intended usage,Davide Berti dberti@nexb.com,NexB,Copyright Davide Berti 2013 statement for this component. You may use copyright_file when this is a long text or to reference a pre-existing file. For instance COPYRIGHT files are commonly found in code archives.,,Text containing a legal notice for this component. You may use notice_file when this is a long text or to reference a pre-existing file. For instance NOTICE files are commonly found in code archives.,,http://msn.com/notice,License text for this component. You may use license_text_file when this is a long text or to reference a pre-existing file. For instance LICENSE or COPYING files are commonly found in code archives.,,http://msn.com/license,"The SPDX license short form identifiers for the license of this component. See http://spdx.org/licenses/ for details. You can separate each identifier with an "" or "" and "" and "" as defined in the SPDX specification 1.1 to document the relationship between multiple license identifiers, such as a choice of license.",yes,"{('about_file', 'optional'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\mandatory_fields\empty.about,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{('about_file', 'empty'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\mandatory_fields\missing-name-and-version.about,,,,2013-01-08 16:18:48+01:00,,,,,,,,,,,,,,,,,,,,,,,,,,,"{('about_file', 'missing-name-and-version'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\mandatory_fields\missing-name.about,,1.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{('about_file', 'missing-name'): 'The file that is being referenced by about file does not exist in the directory'}" -C:\Users\CYL\nexBWorkspace\about\testdata\mandatory_fields\missing-version.about,something.file,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{('about_file', 'missing-version'): 'The file that is being referenced by about file does not exist in the directory'}" diff --git a/tests/testdata/output/thirdparty-testdata.csv b/tests/testdata/output/thirdparty-testdata.csv deleted file mode 100644 index 06cb0c62..00000000 --- a/tests/testdata/output/thirdparty-testdata.csv +++ /dev/null @@ -1,115 +0,0 @@ -Resource,name,version,about_file,about_format,date,description,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,usage,contact,organization,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute_sources,scm_tool,scm_repository,scm_path,scm_tag,scm_branch,scm_rev,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization,warnings,errors -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\csv_serialize.py.ABOUT,csv_serialize,2013-01-16,,,,,http://djangosnippets.org/snippets/2240/,http://djangosnippets.org/snippets/2240/download/,,,,,,,,,,,,,,,,,,,,django_snippets.LICENSE,http://djangosnippets.org/about/tos/,,,,,,,,,,,,,,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\django_snippets_2413.ABOUT,Yet another query string template tag,2011-04-12,django_snippets_2413.py,,,,http://djangosnippets.org/snippets/2413/,http://djangosnippets.org/snippets/2413/download/,,,,,,,,,,"This file was modified to include the line ""register = Library()"" - without which the template tag is not registered.",,,,,,,,,,django_snippets.LICENSE,http://djangosnippets.org/about/tos/,,,,,,,,,,,,,,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\elasticsearch-sources.ABOUT,ElasticSearch,0.19.8,elasticsearch-v0.19.8-g7badcde.tar.gz,,,,http://www.elasticsearch.org/,https://github.com/elasticsearch/elasticsearch/tarball/v0.19.8,,,,,,,,,,Source code for the pre-built binaries we use ,,,ElasticSearch and Shay Banon,Copyright 2009-2011 ElasticSearch and Shay Banon,,,elasticsearch.NOTICE,,,elasticsearch.LICENSE,,,,git,https://github.com/elasticsearch/elasticsearch.git,,,,badcdee74acec84da3de6c6ea55c692aee4a6f9,,,,,apache-2.0,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\elasticsearch.ABOUT,ElasticSearch,0.19.8,elasticsearch-0.19.8.zip,,,,http://www.elasticsearch.org/,https://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.19.8.zip,,,,,,,,,,"This a prebuilt version working on all OSes. - The tar.gz works only with POSIX OSses and not Windows. ",,,ElasticSearch and Shay Banon,Copyright 2009-2011 ElasticSearch and Shay Banon,,,elasticsearch.NOTICE,,,elasticsearch.LICENSE,,,,git,https://github.com/elasticsearch/elasticsearch.git,,,,badcdee74acec84da3de6c6ea55c692aee4a6f9,,,,,apache-2.0,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\ez_setup.py.ABOUT,setuptools boostrap,0.6c11,,,2013-01-01,,http://pypi.python.org/pypi/setuptools,http://peak.telecommunity.com/dist/ez_setup.py,,,,,,,,,,this is not used by default but embedded in virtualenv,,,,,,,,,,,,,,,,,,,,,,,,zpl-2.1,,"{('author', 'Phillip J. Eby'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\FixedHeader.ABOUT,data tables fixed header,2.0.6,FixedHeader-2.0.6.zip,,,"""Fix"" a header at the top of the table, so it scrolls with the table",http://datatables.net,http://datatables.net/releases/FixedHeader-2.0.6.zip,,,,,,,,,,"This source file is free software, under either the GPL v2 license or a - BSD style license, available at: - http://datatables.net/license_gpl2 - http://datatables.net/license_bsd",,,Allan Jardine,"Copyright 2009-2012 Allan Jardine, all rights reserved.",,,,,,,http://datatables.net/license_bsd,,,,,,,,,,,,,bsd-new,,"{('author', 'Allan Jardine (www.sprymedia.co.uk)'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\Font-Awesome.ABOUT,Font-Awesome,3.0.2,Font-Awesome-v3.0.2.zip,,,,http://fortawesome.github.com/Font-Awesome/,https://github.com/FortAwesome/Font-Awesome/archive/v3.0.2.zip,,,,,,,,,,"there are several licenses: SIL Open Font License, MIT License, CC BY 3.0 License, Attribution is no longer required in Font Awesome 3.0",,,FortAwesome,,,,Font-Awesome.NOTICE,,,,,,,git,https://github.com/FortAwesome/Font-Awesome.git,,,,13d5dd373cbf3f2bddd8ac2ee8df3a1966a62d09,,,,,ofl-1.1 and mit and cc-by-3.0 ,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\jquery.js.ABOUT,jQuery,1.7.2,jquery-1.7.2.min.js,,,,http://jquery.com/,http://code.jquery.com/jquery-1.7.2.js,,,,,,,,,,,,,,,,,,,,jquery.js.LICENSE,http://jquery.org/license,,,git,https://github.com/jquery/jquery.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\jquery.jsPlumb.ABOUT,jquery.jsPlumb,1.3.10,jquery.jsPlumb-1.3.10-all-min.js,,,,http://code.google.com/p/jsplumb/,http://code.google.com/p/jsplumb/downloads/detail?name=jquery.jsPlumb-1.3.10-all-min.js,,,,,,,,,,,,,,,,,,,,jquery.js.LICENSE,,,,svn,http://jsplumb.googlecode.com/svn/trunk/,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\jquery.min.js.ABOUT,jQuery,1.7.2,jquery-1.7.2.min.js,,,,http://jquery.com/,http://code.jquery.com/jquery-1.7.2.min.js,,,,,,,,,,,,,,,,,,,,jquery.js.LICENSE,http://jquery.org/license,,,git,https://github.com/jquery/jquery.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\mod_wsgi-3.3.tar.gz.ABOUT,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"{('date-retrieved', '2013-01-16 19:41:10+01:00'): 'This is not a mandatory or optional field. It will be ignored', ('wget', 'http://modwsgi.googlecode.com/files/mod_wsgi-3.3.tar.gz'): 'This is not a mandatory or optional field. It will be ignored'}","{('version', ''): 'Mandatory field is missing', ('name', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\okfn-annotator.ABOUT,OKFN Annotator,549159b554411eba18c34ffffe91ba44f7558be6,okfn-annotator-549159b.zip,,,,http://okfn.org/projects/annotator/,https://github.com/okfn/annotator/zipball/549159b554411eba18c34ffffe91ba44f7558be6,,,,,,,,,,"this component includes several other components, not detailed here. - See archive for details and licenses.",,,OKFN,,,,,,,okfn-annotator.LICENSE,http://okfn.org/ip-policy/,,,git ,https://github.com/okfn/annotator.git,,,,549159b554411eba18c34ffffe91ba44f7558be6,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\setuptools.ABOUT,setuptools,0.6c11,setuptools-0.6c11-py2.6.egg,,,,http://pypi.python.org/pypi/setuptools,http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086,,,,,,,,,,this is not used by default but embedded in virtualenv,,,,,,,,,,,,,,,,,,,,,,,,zpl-2.1,,"{('author', 'Phillip J. Eby'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\twitter_bootstrap.ABOUT,bootstrap,2.0.3,twitter_bootstrap_v2.0.3.zip,,,,http://twitter.github.com/bootstrap/,https://github.com/twitter/bootstrap/archive/v2.0.3.zip,,,,,,,,,,,,,,,,,,,,twitter_bootstrap.LICENSE,,,,git,https://github.com/twitter/bootstrap.git,,,,,,,,,apache 2.0,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\underscore-min.js.ABOUT,underscore.js,1.4.2,underscore-min.js,,,,http://underscorejs.org/,https://raw.github.com/documentcloud/underscore/1.4.2/underscore-min.js,,,,,,,,,,,,,,,,,,,,underscore.js.LICENSE,,,,git,git://github.com/documentcloud/underscore.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\underscore.js.ABOUT,underscore.js,1.4.2,underscore.js,,,,http://underscorejs.org/,https://raw.github.com/documentcloud/underscore/1.4.2/underscore.js,,,,,,,,,,,,,,,,,,,,underscore.js.LICENSE,,,,git,git://github.com/documentcloud/underscore.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\amqp.ABOUT,amqp,1.0.6,amqp-1.0.6.tar.gz,,,,http://github.com/celery/py-amqp,http://pypi.python.org/packages/source/a/amqp/amqp-1.0.6.tar.gz#md5=b7b1ada6f8401b0a75db91440f75a62e,,,,,,,,,,,,pyamqp@celeryproject.org,Celery Project,,,,,,,amqp.LICENSE,,,,git,https://github.com/celery/py-amqp.git,,,,,,b7b1ada6f8401b0a75db91440f75a62e,,,lgpl-2.1,,"{('author', 'Barry Pederson'): 'This is not a mandatory or optional field. It will be ignored', ('maintainer', 'Ask Solem'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\anyjson.ABOUT,anyjson,0.3.3,anyjson-0.3.3.tar.gz,,,,,http://pypi.python.org/packages/source/a/anyjson/anyjson-0.3.3.tar.gz,,,,,,,,,,,,runefh@gmail.com,Rune Halvorsen,,,,,,,anyjson.LICENSE,,,,hg,https://bitbucket.org/runeh/anyjson,,,,,,,,,bsd-simplified,,"{('maintainer', 'Rune F. Halvorsen '): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\argparse.ABOUT,argparse,1.2.1,argparse-1.2.1.tar.gz,,,,http://code.google.com/p/argparse/,http://pypi.python.org/packages/source/a/argparse/argparse-1.2.1.tar.gz,,,,,,,,,,,,,Steven Bethard,,,,argparse.NOTICE,,,PSF.LICENSE,,,,,,,,,,,,,,psf,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\billiard.ABOUT,billiard,2.7.3.19,billiard-2.7.3.19.tar.gz,,,,,http://pypi.python.org/packages/source/b/billiard/billiard-2.7.3.19.tar.gz,,,,,,,,,,,,,Celery Project,,,,,,,billiard.LICENSE,,,,git,https://github.com/celery/billiard.git,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\Bitten-0.6b3.tar.gz.ABOUT,Bitten,0.6b3,,,2012-12-28,,,http://ftp.edgewall.com/pub/bitten/Bitten-0.6b3.tar.gz,,,,,,,,,,,,,,,,,,,,Bitten.LICENSE,,,,,,,,,,,,,,bsd-new,,"{('homepage_url', 'http://bitten.edgewall.org'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\Bitten.ABOUT,Bitten,0.6b3,,,,,,http://ftp.edgewall.com/pub/bitten/Bitten-0.6b3.tar.gz,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,bsd-new,,"{('about_file', ''): 'No about file is found.', ('homepage_url', 'http://bitten.edgewall.org'): 'This is not a mandatory or optional field. It will be ignored', ('date-retrieved', '2012-12-28 22:37:00+00:00'): 'This is not a mandatory or optional field. It will be ignored', ('license-text_file', 'Bitten.LICENSE'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\bittenextranose.ABOUT,bittenextranose,0.2,bittenextranose-0.2.tar.gz,,2012-12-28,,,,,,,,,,,,,,,,,,,,,,,bittenextranose.LICENSE,,,,git,https://github.com/hekevintran/bittenextranose.git,,,,da7a05bc4283bc1281ed22d54369c78942ff00fc,,,,,mit,,"{('homepage_url', 'https://github.com/hekevintran/bittenextranose'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\buildout-versions.ABOUT,buildout-versions,1.7,buildout-versions-1.7.tar.gz,,,,http://www.simplistix.co.uk/software/python/buildout-versions,http://pypi.python.org/packages/source/b/buildout-versions/buildout-versions-1.7.tar.gz#md5=731ecc0c9029f45826fa9f31d44e311d,,,,,,,,,,,,,Simplistix,Copyright (c) 2010 Simplistix Ltd,,,,,,buildout-versions.LICENSE,,,,git,https://github.com/Simplistix/buildout-versions.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\buildout.extensionscripts.ABOUT,buildout.extensionscripts,1.0,buildout.extensionscripts-1.0.zip,,2013-01-12,,,http://pypi.python.org/packages/source/b/buildout.extensionscripts/buildout.extensionscripts-1.0.zip#md5=2fa09cd0577076ee6bfc5edc4bd1d757,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,unknown,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\celery.ABOUT,celery,3.0.13,celery-3.0.13.tar.gz,,2013-02-03,,https://github.com/celery/celery,http://pypi.python.org/packages/source/c/celery/celery-3.0.13.tar.gz#md5=5ca2ed5f71203240c4e7894f4aa7a77d,,,,,,,,,,,,,Celery Project,,,,,,,celery.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\collective.recipe.cmd.ABOUT,collective.recipe.cmd,,collective.recipe.cmd-0.6.tar.gz,,,,https://github.com/collective/collective.recipe.cmd,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,{},"{('version', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\collective.recipe.omelette.ABOUT,collective.recipe.omelette,0.16,collective.recipe.omelette-0.16.zip,,,,https://github.com/collective/collective.recipe.omelette,https://pypi.python.org/packages/source/c/collective.recipe.omelette/collective.recipe.omelette-0.16.zip#md5=16477973ebcdf3c70c3fdb3fb2192595n,,,,,,,,,,the Pypi page list this as GPL-licensed which is not consistent with the code license.,,,,,,,,,,,https://github.com/collective/collective.recipe.omelette/blob/master/collective/recipe/omelette/__init__.py,,,,,,,,,,,,,zpl-2.1,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\collective.recipe.template.ABOUT,collective.recipe.template,,collective.recipe.template-1.9.zip,,,,https://github.com/collective/collective.recipe.template,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,{},"{('version', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\distribute.ABOUT,,,distribute-0.6.34.tar.gz,,2013-01-08,,,http://pypi.python.org/packages/source/d/distribute/distribute-0.6.34.tar.gz#md5=4576ab843a6db5100fb22a72deadf56d,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,{},"{('version', ''): 'Mandatory field is missing', ('name', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\distribute_setup.py.ABOUT,,,distribute_setup.py,,2013-01-08,,,http://python-distribute.org/distribute_setup.py,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,{},"{('version', ''): 'Mandatory field is missing', ('name', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-adminactions.ABOUT,django-adminactions,0.1 ,django-adminactions-0.1.tar.gz,,,,https://github.com/saxix/django-adminactions,https://github.com/saxix/django-adminactions/archive/313871cb598b5f9d4797a5afc23773d006e87ba9.tar.gz,,,,,,,,,,,,,Stefano Apostolico,"Copyright (c) 2010, Stefano Apostolico (s.apostolico@gmail.com)",,,,,,django-adminactions.LICENSE,,,,git,https://github.com/saxix/django-adminactions.git,,,,313871cb598b5f9d4797a5afc23773d006e87ba9,,,,,mit,,"{('license_note', 'Dual licensed under the MIT or GPL Version 2 licenses.'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-autocomplete-light.ABOUT,django-autocomplete-light,1.1.20,,,,,,https://pypi.python.org/packages/source/d/django-autocomplete-light/django-autocomplete-light-1.1.20.tar.gz#md5=25f9015e0562ec8cdacf4f2998d3d5d9,,,,,,,,,,,,,,,,,,,,django-autocomplete-light.LICENSE ,,,,,,,,,,,,,,mit,,"{('about_file', ''): 'No about file is found.'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-background-task.ABOUT,,,django-background-task-0.1.6.zip,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,{},"{('version', ''): 'Mandatory field is missing', ('name', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-celery.ABOUT,django-celery,3.0.11,django-celery-3.0.11.tar.gz,,2013-02-03,,https://github.com/celery/django-celery/,http://pypi.python.org/packages/source/d/django-celery/django-celery-3.0.11.tar.gz#md5=2861a1074ebb7608de2eab883097781f,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,bsd-new,,"{('liense_file', 'django-celery.LICENSE'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-db-signals.ABOUT,django-db-signals,0.1.1,django-db-signals-0.1.1.tar.gz,,2013-02-03,,https://github.com/bradleyayers/django-db-signals,http://pypi.python.org/packages/source/d/django-db-signals/django-db-signals-0.1.1.tar.gz#md5=7abdb1bf809cbd549426d01edf255a34,,,,,,,,,,,,,,,,,,,,django-db-signals.LICENSE,,,,,,,,,,,,,,bsd-simplified,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-debug-toolbar.ABOUT,django-debug-toolbar,0.9.4,django-debug-toolbar-0.9.4.tar.gz,,,,https://github.com/django-debug-toolbar/django-debug-toolbar,http://pypi.python.org/packages/source/d/django-debug-toolbar/django-debug-toolbar-0.9.4.tar.gz#md5=85c1c70a31f0a3a646a603214d235e9f,,,,,,,,,,,,,,,,,,,,django-debug-toolbar.LICENSE,,,,git,https://github.com/django-debug-toolbar/django-debug-toolbar.git,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-extensions.ABOUT,django-extensions,1.0.1,django-extensions-1.0.1.tar.gz,,,,https://github.com/django-extensions/django-extensions,http://pypi.python.org/packages/source/d/django-extensions/django-extensions-1.0.1.tar.gz#md5=8c968c3260ec0faaeb5760f28650c9ce,,,,,,,,,,,,,Michael Trier,Copyright (c) 2007 Michael Trier,,,,,,django-extensions.LICENSE,,,,git,https://github.com/django-extensions/django-extensions.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-forkit.ABOUT,django-forkit,0.9.5,django-forkit-0.9.5.tar.gz,,,,,http://pypi.python.org/packages/source/d/django-forkit/django-forkit-0.9.5.tar.gz,,,,,,,,,,,,,,Copyright (c) The Children's Hospital of Philadelphia and individual contributors.,,,,,,django-forkit.LICENSE,https://raw.github.com/cbmi/django-forkit/f575d3055ee884e60c5b3a28441f971292c3e505/LICENSE,,,,,,,,,,,,,bds-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-grappelli.ABOUT,grappelli,2.4.4,django-grappelli-2.4.4.tar.gz,,,,https://github.com/sehmaschine/django-grappelli,https://pypi.python.org/packages/source/d/django-grappelli/django-grappelli-2.4.4.tar.gz#md5=aa9695ca672de45fa2339877eea11e36,,,,,,,,,,,,,,,,,,,,django-grappelli.LICENSE,,"BSD-3-Clause, MIT",,,,,,,,,,,,"bsd-new, mit",,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-haystack-panel.ABOUT,django-haystack-panel,0.1.3,django-haystack-panel-0.1.3.tar.gz,,2013-2-23,,https://github.com/streeter/django-haystack-panel,https://pypi.python.org/packages/source/d/django-haystack-panel/django-haystack-panel-0.1.3.tar.gz#md5=d0dc787a92e3c78805a67d0fe002df8c,,,,,,,,,,,,,,,,,,,,,,,,git,https://github.com/streeter/django-haystack-panel.git,,,,,,,,,mit,,"{('license_file', 'django-haystack-panel.LICENSE'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-haystack.ABOUT,django-haystack,2.0.0-beta,django-haystack-2.0.0-beta.tar.gz,,2013-02-07,,https://github.com/toastdriven/django-haystack,https://github.com/toastdriven/django-haystack/archive/f0eacb902b07d31846211adb717fe205bca8c59d.tar.gz,,,,,,,,,,,,,,,,,,,,django-haystack.LICENSE,,,,git,https://github.com/toastdriven/django-haystack.git,,,,,,,,,bsd-new,,"{('scm_version', 'f0eacb902b07d31846211adb717fe205bca8c59d'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-nose.ABOUT,django-nose,1.1,django-nose-1.1.tar.gz,,2012-12-19,,https://github.com/jbalogh/django-nose/,http://pypi.python.org/packages/source/d/django-nose/django-nose-1.1.tar.gz#md5=606ab8a582f1d2037b048b7c4e246e85,,,,,,,,,,,,,Jeff Balogh,,,,,,,django-nose.LICENSE,https://github.com/jbalogh/django-nose/blob/1.1/LICENSE,,,git,https://github.com/jbalogh/django-nose.git,,,,,,,,,bsd-new,,"{('maintainer', 'Erik Rose'): 'This is not a mandatory or optional field. It will be ignored', ('author_contact', 'me@jeffbalogh.org'): 'This is not a mandatory or optional field. It will be ignored', ('maintainer_contact', 'erikrose@grinchcentral.com'): 'This is not a mandatory or optional field. It will be ignored', ('author', 'Jeff Balogh'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-registration.ABOUT,django-registration,0.8,django-registration-0.8.tar.gz,,,,https://bitbucket.org/ubernostrum/django-registration/,http://pypi.python.org/packages/source/d/django-registration/django-registration-0.8.tar.gz#md5=d3e785858e0040a6c3201acd43409b2e,,,,,,,,,,,,,,,,,,,,django-registration.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-reversion.ABOUT,django-reversion,1.6.6,,,,,https://github.com/etianen/django-reversion,https://pypi.python.org/packages/source/d/django-reversion/django-reversion-1.6.6.tar.gz#md5=ebc1fd89768170a33e5c78298c852400,,,,,,,,,,,,,,,,,,,,django-reversion.LICENSE,,,,,,,,,,,,,,bsd-new,,"{('about_file', ''): 'No about file is found.'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-tastypie.ABOUT,django-tastypie,0.9.11,,,,,https://github.com/toastdriven/django-tastypie/,http://pypi.python.org/packages/source/d/django-tastypie/django-tastypie-0.9.11.tar.gz#md5=711b29265917405c226f4594782e7e9b,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,bsd-new,,"{('about_file', ''): 'No about file is found.', ('doc', 'http://django-tastypie.readthedocs.org/'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django-template-repl.ABOUT,django-template-repl,0.3.0,django-template-repl-0.3.0.tar.gz,,,,https://github.com/codysoyland/django-template-repl,http://pypi.python.org/packages/source/d/django-template-repl/django-template-repl-0.3.0.tar.gz#md5=389e7df1b844905c6b3653d5a44854a5,,,,,,,,,,,,,,,,,,,,django-template-repl.LICENSE,,,,git,https://github.com/codysoyland/django-template-repl.git,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\django.ABOUT,Django,1.4.5,Django-1.4.5.tar.gz,,,,http://www.djangoproject.com/,https://pypi.python.org/packages/source/D/Django/Django-1.4.5.tar.gz#md5=851d00905eb70e4aa6384b3b8b111fb7,,,,,,,,,,,,,Django Software Foundation,,,,,,,django.LICENSE,,,,git,https://github.com/django/django.git,,,,1f0af3c529885beca39e0d4981fb4794ef3102c2,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\djangorecipe.ABOUT,djangorecipe,1.4,djangorecipe-1.4.zip,,,,https://github.com/rvanlaar/djangorecipe,http://pypi.python.org/packages/source/d/djangorecipe/djangorecipe-1.4.zip#md5=fdfadd561672ab285dbbe35e7d81ca93,,,,,,,,,,,,roland@micite.net,Roland van Laar,,,,,,,,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\fabric.ABOUT,Fabric,1.4.3,Fabric-1.4.3.tar.gz,,,,http://fabfile.org,http://pypi.python.org/packages/source/F/Fabric/Fabric-1.4.3.tar.gz#md5=80925ab37f7bab81ab50f0cc90d39a91,,fabric.README,,,,,,,,,,,Christian Vest Hansen and Jeffrey E. Forcier,"Copyright (c) 2009, Christian Vest Hansen and Jeffrey E. Forcier",,,,,,fabric.LICENSE,,,,git ,https://github.com/fabric/fabric.git,,,,,,,,,bsd-new,,"{('author_file', 'fabric.AUTHORS'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\hexagonit.recipe.download.ABOUT,hexagonit.recipe.download,1.6,hexagonit.recipe.download-1.6.zip,,,,https://github.com/hexagonit/hexagonit.recipe.download,http://pypi.python.org/packages/source/h/hexagonit.recipe.download/hexagonit.recipe.download-1.6.zip#md5=185501d1ff6d3885b3d5edb155d07206,,,,,,,,,,"only reported as ZPL, zpl-2.1 is assumed.",,,,,,,,,,,https://github.com/hexagonit/hexagonit.recipe.download/blob/master/setup.py,,,,,,,,,,,,,zpl-2.1,,"{('author', 'Kai Lautaportti'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\honcho.ABOUT,honcho,0.4.0devwin,honcho-0.4.0devwin.zip,,,Honcho: a python clone of Foreman. For managing Procfile-based applications.,https://github.com/nickstenning/honcho,https://github.com/pombredanne/honcho/archive/108a3a073f308f1d51491234d38730c0207beb7a.zip,,,,,,,,,,"Patched to support Windows, there is a pull request pending with the upstream project",,nick@whiteink.com,Nick Stenning,,,,,,,honcho.LICENSE,,,,git,https://github.com/pombredanne/honcho.git,,,,108a3a073f308f1d51491234d38730c0207beb7a,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\importlib.ABOUT,importlib,1.0.2,importlib-1.0.2.zip,,,Backport of importlib.import_module() from Python 2.7,,http://pypi.python.org/packages/source/i/importlib/importlib-1.0.2.zip,,,,,,,,,,,,brett@python.org,Python Software Foundation ,,,,,,,PSF.LICENSE,,,,svn,http://svn.python.org/view/sandbox/trunk/importlib,,,,,,,,,psf,,"{('author', 'Brett Cannon'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\ipdb.ABOUT,ipdb,0.7,ipdb-0.7.tar.gz,,,a debugger for Python,,http://pypi.python.org/packages/source/i/ipdb/ipdb-0.7.tar.gz,,,,,,,,,,this is used only as a development tool,,,,,,,,,,ipdb.LICENSE,,,,,,,,,,,,,,gpl-2.0,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\ipython.ABOUT,ipython,0.13.1,ipython-0.13.1.zip,,,a better shell and interpreter for Python,,http://pypi.python.org/packages/source/i/ipython/ipython-0.13.1.zip,,,,,,,,,,"this is used only as a development tool. - It contains code with other origins and licenses, not detailed here.",,,,"Copyright (c) 2008-2011, IPython Development Team. - Copyright (c) 2001-2007, Fernando Perez - Copyright (c) 2001, Janko Hauser - Copyright (c) 2001, Nathaniel Gray ",,,,,,ipython.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\Jinja2.ABOUT,Werkzeug,2.6,Jinja2-2.6.tar.gz,,,,http://jinja.pocoo.org/2/,https://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.6.tar.gz#md5=1c49a8825c993bfdcf55bb36897d28a2,,,,,,,,,,,,,,,,,,,,,,,,git,https://github.com/mitsuhiko/jinja2.git,,,,,,,,,bsd-new,,"{('author', 'Armin Ronacher'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\kombu.ABOUT,kombu,2.5.4,kombu-2.5.4.tar.gz,,,,,http://pypi.python.org/packages/source/k/kombu/kombu-2.5.4.tar.gz,,,,,,,,,,,,,Celery Project,"Copyright (c) 2012 VMware, Inc. All rights reserved. - Copyright (c) 2009-2012, Ask Solem & contributors. - All rights reserved.",,,,,,kombu.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\meld3.ABOUT,meld3,0.6.8,meld3-0.6.8.tar.gz,,,meld3 templating system used by Supervisor,https://github.com/supervisor/meld3,http://pypi.python.org/packages/source/m/meld3/meld3-0.6.8.tar.gz#md5=94b1591e518909e239fc17777db3c852,,,,,,,,,,,,,Supervisor,,,,,,,meld3.LICENSE,https://github.com/Supervisor/meld3/blob/master/LICENSE.txt,,,git,https://github.com/Supervisor/meld3git,,,,,,,,,zpl-2.11,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\mimeparse.ABOUT,mimeparse,0.1.3,mimeparse-0.1.3.tar.gz,,,,http://code.google.com/p/mimeparse/,http://pypi.python.org/packages/source/m/mimeparse/mimeparse-0.1.3.tar.gz#md5=03ce207391454db37279e78ce2112365,,,,,,,,,,,,,,,,,,,,mimeparse.LICENSE,http://mimeparse.googlecode.com/svn/trunk/LICENSE,,,svn,http://mimeparse.googlecode.com/svn/trunk/,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\mock.ABOUT,mock,1.0.1,mock-1.0.1.zip,,,,http://www.voidspace.org.uk/python/mock/,http://pypi.python.org/packages/source/m/mock/mock-1.0.1.zip,,,,,,,,,,,,,,"Copyright (c) 2003-2012, Michael Foord",,,,,,mock.LICENSE,http://www.voidspace.org.uk/python/license.shtml,,,,,,,,,,,,,bsd-simplified,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\nose-pudb.ABOUT,nose-pudb,1.2.1,nose-pudb-0.1.2.tar.gz,,,,http://github.com/akaihola/nose-pudb,http://pypi.python.org/packages/source/n/nose-pudb/nose-pudb-0.1.2.tar.gz,,,,,,,,,,,,akaihol+python@ambitone.com,Antti Kaihola,,,,,,,nose-pudb.LICENSE,,,,git,https://github.com/akaihola/nose-pudb.git,,,,,,,,,lgpl-2.1,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\nose-selecttests.ABOUT,nose-selecttests,0.3,nose-selecttests-0.3.zip,,,,https://github.com/iElectric/nose-selecttests,http://pypi.python.org/packages/source/n/nose-selecttests/nose-selecttests-0.3.zip,,,,,,,,,,,,,,"Copyright (c) 2012, Domen Kožar",,,,,,nose-selecttests.LICENSE,https://github.com/iElectric/nose-selecttests/blob/master/LICENSE,,,git,https://github.com/iElectric/nose-selecttests.git,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\nose.ABOUT,nose,1.2.1,nose-1.2.1.tar.gz,,2012-12-19,,https://nose.readthedocs.org/en/latest/,http://pypi.python.org/packages/source/n/nose/nose-1.2.1.tar.gz#md5=735e3f1ce8b07e70ee1b742a8a53585a,,,,,,,,,,,,,,,,,,,,nose.LICENSE,https://github.com/nose-devs/nose/blob/release_1.2.1/lgpl.txt,,,git,https://github.com/nose-devs/nose.git,,,,,,,,,lgpl-2.1,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\ntfsutils.ABOUT,ntfsutils,0.1.2,ntfsutils-0.1.2.tar.gz,,,,https://github.com/sid0/ntfs,http://pypi.python.org/packages/source/n/ntfsutils/ntfsutils-0.1.2.tar.gz#md5=31cb371762796b3016dbb9862a91ee98,,,,,,,,,,this is used by Omelette on Windows.,,,,"Copyright (c) 2012-2013, the Mozilla Foundation and others. All rights reserved.",,"Use of this source code is governed by the Simplified BSD License which can - be found in the LICENSE file.",,,,,https://raw.github.com/sid0/ntfs/master/LICENSE,,,git,https://github.com/sid0/ntfs.git,,,,,,,,,bsd-simplified,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\ordereddict.ABOUT,orderdict,1.1,ordereddict-1.1.tar.gz,,,,http://pypi.python.org/pypi/ordereddict/,http://pypi.python.org/packages/source/o/ordereddict/ordereddict-1.1.tar.gz#md5=a0ed854ee442051b249bfad0f638bbec,,,,,,,,,,,,,,,,,,,,ordereddict.LICENSE,,,,,,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pip.ABOUT,pip,1.2.1,pip-1.2.1.tar.gz,,2013-01-08,,http://www.pip-installer.org,http://pypi.python.org/packages/source/p/pip/pip-1.2.1.tar.gz#md5=db8a6d8a4564d3dc7f337ebed67b1a85,,,,,,,,,,,,python-virtualenv@groups.google.com,The pip developers,,,,,,,pip.LICENSE,,,,,,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\psycopg2.ABOUT,psycopg2,2.4.6,psycopg2-2.4.6.tar.gz,,,a database adapter for PosgtreSQL,http://initd.org/psycopg/,http://pypi.python.org/packages/source/p/psycopg2/psycopg2-2.4.6.tar.gz#md5=79d7f05e67bf70a0ecc6e9103ccece5f,,,,,,,,,,"Newer versions are LGPL 3.0 with otehr expections for OpenSSL. - It includes OpenSSL. ",,,,,,,psycopg2.NOTICE,,,psycopg2.LICENSE,,,,git,git://luna.dndg.it/public/psycopg2.git,,,,,,,,,lgpl-3.0,,"{('notice_file', 'psycopg2.NOTICE'): ""The 'notice_file' 'psycopg2.NOTICE' does not exist in the directory""}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pudb.ABOUT,pudb,2012.3,pudb-2012.3.tar.gz,,,"A full-screen, console-based Python debugger",http://mathema.tician.de/software/pudb,http://pypi.python.org/packages/source/p/pudb/pudb-2012.3.tar.gz#md5=d07d6622901b1addeb64e833fea5eb9b,,,,,,,,,,"no license text was found. a license was created based on the MIT - template at http://opensource.org/licenses/MIT",,inform@tiker.net,Andreas Kloeckner,,,,,,,pudb.LICENSE,,,,git,https://github.com/inducer/pudb.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pycrypto.ABOUT,pycrypto,2.3,pycrypto-2.3.tar.gz,,,,https://www.dlitz.net/software/pycrypto/,http://pypi.python.org/packages/source/p/pycrypto/pycrypto-2.3.tar.gz#md5=2b811cfbfc342d83ee614097effb8101,,,,,,,,,,,,,,,,,,,,pycrypto.LICENSE,,,,git,https://github.com/dlitz/pycrypto.git,,,,,,,,,public-domain,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pyelasticsearch.ABOUT,pyelasticsearch,0.3,pyelasticsearch-0.3.tar.gz,,,,,http://pypi.python.org/packages/source/p/pyelasticsearch/pyelasticsearch-0.3.tar.gz,,,,,,,,,,,,,,,,,,,,pyelasticsearch.LICENSE,,,,git,https://github.com/rhec/pyelasticsearch.git,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pygments.ABOUT,Pygments,0.2,Pygments-1.6rc1.tar.gz,,,,http://pygments.org/,http://pypi.python.org/packages/source/P/Pygments/Pygments-1.6rc1.tar.gz,,,,,,,,,,,,,,,,,,,,pygments.LICENSE,,,,hg,http://bitbucket.org/birkenfeld/pygments-main,,,,,,,,,bsd-new,,"{('author_file', 'pygments.AUTHORS'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pyreadline-1.7.1.zip.ABOUT,pyreadline,1.7.1,pyreadline-1.7.1.zip,,2012-12-17,,,http://pypi.python.org/packages/source/p/pyreadline/pyreadline-1.7.1.zip#md5=293d4e8794f867c122d4290ccc84be8d,,,,,,,,,,,,,,,,,,,,pyreadline.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pyreadline-win-amd64.ABOUT,pyreadline,1.7.1,pyreadline-1.7.1.win-amd64.exe,,2012-12-17,,,http://pypi.python.org/packages/any/p/pyreadline/pyreadline-1.7.1.win-amd64.exe#md5=3b6fe4cf936a6094eac2ad793b311fe1,,,,,,,,,,,,,,,,,,,,pyreadline.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pyreadline-win32.ABOUT,pyreadline,1.7.1,pyreadline-1.7.1.win32.exe,,2012-12-17,,,http://pypi.python.org/packages/any/p/pyreadline/pyreadline-1.7.1.win32.exe#md5=ffe3987562d0891901ebccdd94933a39,,,,,,,,,,,,,,,,,,,,pyreadline.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\python-dateutil.ABOUT,python-dateutil,1.5,python-dateutil-1.5.tar.gz,,,,http://labix.org/python-dateutil,http://pypi.python.org/packages/source/p/python-dateutil/python-dateutil-1.5.tar.gz#md5=0dcb1de5e5cad69490a3b6ab63f0cfa5,,,,,,,,,,,,,,,,,,,,python-dateutil.LICENSE,,,,,,,,,,,,,,bsd-new,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\pytz.ABOUT,pytz,2012j,pytz-2012j.tar.gz,,,,http://pytz.sourceforge.net/,http://pypi.python.org/packages/source/p/pytz/pytz-2012j.tar.gz#md5=c79fb939fa742a5a8824d3e1661d2baa,,,,,,,,,,,,stuart@stuartbishop.net,Stuart Bishop,,,,,,,pytz.LICENSE,,,,bzr,lp:pytz,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\requests.ABOUT,requests,1.10,requests-1.1.0.tar.gz,,2013-01-15,,http://python-requests.org,http://pypi.python.org/packages/source/r/requests/requests-1.1.0.tar.gz#md5=a0158815af244c32041a3147ee09abf3,,,,,,,,,,,,,,,,,requests.NOTICE,,,,,,,git,https://github.com/kennethreitz/requests.git,,,,,,,,,apache-2.0,,"{('documentation_url', 'http://docs.python-requests.org/en/latest/index.html'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\simplejson.ABOUT,simplejson,3.0.7,simplejson-3.0.7.tar.gz,,,,https://github.com/simplejson/simplejson,http://pypi.python.org/packages/source/s/simplejson/simplejson-3.0.7.tar.gz#md5=f674e9035aee1064dae90b22aa76ea98,,,,,,,,,,,,,,,,,,,,simplejson.LICENSE,https://raw.github.com/simplejson/simplejson/master/LICENSE.txt,,,git,https://github.com/simplejson/simplejson.git,,,,,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\south.ABOUT,south,0.7.6,South-0.7.6.tar.gz,,,,http://south.aeracode.org/,http://pypi.python.org/packages/source/S/South/South-0.7.6.tar.gz,,,,,,,,,,,,,,,,,,,,south.LICENSE,,,,,,,,,,,,,,apache-2.0,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\ssh.ABOUT,,,ssh-1.7.14.tar.gz,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,{},"{('version', ''): 'Mandatory field is missing', ('name', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\supervisor.ABOUT,Supervisor,3.0a12,supervisor-3.0a12.tar.gz,,,,http://supervisord.org/,http://pypi.python.org/packages/source/s/supervisor/supervisor-3.0a12.tar.gz#md5=eb2ea5a2c3b665ba9277d17d14584a25,,,,,,,,,,The overall license is a BSD like and embedded code has bsd-style and zpl-2.1 licenses,,,,,,,,,,supervisor.LICENSE,https://github.com/Supervisor/supervisor/blob/master/LICENSES.txt,,,git,https://github.com/Supervisor/supervisor.git,,,,,,,,,supervisor,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\urwid.ABOUT,urwid,1.1.1,urwid-1.1.1.tar.gz,,,,http://excess.org/urwid/,http://pypi.python.org/packages/source/u/urwid/urwid-1.1.1.tar.gz#md5=932d199de6fc847eab2c151512220665,,,,,,,,,,,,,,,,,,,,urwid.LICENSE,,,,,,,,,,,,,,lgpl-2.1,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\virtualenv.py.ABOUT,virtualenv,1.8.54.post1,virtualenv.py,,2013-01-25,,http://virtualenv.org/,https://github.com/pypa/virtualenv/raw/29cb45ea7be02218df36dff41242fe7790cb4007/virtualenv.py,,,,,,,,,,"this python file contains setuptools, distribute and pip as encoded byte arrays. ",,,,"Copyright (c) 2007 Ian Bicking and Contributors - Copyright (c) 2009 Ian Bicking, The Open Planning Project - Copyright (c) 2011-2012 The virtualenv developers",,,,,,virtualenv.LICENSE,https://raw.github.com/pypa/virtualenv/develop/LICENSE.txt,,,git,https://github.com/pypa/virtualenv.git,,,,29cb45ea7be02218df36dff41242fe7790cb4007,,,,,mit,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\Werkzeug.ABOUT,Werkzeug,0.8.3,Werkzeug-0.8.3.tar.gz,,,,http://werkzeug.pocoo.org,http://pypi.python.org/packages/source/W/Werkzeug/Werkzeug-0.8.3.tar.gz#md5=12aa03e302ce49da98703938f257347a,,,,,,,,,,,,,,,,,,,,Werkzeug.LICENSE,,,,git,https://github.com/mitsuhiko/werkzeug.git,,,,,,,,,bsd-new,,"{('author_file', 'Werkzeug.AUTHORS'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\z3c.recipe.usercrontab.ABOUT,z3c.recipe.usercrontab,1.1,z3c.recipe.usercrontab-1.1.tar.gz,,,,,http://pypi.python.org/packages/source/z/z3c.recipe.usercrontab/z3c.recipe.usercrontab-1.1.tar.gz#md5=a77d83a730289797cfb37845508b87f4,,,,,,,,,,"Original authors: Jasper Spaans and Jan-Jaap Driessen at The Health Agency. - Most recent versions: Reinout van Rees, also at The Health Agency",,,The Health Agency,,,,z3c.recipe.usercrontab.NOTICE,,,z3c.recipe.usercrontab.LICENSE,,,,svm,,,,,,,,,,zpl-2.1,,"{('homepage_url', 'http://pypi.python.org/pypi/z3c.recipe.usercrontab/'): 'This is not a mandatory or optional field. It will be ignored', ('scm_url', 'http://svn.zope.org/z3c.recipe.usercrontab/trunk/'): 'This is not a mandatory or optional field. It will be ignored'}",{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\zc.buildout-eb824e.ABOUT,zc.buildout,,zc.buildout-eb824e.zip,,,source export for zc.buildout 2.1.0dev1,http://www.buildout.org/,https://github.com/pombredanne/buildout/archive/eb824e0a6c7b9591989fafe0ece295976f7fbd8b.zip,,,,,,,,,,"To build the actual package, extract and run python setup.py sdist. - The dist dir contains the built distribution. - This is an advanced patched version of buildout 2.x including support - for header expressions, a pull request not yet included in the buildout trunk - See https://github.com/buildout/buildout/pull/76 . This package was built - from sources with: python setup.py sdist",,,,,,,,,,zc.buildout.LICENSE,,,,git,https://github.com/pombredanne/buildout.git,,2.1.0dev1,,eb824e0a6c7b9591989fafe0ece295976f7fbd8b,,,,,zpl-2.1,,{},"{('version', ''): 'Mandatory field is missing'}" -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\zc.buildout.ABOUT,zc.buildout,2.1.0dev1,zc.buildout-2.1.0dev1.tar.gz,,,,http://www.buildout.org/,https://github.com/pombredanne/buildout/archive/eb824e0a6c7b9591989fafe0ece295976f7fbd8b.zip,,,,,,,,,,"this is an advanced patched version of buildout 2.x including support - for header expressions, a pull request not yet included in the buildout trunk - See https://github.com/buildout/buildout/pull/76 . This package was built - from sources with: python setup.py sdist",,,,,,,,,,zc.buildout.LICENSE,,,,,,,,,,,,,,zpl-2.1,,{},{} -C:\Users\CYL\nexBWorkspace\about\thirdparty-testdata\dist\zc.recipe.egg.ABOUT,zc.recipe.egg,2.0.0a3,zc.recipe.egg-2.0.0a3.tar.gz,,,,http://pypi.python.org/pypi/zc.recipe.egg/,http://pypi.python.org/packages/source/z/zc.recipe.egg/zc.recipe.egg-2.0.0a3.tar.gz,,,,,,,,,,,,,,,,,,,,zc.recipe.egg.LICENSE,,,,git,https://github.com/buildout/buildout.git,,,,,,,,,zpl-2.1,,{},{} diff --git a/tests/testdata/parse/basic.about b/tests/testdata/parse/basic.about deleted file mode 100644 index d7aee78d..00000000 --- a/tests/testdata/parse/basic.about +++ /dev/null @@ -1,2 +0,0 @@ -single_line:optional -other_field: value diff --git a/tests/testdata/parse/complex.about b/tests/testdata/parse/complex.about deleted file mode 100644 index f4cb2da2..00000000 --- a/tests/testdata/parse/complex.about +++ /dev/null @@ -1,10 +0,0 @@ -single_line:optional -other_field: value - - -multi_line : some value - and more - and yet more - -yetanother : - sdasd \ No newline at end of file diff --git a/tests/testdata/parse/continuation.about b/tests/testdata/parse/continuation.about deleted file mode 100644 index 1c7c22b7..00000000 --- a/tests/testdata/parse/continuation.about +++ /dev/null @@ -1,5 +0,0 @@ -single_line:optional -other_field: value -multi_line : some value - and more - and yet more diff --git a/tests/testdata/parse/invalid_continuation.about b/tests/testdata/parse/invalid_continuation.about deleted file mode 100644 index e7e837ed..00000000 --- a/tests/testdata/parse/invalid_continuation.about +++ /dev/null @@ -1,8 +0,0 @@ - invalid continuation1 -single_line:optional -other_field: value - -multi_line : some value - and more - - invalid continuation2 diff --git a/tests/testdata/parse/license_text.LICENSE b/tests/testdata/parse/license_text.LICENSE deleted file mode 100644 index 53087fd6..00000000 --- a/tests/testdata/parse/license_text.LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -Tester holds the copyright for test component. Tester relinquishes copyright of -this software and releases the component to Public Domain. - -* Email Test@tester.com for any questions \ No newline at end of file diff --git a/tests/testdata/parse/notice_text.NOTICE b/tests/testdata/parse/notice_text.NOTICE deleted file mode 100644 index fce3b8d5..00000000 --- a/tests/testdata/parse/notice_text.NOTICE +++ /dev/null @@ -1 +0,0 @@ -Test component is released to Public Domain. \ No newline at end of file diff --git a/tests/testdata/parser_tests/COPYING b/tests/testdata/parser_tests/COPYING deleted file mode 100644 index 7d363c1e..00000000 --- a/tests/testdata/parser_tests/COPYING +++ /dev/null @@ -1 +0,0 @@ -test file for _validate_file_fields test \ No newline at end of file diff --git a/tests/testdata/parser_tests/about_file_empty_value_for_dje_license_license_text_file.ABOUT b/tests/testdata/parser_tests/about_file_empty_value_for_dje_license_license_text_file.ABOUT deleted file mode 100644 index 9424637f..00000000 --- a/tests/testdata/parser_tests/about_file_empty_value_for_dje_license_license_text_file.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -name: Apache HTTP Server -version: 2.4.3 - -dje_license: -license_text_file: -dje_license_name: \ No newline at end of file diff --git a/tests/testdata/parser_tests/about_file_no_dje_license_no_license_text_file_keys.ABOUT b/tests/testdata/parser_tests/about_file_no_dje_license_no_license_text_file_keys.ABOUT deleted file mode 100644 index 81e66f80..00000000 --- a/tests/testdata/parser_tests/about_file_no_dje_license_no_license_text_file_keys.ABOUT +++ /dev/null @@ -1,2 +0,0 @@ -name: Apache HTTP Server -version: 2.4.3 diff --git a/tests/testdata/parser_tests/about_file_ref.c.ABOUT b/tests/testdata/parser_tests/about_file_ref.c.ABOUT deleted file mode 100644 index 951f2a09..00000000 --- a/tests/testdata/parser_tests/about_file_ref.c.ABOUT +++ /dev/null @@ -1,3 +0,0 @@ -name: Apache HTTP Server -version: 2.4.3 -notice_file: \ No newline at end of file diff --git a/tests/testdata/parser_tests/dupe_field_name.ABOUT b/tests/testdata/parser_tests/dupe_field_name.ABOUT deleted file mode 100644 index 70b43f7a..00000000 --- a/tests/testdata/parser_tests/dupe_field_name.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: Apache HTTP Server -version: 2.4.3 -name: Apache HTTP Server dupe -about_resource: about_file_ref.c \ No newline at end of file diff --git a/tests/testdata/parser_tests/missing_mand.ABOUT b/tests/testdata/parser_tests/missing_mand.ABOUT deleted file mode 100644 index 7d9a52d2..00000000 --- a/tests/testdata/parser_tests/missing_mand.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -download_url: http://archive.apache.org/dist/httpd/httpd-2.4.3.tar.gz -date: 2012-08-21 -license_spdx: Apache-2.0 -license_text_file: httpd.LICENSE -copyright: Copyright 2012 The Apache Software Foundation. -notice_file: httpd.NOTICE \ No newline at end of file diff --git a/tests/testdata/parser_tests/missing_mand_values.ABOUT b/tests/testdata/parser_tests/missing_mand_values.ABOUT deleted file mode 100644 index 16e591ea..00000000 --- a/tests/testdata/parser_tests/missing_mand_values.ABOUT +++ /dev/null @@ -1,9 +0,0 @@ -name: -about_resource: -download_url: http://archive.apache.org/dist/httpd/httpd-2.4.3.tar.gz -version: -date: 2012-08-21 -license_spdx: Apache-2.0 -license_text_file: httpd.LICENSE -copyright: Copyright 2012 The Apache Software Foundation. -notice_file: httpd.NOTICE \ No newline at end of file diff --git a/tests/testdata/parser_tests/test.ABOUT b/tests/testdata/parser_tests/test.ABOUT deleted file mode 100644 index f8e6c75f..00000000 --- a/tests/testdata/parser_tests/test.ABOUT +++ /dev/null @@ -1,9 +0,0 @@ -name: Apache HTTP Server -homepage_url: http://httpd.apache.org -download_url: http://archive.apache.org/dist/httpd/httpd-2.4.3.tar.gz -version: 2.4.3 -date: 2012-08-21 -license_spdx: Apache-2.0 -license_text_file: httpd.LICENSE -copyright: Copyright 2012 The Apache Software Foundation. -notice_file: httpd.NOTICE \ No newline at end of file diff --git a/tests/testdata/spdx_licenses/incorrect_spdx.about b/tests/testdata/spdx_licenses/incorrect_spdx.about deleted file mode 100644 index 235ce5ce..00000000 --- a/tests/testdata/spdx_licenses/incorrect_spdx.about +++ /dev/null @@ -1,5 +0,0 @@ -about_resource: . -license_spdx: nothing_here - -name: incorrect -version: blank \ No newline at end of file diff --git a/tests/testdata/spdx_licenses/invalid_multi_format_spdx.ABOUT b/tests/testdata/spdx_licenses/invalid_multi_format_spdx.ABOUT deleted file mode 100644 index ee0734b8..00000000 --- a/tests/testdata/spdx_licenses/invalid_multi_format_spdx.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: . -version: 2011-04-12 - -name: multi - -license_spdx: Aladdin, Apache-2.0 \ No newline at end of file diff --git a/tests/testdata/spdx_licenses/invalid_multi_name.ABOUT b/tests/testdata/spdx_licenses/invalid_multi_name.ABOUT deleted file mode 100644 index d6bb3338..00000000 --- a/tests/testdata/spdx_licenses/invalid_multi_name.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: . -version: 2011-04-12 - -name: multi - -license_spdx: Something and SomeOtherThings \ No newline at end of file diff --git a/tests/testdata/spdx_licenses/lower_case_spdx.ABOUT b/tests/testdata/spdx_licenses/lower_case_spdx.ABOUT deleted file mode 100644 index 9255ccbf..00000000 --- a/tests/testdata/spdx_licenses/lower_case_spdx.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: . - -name: test -version: test - -license_spdx: apache-2.0 diff --git a/tests/testdata/spdx_licenses/multi_and.ABOUT b/tests/testdata/spdx_licenses/multi_and.ABOUT deleted file mode 100644 index c9a9b705..00000000 --- a/tests/testdata/spdx_licenses/multi_and.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: . -version: 2011-04-12 - -name: multi - -license_spdx: Aladdin and Apache-2.0 \ No newline at end of file diff --git a/tests/testdata/spdx_licenses/multi_or.ABOUT b/tests/testdata/spdx_licenses/multi_or.ABOUT deleted file mode 100644 index 44715325..00000000 --- a/tests/testdata/spdx_licenses/multi_or.ABOUT +++ /dev/null @@ -1,6 +0,0 @@ -about_resource: . -version: 2011-04-12 - -name: multi - -license_spdx: aladdin OR Apache-2.0 \ No newline at end of file diff --git a/tests/testdata/spdx_licenses/test.ABOUT b/tests/testdata/spdx_licenses/test.ABOUT deleted file mode 100644 index 931921ba..00000000 --- a/tests/testdata/spdx_licenses/test.ABOUT +++ /dev/null @@ -1,5 +0,0 @@ -about_resource: . -name: test -version: test - -license_spdx: Apache-2.0 diff --git a/tests/testdata/attrib/attrib.ABOUT b/tests/testdata/test_attrib/gen_default_template/attrib.ABOUT similarity index 100% rename from tests/testdata/attrib/attrib.ABOUT rename to tests/testdata/test_attrib/gen_default_template/attrib.ABOUT diff --git a/tests/testdata/attrib_gen/expected_default_attrib.html b/tests/testdata/test_attrib/gen_default_template/expected_default_attrib.html similarity index 100% rename from tests/testdata/attrib_gen/expected_default_attrib.html rename to tests/testdata/test_attrib/gen_default_template/expected_default_attrib.html diff --git a/tests/testdata/test_attrib/gen_default_template/httpd-2.4.3.tar.gz b/tests/testdata/test_attrib/gen_default_template/httpd-2.4.3.tar.gz new file mode 100644 index 00000000..f1049ec8 --- /dev/null +++ b/tests/testdata/test_attrib/gen_default_template/httpd-2.4.3.tar.gz @@ -0,0 +1 @@ +mock archive document in attrib.ABOUT \ No newline at end of file diff --git a/tests/testdata/attrib_gen/attrib.ABOUT b/tests/testdata/test_attrib/gen_simple/attrib.ABOUT similarity index 100% rename from tests/testdata/attrib_gen/attrib.ABOUT rename to tests/testdata/test_attrib/gen_simple/attrib.ABOUT diff --git a/tests/testdata/test_attrib/gen_simple/httpd-2.4.3.tar.gz b/tests/testdata/test_attrib/gen_simple/httpd-2.4.3.tar.gz new file mode 100644 index 00000000..f1049ec8 --- /dev/null +++ b/tests/testdata/test_attrib/gen_simple/httpd-2.4.3.tar.gz @@ -0,0 +1 @@ +mock archive document in attrib.ABOUT \ No newline at end of file diff --git a/tests/testdata/attrib_gen/test.template b/tests/testdata/test_attrib/gen_simple/test.template similarity index 100% rename from tests/testdata/attrib_gen/test.template rename to tests/testdata/test_attrib/gen_simple/test.template diff --git a/tests/testdata/test_cmd/geninventory.csv b/tests/testdata/test_cmd/geninventory.csv new file mode 100644 index 00000000..d594efaa --- /dev/null +++ b/tests/testdata/test_cmd/geninventory.csv @@ -0,0 +1,23 @@ +"about_file_path","name","version" +"PyJWT-1.6.4-py2.py3-none-any.whl","PyJWT","1.6.4" +"pyldap-2.4.45.tar.gz","pyldap","2.4.45" +"xmlcatmgr-2.2_2.txz","xmlcatmgr","v 2.2_2" +"sdocbook-xml-1.1_2.2.txz","sdocbook-xml","v 1.1_2,2" +"Pygments-2.2.0-py2.py3-none-any.whl","Pygments","2.2.0" +"ruby22-gems-2.6.4.txz","ruby22-gems","v 2.6.4" +"xorg-macros-1.19.0.txz","xorg-macros","v 1.19.0" +"samba42-4.2.14.txz","samba42","v 4.2.14" +"xmlto-0.0.26_2.txz","xmlto","v 0.0.26_2" +"xtrans-1.3.5.txz","xtrans","v 1.3.5" +"w3m-0.5.3_4.txz","w3m","v 0.5.3_4" +"pyaml-17.12.1-py2.py3-none-any.whl","pyaml","17.12.1" +"siproxd-0.8.1.txz","siproxd","v 0.8.1" +"xcb-util-0.4.0_1.1.txz","xcb-util","v 0.4.0_1,1" +"requests-2.17.3-py2.py3-none-any.whl","requests","2.17.3" +"setuptools-36.6.0-py2.py3-none-any.whl","setuptools","36.6.0" +"ruby-2.2.5_1.1.txz","ruby","v 2.2.5_1,1" +"packageurl_python-0.8.1-py2.py3-none-any.whl","packageurl-python","0.8.1" +"certifi-2017.4.17-py2.py3-none-any.whl","certifi","2017.4.17" +"psycopg2-2.7.5.tar.gz","psycopg2","2.7.5" +"xorg-fonts-truetype-7.7_1.txz","xorg-fonts-truetype","v 7.7_1" +"xerces-c3-3.1.4.txz","xerces-c3","v 3.1.4" diff --git a/tests/testdata/test_cmd/help/about_attrib_help.txt b/tests/testdata/test_cmd/help/about_attrib_help.txt new file mode 100644 index 00000000..cfc1e7fd --- /dev/null +++ b/tests/testdata/test_cmd/help/about_attrib_help.txt @@ -0,0 +1,27 @@ +Usage: about attrib [OPTIONS] LOCATION OUTPUT + + Generate an attribution document at OUTPUT using .ABOUT files at LOCATION. + + LOCATION: Path to a file, directory or .zip archive containing .ABOUT files. + + OUTPUT: Path where to write the attribution document. + +Options: + --template FILE Path to an optional custom attribution template to + generate the attribution document. If not provided + the default built-in template is used. + --vartext = Add variable text as key=value for use in a custom + attribution template. + --inventory FILE Path to an optional JSON or CSV inventory FILE + listing the subset of .ABOUT files paths to consider + when generating the attribution document. + --mapping Use the default built-in "mapping.config" file with + mapping between input keys and .ABOUT field + names.Cannot be combined with the --mapping-file + option. + --mapping-file FILE Path to an optional custom mapping FILE with mapping + between input keys and .ABOUT field names. Cannot be + combined with the --mapping option. + -q, --quiet Do not print error or warning messages. + --verbose Show all error and warning messages. + -h, --help Show this message and exit. diff --git a/tests/testdata/test_cmd/help/about_check_help.txt b/tests/testdata/test_cmd/help/about_check_help.txt new file mode 100644 index 00000000..e895edc4 --- /dev/null +++ b/tests/testdata/test_cmd/help/about_check_help.txt @@ -0,0 +1,9 @@ +Usage: about check [OPTIONS] LOCATION + + Check .ABOUT file(s) at LOCATION for validity and print error messages. + + LOCATION: Path to a file or directory containing .ABOUT files. + +Options: + --verbose Show all error and warning messages. + -h, --help Show this message and exit. diff --git a/tests/testdata/test_cmd/help/about_gen_help.txt b/tests/testdata/test_cmd/help/about_gen_help.txt new file mode 100644 index 00000000..40ff4651 --- /dev/null +++ b/tests/testdata/test_cmd/help/about_gen_help.txt @@ -0,0 +1,24 @@ +Usage: about gen [OPTIONS] LOCATION OUTPUT + + Generate .ABOUT files in OUTPUT from an inventory of .ABOUT files at + LOCATION. + + LOCATION: Path to a JSON or CSV inventory file. + + OUTPUT: Path to a directory where ABOUT files are generated. + +Options: + --fetch-license URL KEY Fetch license data and text files from a DejaCode + License Library API URL using the API KEY. + --reference DIR Path to a directory with reference license data and + text files. + --mapping Use the default built-in "mapping.config" file with + mapping between input keys and .ABOUT field + names.Cannot be combined with the --mapping-file + option. + --mapping-file FILE Path to an optional custom mapping FILE with mapping + between input keys and .ABOUT field names. Cannot be + combined with the --mapping option. + -q, --quiet Do not print error or warning messages. + --verbose Show all error and warning messages. + -h, --help Show this message and exit. diff --git a/tests/testdata/test_cmd/help/about_help.txt b/tests/testdata/test_cmd/help/about_help.txt new file mode 100644 index 00000000..173b1c44 --- /dev/null +++ b/tests/testdata/test_cmd/help/about_help.txt @@ -0,0 +1,21 @@ +Usage: about [OPTIONS] COMMAND [ARGS]... + + Generate licensing attribution and credit notices from .ABOUT files and + inventories. + + Read, write and collect provenance and license inventories from .ABOUT files + to and from JSON or CSV files. + + Use about --help for help on a command. + +Options: + --version Show the version and exit. + -h, --help Show this message and exit. + +Commands: + attrib Generate an attribution document from .ABOUT files. + check Validate that the format of .ABOUT files is correct and report + errors and warnings. + gen Generate .ABOUT files from an inventory as CSV or JSON. + inventory Collect the inventory of .ABOUT files to a CSV or JSON file. + transform Transform a CSV by applying renamings, filters and checks. diff --git a/tests/testdata/test_cmd/help/about_inventory_help.txt b/tests/testdata/test_cmd/help/about_inventory_help.txt new file mode 100644 index 00000000..416c1f29 --- /dev/null +++ b/tests/testdata/test_cmd/help/about_inventory_help.txt @@ -0,0 +1,22 @@ +Usage: about inventory [OPTIONS] LOCATION OUTPUT + + Collect the inventory of .ABOUT file data as CSV or JSON. + + LOCATION: Path to an .ABOUT file or a directory with .ABOUT files. + + OUTPUT: Path to the JSON or CSV inventory file to create. + +Options: + --filter = Filter the inventory to ABOUT matching these + key=value e.g. "license_expression=gpl-2.0 + -f, --format [json|csv] Set OUTPUT inventory file format. [default: csv] + --mapping Use the default built-in "mapping.config" file with + mapping between input keys and .ABOUT field + names.Cannot be combined with the --mapping-file + option. + --mapping-file FILE Path to an optional custom mapping FILE with mapping + between input keys and .ABOUT field names. Cannot be + combined with the --mapping option. + -q, --quiet Do not print error or warning messages. + --verbose Show all error and warning messages. + -h, --help Show this message and exit. diff --git a/tests/testdata/test_cmd/help/about_transform_config_help.txt b/tests/testdata/test_cmd/help/about_transform_config_help.txt new file mode 100644 index 00000000..28e28528 --- /dev/null +++ b/tests/testdata/test_cmd/help/about_transform_config_help.txt @@ -0,0 +1,58 @@ + +A transform configuration file is used to describe which transformations and +validations to apply to a source CSV file. This is a simple text file using YAML +format, using the same format as an .ABOUT file. + +The attributes that can be set in a configuration file are: + +* column_renamings: +An optional mapping of source CSV column name to target CSV new column name that +is used to rename CSV columns. + +For instance with this configuration the columns "Directory/Location" will be +renamed to "about_resource" and "foo" to "bar": + renamings: + 'Directory/Location' : about_resource + foo : bar + +The renaming is always applied first before other transforms and checks. All +other column names referenced below are these that exist AFTER the renamings +have been applied to the existing column names. + +* required_columns: +An optional list of required column names that must have a value, beyond the +standard columns names. If a source CSV does not have such a column or a row is +missing a value for a required column, an error is reported. + +For instance with this configuration an error will be reported if the columns +"name" and "version" are missing or if any row does not have a value set for +these columns: + required_columns: + - name + - version + +* column_filters: +An optional list of column names that should be kept in the transformed CSV. If +this list is provided, all the columns from the source CSV that should be kept +in the target CSV must be listed be even if they are standard or required +columns. If this list is not provided, all source CSV columns are kept in the +transformed target CSV. + +For instance with this configuration the target CSV will only contains the "name" +and "version" columns and no other column: + column_filters: + - name + - version + +* row_filters: +An optional list of mappings of : that a source CSV row +should match to be added to the transformed target CSV. If any column value of a +row matches any such filter it is kept. Otherwise it is skipped. Filters are +applied last after all renamings, checks and tranforms and can therefore onlu +use remaining column names. + +For instance with this configuration the target CSV will only contain rows that +have a "path" equal to "/root/user/lib": + row_filters: + path : /root/user/lib + diff --git a/tests/testdata/test_cmd/help/about_transform_help.txt b/tests/testdata/test_cmd/help/about_transform_help.txt new file mode 100644 index 00000000..0aad484e --- /dev/null +++ b/tests/testdata/test_cmd/help/about_transform_help.txt @@ -0,0 +1,16 @@ +Usage: about transform [OPTIONS] LOCATION OUTPUT + + Transform the CSV file at LOCATION by applying renamings, filters and checks + and write a new CSV to OUTPUT. + + LOCATION: Path to a CSV file. + + OUTPUT: Path to CSV inventory file to create. + +Options: + -c, --configuration FILE Path to an optional YAML configuration file. See + --help-format for format help. + --help-format Show configuration file format help and exit. + -q, --quiet Do not print error or warning messages. + --verbose Show all error and warning messages. + -h, --help Show this message and exit. diff --git a/tests/testdata/parse/complete2/apache-2.0.LICENSE b/tests/testdata/test_cmd/repository-mini/apache-2.0.LICENSE similarity index 64% rename from tests/testdata/parse/complete2/apache-2.0.LICENSE rename to tests/testdata/test_cmd/repository-mini/apache-2.0.LICENSE index 14f93ab8..0cb1aacb 100644 --- a/tests/testdata/parse/complete2/apache-2.0.LICENSE +++ b/tests/testdata/test_cmd/repository-mini/apache-2.0.LICENSE @@ -1,3 +1,3 @@ + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ diff --git a/tests/testdata/SCMTests/elasticsearch-0.19.8.zip b/tests/testdata/test_cmd/repository-mini/appdirs-1.4.3-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/SCMTests/elasticsearch-0.19.8.zip rename to tests/testdata/test_cmd/repository-mini/appdirs-1.4.3-py2.py3-none-any.whl diff --git a/tests/testdata/test_cmd/repository-mini/appdirs.ABOUT b/tests/testdata/test_cmd/repository-mini/appdirs.ABOUT new file mode 100644 index 00000000..50ef9e6d --- /dev/null +++ b/tests/testdata/test_cmd/repository-mini/appdirs.ABOUT @@ -0,0 +1,12 @@ +about_resource: appdirs-1.4.3-py2.py3-none-any.whl +name: appdirs +version: 1.4.3 + +download_url: https://pypi.python.org/packages/56/eb/810e700ed1349edde4cbdc1b2a21e28cdf115f9faf263f6bbf8447c1abf3/appdirs-1.4.3-py2.py3-none-any.whl#md5=9ed4b51c9611775c3078b3831072e153 + +homepage_url: https://pypi.python.org/pypi/appdirs +copyright: Copyright (c) 2010 ActiveState Software Inc. + +license_expression: mit +license_text_file: + - appdirs.LICENSE diff --git a/tests/testdata/test_cmd/repository-mini/appdirs.LICENSE b/tests/testdata/test_cmd/repository-mini/appdirs.LICENSE new file mode 100644 index 00000000..e40fd9d3 --- /dev/null +++ b/tests/testdata/test_cmd/repository-mini/appdirs.LICENSE @@ -0,0 +1,4 @@ +Copyright (c) 2010 ActiveState Software Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this softwa \ No newline at end of file diff --git a/tests/testdata/test_cmd/repository-mini/mit.LICENSE b/tests/testdata/test_cmd/repository-mini/mit.LICENSE new file mode 100644 index 00000000..4bbcb207 --- /dev/null +++ b/tests/testdata/test_cmd/repository-mini/mit.LICENSE @@ -0,0 +1 @@ +Permission is hereby granted, free of charge, to any person obtaining a diff --git a/tests/testdata/about_locations/dir1/dir2/file1.about b/tests/testdata/test_cmd/repository-mini/xtrans-1.3.5.txz similarity index 100% rename from tests/testdata/about_locations/dir1/dir2/file1.about rename to tests/testdata/test_cmd/repository-mini/xtrans-1.3.5.txz diff --git a/tests/testdata/test_cmd/repository-mini/xtrans-1.3.5.txz.ABOUT b/tests/testdata/test_cmd/repository-mini/xtrans-1.3.5.txz.ABOUT new file mode 100644 index 00000000..9c84376a --- /dev/null +++ b/tests/testdata/test_cmd/repository-mini/xtrans-1.3.5.txz.ABOUT @@ -0,0 +1,12 @@ +about_resource: xtrans-1.3.5.txz +name: xtrans +about_resource_path: xtrans-1.3.5.txz +version: v 1.3.5 +license_expression: mit +license_name: MIT License +license_file: mit.LICENSE +license_url: https://enterprise.dejacode.com/urn?urn=urn:dje:license:mit +confirmed_license: MIT License +attribution_type: 3 +resource: repository/packages/xtrans-1.3.5.txz +audit_ref_nbr: INFO-23470 diff --git a/tests/testdata/test_cmd/transform.csv b/tests/testdata/test_cmd/transform.csv new file mode 100644 index 00000000..653d190a --- /dev/null +++ b/tests/testdata/test_cmd/transform.csv @@ -0,0 +1,3 @@ +"About_file_path","about_resource","name","version","download_url","description","homepage_url","notes","license_expression","license_key","license_name","license_file","license_url","copyright","notice_file","notice_url","redistribute","attribute","track_changes","modified","internal_use_only","changelog_file","owner","owner_url","contact","author","author_file","vcs_tool","vcs_repository","vcs_path","vcs_tag","vcs_branch","vcs_revision","checksum_md5","checksum_sha1","checksum_sha256","spec_version" +"/about/about.ABOUT",0,"AboutCode","0.11.0",,"AboutCode is a tool to process ABOUT files. An ABOUT file is a file.","http://dejacode.org",,"apache-2.0","apache-2.0",,"apache-2.0.LICENSE",,"Copyright (c) 2013-2014 nexB Inc.","NOTICE",,,,,,,,"nexB Inc.",,,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,"git","https://github.com/dejacode/about-code-tool.git",,,,,,,, +"/about/about.ABOUT2",0,"AboutCode","0.11.0",,"AboutCode is a tool to process ABOUT files. An ABOUT file is a file.","http://dejacode.org",,"apache-2.0","apache-2.0",,"apache-2.0.LICENSE",,"Copyright (c) 2013-2014 nexB Inc.","NOTICE",,,,,,,,"nexB Inc.",,,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,"git","https://github.com/dejacode/about-code-tool.git",,,,,,,, diff --git a/tests/testdata/test_files_for_genabout/about-mapping-sample.csv b/tests/testdata/test_files_for_genabout/about-mapping-sample.csv deleted file mode 100644 index b76a4ca0..00000000 --- a/tests/testdata/test_files_for_genabout/about-mapping-sample.csv +++ /dev/null @@ -1,2 +0,0 @@ -Directory/Filename,file_name,Component,Confirmed Version -about.py.ABOUT,about.py,ABOUT tool,0.8.1 diff --git a/tests/testdata/test_files_for_genabout/about.py.ABOUT b/tests/testdata/test_files_for_genabout/about.py.ABOUT deleted file mode 100644 index 2264df6e..00000000 --- a/tests/testdata/test_files_for_genabout/about.py.ABOUT +++ /dev/null @@ -1,5 +0,0 @@ -about_resource: . -name: ABOUT tool -version: 0.8.1 - -about_file: about.py.ABOUT diff --git a/tests/testdata/test_files_for_genabout/contains_blank_line.csv b/tests/testdata/test_files_for_genabout/contains_blank_line.csv deleted file mode 100644 index a98c1d90..00000000 --- a/tests/testdata/test_files_for_genabout/contains_blank_line.csv +++ /dev/null @@ -1,5 +0,0 @@ -about_file,about_resource,name,version -test_blank line,/tmp/,blank line,0.8.1 -blank line below,/tmp/1/,, -,,, -blank line above,/tmp/2/,, diff --git a/tests/testdata/test_files_for_genabout/dup_keys.csv b/tests/testdata/test_files_for_genabout/dup_keys.csv deleted file mode 100644 index 6aa8b36a..00000000 --- a/tests/testdata/test_files_for_genabout/dup_keys.csv +++ /dev/null @@ -1,2 +0,0 @@ -about_file,about_resource,copyright,name,version,copyright -about.ABOUT,.,nexB,ABOUT tool,0.8.1,someone \ No newline at end of file diff --git a/tests/testdata/test_files_for_genabout/dup_keys_with_diff_case.csv b/tests/testdata/test_files_for_genabout/dup_keys_with_diff_case.csv deleted file mode 100644 index d79ba8d8..00000000 --- a/tests/testdata/test_files_for_genabout/dup_keys_with_diff_case.csv +++ /dev/null @@ -1,2 +0,0 @@ -about_file,about_resource,copyright,name,version,Copyright -about.ABOUT,.,nexB,ABOUT tool,0.8.1,someone \ No newline at end of file diff --git a/tests/testdata/test_files_for_genabout/elasticsearch.ABOUT b/tests/testdata/test_files_for_genabout/elasticsearch.ABOUT deleted file mode 100644 index 8f719c3c..00000000 --- a/tests/testdata/test_files_for_genabout/elasticsearch.ABOUT +++ /dev/null @@ -1,5 +0,0 @@ -about_resource: elasticsearch-0.19.8.zip -name: ElasticSearch -version: 0.19.8 - -about_file: test_generation/elasticsearch.ABOUT diff --git a/tests/testdata/test_files_for_genabout/missing_about_file.csv b/tests/testdata/test_files_for_genabout/missing_about_file.csv deleted file mode 100644 index aef4021a..00000000 --- a/tests/testdata/test_files_for_genabout/missing_about_file.csv +++ /dev/null @@ -1,14 +0,0 @@ -about_file,about_resource,name,version,spec_version,date,description,description_file,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,notes_file,contact,owner,author,author_file,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute,attribute,track_changes,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization -,.,ABOUT tool,0.8.1,,,"This is a tool to process ABOUT files. An ABOUT file provides a -simple way to document the provenance (origin and license) 'about' a software -component. An ABOUT file is a small text file stored in the codebase -side-by-side with the software component that it documents.",,http://dejacode.org,,,README.rst,,,,CHANGELOG.txt,,,,,,,nexB Inc.,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,"Copyright (c) 2013 nexB, Inc.",," Copyright (c) 2013 nexB, Inc. http://www.nexb.com/ - All rights reserved. -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.",,,,apache2.LICENSE.txt,,Apache-2.0,,,,git,https://github.com/dejacode/about-code-tool.git,,,,,,,,,, diff --git a/tests/testdata/test_files_for_genabout/missing_about_file_and_resource.csv b/tests/testdata/test_files_for_genabout/missing_about_file_and_resource.csv deleted file mode 100644 index b8790fb6..00000000 --- a/tests/testdata/test_files_for_genabout/missing_about_file_and_resource.csv +++ /dev/null @@ -1,14 +0,0 @@ -about_file,about_resource,name,version,spec_version,date,description,description_file,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,notes_file,contact,owner,author,author_file,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute,attribute,track_changes,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization -,,ABOUT tool,0.8.1,,,"This is a tool to process ABOUT files. An ABOUT file provides a -simple way to document the provenance (origin and license) 'about' a software -component. An ABOUT file is a small text file stored in the codebase -side-by-side with the software component that it documents.",,http://dejacode.org,,,README.rst,,,,CHANGELOG.txt,,,,,,,nexB Inc.,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,"Copyright (c) 2013 nexB, Inc.",," Copyright (c) 2013 nexB, Inc. http://www.nexb.com/ - All rights reserved. -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.",,,,apache2.LICENSE.txt,,Apache-2.0,,,,git,https://github.com/dejacode/about-code-tool.git,,,,,,,,,, diff --git a/tests/testdata/test_files_for_genabout/missing_about_resource.csv b/tests/testdata/test_files_for_genabout/missing_about_resource.csv deleted file mode 100644 index 35686140..00000000 --- a/tests/testdata/test_files_for_genabout/missing_about_resource.csv +++ /dev/null @@ -1,14 +0,0 @@ -about_file,about_resource,name,version,spec_version,date,description,description_file,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,notes_file,contact,owner,author,author_file,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute,attribute,track_changes,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization -about.py.ABOUT,,ABOUT tool,0.8.1,,,"This is a tool to process ABOUT files. An ABOUT file provides a -simple way to document the provenance (origin and license) 'about' a software -component. An ABOUT file is a small text file stored in the codebase -side-by-side with the software component that it documents.",,http://dejacode.org,,,README.rst,,,,CHANGELOG.txt,,,,,,,nexB Inc.,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,"Copyright (c) 2013 nexB, Inc.",," Copyright (c) 2013 nexB, Inc. http://www.nexb.com/ - All rights reserved. -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.",,,,apache2.LICENSE.txt,,Apache-2.0,,,,git,https://github.com/dejacode/about-code-tool.git,,,,,,,,,, diff --git a/tests/testdata/test_files_for_genabout/thirdparty.csv b/tests/testdata/test_files_for_genabout/thirdparty.csv deleted file mode 100644 index eecc941d..00000000 --- a/tests/testdata/test_files_for_genabout/thirdparty.csv +++ /dev/null @@ -1,45 +0,0 @@ -about_file,about_resource,name,version,spec_version,date,description,description_file,homepage_url,download_url,readme,readme_file,install,install_file,changelog,changelog_file,news,news_file,news_url,notes,notes_file,contact,owner,author,author_file,copyright,copyright_file,notice,notice_file,notice_url,license_text,license_text_file,license_url,license_spdx,redistribute,attribute,track_changes,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_sha1,checksum_md5,checksum_sha256,dje_component,dje_license,dje_organization,warnings,errors -testdata/thirdparty/setuptools.ABOUT,setuptools-0.6c11-py2.6.egg,setuptools,0.6c11,,1/1/2013,,http://pypi.python.org/pypi/setuptools,,http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086,,,,,,,,,,this is not used by default but embedded in virtualenv,,,,Phillip J. Eby,,,,,,,,,,,,,,,,,,,,,,,,zpl-2.1,,,"Field: about_resource, Value: setuptools-0.6c11-py2.6.egg, Message: File does not exist." -testdata/thirdparty/django_snippets_2413.ABOUT,django_snippets_2413.py,Yet another query string template tag,4/12/2011,,,,,http://djangosnippets.org/snippets/2413/,http://djangosnippets.org/snippets/2413/download/,,,,,,,,,,"This file was modified to include the line ""register = Library()"" -without which the template tag is not registered.",,,,,,,,,,,,django_snippets.LICENSE,http://djangosnippets.org/about/tos/,,,,,,,,,,,,,,,,,, -testdata/thirdparty/underscore.js.ABOUT,underscore.js,underscore.js,1.4.2,,,,,http://underscorejs.org/,https://raw.github.com/documentcloud/underscore/1.4.2/underscore.js,,,,,,,,,,,,,,,,,,,,,,underscore.js.LICENSE,,,,,,,,,,,,,,,,mit,,"Field: scm_repository, Value: git://github.com/documentcloud/underscore.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field", -testdata/thirdparty/jquery.min.js.ABOUT,jquery-1.7.2.min.js,jQuery,1.7.2,,,,,http://jquery.com/,http://code.jquery.com/jquery-1.7.2.min.js,,,,,,,,,,,,,,,,,,,,,,jquery.js.LICENSE,http://jquery.org/license,,,,,,,,,,,,,,,mit,,"Field: scm_repository, Value: https://github.com/jquery/jquery.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field", -testdata/thirdparty/FixedHeader.ABOUT,FixedHeader-2.0.6.zip,data tables fixed header,2.0.6,,,"""Fix"" a header at the top of the table, so it scrolls with the table",,http://datatables.net,http://datatables.net/releases/FixedHeader-2.0.6.zip,,,,,,,,,,"This source file is free software, under either the GPL v2 license or a -BSD style license, available at: -http://datatables.net/license_gpl2 -http://datatables.net/license_bsd",,,,Allan Jardine (www.sprymedia.co.uk),,"Copyright 2009-2012 Allan Jardine, all rights reserved.",,,,,,,http://datatables.net/license_bsd,,,,,,,,,,,,,,,bsd-new,,"Field: organization, Value: Allan Jardine, Message: Not a mandatory or optional field", -testdata/thirdparty/Font-Awesome.ABOUT,Font-Awesome-v3.0.2.zip,Font-Awesome,3.0.2,,,,,http://fortawesome.github.com/Font-Awesome/,https://github.com/FortAwesome/Font-Awesome/archive/v3.0.2.zip,,,,,,,,,,"there are several licenses: SIL Open Font License, MIT License, CC BY 3.0 License, Attribution is no longer required in Font Awesome 3.0",,,,,,,,,Font-Awesome.NOTICE,,,,,,,,,,,,,,,,,,,ofl-1.1 and mit and cc-by-3.0 ,,"Field: scm_repository, Value: https://github.com/FortAwesome/Font-Awesome.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field -Field: scm_rev, Value: 13d5dd373cbf3f2bddd8ac2ee8df3a1966a62d09, Message: Not a mandatory or optional field -Field: organization, Value: FortAwesome, Message: Not a mandatory or optional field", -testdata/thirdparty/ez_setup.py.ABOUT,,setuptools boostrap,0.6c11,,1/1/2013,,,http://pypi.python.org/pypi/setuptools,http://peak.telecommunity.com/dist/ez_setup.py,,,,,,,,,,this is not used by default but embedded in virtualenv,,,,Phillip J. Eby,,,,,,,,,,,,,,,,,,,,,,,,zpl-2.1,,,"Field: about_resource, Value: None, Message: Mandatory field missing" -testdata/thirdparty/jquery.jsPlumb.ABOUT,jquery.jsPlumb-1.3.10-all-min.js,jquery.jsPlumb,1.3.10,,,,,http://code.google.com/p/jsplumb/,http://code.google.com/p/jsplumb/downloads/detail?name=jquery.jsPlumb-1.3.10-all-min.js,,,,,,,,,,,,,,,,,,,,,,jquery.js.LICENSE,,,,,,,,,,,,,,,,mit,,"Field: scm_repository, Value: http://jsplumb.googlecode.com/svn/trunk/, Message: Not a mandatory or optional field -Field: scm_tool, Value: svn, Message: Not a mandatory or optional field", -testdata/thirdparty/underscore-min.js.ABOUT,underscore-min.js,underscore.js,1.4.2,,,,,http://underscorejs.org/,https://raw.github.com/documentcloud/underscore/1.4.2/underscore-min.js,,,,,,,,,,,,,,,,,,,,,,underscore.js.LICENSE,,,,,,,,,,,,,,,,mit,,"Field: scm_repository, Value: git://github.com/documentcloud/underscore.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field", -testdata/thirdparty/csv_serialize.py.ABOUT,,csv_serialize,1/16/2013,,,,,http://djangosnippets.org/snippets/2240/,http://djangosnippets.org/snippets/2240/download/,,,,,,,,,,,,,,,,,,,,,,django_snippets.LICENSE,http://djangosnippets.org/about/tos/,,,,,,,,,,,,,,,,,"Field: date-retrieved, Value: date-retrieved: 2013-01-16, Message: Field name contains invalid characters: '-': line ignored.","Field: about_resource, Value: None, Message: Mandatory field missing" -testdata/thirdparty/elasticsearch.ABOUT,elasticsearch-0.19.8.zip,ElasticSearch,0.19.8,,,,,http://www.elasticsearch.org/,https://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.19.8.zip,,,,,,,,,,"This a prebuilt version working on all OSes. -The tar.gz works only with POSIX OSses and not Windows. ",,,,,,Copyright 2009-2011 ElasticSearch and Shay Banon,,,elasticsearch.NOTICE,,,elasticsearch.LICENSE,,,,,,,,,,,,,,,,apache-2.0,,"Field: scm_repository, Value: https://github.com/elasticsearch/elasticsearch.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field -Field: scm_rev, Value: badcdee74acec84da3de6c6ea55c692aee4a6f9, Message: Not a mandatory or optional field -Field: organization, Value: ElasticSearch and Shay Banon, Message: Not a mandatory or optional field", -testdata/thirdparty/okfn-annotator.ABOUT,okfn-annotator-549159b.zip,OKFN Annotator,549159b554411eba18c34ffffe91ba44f7558be6,,,,,http://okfn.org/projects/annotator/,https://github.com/okfn/annotator/zipball/549159b554411eba18c34ffffe91ba44f7558be6,,,,,,,,,,"this component includes several other components, not detailed here. -See archive for details and licenses.",,,,,,,,,,,,okfn-annotator.LICENSE,http://okfn.org/ip-policy/,,,,,,,,,,,,,,,mit,,"Field: scm_repository, Value: https://github.com/okfn/annotator.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git , Message: Not a mandatory or optional field -Field: scm_rev, Value: 549159b554411eba18c34ffffe91ba44f7558be6, Message: Not a mandatory or optional field -Field: organization, Value: OKFN, Message: Not a mandatory or optional field", -testdata/thirdparty/mod_wsgi-3.3.tar.gz.ABOUT,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"Field: date-retrieved, Value: date-retrieved:2013-01-16 19:41:10+01:00 -, Message: Field name contains invalid characters: '-': line ignored. -Field: wget, Value: http://modwsgi.googlecode.com/files/mod_wsgi-3.3.tar.gz, Message: Not a mandatory or optional field","Field: about_resource, Value: None, Message: Mandatory field missing -Field: name, Value: None, Message: Mandatory field missing -Field: version, Value: None, Message: Mandatory field missing" -testdata/thirdparty/elasticsearch-sources.ABOUT,elasticsearch-v0.19.8-g7badcde.tar.gz,ElasticSearch,0.19.8,,,,,http://www.elasticsearch.org/,https://github.com/elasticsearch/elasticsearch/tarball/v0.19.8,,,,,,,,,,Source code for the pre-built binaries we use ,,,,,,Copyright 2009-2011 ElasticSearch and Shay Banon,,,elasticsearch.NOTICE,,,elasticsearch.LICENSE,,,,,,,,,,,,,,,,apache-2.0,,"Field: scm_repository, Value: https://github.com/elasticsearch/elasticsearch.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field -Field: scm_rev, Value: badcdee74acec84da3de6c6ea55c692aee4a6f9, Message: Not a mandatory or optional field -Field: organization, Value: ElasticSearch and Shay Banon, Message: Not a mandatory or optional field", -testdata/thirdparty/jquery.js.ABOUT,jquery-1.7.2.min.js,jQuery,1.7.2,,,,,http://jquery.com/,http://code.jquery.com/jquery-1.7.2.js,,,,,,,,,,,,,,,,,,,,,,jquery.js.LICENSE,http://jquery.org/license,,,,,,,,,,,,,,,mit,,"Field: scm_repository, Value: https://github.com/jquery/jquery.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field", -testdata/thirdparty/twitter_bootstrap.ABOUT,twitter_bootstrap_v2.0.3.zip,bootstrap,2.0.3,,,,,http://twitter.github.com/bootstrap/,https://github.com/twitter/bootstrap/archive/v2.0.3.zip,,,,,,,,,,,,,,,,,,,,,,twitter_bootstrap.LICENSE,,,,,,,,,,,,,,,,apache 2.0,,"Field: scm_repository, Value: https://github.com/twitter/bootstrap.git, Message: Not a mandatory or optional field -Field: scm_tool, Value: git, Message: Not a mandatory or optional field", diff --git a/tests/testdata/test_files_for_genabout/valid_and_invalid_rows.csv b/tests/testdata/test_files_for_genabout/valid_and_invalid_rows.csv deleted file mode 100644 index a0c1c87c..00000000 --- a/tests/testdata/test_files_for_genabout/valid_and_invalid_rows.csv +++ /dev/null @@ -1,4 +0,0 @@ -about_file,about_resource,name,version -,.,ABOUT tool,0.8.1 -about.py.ABOUT,,ABOUT tool,0.8.1 -about.py.ABOUT,.,ABOUT tool,0.8.1 diff --git a/tests/testdata/test_for_continuation_lines/test_input.ABOUT b/tests/testdata/test_for_continuation_lines/test_input.ABOUT deleted file mode 100644 index 49d89bc1..00000000 --- a/tests/testdata/test_for_continuation_lines/test_input.ABOUT +++ /dev/null @@ -1,7 +0,0 @@ -about_resource: jquery.js -name: jQuery -version: 1.2.3 -notes: one - two - - three \ No newline at end of file diff --git a/tests/testdata/test_for_dir/include_all-1.ABOUT b/tests/testdata/test_for_dir/include_all-1.ABOUT deleted file mode 100644 index 1e3cb228..00000000 --- a/tests/testdata/test_for_dir/include_all-1.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: test_dir -version: a -about_resource: ../test_for_dir -description: This is the ABOUT file referencing all the files/directories in the current directory diff --git a/tests/testdata/test_for_dir/include_all.ABOUT b/tests/testdata/test_for_dir/include_all.ABOUT deleted file mode 100644 index c70f0fa7..00000000 --- a/tests/testdata/test_for_dir/include_all.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: test_dir -version: 1.2.3 -about_resource: . -description: This is the ABOUT file referencing all the files/directories in the current directory diff --git a/tests/testdata/test_for_dir/test_dir.ABOUT b/tests/testdata/test_for_dir/test_dir.ABOUT deleted file mode 100644 index 6b06375d..00000000 --- a/tests/testdata/test_for_dir/test_dir.ABOUT +++ /dev/null @@ -1,4 +0,0 @@ -name: test_dir -version: 123 -about_resource: test_dir -description: Testing directory diff --git a/tests/testdata/gen/about.py.ABOUT b/tests/testdata/test_gen/about.py.ABOUT similarity index 100% rename from tests/testdata/gen/about.py.ABOUT rename to tests/testdata/test_gen/about.py.ABOUT diff --git a/tests/testdata/gen/dup_keys.csv b/tests/testdata/test_gen/dup_keys.csv similarity index 100% rename from tests/testdata/gen/dup_keys.csv rename to tests/testdata/test_gen/dup_keys.csv diff --git a/tests/testdata/gen/dup_keys_with_diff_case.csv b/tests/testdata/test_gen/dup_keys_with_diff_case.csv similarity index 100% rename from tests/testdata/gen/dup_keys_with_diff_case.csv rename to tests/testdata/test_gen/dup_keys_with_diff_case.csv diff --git a/tests/testdata/gen/inv.csv b/tests/testdata/test_gen/inv.csv similarity index 100% rename from tests/testdata/gen/inv.csv rename to tests/testdata/test_gen/inv.csv diff --git a/tests/testdata/gen/inv2.csv b/tests/testdata/test_gen/inv2.csv similarity index 100% rename from tests/testdata/gen/inv2.csv rename to tests/testdata/test_gen/inv2.csv diff --git a/tests/testdata/gen/inv3.csv b/tests/testdata/test_gen/inv3.csv similarity index 100% rename from tests/testdata/gen/inv3.csv rename to tests/testdata/test_gen/inv3.csv diff --git a/tests/testdata/gen/inv4.csv b/tests/testdata/test_gen/inv4.csv similarity index 100% rename from tests/testdata/gen/inv4.csv rename to tests/testdata/test_gen/inv4.csv diff --git a/tests/testdata/gen/inv5.csv b/tests/testdata/test_gen/inv5.csv similarity index 100% rename from tests/testdata/gen/inv5.csv rename to tests/testdata/test_gen/inv5.csv diff --git a/tests/testdata/inventory/complex/about/Jinja2-2.7.3-py2-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/Jinja2-2.7.3-py2-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/Jinja2-2.7.3-py2-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/Jinja2-2.7.3-py2-none-any.whl diff --git a/tests/testdata/inventory/complex/about/Jinja2.ABOUT b/tests/testdata/test_gen/inventory/complex/about/Jinja2.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/Jinja2.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/Jinja2.ABOUT diff --git a/tests/testdata/inventory/complex/about/Jinja2.LICENSE b/tests/testdata/test_gen/inventory/complex/about/Jinja2.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/Jinja2.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/Jinja2.LICENSE diff --git a/tests/testdata/inventory/complex/about/MarkupSafe-0.23-py2-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/MarkupSafe-0.23-py2-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/MarkupSafe-0.23-py2-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/MarkupSafe-0.23-py2-none-any.whl diff --git a/tests/testdata/inventory/complex/about/MarkupSafe.ABOUT b/tests/testdata/test_gen/inventory/complex/about/MarkupSafe.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/MarkupSafe.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/MarkupSafe.ABOUT diff --git a/tests/testdata/inventory/complex/about/MarkupSafe.LICENSE b/tests/testdata/test_gen/inventory/complex/about/MarkupSafe.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/MarkupSafe.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/MarkupSafe.LICENSE diff --git a/tests/testdata/inventory/basic/about/NOTICE b/tests/testdata/test_gen/inventory/complex/about/NOTICE similarity index 100% rename from tests/testdata/inventory/basic/about/NOTICE rename to tests/testdata/test_gen/inventory/complex/about/NOTICE diff --git a/tests/testdata/inventory/complex/about/PSF.LICENSE b/tests/testdata/test_gen/inventory/complex/about/PSF.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/PSF.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/PSF.LICENSE diff --git a/tests/testdata/inventory/complex/about/about.ABOUT b/tests/testdata/test_gen/inventory/complex/about/about.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/about.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/about.ABOUT diff --git a/tests/testdata/inventory/complex/about/apache-2.0.LICENSE b/tests/testdata/test_gen/inventory/complex/about/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/apache-2.0.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/apache-2.0.LICENSE diff --git a/tests/testdata/inventory/complex/about/certifi-14.05.14-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/certifi-14.05.14-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/certifi-14.05.14-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/certifi-14.05.14-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/certifi.ABOUT b/tests/testdata/test_gen/inventory/complex/about/certifi.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/certifi.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/certifi.ABOUT diff --git a/tests/testdata/inventory/complex/about/certifi.LICENSE b/tests/testdata/test_gen/inventory/complex/about/certifi.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/certifi.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/certifi.LICENSE diff --git a/tests/testdata/inventory/complex/about/click-3.2-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/click-3.2-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/click-3.2-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/click-3.2-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/click.ABOUT b/tests/testdata/test_gen/inventory/complex/about/click.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/click.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/click.ABOUT diff --git a/tests/testdata/inventory/complex/about/click.LICENSE b/tests/testdata/test_gen/inventory/complex/about/click.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/click.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/click.LICENSE diff --git a/tests/testdata/inventory/complex/about/colorama-0.3.1-py2-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/colorama-0.3.1-py2-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/colorama-0.3.1-py2-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/colorama-0.3.1-py2-none-any.whl diff --git a/tests/testdata/inventory/complex/about/colorama.ABOUT b/tests/testdata/test_gen/inventory/complex/about/colorama.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/colorama.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/colorama.ABOUT diff --git a/tests/testdata/inventory/complex/about/colorama.LICENSE b/tests/testdata/test_gen/inventory/complex/about/colorama.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/colorama.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/colorama.LICENSE diff --git a/tests/testdata/inventory/complex/about/lgpl-2.1.LICENSE b/tests/testdata/test_gen/inventory/complex/about/lgpl-2.1.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/lgpl-2.1.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/lgpl-2.1.LICENSE diff --git a/tests/testdata/inventory/complex/about/pip-1.5.6-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/pip-1.5.6-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/pip-1.5.6-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/pip-1.5.6-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/pip.ABOUT b/tests/testdata/test_gen/inventory/complex/about/pip.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/pip.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/pip.ABOUT diff --git a/tests/testdata/inventory/complex/about/pip.AUTHORS b/tests/testdata/test_gen/inventory/complex/about/pip.AUTHORS similarity index 100% rename from tests/testdata/inventory/complex/about/pip.AUTHORS rename to tests/testdata/test_gen/inventory/complex/about/pip.AUTHORS diff --git a/tests/testdata/inventory/complex/about/pip.LICENSE b/tests/testdata/test_gen/inventory/complex/about/pip.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/pip.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/pip.LICENSE diff --git a/tests/testdata/inventory/complex/about/py-1.4.23-py2-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/py-1.4.23-py2-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/py-1.4.23-py2-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/py-1.4.23-py2-none-any.whl diff --git a/tests/testdata/inventory/complex/about/py.ABOUT b/tests/testdata/test_gen/inventory/complex/about/py.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/py.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/py.ABOUT diff --git a/tests/testdata/inventory/complex/about/py.LICENSE b/tests/testdata/test_gen/inventory/complex/about/py.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/py.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/py.LICENSE diff --git a/tests/testdata/inventory/complex/about/pytest-2.6.1-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/pytest-2.6.1-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/pytest-2.6.1-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/pytest-2.6.1-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/pytest.ABOUT b/tests/testdata/test_gen/inventory/complex/about/pytest.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/pytest.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/pytest.ABOUT diff --git a/tests/testdata/inventory/complex/about/pytest.LICENSE b/tests/testdata/test_gen/inventory/complex/about/pytest.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/pytest.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/pytest.LICENSE diff --git a/tests/testdata/inventory/complex/about/schematics-0.9_5-py2-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/schematics-0.9_5-py2-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/schematics-0.9_5-py2-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/schematics-0.9_5-py2-none-any.whl diff --git a/tests/testdata/inventory/complex/about/schematics.ABOUT b/tests/testdata/test_gen/inventory/complex/about/schematics.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/schematics.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/schematics.ABOUT diff --git a/tests/testdata/inventory/complex/about/schematics.LICENSE b/tests/testdata/test_gen/inventory/complex/about/schematics.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/schematics.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/schematics.LICENSE diff --git a/tests/testdata/inventory/complex/about/setuptools-5.6-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/setuptools-5.6-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/setuptools-5.6-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/setuptools-5.6-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/setuptools.ABOUT b/tests/testdata/test_gen/inventory/complex/about/setuptools.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/setuptools.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/setuptools.ABOUT diff --git a/tests/testdata/inventory/complex/about/unicodecsv-0.9.4-py2-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/unicodecsv-0.9.4-py2-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/unicodecsv-0.9.4-py2-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/unicodecsv-0.9.4-py2-none-any.whl diff --git a/tests/testdata/inventory/complex/about/unicodecsv.ABOUT b/tests/testdata/test_gen/inventory/complex/about/unicodecsv.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/unicodecsv.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/unicodecsv.ABOUT diff --git a/tests/testdata/inventory/complex/about/unicodecsv.LICENSE b/tests/testdata/test_gen/inventory/complex/about/unicodecsv.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/unicodecsv.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/unicodecsv.LICENSE diff --git a/tests/testdata/inventory/complex/about/virtualenv-1.11.6-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/virtualenv-1.11.6-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/virtualenv-1.11.6-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/virtualenv-1.11.6-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/virtualenv.ABOUT b/tests/testdata/test_gen/inventory/complex/about/virtualenv.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/virtualenv.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/virtualenv.ABOUT diff --git a/tests/testdata/inventory/complex/about/virtualenv.LICENSE b/tests/testdata/test_gen/inventory/complex/about/virtualenv.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/virtualenv.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/virtualenv.LICENSE diff --git a/tests/testdata/inventory/complex/about/virtualenv.py b/tests/testdata/test_gen/inventory/complex/about/virtualenv.py similarity index 100% rename from tests/testdata/inventory/complex/about/virtualenv.py rename to tests/testdata/test_gen/inventory/complex/about/virtualenv.py diff --git a/tests/testdata/inventory/complex/about/virtualenv.py.ABOUT b/tests/testdata/test_gen/inventory/complex/about/virtualenv.py.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/virtualenv.py.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/virtualenv.py.ABOUT diff --git a/tests/testdata/inventory/complex/about/wheel-0.24.0-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/wheel-0.24.0-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/wheel-0.24.0-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/wheel-0.24.0-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/wheel.ABOUT b/tests/testdata/test_gen/inventory/complex/about/wheel.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/wheel.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/wheel.ABOUT diff --git a/tests/testdata/inventory/complex/about/wheel.LICENSE b/tests/testdata/test_gen/inventory/complex/about/wheel.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/wheel.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/wheel.LICENSE diff --git a/tests/testdata/inventory/complex/about/wincertstore-0.2-py2.py3-none-any.whl b/tests/testdata/test_gen/inventory/complex/about/wincertstore-0.2-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/inventory/complex/about/wincertstore-0.2-py2.py3-none-any.whl rename to tests/testdata/test_gen/inventory/complex/about/wincertstore-0.2-py2.py3-none-any.whl diff --git a/tests/testdata/inventory/complex/about/wincertstore.ABOUT b/tests/testdata/test_gen/inventory/complex/about/wincertstore.ABOUT similarity index 100% rename from tests/testdata/inventory/complex/about/wincertstore.ABOUT rename to tests/testdata/test_gen/inventory/complex/about/wincertstore.ABOUT diff --git a/tests/testdata/inventory/complex/about/wincertstore.LICENSE b/tests/testdata/test_gen/inventory/complex/about/wincertstore.LICENSE similarity index 100% rename from tests/testdata/inventory/complex/about/wincertstore.LICENSE rename to tests/testdata/test_gen/inventory/complex/about/wincertstore.LICENSE diff --git a/tests/testdata/inventory/complex/about_file_path_dir_endswith_space.csv b/tests/testdata/test_gen/inventory/complex/about_file_path_dir_endswith_space.csv similarity index 100% rename from tests/testdata/inventory/complex/about_file_path_dir_endswith_space.csv rename to tests/testdata/test_gen/inventory/complex/about_file_path_dir_endswith_space.csv diff --git a/tests/testdata/inventory/complex/expected.csv b/tests/testdata/test_gen/inventory/complex/expected.csv similarity index 100% rename from tests/testdata/inventory/complex/expected.csv rename to tests/testdata/test_gen/inventory/complex/expected.csv diff --git a/tests/testdata/parser_tests/.ABOUT b/tests/testdata/test_gen/parser_tests/.ABOUT similarity index 100% rename from tests/testdata/parser_tests/.ABOUT rename to tests/testdata/test_gen/parser_tests/.ABOUT diff --git a/tests/testdata/parser_tests/about_resource.c b/tests/testdata/test_gen/parser_tests/about_resource.c similarity index 100% rename from tests/testdata/parser_tests/about_resource.c rename to tests/testdata/test_gen/parser_tests/about_resource.c diff --git a/tests/testdata/parser_tests/about_resource_field.ABOUT b/tests/testdata/test_gen/parser_tests/about_resource_field.ABOUT similarity index 100% rename from tests/testdata/parser_tests/about_resource_field.ABOUT rename to tests/testdata/test_gen/parser_tests/about_resource_field.ABOUT diff --git a/tests/testdata/parser_tests/about_resource_field_present.ABOUT b/tests/testdata/test_gen/parser_tests/about_resource_field_present.ABOUT similarity index 100% rename from tests/testdata/parser_tests/about_resource_field_present.ABOUT rename to tests/testdata/test_gen/parser_tests/about_resource_field_present.ABOUT diff --git a/tests/testdata/parser_tests/missing_about_ref.ABOUT b/tests/testdata/test_gen/parser_tests/missing_about_ref.ABOUT similarity index 100% rename from tests/testdata/parser_tests/missing_about_ref.ABOUT rename to tests/testdata/test_gen/parser_tests/missing_about_ref.ABOUT diff --git a/tests/testdata/parser_tests/upper_field_names.ABOUT b/tests/testdata/test_gen/parser_tests/upper_field_names.ABOUT similarity index 100% rename from tests/testdata/parser_tests/upper_field_names.ABOUT rename to tests/testdata/test_gen/parser_tests/upper_field_names.ABOUT diff --git a/tests/testdata/gen/this.ABOUT b/tests/testdata/test_gen/this.ABOUT similarity index 100% rename from tests/testdata/gen/this.ABOUT rename to tests/testdata/test_gen/this.ABOUT diff --git a/tests/testdata/collect-inventory-errors/non-supported_date_format.ABOUT b/tests/testdata/test_model/collect_inventory_errors/non-supported_date_format.ABOUT similarity index 100% rename from tests/testdata/collect-inventory-errors/non-supported_date_format.ABOUT rename to tests/testdata/test_model/collect_inventory_errors/non-supported_date_format.ABOUT diff --git a/tests/testdata/collect-inventory-errors/supported_date_format.ABOUT b/tests/testdata/test_model/collect_inventory_errors/supported_date_format.ABOUT similarity index 100% rename from tests/testdata/collect-inventory-errors/supported_date_format.ABOUT rename to tests/testdata/test_model/collect_inventory_errors/supported_date_format.ABOUT diff --git a/tests/testdata/parse/custom_fields.about b/tests/testdata/test_model/custom_fields/custom_fields.about similarity index 86% rename from tests/testdata/parse/custom_fields.about rename to tests/testdata/test_model/custom_fields/custom_fields.about index ad0a684b..74f20dba 100644 --- a/tests/testdata/parse/custom_fields.about +++ b/tests/testdata/test_model/custom_fields/custom_fields.about @@ -7,4 +7,6 @@ multi_line: | line1 line2 +other: sasasas + empty : diff --git a/tests/testdata/test_model/custom_fields/mapping.config b/tests/testdata/test_model/custom_fields/mapping.config new file mode 100644 index 00000000..eed850ca --- /dev/null +++ b/tests/testdata/test_model/custom_fields/mapping.config @@ -0,0 +1,3 @@ +multi_line: multi_line +empty: empty +single_line: single_line \ No newline at end of file diff --git a/tests/testdata/custom-mapping-file/mapping.config b/tests/testdata/test_model/custom_mapping/mapping.config similarity index 100% rename from tests/testdata/custom-mapping-file/mapping.config rename to tests/testdata/test_model/custom_mapping/mapping.config diff --git a/tests/testdata/equal/complete/NOTICE b/tests/testdata/test_model/equal/complete/NOTICE similarity index 100% rename from tests/testdata/equal/complete/NOTICE rename to tests/testdata/test_model/equal/complete/NOTICE diff --git a/tests/testdata/equal/complete/about.ABOUT b/tests/testdata/test_model/equal/complete/about.ABOUT similarity index 100% rename from tests/testdata/equal/complete/about.ABOUT rename to tests/testdata/test_model/equal/complete/about.ABOUT diff --git a/tests/testdata/equal/complete/apache-2.0.LICENSE b/tests/testdata/test_model/equal/complete/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/equal/complete/apache-2.0.LICENSE rename to tests/testdata/test_model/equal/complete/apache-2.0.LICENSE diff --git a/tests/testdata/dumps/complete2/NOTICE b/tests/testdata/test_model/equal/complete2/NOTICE similarity index 100% rename from tests/testdata/dumps/complete2/NOTICE rename to tests/testdata/test_model/equal/complete2/NOTICE diff --git a/tests/testdata/equal/complete2/about.ABOUT b/tests/testdata/test_model/equal/complete2/about.ABOUT similarity index 100% rename from tests/testdata/equal/complete2/about.ABOUT rename to tests/testdata/test_model/equal/complete2/about.ABOUT diff --git a/tests/testdata/dumps/complete2/apache-2.0.LICENSE b/tests/testdata/test_model/equal/complete2/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/dumps/complete2/apache-2.0.LICENSE rename to tests/testdata/test_model/equal/complete2/apache-2.0.LICENSE diff --git a/tests/testdata/load/expected.csv b/tests/testdata/test_model/expected.csv similarity index 72% rename from tests/testdata/load/expected.csv rename to tests/testdata/test_model/expected.csv index 98e23b0f..dca593aa 100644 --- a/tests/testdata/load/expected.csv +++ b/tests/testdata/test_model/expected.csv @@ -1,2 +1,2 @@ -about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_key,license_expression,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version -/load/this.ABOUT,.,AboutCode,0.11.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_expression,license_key,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version +/test_model/this.ABOUT,.,AboutCode,0.11.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/tests/testdata/test_model/expected.json b/tests/testdata/test_model/expected.json new file mode 100644 index 00000000..feb6cf51 --- /dev/null +++ b/tests/testdata/test_model/expected.json @@ -0,0 +1,8 @@ +[ + { + "about_file_path": "/test_model/this.ABOUT", + "about_resource": ".", + "name": "AboutCode", + "version": "0.11.0" + } +] \ No newline at end of file diff --git a/tests/testdata/fields/license.LICENSE b/tests/testdata/test_model/fields/license.LICENSE similarity index 100% rename from tests/testdata/fields/license.LICENSE rename to tests/testdata/test_model/fields/license.LICENSE diff --git a/tests/testdata/inventory/complex/about/NOTICE b/tests/testdata/test_model/inventory/basic/about/NOTICE similarity index 100% rename from tests/testdata/inventory/complex/about/NOTICE rename to tests/testdata/test_model/inventory/basic/about/NOTICE diff --git a/tests/testdata/inventory/basic/about/about.ABOUT b/tests/testdata/test_model/inventory/basic/about/about.ABOUT similarity index 100% rename from tests/testdata/inventory/basic/about/about.ABOUT rename to tests/testdata/test_model/inventory/basic/about/about.ABOUT diff --git a/tests/testdata/genattrib/apache-2.0.LICENSE b/tests/testdata/test_model/inventory/basic/about/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/genattrib/apache-2.0.LICENSE rename to tests/testdata/test_model/inventory/basic/about/apache-2.0.LICENSE diff --git a/tests/testdata/inventory/basic/expected.csv b/tests/testdata/test_model/inventory/basic/expected.csv similarity index 91% rename from tests/testdata/inventory/basic/expected.csv rename to tests/testdata/test_model/inventory/basic/expected.csv index 3a487e52..294ca176 100644 --- a/tests/testdata/inventory/basic/expected.csv +++ b/tests/testdata/test_model/inventory/basic/expected.csv @@ -1,2 +1,2 @@ -about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_key,license_expression,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version +about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_expression,license_key,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version /about/about.ABOUT,.,AboutCode,0.11.0,,AboutCode is a tool to process ABOUT files. An ABOUT file is a file.,http://dejacode.org,,apache-2.0,apache-2.0,,apache-2.0.LICENSE,,Copyright (c) 2013-2014 nexB Inc.,NOTICE,,,,,,,,nexB Inc.,,,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,git,https://github.com/dejacode/about-code-tool.git,,,,,,,, diff --git a/tests/testdata/inventory/basic_with_about_resource_path/about/about_with_about_resource_path.ABOUT b/tests/testdata/test_model/inventory/basic_with_about_resource_path/about/about_with_about_resource_path.ABOUT similarity index 100% rename from tests/testdata/inventory/basic_with_about_resource_path/about/about_with_about_resource_path.ABOUT rename to tests/testdata/test_model/inventory/basic_with_about_resource_path/about/about_with_about_resource_path.ABOUT diff --git a/tests/testdata/inventory/basic/about/apache-2.0.LICENSE b/tests/testdata/test_model/inventory/basic_with_about_resource_path/about/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/inventory/basic/about/apache-2.0.LICENSE rename to tests/testdata/test_model/inventory/basic_with_about_resource_path/about/apache-2.0.LICENSE diff --git a/tests/testdata/inventory/basic_with_about_resource_path/expected.csv b/tests/testdata/test_model/inventory/basic_with_about_resource_path/expected.csv similarity index 88% rename from tests/testdata/inventory/basic_with_about_resource_path/expected.csv rename to tests/testdata/test_model/inventory/basic_with_about_resource_path/expected.csv index e241244d..09887d6b 100644 --- a/tests/testdata/inventory/basic_with_about_resource_path/expected.csv +++ b/tests/testdata/test_model/inventory/basic_with_about_resource_path/expected.csv @@ -1,2 +1,2 @@ -about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_key,license_expression,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version +about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_expression,license_key,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version /about/about_with_about_resource_path.ABOUT,apache-2.0.LICENSE,Apache License 2.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/tests/testdata/about_locations/dir1/file2.aBout b/tests/testdata/test_model/inventory/complex/about/Jinja2-2.7.3-py2-none-any.whl similarity index 100% rename from tests/testdata/about_locations/dir1/file2.aBout rename to tests/testdata/test_model/inventory/complex/about/Jinja2-2.7.3-py2-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/Jinja2.ABOUT b/tests/testdata/test_model/inventory/complex/about/Jinja2.ABOUT new file mode 100644 index 00000000..1fff666d --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/Jinja2.ABOUT @@ -0,0 +1,15 @@ +about_resource: Jinja2-2.7.3-py2-none-any.whl +version: 2.7.3 +download_url: https://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.7.3.tar.gz#md5=b9dffd2f3b43d673802fe857c8445b1a + +name: Jinja2 +homepage_url: http://jinja.pocoo.org/ + +dje_license: bsd-new +license_text_file: Jinja2.LICENSE + +vcs_tool: git +vcs_repository: https://github.com/mitsuhiko/jinja2.git + +owner: Armin Ronacher +copyright: Copyright (c) 2009 by the Jinja Team diff --git a/tests/testdata/test_model/inventory/complex/about/Jinja2.LICENSE b/tests/testdata/test_model/inventory/complex/about/Jinja2.LICENSE new file mode 100644 index 00000000..31bf900e --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/Jinja2.LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2009 by the Jinja Team, see AUTHORS for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/testdata/about_locations/dir2/file1 b/tests/testdata/test_model/inventory/complex/about/MarkupSafe-0.23-py2-none-any.whl similarity index 100% rename from tests/testdata/about_locations/dir2/file1 rename to tests/testdata/test_model/inventory/complex/about/MarkupSafe-0.23-py2-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/MarkupSafe.ABOUT b/tests/testdata/test_model/inventory/complex/about/MarkupSafe.ABOUT new file mode 100644 index 00000000..dd631ddf --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/MarkupSafe.ABOUT @@ -0,0 +1,15 @@ +about_resource: MarkupSafe-0.23-py2-none-any.whl +version: 0.23 +download_url: https://pypi.python.org/packages/source/m/MarkupSafe/MarkupSafe-0.23.tar.gz + +name: MarkupSafe +homepage_url: https://github.com/mitsuhiko/markupsafe + +dje_license: bsd-new + +vcs_tool: git +vcs_repository: https://github.com/mitsuhiko/jinja2.git +license_text_file: MarkupSafe.LICENSE + +copyright: Copyright (c) 2010 by Armin Ronacher and contributors. +owner: Armin Ronacher diff --git a/tests/testdata/test_model/inventory/complex/about/MarkupSafe.LICENSE b/tests/testdata/test_model/inventory/complex/about/MarkupSafe.LICENSE new file mode 100644 index 00000000..5d269389 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/MarkupSafe.LICENSE @@ -0,0 +1,33 @@ +Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS +for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as well +as documentation, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/tests/testdata/parse/complete/NOTICE b/tests/testdata/test_model/inventory/complex/about/NOTICE similarity index 100% rename from tests/testdata/parse/complete/NOTICE rename to tests/testdata/test_model/inventory/complex/about/NOTICE diff --git a/tests/testdata/test_model/inventory/complex/about/PSF.LICENSE b/tests/testdata/test_model/inventory/complex/about/PSF.LICENSE new file mode 100644 index 00000000..4e9d7f25 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/PSF.LICENSE @@ -0,0 +1,635 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations (now Zope +Corporation, see http://www.zope.com). In 2001, the Python Software +Foundation (PSF, see http://www.python.org/psf/) was formed, a +non-profit organization created specifically to own Python-related +Intellectual Property. Zope Corporation is a sponsoring member of +the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.2 2.1.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2.1 2.2 2002 PSF yes + 2.2.2 2.2.1 2002 PSF yes + 2.2.3 2.2.2 2003 PSF yes + 2.3 2.2.2 2002-2003 PSF yes + 2.3.1 2.3 2002-2003 PSF yes + 2.3.2 2.3.1 2002-2003 PSF yes + 2.3.3 2.3.2 2002-2003 PSF yes + 2.3.4 2.3.3 2004 PSF yes + 2.3.5 2.3.4 2005 PSF yes + 2.4 2.3 2004 PSF yes + 2.4.1 2.4 2005 PSF yes + 2.4.2 2.4.1 2005 PSF yes + 2.4.3 2.4.2 2006 PSF yes + 2.4.4 2.4.3 2006 PSF yes + 2.5 2.4 2006 PSF yes + 2.5.1 2.5 2007 PSF yes + 2.5.2 2.5.2 2008 PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python +alone or in any derivative version, provided, however, that PSF's +License Agreement and PSF's notice of copyright, i.e., "Copyright (c) +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative +version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +This copy of Python includes a copy of bzip2, which is licensed under the following terms: + + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2005 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, Cambridge, UK. +jseward@acm.org +bzip2/libbzip2 version 1.0.3 of 15 February 2005 + + +This copy of Python includes a copy of db, which is licensed under the following terms: + +/*- + * $Id: LICENSE,v 12.1 2005/06/16 20:20:10 bostic Exp $ + */ + +The following is the license that applies to this copy of the Berkeley DB +software. For a license to use the Berkeley DB software under conditions +other than those described here, or to purchase support for this software, +please contact Sleepycat Software by email at info@sleepycat.com, or on +the Web at http://www.sleepycat.com. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +/* + * Copyright (c) 1990-2005 + * Sleepycat Software. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Redistributions in any form must be accompanied by information on + * how to obtain complete source code for the DB software and any + * accompanying software that uses the DB software. The source code + * must either be included in the distribution or be available for no + * more than the cost of distribution plus a nominal fee, and must be + * freely redistributable under reasonable conditions. For an + * executable file, complete source code means the source code for all + * modules it contains. It does not include source code for modules or + * files that typically accompany the major components of the operating + * system on which the executable file runs. + * + * THIS SOFTWARE IS PROVIDED BY SLEEPYCAT SOFTWARE ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SLEEPYCAT SOFTWARE + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1990, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Copyright (c) 1995, 1996 + * The President and Fellows of Harvard University. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY HARVARD AND ITS CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL HARVARD OR ITS CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +This copy of Python includes a copy of openssl, which is licensed under the following terms: + + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a dual license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. Actually both licenses are BSD-style + Open Source licenses. In case of any license issues related to OpenSSL + please contact openssl-core@openssl.org. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + + +This copy of Python includes a copy of tcl, which is licensed under the following terms: + +This software is copyrighted by the Regents of the University of +California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState +Corporation and other parties. The following terms apply to all files +associated with the software unless explicitly disclaimed in +individual files. + +The authors hereby grant permission to use, copy, modify, distribute, +and license this software and its documentation for any purpose, provided +that existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written agreement, +license, or royalty fee is required for any of the authorized uses. +Modifications to this software may be copyrighted by their authors +and need not follow the licensing terms described here, provided that +the new terms are clearly indicated on the first page of each file where +they apply. + +IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. + +GOVERNMENT USE: If you are acquiring this software on behalf of the +U.S. government, the Government shall have only "Restricted Rights" +in the software and related documentation as defined in the Federal +Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you +are acquiring the software on behalf of the Department of Defense, the +software shall be classified as "Commercial Computer Software" and the +Government shall have only "Restricted Rights" as defined in Clause +252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the +authors grant the U.S. Government and others acting in its behalf +permission to use and distribute the software in accordance with the +terms specified in this license. + +This copy of Python includes a copy of tk, which is licensed under the following terms: + +This software is copyrighted by the Regents of the University of +California, Sun Microsystems, Inc., and other parties. The following +terms apply to all files associated with the software unless explicitly +disclaimed in individual files. + +The authors hereby grant permission to use, copy, modify, distribute, +and license this software and its documentation for any purpose, provided +that existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written agreement, +license, or royalty fee is required for any of the authorized uses. +Modifications to this software may be copyrighted by their authors +and need not follow the licensing terms described here, provided that +the new terms are clearly indicated on the first page of each file where +they apply. + +IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. + +GOVERNMENT USE: If you are acquiring this software on behalf of the +U.S. government, the Government shall have only "Restricted Rights" +in the software and related documentation as defined in the Federal +Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you +are acquiring the software on behalf of the Department of Defense, the +software shall be classified as "Commercial Computer Software" and the +Government shall have only "Restricted Rights" as defined in Clause +252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the +authors grant the U.S. Government and others acting in its behalf +permission to use and distribute the software in accordance with the +terms specified in this license. diff --git a/tests/testdata/genattrib/about.ABOUT b/tests/testdata/test_model/inventory/complex/about/about.ABOUT similarity index 79% rename from tests/testdata/genattrib/about.ABOUT rename to tests/testdata/test_model/inventory/complex/about/about.ABOUT index 647a8adc..1e5d864b 100644 --- a/tests/testdata/genattrib/about.ABOUT +++ b/tests/testdata/test_model/inventory/complex/about/about.ABOUT @@ -11,9 +11,10 @@ homepage_url: http://dejacode.org vcs_tool: git vcs_repository: https://github.com/dejacode/about-code-tool.git -description: AboutCode is a tool - to process ABOUT files. - An ABOUT file is a file. +description: | + AboutCode is a tool + to process ABOUT files. + An ABOUT file is a file. license: apache-2.0 license_file: apache-2.0.LICENSE diff --git a/tests/testdata/test_model/inventory/complex/about/apache-2.0.LICENSE b/tests/testdata/test_model/inventory/complex/about/apache-2.0.LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/apache-2.0.LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/tests/testdata/about_locations/file with_spaces.ABOUT b/tests/testdata/test_model/inventory/complex/about/certifi-14.05.14-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/about_locations/file with_spaces.ABOUT rename to tests/testdata/test_model/inventory/complex/about/certifi-14.05.14-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/certifi.ABOUT b/tests/testdata/test_model/inventory/complex/about/certifi.ABOUT new file mode 100644 index 00000000..37c0519b --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/certifi.ABOUT @@ -0,0 +1,10 @@ +about_resource: certifi-14.05.14-py2.py3-none-any.whl +version: 14.05.14 +description: Python package for providing Mozilla's CA Bundle. + +owner: Kenneth Reitz +contact: me@kennethreitz.com +homepage_url: http://python-requests.org +name: certifi + +dje_license: mpl-2.0 \ No newline at end of file diff --git a/tests/testdata/test_model/inventory/complex/about/certifi.LICENSE b/tests/testdata/test_model/inventory/complex/about/certifi.LICENSE new file mode 100644 index 00000000..802b53ff --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/certifi.LICENSE @@ -0,0 +1,21 @@ +This packge contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +Certificate data from Mozilla as of: Thu Nov 3 19:04:19 2011# +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +http://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1# +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/tests/testdata/about_locations/file1 b/tests/testdata/test_model/inventory/complex/about/click-3.2-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/about_locations/file1 rename to tests/testdata/test_model/inventory/complex/about/click-3.2-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/click.ABOUT b/tests/testdata/test_model/inventory/complex/about/click.ABOUT new file mode 100644 index 00000000..c38195c3 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/click.ABOUT @@ -0,0 +1,18 @@ +about_resource: click-3.2-py2.py3-none-any.whl +version: 3.2 +download_url: https://pypi.python.org/packages/2.7/c/click/click-3.2-py2.py3-none-any.whl#md5=7d0bf0ca4e8ce6056e35cc8135d21abd +name: click +owner: Armin Ronacher +contact: armin.ronacher@active-4.com +homepage_url: http://click.pocoo.org/ +vcs_tool: git +vcs_repository: https://github.com/mitsuhiko/click.git +description: | + A simple wrapper around optparse for + powerful command line utilities. +dje_license: bsd-new +license_text_file: click.LICENSE +notes: | + Click uses parts of optparse written by Gregory P. Ward and maintained + by the Python software foundation. This is limited to code in the parser.py + module and is under the same license as clikc itself. diff --git a/tests/testdata/test_model/inventory/complex/about/click.LICENSE b/tests/testdata/test_model/inventory/complex/about/click.LICENSE new file mode 100644 index 00000000..1704daa2 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/click.LICENSE @@ -0,0 +1,38 @@ +Copyright (c) 2014 by Armin Ronacher. + +Click uses parts of optparse written by Gregory P. Ward and maintained by the +Python software foundation. This is limited to code in the parser.py +module: + +Copyright (c) 2001-2006 Gregory P. Ward. All rights reserved. +Copyright (c) 2002-2006 Python Software Foundation. All rights reserved. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/testdata/about_locations/file2 b/tests/testdata/test_model/inventory/complex/about/colorama-0.3.1-py2-none-any.whl similarity index 100% rename from tests/testdata/about_locations/file2 rename to tests/testdata/test_model/inventory/complex/about/colorama-0.3.1-py2-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/colorama.ABOUT b/tests/testdata/test_model/inventory/complex/about/colorama.ABOUT new file mode 100644 index 00000000..3a9a78e8 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/colorama.ABOUT @@ -0,0 +1,11 @@ +about_resource: colorama-0.3.1-py2-none-any.whl +name: colorama +version: 0.3.1 +description: Cross-platform colored terminal text. +homepage_url: https://pypi.python.org/pypi/colorama +owner: Jonathan Hartley +contact: tartley@tartley.com +dje_license: bds-new +keywords: color colour terminal text ansi windows crossplatform xplatform +license_text_file: colorama.LICENSE +download_url: https://pypi.python.org/packages/source/c/colorama/colorama-0.3.1.tar.gz#md5=95ce8bf32f5c25adea14b809db3509cb \ No newline at end of file diff --git a/tests/testdata/test_model/inventory/complex/about/colorama.LICENSE b/tests/testdata/test_model/inventory/complex/about/colorama.LICENSE new file mode 100644 index 00000000..5f567799 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/colorama.LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2010 Jonathan Hartley +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holders, nor those of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/tests/testdata/test_model/inventory/complex/about/lgpl-2.1.LICENSE b/tests/testdata/test_model/inventory/complex/about/lgpl-2.1.LICENSE new file mode 100644 index 00000000..ba428aae --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/lgpl-2.1.LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/tests/testdata/allAboutInOneDir/about_ref/t1/t2/ez_setup.py b/tests/testdata/test_model/inventory/complex/about/pip-1.5.6-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/allAboutInOneDir/about_ref/t1/t2/ez_setup.py rename to tests/testdata/test_model/inventory/complex/about/pip-1.5.6-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/pip.ABOUT b/tests/testdata/test_model/inventory/complex/about/pip.ABOUT new file mode 100644 index 00000000..f8b6fa21 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/pip.ABOUT @@ -0,0 +1,15 @@ +about_resource: pip-1.5.6-py2.py3-none-any.whl +version: 1.5.6 +download_url: https://pypi.python.org/packages/source/p/pip/pip-1.5.6.tar.gz#md5=01026f87978932060cc86c1dc527903e + +name: pip +owner: The pip developers +contact: python-virtualenv@groups.google.com +homepage_url: http://www.pip-installer.org + +author_file: pip.AUTHORS +dje_license: mit, lgpl-2.1 +license_text_file: pip.LICENSE + +vcs_tool: git +vcs_repository: https://github.com/pypa/pip.git \ No newline at end of file diff --git a/tests/testdata/test_model/inventory/complex/about/pip.AUTHORS b/tests/testdata/test_model/inventory/complex/about/pip.AUTHORS new file mode 100644 index 00000000..321c6a59 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/pip.AUTHORS @@ -0,0 +1,134 @@ +Adam Wentz +Alex Gaynor +Alex Grönholm +Alex Morega +Alexandre Conrad +Anatoly Techtonik +Andrei Geacar +Andrey Bulgakov +Anrs Hu +Anton Patrushev +Antti Kaihola +Armin Ronacher +Ashley Manton +Baptiste Mispelon +Ben Darnell +Ben Rosser +Bernardo B. Marques +Bradley Ayers +Brian Rosner +Carl Meyer +Chris McDonough +Christian Oudard +Clay McClure +Cody Soyland +Craig Kerstiens +Cristian Sorinel +Dan Sully +Daniel Holth +Daniel Jost +Dave Abrahams +David Aguilar +David Black +David Evans +David Pursehouse +Dmitry Gladkov +Donald Stufft +Dongweiming +Endoh Takanao +enoch +Erik M. Bray +Francesco +Gabriel de Perthuis +Garry Polley +Geoffrey Lehée +George Song +Georgi Valkov +Herbert Pfennig +Hsiaoming Yang +Hugo Lopes Tavares +Hynek Schlawack +Ian Bicking +Igor Sobreira +Ilya Baryshev +Ionel Maries Cristian +Jakub Stasiak +Jakub Vysoky +James Cleveland +Jannis Leidel +Jay Graves +Jeff Dairiki +Jim Garrison +John-Scott Atlakson +Jon Parise +Jonas Nockert +Jorge Niedbalski +Josh Bronson +Josh Hansen +Kamal Bin Mustafa +Kelsey Hightower +Kenneth Belitzky +Kenneth Reitz +Kevin Frommelt +Kumar McMillan +Lev Givon +Lincoln de Sousa +Luke Macken +Marc Abramowitz +Marc Tamlyn +Marcus Smith +Markus Hametner +Masklinn +Matt Maker +Matthew Iversen +Maxime Rouyrre +Michael Williamson +Miguel Araujo Perez +Monty Taylor +Nick Stenning +Nowell Strite +Oliver Tonnhofer +Olivier Girardot +Ollie Rutherfurd +Oren Held +Oscar Benjamin +Patrick Dubroy +Patrick Jenkins +Paul Moore +Paul Nasrat +Paul Oswald +Paul van der Linden +Peter Waller +Phil Freo +Phil Whelan +Piet Delport +Preston Holmes +Przemek Wrzos +Qiangning Hong +Rafael Caricio +Ralf Schmitt +Rene Dudfield +Roey Berman +Ronny Pfannschmidt +Rory McCann +Ross Brattain +Sergey Vasilyev +Seth Woodworth +Simon Cross +Stavros Korokithakis +Stefan Scherfke +Steven Myint +Stéphane Klein +Takayuki SHIMIZUKAWA +Thomas Fenzl +Thomas Johansson +Toshio Kuratomi +Travis Swicegood +Vinay Sajip +Vitaly Babiy +W. Trevor King +Wil Tan +Yoval P +Yu Jian +Zearin +Zhiping Deng \ No newline at end of file diff --git a/tests/testdata/test_model/inventory/complex/about/pip.LICENSE b/tests/testdata/test_model/inventory/complex/about/pip.LICENSE new file mode 100644 index 00000000..6fd7633e --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/pip.LICENSE @@ -0,0 +1,39 @@ +Copyright (c) 2008-2014 The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +License for Bundle of CA Root Certificates (pip/cacert.pem) +=========================================================== + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +02110-1301 diff --git a/tests/testdata/filesfields/test_folder/test.py b/tests/testdata/test_model/inventory/complex/about/py-1.4.23-py2-none-any.whl similarity index 100% rename from tests/testdata/filesfields/test_folder/test.py rename to tests/testdata/test_model/inventory/complex/about/py-1.4.23-py2-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/py.ABOUT b/tests/testdata/test_model/inventory/complex/about/py.ABOUT new file mode 100644 index 00000000..cdc78953 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/py.ABOUT @@ -0,0 +1,15 @@ +about_resource: py-1.4.23-py2-none-any.whl +version: 1.4.23 +download_url: https://pypi.python.org/packages/source/p/py/py-1.4.23.tar.gz#md5=b40aea711eeb8adba0c44f0b750a3205 + +name: py +description: library with cross-python path, ini-parsing, io, code, log facilities +homepage_url: http://pylib.readthedocs.org/ +owner: holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others +contact: pytest-dev@python.org +dje_license: mit +license_text_file: py.LICENSE +copyright: Holger Krekel and others, 2004-2014 + + + diff --git a/tests/testdata/test_model/inventory/complex/about/py.LICENSE b/tests/testdata/test_model/inventory/complex/about/py.LICENSE new file mode 100644 index 00000000..31ecdfb1 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/py.LICENSE @@ -0,0 +1,19 @@ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + diff --git a/tests/testdata/locations/dir1/dir2/file1 b/tests/testdata/test_model/inventory/complex/about/pytest-2.6.1-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/locations/dir1/dir2/file1 rename to tests/testdata/test_model/inventory/complex/about/pytest-2.6.1-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/pytest.ABOUT b/tests/testdata/test_model/inventory/complex/about/pytest.ABOUT new file mode 100644 index 00000000..bc05ec02 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/pytest.ABOUT @@ -0,0 +1,12 @@ +download_url: https://pypi.python.org/packages/source/p/pytest/pytest-2.6.1.tar.gz#md5=bb353f6cf6d9ff83ff7f2dfbeaca47a3 +about_resource: pytest-2.6.1-py2.py3-none-any.whl +version: 2.6.1 + +name: pytest +description: pytest - simple powerful testing with Python +homepage_url: http://pytest.org +owner: Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others +contact: holger at merlinux.eu +dje_license: mit +license_text_file: pytest.LICENSE +copyright: Copyright Holger Krekel and others, 2004-2014 diff --git a/tests/testdata/test_model/inventory/complex/about/pytest.LICENSE b/tests/testdata/test_model/inventory/complex/about/pytest.LICENSE new file mode 100644 index 00000000..31ecdfb1 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/pytest.LICENSE @@ -0,0 +1,19 @@ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + diff --git a/tests/testdata/locations/dir1/file2 b/tests/testdata/test_model/inventory/complex/about/schematics-0.9_5-py2-none-any.whl similarity index 100% rename from tests/testdata/locations/dir1/file2 rename to tests/testdata/test_model/inventory/complex/about/schematics-0.9_5-py2-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/schematics.ABOUT b/tests/testdata/test_model/inventory/complex/about/schematics.ABOUT new file mode 100644 index 00000000..8fdaf90a --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/schematics.ABOUT @@ -0,0 +1,12 @@ +about_resource: schematics-0.9_5-py2-none-any.whl +name: schematics +version: 0.9-5 + +download_url: https://pypi.python.org/packages/source/s/schematics/schematics-0.9-5.tar.gz#md5=82ba0d67aa2600421877edcd9e7500f7 + +homepage_url: https://github.com/schematics/schematics +owner: J2 Labs LLC. +copyright: Copyright (c) 2013, J2 Labs LLC. + +dje_license: bsd-new +license_text_file: schematics.LICENSE \ No newline at end of file diff --git a/tests/testdata/test_model/inventory/complex/about/schematics.LICENSE b/tests/testdata/test_model/inventory/complex/about/schematics.LICENSE new file mode 100644 index 00000000..13f74e1a --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/schematics.LICENSE @@ -0,0 +1,30 @@ +(The BSD License) + +Copyright (c) 2013, J2 Labs LLC. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Schematics nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tests/testdata/locations/dir2/file1 b/tests/testdata/test_model/inventory/complex/about/setuptools-5.6-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/locations/dir2/file1 rename to tests/testdata/test_model/inventory/complex/about/setuptools-5.6-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/setuptools.ABOUT b/tests/testdata/test_model/inventory/complex/about/setuptools.ABOUT new file mode 100644 index 00000000..80231477 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/setuptools.ABOUT @@ -0,0 +1,11 @@ +about_resource: setuptools-5.6-py2.py3-none-any.whl +name: setuptools +version: 5.6 + +download_url: https://pypi.python.org/packages/3.4/s/setuptools/setuptools-5.6-py2.py3-none-any.whl#md5=4503e42d67edc51e293ba9be4af799a5 + +homepage_url: https://pypi.python.org/pypi/setuptools +owner: Python Packaging Authority + +dje_license: psf +license_text_file: PSF.LICENSE \ No newline at end of file diff --git a/tests/testdata/locations/file with_spaces b/tests/testdata/test_model/inventory/complex/about/unicodecsv-0.9.4-py2-none-any.whl similarity index 100% rename from tests/testdata/locations/file with_spaces rename to tests/testdata/test_model/inventory/complex/about/unicodecsv-0.9.4-py2-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/unicodecsv.ABOUT b/tests/testdata/test_model/inventory/complex/about/unicodecsv.ABOUT new file mode 100644 index 00000000..932c01e3 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/unicodecsv.ABOUT @@ -0,0 +1,13 @@ +about_resource: unicodecsv-0.9.4-py2-none-any.whl +version: 0.9.4 +download_url: https://pypi.python.org/packages/source/u/unicodecsv/unicodecsv-0.9.4.tar.gz#md5=344fa55f299ba198cb73db48546002fd + +name: unicodecsv +homepage_url: https://github.com/jdunck/python-unicodecsv +owner: Jeremy Dunck + +dje_license: bsd-new +license_text_file: unicodecsv.LICENSE + +vcs_tool: git +vcs_repository: https://github.com/jdunck/python-unicodecsv.git diff --git a/tests/testdata/test_model/inventory/complex/about/unicodecsv.LICENSE b/tests/testdata/test_model/inventory/complex/about/unicodecsv.LICENSE new file mode 100644 index 00000000..6d004c77 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/unicodecsv.LICENSE @@ -0,0 +1,25 @@ +Copyright 2010 Jeremy Dunck. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY JEREMY DUNCK ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JEREMY DUNCK OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of Jeremy Dunck. diff --git a/tests/testdata/locations/file1 b/tests/testdata/test_model/inventory/complex/about/virtualenv-1.11.6-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/locations/file1 rename to tests/testdata/test_model/inventory/complex/about/virtualenv-1.11.6-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/virtualenv.ABOUT b/tests/testdata/test_model/inventory/complex/about/virtualenv.ABOUT new file mode 100644 index 00000000..79170066 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/virtualenv.ABOUT @@ -0,0 +1,18 @@ +about_resource: virtualenv-1.11.6-py2.py3-none-any.whl +version: 1.11.6 +download_url: https://raw.githubusercontent.com/pypa/virtualenv/1.11.6/virtualenv.py + +vcs_tool: git +vcs_repository: https://github.com/pypa/virtualenv.git + +name: virtualenv +homepage_url: http://virtualenv.org/ +owner: The virtualenv developers + +license_url: https://raw.github.com/pypa/virtualenv/develop/LICENSE.txt +license_text_file: virtualenv.LICENSE +dje_license: mit +copyright: | + Copyright (c) 2007 Ian Bicking and Contributors + Copyright (c) 2009 Ian Bicking, The Open Planning Project + Copyright (c) 2011-2014 The virtualenv developers diff --git a/tests/testdata/test_model/inventory/complex/about/virtualenv.LICENSE b/tests/testdata/test_model/inventory/complex/about/virtualenv.LICENSE new file mode 100644 index 00000000..7e00d5d5 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/virtualenv.LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2007 Ian Bicking and Contributors +Copyright (c) 2009 Ian Bicking, The Open Planning Project +Copyright (c) 2011-2014 The virtualenv developers + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/testdata/locations/file2 b/tests/testdata/test_model/inventory/complex/about/virtualenv.py similarity index 100% rename from tests/testdata/locations/file2 rename to tests/testdata/test_model/inventory/complex/about/virtualenv.py diff --git a/tests/testdata/test_model/inventory/complex/about/virtualenv.py.ABOUT b/tests/testdata/test_model/inventory/complex/about/virtualenv.py.ABOUT new file mode 100644 index 00000000..b3192200 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/virtualenv.py.ABOUT @@ -0,0 +1,18 @@ +about_resource: virtualenv.py +version: 1.11.6 +download_url: https://raw.githubusercontent.com/pypa/virtualenv/1.11.6/virtualenv.py + +vcs_tool: git +vcs_repository: https://github.com/pypa/virtualenv.git + +name: virtualenv +homepage_url: http://virtualenv.org/ +owner: The virtualenv developers + +license_url: https://raw.github.com/pypa/virtualenv/develop/LICENSE.txt +license_text_file: virtualenv.LICENSE +dje_license: mit +copyright: | + Copyright (c) 2007 Ian Bicking and Contributors + Copyright (c) 2009 Ian Bicking, The Open Planning Project + Copyright (c) 2011-2014 The virtualenv developers diff --git a/tests/testdata/mandatory_fields/empty.about b/tests/testdata/test_model/inventory/complex/about/wheel-0.24.0-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/mandatory_fields/empty.about rename to tests/testdata/test_model/inventory/complex/about/wheel-0.24.0-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/wheel.ABOUT b/tests/testdata/test_model/inventory/complex/about/wheel.ABOUT new file mode 100644 index 00000000..338c37da --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/wheel.ABOUT @@ -0,0 +1,14 @@ +about_resource: wheel-0.24.0-py2.py3-none-any.whl +version: 0.24.0 +download_url: https://pypi.python.org/packages/py2.py3/w/wheel/wheel-0.24.0-py2.py3-none-any.whl#md5=4c24453cda2177fd42c5d62d6434679a + +name: wheel +homepage_url: https://bitbucket.org/pypa/wheel +vcs_tool: hg +vcs_repository: https://bitbucket.org/pypa/wheel + +copyright: | + copyright (c) 2012-2014 Daniel Holth and + contributors. +dje_license: mit +license_text_file: wheel.LICENSE diff --git a/tests/testdata/test_model/inventory/complex/about/wheel.LICENSE b/tests/testdata/test_model/inventory/complex/about/wheel.LICENSE new file mode 100644 index 00000000..c3441e6c --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/wheel.LICENSE @@ -0,0 +1,22 @@ +"wheel" copyright (c) 2012-2014 Daniel Holth and +contributors. + +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/testdata/thirdparty/FixedHeader-2.0.6.zip b/tests/testdata/test_model/inventory/complex/about/wincertstore-0.2-py2.py3-none-any.whl similarity index 100% rename from tests/testdata/thirdparty/FixedHeader-2.0.6.zip rename to tests/testdata/test_model/inventory/complex/about/wincertstore-0.2-py2.py3-none-any.whl diff --git a/tests/testdata/test_model/inventory/complex/about/wincertstore.ABOUT b/tests/testdata/test_model/inventory/complex/about/wincertstore.ABOUT new file mode 100644 index 00000000..634c43ce --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/wincertstore.ABOUT @@ -0,0 +1,13 @@ +about_resource: wincertstore-0.2-py2.py3-none-any.whl +version: 0.2 +download_url: https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip + +name: wincertstore + +dje_license: psf +license_text_file: wincertstore.LICENSE + +contact: christian@python.org +owner: Christian Heimes +homepage_url: https://bitbucket.org/tiran/wincertstore +description: Python module to extract CA and CRL certs from Windows' cert store (ctypes based). diff --git a/tests/testdata/test_model/inventory/complex/about/wincertstore.LICENSE b/tests/testdata/test_model/inventory/complex/about/wincertstore.LICENSE new file mode 100644 index 00000000..311690c6 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about/wincertstore.LICENSE @@ -0,0 +1,49 @@ +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python +alone or in any derivative version, provided, however, that PSF's +License Agreement and PSF's notice of copyright, i.e., "Copyright (c) +2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative +version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + diff --git a/tests/testdata/test_model/inventory/complex/about_file_path_dir_endswith_space.csv b/tests/testdata/test_model/inventory/complex/about_file_path_dir_endswith_space.csv new file mode 100644 index 00000000..a1bbfbd7 --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/about_file_path_dir_endswith_space.csv @@ -0,0 +1,2 @@ +about_file_path,about_resource,name,version +about /about.ABOUT,.,AboutCode,0.11.0 diff --git a/tests/testdata/test_model/inventory/complex/expected.csv b/tests/testdata/test_model/inventory/complex/expected.csv new file mode 100644 index 00000000..4875207b --- /dev/null +++ b/tests/testdata/test_model/inventory/complex/expected.csv @@ -0,0 +1,27 @@ +about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_expression,license_key,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version +/about/pytest.ABOUT,pytest-2.6.1-py2.py3-none-any.whl,pytest,2.6.1,https://pypi.python.org/packages/source/p/pytest/pytest-2.6.1.tar.gz#md5=bb353f6cf6d9ff83ff7f2dfbeaca47a3,pytest - simple powerful testing with Python,http://pytest.org,,,,,,,"Copyright Holger Krekel and others, 2004-2014",,,,,,,,,"Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others",,holger at merlinux.eu,,,,,,,,,,,, +/about/Jinja2.ABOUT,Jinja2-2.7.3-py2-none-any.whl,Jinja2,2.7.3,https://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.7.3.tar.gz#md5=b9dffd2f3b43d673802fe857c8445b1a,,http://jinja.pocoo.org/,,,,,,,Copyright (c) 2009 by the Jinja Team,,,,,,,,,Armin Ronacher,,,,,git,https://github.com/mitsuhiko/jinja2.git,,,,,,,, +/about/about.ABOUT,.,AboutCode,0.11.0,,"AboutCode is a tool +to process ABOUT files. +An ABOUT file is a file.",http://dejacode.org,,,,,apache-2.0.LICENSE,,Copyright (c) 2013-2014 nexB Inc.,NOTICE,,,,,,,,nexB Inc.,,,"Jillian Daguil, Chin Yeung Li, Philippe Ombredanne, Thomas Druez",,git,https://github.com/dejacode/about-code-tool.git,,,,,,,, +/about/virtualenv.py.ABOUT,virtualenv.py,virtualenv,1.11.6,https://raw.githubusercontent.com/pypa/virtualenv/1.11.6/virtualenv.py,,http://virtualenv.org/,,,,,,https://raw.github.com/pypa/virtualenv/develop/LICENSE.txt,"Copyright (c) 2007 Ian Bicking and Contributors +Copyright (c) 2009 Ian Bicking, The Open Planning Project +Copyright (c) 2011-2014 The virtualenv developers",,,,,,,,,The virtualenv developers,,,,,git,https://github.com/pypa/virtualenv.git,,,,,,,, +/about/setuptools.ABOUT,setuptools-5.6-py2.py3-none-any.whl,setuptools,5.6,https://pypi.python.org/packages/3.4/s/setuptools/setuptools-5.6-py2.py3-none-any.whl#md5=4503e42d67edc51e293ba9be4af799a5,,https://pypi.python.org/pypi/setuptools,,,,,,,,,,,,,,,,Python Packaging Authority,,,,,,,,,,,,,, +/about/MarkupSafe.ABOUT,MarkupSafe-0.23-py2-none-any.whl,MarkupSafe,0.23,https://pypi.python.org/packages/source/m/MarkupSafe/MarkupSafe-0.23.tar.gz,,https://github.com/mitsuhiko/markupsafe,,,,,,,Copyright (c) 2010 by Armin Ronacher and contributors.,,,,,,,,,Armin Ronacher,,,,,git,https://github.com/mitsuhiko/jinja2.git,,,,,,,, +/about/certifi.ABOUT,certifi-14.05.14-py2.py3-none-any.whl,certifi,14.05.14,,Python package for providing Mozilla's CA Bundle.,http://python-requests.org,,,,,,,,,,,,,,,,Kenneth Reitz,,me@kennethreitz.com,,,,,,,,,,,, +/about/virtualenv.ABOUT,virtualenv-1.11.6-py2.py3-none-any.whl,virtualenv,1.11.6,https://raw.githubusercontent.com/pypa/virtualenv/1.11.6/virtualenv.py,,http://virtualenv.org/,,,,,,https://raw.github.com/pypa/virtualenv/develop/LICENSE.txt,"Copyright (c) 2007 Ian Bicking and Contributors +Copyright (c) 2009 Ian Bicking, The Open Planning Project +Copyright (c) 2011-2014 The virtualenv developers",,,,,,,,,The virtualenv developers,,,,,git,https://github.com/pypa/virtualenv.git,,,,,,,, +/about/wincertstore.ABOUT,wincertstore-0.2-py2.py3-none-any.whl,wincertstore,0.2,https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip,Python module to extract CA and CRL certs from Windows' cert store (ctypes based).,https://bitbucket.org/tiran/wincertstore,,,,,,,,,,,,,,,,Christian Heimes,,christian@python.org,,,,,,,,,,,, +/about/colorama.ABOUT,colorama-0.3.1-py2-none-any.whl,colorama,0.3.1,https://pypi.python.org/packages/source/c/colorama/colorama-0.3.1.tar.gz#md5=95ce8bf32f5c25adea14b809db3509cb,Cross-platform colored terminal text.,https://pypi.python.org/pypi/colorama,,,,,,,,,,,,,,,,Jonathan Hartley,,tartley@tartley.com,,,,,,,,,,,, +/about/schematics.ABOUT,schematics-0.9_5-py2-none-any.whl,schematics,0.9-5,https://pypi.python.org/packages/source/s/schematics/schematics-0.9-5.tar.gz#md5=82ba0d67aa2600421877edcd9e7500f7,,https://github.com/schematics/schematics,,,,,,,"Copyright (c) 2013, J2 Labs LLC.",,,,,,,,,J2 Labs LLC.,,,,,,,,,,,,,, +/about/click.ABOUT,click-3.2-py2.py3-none-any.whl,click,3.2,https://pypi.python.org/packages/2.7/c/click/click-3.2-py2.py3-none-any.whl#md5=7d0bf0ca4e8ce6056e35cc8135d21abd,"A simple wrapper around optparse for +powerful command line utilities.",http://click.pocoo.org/,"Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python software foundation. This is limited to code in the parser.py +module and is under the same license as clikc itself.",,,,,,,,,,,,,,,Armin Ronacher,,armin.ronacher@active-4.com,,,git,https://github.com/mitsuhiko/click.git,,,,,,,, +/about/wheel.ABOUT,wheel-0.24.0-py2.py3-none-any.whl,wheel,0.24.0,https://pypi.python.org/packages/py2.py3/w/wheel/wheel-0.24.0-py2.py3-none-any.whl#md5=4c24453cda2177fd42c5d62d6434679a,,https://bitbucket.org/pypa/wheel,,,,,,,"copyright (c) 2012-2014 Daniel Holth and +contributors.",,,,,,,,,,,,,,hg,https://bitbucket.org/pypa/wheel,,,,,,,, +/about/unicodecsv.ABOUT,unicodecsv-0.9.4-py2-none-any.whl,unicodecsv,0.9.4,https://pypi.python.org/packages/source/u/unicodecsv/unicodecsv-0.9.4.tar.gz#md5=344fa55f299ba198cb73db48546002fd,,https://github.com/jdunck/python-unicodecsv,,,,,,,,,,,,,,,,Jeremy Dunck,,,,,git,https://github.com/jdunck/python-unicodecsv.git,,,,,,,, +/about/pip.ABOUT,pip-1.5.6-py2.py3-none-any.whl,pip,1.5.6,https://pypi.python.org/packages/source/p/pip/pip-1.5.6.tar.gz#md5=01026f87978932060cc86c1dc527903e,,http://www.pip-installer.org,,,,,,,,,,,,,,,,The pip developers,,python-virtualenv@groups.google.com,,pip.AUTHORS,git,https://github.com/pypa/pip.git,,,,,,,, +/about/py.ABOUT,py-1.4.23-py2-none-any.whl,py,1.4.23,https://pypi.python.org/packages/source/p/py/py-1.4.23.tar.gz#md5=b40aea711eeb8adba0c44f0b750a3205,"library with cross-python path, ini-parsing, io, code, log facilities",http://pylib.readthedocs.org/,,,,,,,"Holger Krekel and others, 2004-2014",,,,,,,,,"holger krekel, Ronny Pfannschmidt, Benjamin Peterson and others",,pytest-dev@python.org,,,,,,,,,,,, diff --git a/tests/testdata/inventory/no_about_resource_key/about/about.ABOUT b/tests/testdata/test_model/inventory/no_about_resource_key/about/about.ABOUT similarity index 100% rename from tests/testdata/inventory/no_about_resource_key/about/about.ABOUT rename to tests/testdata/test_model/inventory/no_about_resource_key/about/about.ABOUT diff --git a/tests/testdata/inventory/no_about_resource_key/expected.csv b/tests/testdata/test_model/inventory/no_about_resource_key/expected.csv similarity index 86% rename from tests/testdata/inventory/no_about_resource_key/expected.csv rename to tests/testdata/test_model/inventory/no_about_resource_key/expected.csv index 35350469..0cae102c 100644 --- a/tests/testdata/inventory/no_about_resource_key/expected.csv +++ b/tests/testdata/test_model/inventory/no_about_resource_key/expected.csv @@ -1,2 +1,2 @@ -about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_key,license_expression,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version +about_file_path,about_resource,name,version,download_url,description,homepage_url,notes,license_expression,license_key,license_name,license_file,license_url,copyright,notice_file,notice_url,redistribute,attribute,track_changes,modified,internal_use_only,changelog_file,owner,owner_url,contact,author,author_file,vcs_tool,vcs_repository,vcs_path,vcs_tag,vcs_branch,vcs_revision,checksum_md5,checksum_sha1,checksum_sha256,spec_version /about/about.ABOUT,,AboutCode,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, diff --git a/tests/testdata/longpath.zip b/tests/testdata/test_model/longpath.zip similarity index 100% rename from tests/testdata/longpath.zip rename to tests/testdata/test_model/longpath.zip diff --git a/tests/testdata/test_model/parse/basic.about b/tests/testdata/test_model/parse/basic.about new file mode 100644 index 00000000..b722a270 --- /dev/null +++ b/tests/testdata/test_model/parse/basic.about @@ -0,0 +1,2 @@ +single_line: optional +other_field: value diff --git a/tests/testdata/parse/complete2/NOTICE b/tests/testdata/test_model/parse/complete/NOTICE similarity index 100% rename from tests/testdata/parse/complete2/NOTICE rename to tests/testdata/test_model/parse/complete/NOTICE diff --git a/tests/testdata/parse/complete/about.ABOUT b/tests/testdata/test_model/parse/complete/about.ABOUT similarity index 100% rename from tests/testdata/parse/complete/about.ABOUT rename to tests/testdata/test_model/parse/complete/about.ABOUT diff --git a/tests/testdata/inventory/basic_with_about_resource_path/about/apache-2.0.LICENSE b/tests/testdata/test_model/parse/complete/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/inventory/basic_with_about_resource_path/about/apache-2.0.LICENSE rename to tests/testdata/test_model/parse/complete/apache-2.0.LICENSE diff --git a/tests/testdata/test_model/parse/complete2/NOTICE b/tests/testdata/test_model/parse/complete2/NOTICE new file mode 100644 index 00000000..3ffe90b9 --- /dev/null +++ b/tests/testdata/test_model/parse/complete2/NOTICE @@ -0,0 +1 @@ + Copyright (c) 2013-2014 nexB Inc. http://www.nexb.com/ - All rights reserved. diff --git a/tests/testdata/parse/complete2/about.ABOUT b/tests/testdata/test_model/parse/complete2/about.ABOUT similarity index 100% rename from tests/testdata/parse/complete2/about.ABOUT rename to tests/testdata/test_model/parse/complete2/about.ABOUT diff --git a/tests/testdata/parse/complete2/about2.ABOUT b/tests/testdata/test_model/parse/complete2/about2.ABOUT similarity index 100% rename from tests/testdata/parse/complete2/about2.ABOUT rename to tests/testdata/test_model/parse/complete2/about2.ABOUT diff --git a/tests/testdata/parse/complete/apache-2.0.LICENSE b/tests/testdata/test_model/parse/complete2/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/parse/complete/apache-2.0.LICENSE rename to tests/testdata/test_model/parse/complete2/apache-2.0.LICENSE diff --git a/tests/testdata/test_model/parse/complex.about b/tests/testdata/test_model/parse/complex.about new file mode 100644 index 00000000..242a4847 --- /dev/null +++ b/tests/testdata/test_model/parse/complex.about @@ -0,0 +1,10 @@ +single_line: optional +other_field: value + + +multi_line: some value + and more + and yet more + +yetanother: + sdasd \ No newline at end of file diff --git a/tests/testdata/test_model/parse/continuation.about b/tests/testdata/test_model/parse/continuation.about new file mode 100644 index 00000000..27d6ab2d --- /dev/null +++ b/tests/testdata/test_model/parse/continuation.about @@ -0,0 +1,5 @@ +single_line: optional +other_field: value +multi_line: some value + and more + and yet more diff --git a/tests/testdata/test_model/parse/continuation_verbatim.about b/tests/testdata/test_model/parse/continuation_verbatim.about new file mode 100644 index 00000000..293795a5 --- /dev/null +++ b/tests/testdata/test_model/parse/continuation_verbatim.about @@ -0,0 +1,9 @@ +single_line: optional +other_field: value + + +multi_line: | + some value + and more + and yet more + diff --git a/tests/testdata/parse/dupe_field_name.ABOUT b/tests/testdata/test_model/parse/dupe_field_name.ABOUT similarity index 100% rename from tests/testdata/parse/dupe_field_name.ABOUT rename to tests/testdata/test_model/parse/dupe_field_name.ABOUT diff --git a/tests/testdata/parse/empty_notice_field.about b/tests/testdata/test_model/parse/empty_notice_field.about similarity index 100% rename from tests/testdata/parse/empty_notice_field.about rename to tests/testdata/test_model/parse/empty_notice_field.about diff --git a/tests/testdata/parse/empty_required.ABOUT b/tests/testdata/test_model/parse/empty_required.ABOUT similarity index 100% rename from tests/testdata/parse/empty_required.ABOUT rename to tests/testdata/test_model/parse/empty_required.ABOUT diff --git a/tests/testdata/parse/illegal_custom_field.about b/tests/testdata/test_model/parse/illegal_custom_field.about similarity index 100% rename from tests/testdata/parse/illegal_custom_field.about rename to tests/testdata/test_model/parse/illegal_custom_field.about diff --git a/tests/testdata/parse/invalid_boolean.about b/tests/testdata/test_model/parse/invalid_boolean.about similarity index 100% rename from tests/testdata/parse/invalid_boolean.about rename to tests/testdata/test_model/parse/invalid_boolean.about diff --git a/tests/testdata/test_model/parse/invalid_continuation.about b/tests/testdata/test_model/parse/invalid_continuation.about new file mode 100644 index 00000000..a9be4d1f --- /dev/null +++ b/tests/testdata/test_model/parse/invalid_continuation.about @@ -0,0 +1,7 @@ +single_line: optional +other_field: value + +multi_line: some value + and more + + invalid continuation2 diff --git a/tests/testdata/parse/invalid_names.about b/tests/testdata/test_model/parse/invalid_names.about similarity index 100% rename from tests/testdata/parse/invalid_names.about rename to tests/testdata/test_model/parse/invalid_names.about diff --git a/tests/testdata/parse/license_file_notice_file.ABOUT b/tests/testdata/test_model/parse/license_file_notice_file.ABOUT similarity index 100% rename from tests/testdata/parse/license_file_notice_file.ABOUT rename to tests/testdata/test_model/parse/license_file_notice_file.ABOUT diff --git a/tests/testdata/attrib/license_text.LICENSE b/tests/testdata/test_model/parse/license_text.LICENSE similarity index 100% rename from tests/testdata/attrib/license_text.LICENSE rename to tests/testdata/test_model/parse/license_text.LICENSE diff --git a/tests/testdata/parse/missing_notice_file.ABOUT b/tests/testdata/test_model/parse/missing_notice_file.ABOUT similarity index 100% rename from tests/testdata/parse/missing_notice_file.ABOUT rename to tests/testdata/test_model/parse/missing_notice_file.ABOUT diff --git a/tests/testdata/parse/missing_notice_license_files.ABOUT b/tests/testdata/test_model/parse/missing_notice_license_files.ABOUT similarity index 100% rename from tests/testdata/parse/missing_notice_license_files.ABOUT rename to tests/testdata/test_model/parse/missing_notice_license_files.ABOUT diff --git a/tests/testdata/parse/missing_required.ABOUT b/tests/testdata/test_model/parse/missing_required.ABOUT similarity index 100% rename from tests/testdata/parse/missing_required.ABOUT rename to tests/testdata/test_model/parse/missing_required.ABOUT diff --git a/tests/testdata/parse/multi_line_license_expresion.ABOUT b/tests/testdata/test_model/parse/multi_line_license_expresion.ABOUT similarity index 100% rename from tests/testdata/parse/multi_line_license_expresion.ABOUT rename to tests/testdata/test_model/parse/multi_line_license_expresion.ABOUT diff --git a/tests/testdata/parse/name_mapping_test.ABOUT b/tests/testdata/test_model/parse/name_mapping_test.ABOUT similarity index 100% rename from tests/testdata/parse/name_mapping_test.ABOUT rename to tests/testdata/test_model/parse/name_mapping_test.ABOUT diff --git a/tests/testdata/parse/no_file_fields.ABOUT b/tests/testdata/test_model/parse/no_file_fields.ABOUT similarity index 100% rename from tests/testdata/parse/no_file_fields.ABOUT rename to tests/testdata/test_model/parse/no_file_fields.ABOUT diff --git a/tests/testdata/parse/non_ascii_field_name_value.about b/tests/testdata/test_model/parse/non_ascii_field_name_value.about similarity index 100% rename from tests/testdata/parse/non_ascii_field_name_value.about rename to tests/testdata/test_model/parse/non_ascii_field_name_value.about diff --git a/tests/testdata/attrib/notice_text.NOTICE b/tests/testdata/test_model/parse/notice_text.NOTICE similarity index 100% rename from tests/testdata/attrib/notice_text.NOTICE rename to tests/testdata/test_model/parse/notice_text.NOTICE diff --git a/tests/testdata/parse/ordered_fields.ABOUT b/tests/testdata/test_model/parse/ordered_fields.ABOUT similarity index 100% rename from tests/testdata/parse/ordered_fields.ABOUT rename to tests/testdata/test_model/parse/ordered_fields.ABOUT diff --git a/tests/testdata/allAboutInOneDir/about_ref/csv_serialize.py.ABOUT b/tests/testdata/test_model/rel/allAboutInOneDir/about_ref/csv_serialize.py.ABOUT similarity index 100% rename from tests/testdata/allAboutInOneDir/about_ref/csv_serialize.py.ABOUT rename to tests/testdata/test_model/rel/allAboutInOneDir/about_ref/csv_serialize.py.ABOUT diff --git a/tests/testdata/allAboutInOneDir/about_ref/django_snippets_2413.ABOUT b/tests/testdata/test_model/rel/allAboutInOneDir/about_ref/django_snippets_2413.ABOUT similarity index 100% rename from tests/testdata/allAboutInOneDir/about_ref/django_snippets_2413.ABOUT rename to tests/testdata/test_model/rel/allAboutInOneDir/about_ref/django_snippets_2413.ABOUT diff --git a/tests/testdata/allAboutInOneDir/about_ref/elasticsearch.ABOUT b/tests/testdata/test_model/rel/allAboutInOneDir/about_ref/elasticsearch.ABOUT similarity index 100% rename from tests/testdata/allAboutInOneDir/about_ref/elasticsearch.ABOUT rename to tests/testdata/test_model/rel/allAboutInOneDir/about_ref/elasticsearch.ABOUT diff --git a/tests/testdata/allAboutInOneDir/about_ref/ez_setup.py.ABOUT b/tests/testdata/test_model/rel/allAboutInOneDir/about_ref/ez_setup.py.ABOUT similarity index 100% rename from tests/testdata/allAboutInOneDir/about_ref/ez_setup.py.ABOUT rename to tests/testdata/test_model/rel/allAboutInOneDir/about_ref/ez_setup.py.ABOUT diff --git a/tests/testdata/thirdparty/ez_setup.py b/tests/testdata/test_model/rel/allAboutInOneDir/about_ref/t1/t2/ez_setup.py similarity index 100% rename from tests/testdata/thirdparty/ez_setup.py rename to tests/testdata/test_model/rel/allAboutInOneDir/about_ref/t1/t2/ez_setup.py diff --git a/tests/testdata/thirdparty/elasticsearch.NOTICE b/tests/testdata/test_model/rel/thirdparty/elasticsearch.NOTICE similarity index 100% rename from tests/testdata/thirdparty/elasticsearch.NOTICE rename to tests/testdata/test_model/rel/thirdparty/elasticsearch.NOTICE diff --git a/tests/testdata/filesfields/django_snippets.LICENSE b/tests/testdata/test_model/single_file/django_snippets.LICENSE similarity index 100% rename from tests/testdata/filesfields/django_snippets.LICENSE rename to tests/testdata/test_model/single_file/django_snippets.LICENSE diff --git a/tests/testdata/thirdparty/django_snippets_2413.ABOUT b/tests/testdata/test_model/single_file/django_snippets_2413.ABOUT similarity index 100% rename from tests/testdata/thirdparty/django_snippets_2413.ABOUT rename to tests/testdata/test_model/single_file/django_snippets_2413.ABOUT diff --git a/tests/testdata/thirdparty/django_snippets_2413.py b/tests/testdata/test_model/single_file/django_snippets_2413.py similarity index 100% rename from tests/testdata/thirdparty/django_snippets_2413.py rename to tests/testdata/test_model/single_file/django_snippets_2413.py diff --git a/tests/testdata/load/this.ABOUT b/tests/testdata/test_model/this.ABOUT similarity index 100% rename from tests/testdata/load/this.ABOUT rename to tests/testdata/test_model/this.ABOUT diff --git a/tests/testdata/unicode/nose-selecttests.ABOUT b/tests/testdata/test_model/unicode/nose-selecttests.ABOUT similarity index 100% rename from tests/testdata/unicode/nose-selecttests.ABOUT rename to tests/testdata/test_model/unicode/nose-selecttests.ABOUT diff --git a/tests/testdata/unicode/not-unicode.ABOUT b/tests/testdata/test_model/unicode/not-unicode.ABOUT similarity index 100% rename from tests/testdata/unicode/not-unicode.ABOUT rename to tests/testdata/test_model/unicode/not-unicode.ABOUT diff --git a/tests/testdata/thirdparty/Font-Awesome-v3.0.2.zip b/tests/testdata/test_util/about_locations/dir1/dir2/file1.about similarity index 100% rename from tests/testdata/thirdparty/Font-Awesome-v3.0.2.zip rename to tests/testdata/test_util/about_locations/dir1/dir2/file1.about diff --git a/tests/testdata/thirdparty/csv_serialize.py b/tests/testdata/test_util/about_locations/dir1/file2 similarity index 100% rename from tests/testdata/thirdparty/csv_serialize.py rename to tests/testdata/test_util/about_locations/dir1/file2 diff --git a/tests/testdata/thirdparty/elasticsearch-0.19.8.zip b/tests/testdata/test_util/about_locations/dir1/file2.aBout similarity index 100% rename from tests/testdata/thirdparty/elasticsearch-0.19.8.zip rename to tests/testdata/test_util/about_locations/dir1/file2.aBout diff --git a/tests/testdata/thirdparty/elasticsearch-v0.19.8-g7badcde.tar.gz b/tests/testdata/test_util/about_locations/dir2/file1 similarity index 100% rename from tests/testdata/thirdparty/elasticsearch-v0.19.8-g7badcde.tar.gz rename to tests/testdata/test_util/about_locations/dir2/file1 diff --git a/tests/testdata/thirdparty/jquery-1.7.2.js b/tests/testdata/test_util/about_locations/file with_spaces.ABOUT similarity index 100% rename from tests/testdata/thirdparty/jquery-1.7.2.js rename to tests/testdata/test_util/about_locations/file with_spaces.ABOUT diff --git a/tests/testdata/thirdparty/jquery-1.7.2.min.js b/tests/testdata/test_util/about_locations/file1 similarity index 100% rename from tests/testdata/thirdparty/jquery-1.7.2.min.js rename to tests/testdata/test_util/about_locations/file1 diff --git a/tests/testdata/thirdparty/jquery.jsPlumb-1.3.10-all-min.js b/tests/testdata/test_util/about_locations/file2 similarity index 100% rename from tests/testdata/thirdparty/jquery.jsPlumb-1.3.10-all-min.js rename to tests/testdata/test_util/about_locations/file2 diff --git a/tests/testdata/test_files_for_genabout/about.csv b/tests/testdata/test_util/csv/about.csv similarity index 100% rename from tests/testdata/test_files_for_genabout/about.csv rename to tests/testdata/test_util/csv/about.csv diff --git a/tests/testdata/test_files_for_genabout/about_key_with_upper_case.csv b/tests/testdata/test_util/csv/about_key_with_upper_case.csv similarity index 100% rename from tests/testdata/test_files_for_genabout/about_key_with_upper_case.csv rename to tests/testdata/test_util/csv/about_key_with_upper_case.csv diff --git a/tests/testdata/thirdparty/mod_wsgi-3.3.tar.gz b/tests/testdata/test_util/get_about_locations/NOTICE similarity index 100% rename from tests/testdata/thirdparty/mod_wsgi-3.3.tar.gz rename to tests/testdata/test_util/get_about_locations/NOTICE diff --git a/tests/testdata/thirdparty/okfn-annotator-549159b.zip b/tests/testdata/test_util/get_about_locations/about.ABOUT similarity index 100% rename from tests/testdata/thirdparty/okfn-annotator-549159b.zip rename to tests/testdata/test_util/get_about_locations/about.ABOUT diff --git a/tests/testdata/thirdparty/twitter_bootstrap_v2.0.3.zip b/tests/testdata/test_util/get_about_locations/apache-2.0.LICENSE similarity index 100% rename from tests/testdata/thirdparty/twitter_bootstrap_v2.0.3.zip rename to tests/testdata/test_util/get_about_locations/apache-2.0.LICENSE diff --git a/tests/testdata/basic/basic.about b/tests/testdata/test_util/inventory_filter/basic.about similarity index 100% rename from tests/testdata/basic/basic.about rename to tests/testdata/test_util/inventory_filter/basic.about diff --git a/tests/testdata/basic/simple.about b/tests/testdata/test_util/inventory_filter/simple.about similarity index 100% rename from tests/testdata/basic/simple.about rename to tests/testdata/test_util/inventory_filter/simple.about diff --git a/tests/testdata/load/aboutcode_manager_exported.json b/tests/testdata/test_util/json/aboutcode_manager_exported.json similarity index 100% rename from tests/testdata/load/aboutcode_manager_exported.json rename to tests/testdata/test_util/json/aboutcode_manager_exported.json diff --git a/tests/testdata/load/expected.json b/tests/testdata/test_util/json/expected.json similarity index 100% rename from tests/testdata/load/expected.json rename to tests/testdata/test_util/json/expected.json diff --git a/tests/testdata/load/expected_need_mapping.json b/tests/testdata/test_util/json/expected_need_mapping.json similarity index 100% rename from tests/testdata/load/expected_need_mapping.json rename to tests/testdata/test_util/json/expected_need_mapping.json diff --git a/tests/testdata/load/not_a_list.json b/tests/testdata/test_util/json/not_a_list.json similarity index 100% rename from tests/testdata/load/not_a_list.json rename to tests/testdata/test_util/json/not_a_list.json diff --git a/tests/testdata/load/not_a_list_need_mapping.json b/tests/testdata/test_util/json/not_a_list_need_mapping.json similarity index 100% rename from tests/testdata/load/not_a_list_need_mapping.json rename to tests/testdata/test_util/json/not_a_list_need_mapping.json diff --git a/tests/testdata/load/scancode_info.json b/tests/testdata/test_util/json/scancode_info.json similarity index 100% rename from tests/testdata/load/scancode_info.json rename to tests/testdata/test_util/json/scancode_info.json diff --git a/tests/testdata/test_util/longpath.zip b/tests/testdata/test_util/longpath.zip new file mode 100644 index 00000000..22107a6c Binary files /dev/null and b/tests/testdata/test_util/longpath.zip differ diff --git a/tests/testdata/test_util/mapping/case_mapping.config b/tests/testdata/test_util/mapping/case_mapping.config new file mode 100644 index 00000000..4dfceb50 --- /dev/null +++ b/tests/testdata/test_util/mapping/case_mapping.config @@ -0,0 +1,13 @@ +about_file_path: about_file +name: Component +version: Confirmed Version + +# Optional Fields +description: description +licEnse_expression: dje_license_key +copYright: Confirmed Copyright + +copYright: Confirmed Copyright + there is no multiline + +some junk line is also a comment \ No newline at end of file diff --git a/tests/testdata/test_util/mapping/dupe_keys_mapping.config b/tests/testdata/test_util/mapping/dupe_keys_mapping.config new file mode 100644 index 00000000..39d87b50 --- /dev/null +++ b/tests/testdata/test_util/mapping/dupe_keys_mapping.config @@ -0,0 +1,7 @@ +descr: description1 +descr: description2 +other: foo +descr: description3 + + +other: bar diff --git a/tests/testdata/mapping_config/mapping.config b/tests/testdata/test_util/mapping/mapping.config similarity index 55% rename from tests/testdata/mapping_config/mapping.config rename to tests/testdata/test_util/mapping/mapping.config index 1195d382..18c6c512 100644 --- a/tests/testdata/mapping_config/mapping.config +++ b/tests/testdata/test_util/mapping/mapping.config @@ -5,4 +5,9 @@ version: Confirmed Version # Optional Fields description: description license_expression: dje_license_key -copyright: Confirmed Copyright \ No newline at end of file +copyright: Confirmed Copyright + +copyright: Confirmed Copyright + there is no multiline + +some junk line is also a comment \ No newline at end of file diff --git a/tests/testdata/test_util/mapping/space_mapping.config b/tests/testdata/test_util/mapping/space_mapping.config new file mode 100644 index 00000000..1a0d6020 --- /dev/null +++ b/tests/testdata/test_util/mapping/space_mapping.config @@ -0,0 +1,3 @@ +# Optional Fields +des cription: description +copy right: Confirmed Copyright diff --git a/tests/testdata/thirdparty/FixedHeader.ABOUT b/tests/testdata/thirdparty/FixedHeader.ABOUT deleted file mode 100644 index 462cff7e..00000000 --- a/tests/testdata/thirdparty/FixedHeader.ABOUT +++ /dev/null @@ -1,18 +0,0 @@ -about_resource:FixedHeader-2.0.6.zip -download_url: http://datatables.net/releases/FixedHeader-2.0.6.zip -version:2.0.6 - -name: data tables fixed header -organization: Allan Jardine -homepage_url: http://datatables.net -description: "Fix" a header at the top of the table, so it scrolls with the table -author: Allan Jardine (www.sprymedia.co.uk) - -dje_license:bsd-new -license_url: http://datatables.net/license_bsd -copyright: Copyright 2009-2012 Allan Jardine, all rights reserved. - -notes: This source file is free software, under either the GPL v2 license or a - BSD style license, available at: - http://datatables.net/license_gpl2 - http://datatables.net/license_bsd diff --git a/tests/testdata/thirdparty/FixedHeader.LICENSE b/tests/testdata/thirdparty/FixedHeader.LICENSE deleted file mode 100644 index 817e095c..00000000 --- a/tests/testdata/thirdparty/FixedHeader.LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -/* - * File: FixedHeader.js - * Version: 2.0.6 - * Description: "Fix" a header at the top of the table, so it scrolls with the table - * Author: Allan Jardine (www.sprymedia.co.uk) - * Created: Wed 16 Sep 2009 19:46:30 BST - * Language: Javascript - * License: GPL v2 or BSD 3 point style - * Project: Just a little bit of fun - enjoy :-) - * Contact: www.sprymedia.co.uk/contact - * - * Copyright 2009-2012 Allan Jardine, all rights reserved. - * - * This source file is free software, under either the GPL v2 license or a - * BSD style license, available at: - * http://datatables.net/license_gpl2 - * http://datatables.net/license_bsd - */ diff --git a/tests/testdata/thirdparty/Font-Awesome.ABOUT b/tests/testdata/thirdparty/Font-Awesome.ABOUT deleted file mode 100644 index 8a419896..00000000 --- a/tests/testdata/thirdparty/Font-Awesome.ABOUT +++ /dev/null @@ -1,15 +0,0 @@ -about_resource: Font-Awesome-v3.0.2.zip -version: 3.0.2 -download_url: https://github.com/FortAwesome/Font-Awesome/archive/v3.0.2.zip - -organization: FortAwesome -name: Font-Awesome -homepage_url: http://fortawesome.github.com/Font-Awesome/ - -dje_license: ofl-1.1 and mit and cc-by-3.0 -notes: there are several licenses: SIL Open Font License, MIT License, CC BY 3.0 License, Attribution is no longer required in Font Awesome 3.0 -notice_file: Font-Awesome.NOTICE - -scm_tool: git -scm_repository: https://github.com/FortAwesome/Font-Awesome.git -scm_rev: 13d5dd373cbf3f2bddd8ac2ee8df3a1966a62d09 diff --git a/tests/testdata/thirdparty/Font-Awesome.NOTICE b/tests/testdata/thirdparty/Font-Awesome.NOTICE deleted file mode 100644 index 353e36a8..00000000 --- a/tests/testdata/thirdparty/Font-Awesome.NOTICE +++ /dev/null @@ -1,4 +0,0 @@ -The Font Awesome font is licensed under the SIL Open Font License - http://scripts.sil.org/OFL. -Font Awesome CSS, LESS, and SASS files are licensed under the MIT License - http://opensource.org/licenses/mit-license.html. -The Font Awesome pictograms are licensed under the CC BY 3.0 License - http://creativecommons.org/licenses/by/3.0/ -Attribution is no longer required in Font Awesome 3.0, but much appreciated: Font Awesome by Dave Gandy - http://fortawesome.github.com/Font-Awesome. diff --git a/tests/testdata/thirdparty/csv_serialize.py.ABOUT b/tests/testdata/thirdparty/csv_serialize.py.ABOUT deleted file mode 100644 index b56db0c3..00000000 --- a/tests/testdata/thirdparty/csv_serialize.py.ABOUT +++ /dev/null @@ -1,8 +0,0 @@ -download_url: http://djangosnippets.org/snippets/2240/download/ -name: csv_serialize -version: 2013-01-16 -homepage_url: http://djangosnippets.org/snippets/2240/ -license_url: http://djangosnippets.org/about/tos/ -license_text_file: django_snippets.LICENSE - -date-retrieved: 2013-01-16 \ No newline at end of file diff --git a/tests/testdata/thirdparty/django_snippets.LICENSE b/tests/testdata/thirdparty/django_snippets.LICENSE deleted file mode 100644 index 01b3fadf..00000000 --- a/tests/testdata/thirdparty/django_snippets.LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Terms of Service -We hate legal-speak as much as anybody, but on a site which is geared toward sharing code there has to be at least a little bit of it, so here goes: - -By creating an account here you agree to three things: - -That you will only post code which you wrote yourself and that you have the legal right to release under these terms. -That you grant any third party who sees the code you post a royalty-free, non-exclusive license to copy and distribute that code and to make and distribute derivative works based on that code. You may include license terms in snippets you post, if you wish to use a particular license (such as the BSD license or GNU GPL), but that license must permit royalty-free copying, distribution and modification of the code to which it is applied. -That if you post code of which you are not the author or for which you do not have the legal right to distribute according to these terms, you will indemnify and hold harmless the operators of this site and any third parties who are exposed to liability as a result of your actions. -If you can't legally agree to these terms, or don't want to, you cannot create an account here. \ No newline at end of file diff --git a/tests/testdata/thirdparty/elasticsearch-sources.ABOUT b/tests/testdata/thirdparty/elasticsearch-sources.ABOUT deleted file mode 100644 index 169a7607..00000000 --- a/tests/testdata/thirdparty/elasticsearch-sources.ABOUT +++ /dev/null @@ -1,20 +0,0 @@ -about_resource: elasticsearch-v0.19.8-g7badcde.tar.gz -download_url: https://github.com/elasticsearch/elasticsearch/tarball/v0.19.8 -version: 0.19.8 -scm_rev: badcdee74acec84da3de6c6ea55c692aee4a6f9 - -organization: ElasticSearch and Shay Banon -name: ElasticSearch - -homepage_url: http://www.elasticsearch.org/ - -scm_tool: git -scm_repository: https://github.com/elasticsearch/elasticsearch.git - -dje_license: apache-2.0 -notice_file: elasticsearch.NOTICE -license_text_file: elasticsearch.LICENSE -copyright: Copyright 2009-2011 ElasticSearch and Shay Banon - -notes: Source code for the pre-built binaries we use - diff --git a/tests/testdata/thirdparty/elasticsearch.ABOUT b/tests/testdata/thirdparty/elasticsearch.ABOUT deleted file mode 100644 index f8555d09..00000000 --- a/tests/testdata/thirdparty/elasticsearch.ABOUT +++ /dev/null @@ -1,22 +0,0 @@ -about_resource:elasticsearch-0.19.8.zip -download_url: https://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.19.8.zip -version: 0.19.8 -scm_rev: badcdee74acec84da3de6c6ea55c692aee4a6f9 - -organization: ElasticSearch and Shay Banon -name: ElasticSearch - -homepage_url: http://www.elasticsearch.org/ - -scm_tool:git -scm_repository: https://github.com/elasticsearch/elasticsearch.git - -dje_license:apache-2.0 -notice_file: elasticsearch.NOTICE -license_text_file: elasticsearch.LICENSE -copyright: Copyright 2009-2011 ElasticSearch and Shay Banon - -notes: This a prebuilt version working on all OSes. - The tar.gz works only with POSIX OSses and not Windows. - - diff --git a/tests/testdata/thirdparty/elasticsearch.LICENSE b/tests/testdata/thirdparty/elasticsearch.LICENSE deleted file mode 100644 index 65ee1c12..00000000 --- a/tests/testdata/thirdparty/elasticsearch.LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. \ No newline at end of file diff --git a/tests/testdata/thirdparty/ez_setup.py.ABOUT b/tests/testdata/thirdparty/ez_setup.py.ABOUT deleted file mode 100644 index f5c1edd0..00000000 --- a/tests/testdata/thirdparty/ez_setup.py.ABOUT +++ /dev/null @@ -1,11 +0,0 @@ -date: 2013-01-01 -download_url: http://peak.telecommunity.com/dist/ez_setup.py -version:0.6c11 - -name: setuptools boostrap -homepage_url: http://pypi.python.org/pypi/setuptools -author: Phillip J. Eby - -dje_license: zpl-2.1 -notes: this is not used by default but embedded in virtualenv - diff --git a/tests/testdata/thirdparty/jquery.js.ABOUT b/tests/testdata/thirdparty/jquery.js.ABOUT deleted file mode 100644 index ad6e4e88..00000000 --- a/tests/testdata/thirdparty/jquery.js.ABOUT +++ /dev/null @@ -1,13 +0,0 @@ -about_resource: jquery-1.7.2.min.js -download_url: http://code.jquery.com/jquery-1.7.2.js - -version: 1.7.2 -name: jQuery -homepage_url: http://jquery.com/ - -scm_tool: git -scm_repository: https://github.com/jquery/jquery.git - -license_url: http://jquery.org/license -dje_license: mit -license_text_file: jquery.js.LICENSE diff --git a/tests/testdata/thirdparty/jquery.js.LICENSE b/tests/testdata/thirdparty/jquery.js.LICENSE deleted file mode 100644 index fd1abd89..00000000 --- a/tests/testdata/thirdparty/jquery.js.LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -/*! - * jQuery JavaScript Library v1.7.2 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Wed Mar 21 12:46:34 2012 -0700 - */ \ No newline at end of file diff --git a/tests/testdata/thirdparty/jquery.jsPlumb.ABOUT b/tests/testdata/thirdparty/jquery.jsPlumb.ABOUT deleted file mode 100644 index 96cfb070..00000000 --- a/tests/testdata/thirdparty/jquery.jsPlumb.ABOUT +++ /dev/null @@ -1,12 +0,0 @@ -about_resource: jquery.jsPlumb-1.3.10-all-min.js -version: 1.3.10 -download_url: http://code.google.com/p/jsplumb/downloads/detail?name=jquery.jsPlumb-1.3.10-all-min.js - -name: jquery.jsPlumb -homepage_url: http://code.google.com/p/jsplumb/ - -scm_tool: svn -scm_repository: http://jsplumb.googlecode.com/svn/trunk/ - -license_text_file: jquery.js.LICENSE -dje_license: mit \ No newline at end of file diff --git a/tests/testdata/thirdparty/jquery.jsPlumb.LICENSE b/tests/testdata/thirdparty/jquery.jsPlumb.LICENSE deleted file mode 100644 index b825080b..00000000 --- a/tests/testdata/thirdparty/jquery.jsPlumb.LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -/* - * jsPlumb - * - * Title:jsPlumb 1.3.9 - * - * Provides a way to visually connect elements on an HTML page, using either SVG, Canvas - * elements, or VML. - * - * This file contains the util functions - * - * Copyright (c) 2010 - 2012 Simon Porritt (http://jsplumb.org) - * - * http://jsplumb.org - * http://github.com/sporritt/jsplumb - * http://code.google.com/p/jsplumb - * - * Dual licensed under the MIT and GPL2 licenses. - */ \ No newline at end of file diff --git a/tests/testdata/thirdparty/jquery.min.js.ABOUT b/tests/testdata/thirdparty/jquery.min.js.ABOUT deleted file mode 100644 index 2d73763f..00000000 --- a/tests/testdata/thirdparty/jquery.min.js.ABOUT +++ /dev/null @@ -1,13 +0,0 @@ -about_resource: jquery-1.7.2.min.js -download_url:http://code.jquery.com/jquery-1.7.2.min.js - -version:1.7.2 -name: jQuery -homepage_url:http://jquery.com/ - -scm_tool: git -scm_repository:https://github.com/jquery/jquery.git - -license_url:http://jquery.org/license -dje_license:mit -license_text_file:jquery.js.LICENSE diff --git a/tests/testdata/thirdparty/mod_wsgi-3.3.tar.gz.ABOUT b/tests/testdata/thirdparty/mod_wsgi-3.3.tar.gz.ABOUT deleted file mode 100644 index 81fa8fbf..00000000 --- a/tests/testdata/thirdparty/mod_wsgi-3.3.tar.gz.ABOUT +++ /dev/null @@ -1,2 +0,0 @@ -date-retrieved:2013-01-16 19:41:10+01:00 -wget:http://modwsgi.googlecode.com/files/mod_wsgi-3.3.tar.gz diff --git a/tests/testdata/thirdparty/okfn-annotator.ABOUT b/tests/testdata/thirdparty/okfn-annotator.ABOUT deleted file mode 100644 index fe70b842..00000000 --- a/tests/testdata/thirdparty/okfn-annotator.ABOUT +++ /dev/null @@ -1,17 +0,0 @@ -about_resource: okfn-annotator-549159b.zip -download_url:https://github.com/okfn/annotator/zipball/549159b554411eba18c34ffffe91ba44f7558be6 -version: 549159b554411eba18c34ffffe91ba44f7558be6 - -homepage_url:http://okfn.org/projects/annotator/ -name: OKFN Annotator -organization: OKFN - -license_text_file: okfn-annotator.LICENSE -license_url:http://okfn.org/ip-policy/ -dje_license:mit -notes: this component includes several other components, not detailed here. - See archive for details and licenses. - -scm_tool: git -scm_repository:https://github.com/okfn/annotator.git -scm_rev: 549159b554411eba18c34ffffe91ba44f7558be6 diff --git a/tests/testdata/thirdparty/okfn-annotator.LICENSE b/tests/testdata/thirdparty/okfn-annotator.LICENSE deleted file mode 100644 index fad0a134..00000000 --- a/tests/testdata/thirdparty/okfn-annotator.LICENSE +++ /dev/null @@ -1,45 +0,0 @@ -# Annotator licensing terms - -Annotator is free software, and you may use it under the terms of either the -MIT or the GNU GPL licenses: - -## GNU GPLv3 - -You can redistribute this program and/or modify it under the terms of the -GNU General Public License as published by the Free Software Foundation, -either version 3 of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. See LICENSE-GPL, or, if this file is missing, -. - -## MIT - -You may use the software under the terms of the MIT license, which can be -found in LICENSE-MIT, or, if this file is missing, -. - - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - \ No newline at end of file diff --git a/tests/testdata/thirdparty/setuptools.ABOUT b/tests/testdata/thirdparty/setuptools.ABOUT deleted file mode 100644 index bfe856a8..00000000 --- a/tests/testdata/thirdparty/setuptools.ABOUT +++ /dev/null @@ -1,10 +0,0 @@ -about_resource: setuptools-0.6c11-py2.6.egg -version:0.6c11 -download_url: http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086 - -name: setuptools -homepage_url: http://pypi.python.org/pypi/setuptools -author: Phillip J. Eby - -dje_license: zpl-2.1 -notes: this is not used by default but embedded in virtualenv \ No newline at end of file diff --git a/tests/testdata/thirdparty/twitter_bootstrap.ABOUT b/tests/testdata/thirdparty/twitter_bootstrap.ABOUT deleted file mode 100644 index 21394c95..00000000 --- a/tests/testdata/thirdparty/twitter_bootstrap.ABOUT +++ /dev/null @@ -1,12 +0,0 @@ -about_resource: twitter_bootstrap_v2.0.3.zip -download_url:https://github.com/twitter/bootstrap/archive/v2.0.3.zip -version:2.0.3 - -name: bootstrap -homepage_url:http://twitter.github.com/bootstrap/ - -dje_license:apache 2.0 -license_text_file: twitter_bootstrap.LICENSE - -scm_tool:git -scm_repository:https://github.com/twitter/bootstrap.git \ No newline at end of file diff --git a/tests/testdata/thirdparty/twitter_bootstrap.LICENSE b/tests/testdata/thirdparty/twitter_bootstrap.LICENSE deleted file mode 100644 index 5d329831..00000000 --- a/tests/testdata/thirdparty/twitter_bootstrap.LICENSE +++ /dev/null @@ -1,55 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and - - 2. You must cause any modified files to carry prominent notices stating that You changed the files; and - - 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/tests/testdata/thirdparty/underscore-min.js b/tests/testdata/thirdparty/underscore-min.js deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/testdata/thirdparty/underscore-min.js.ABOUT b/tests/testdata/thirdparty/underscore-min.js.ABOUT deleted file mode 100644 index 123ccc50..00000000 --- a/tests/testdata/thirdparty/underscore-min.js.ABOUT +++ /dev/null @@ -1,12 +0,0 @@ -about_resource: underscore-min.js -download_url: https://raw.github.com/documentcloud/underscore/1.4.2/underscore-min.js -version: 1.4.2 - -name: underscore.js -homepage_url: http://underscorejs.org/ - -scm_tool: git -scm_repository: git://github.com/documentcloud/underscore.git - -dje_license: mit -license_text_file: underscore.js.LICENSE diff --git a/tests/testdata/thirdparty/underscore.js b/tests/testdata/thirdparty/underscore.js deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/testdata/thirdparty/underscore.js.ABOUT b/tests/testdata/thirdparty/underscore.js.ABOUT deleted file mode 100644 index a9a1eba6..00000000 --- a/tests/testdata/thirdparty/underscore.js.ABOUT +++ /dev/null @@ -1,12 +0,0 @@ -about_resource: underscore.js -download_url: https://raw.github.com/documentcloud/underscore/1.4.2/underscore.js -version: 1.4.2 - -name: underscore.js -homepage_url: http://underscorejs.org/ - -scm_tool: git -scm_repository: git://github.com/documentcloud/underscore.git - -dje_license: mit -license_text_file: underscore.js.LICENSE diff --git a/tests/testdata/thirdparty/underscore.js.LICENSE b/tests/testdata/thirdparty/underscore.js.LICENSE deleted file mode 100644 index 14af6e8b..00000000 --- a/tests/testdata/thirdparty/underscore.js.LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2009-2012 Jeremy Ashkenas, DocumentCloud - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/testdata/util/about.csv b/tests/testdata/util/about.csv deleted file mode 100644 index 43b8b3b2..00000000 --- a/tests/testdata/util/about.csv +++ /dev/null @@ -1,2 +0,0 @@ -about_file,about_resource,name,version -about.ABOUT,.,ABOUT tool,0.8.1 diff --git a/tests/testdata/util/about_key_with_upper_case.csv b/tests/testdata/util/about_key_with_upper_case.csv deleted file mode 100644 index bc841c6e..00000000 --- a/tests/testdata/util/about_key_with_upper_case.csv +++ /dev/null @@ -1,2 +0,0 @@ -about_file,about_resource,nAme,Version -about.ABOUT,.,ABOUT tool,0.8.1 diff --git a/tests/testdata/util/dup_keys_with_diff_case.csv b/tests/testdata/util/dup_keys_with_diff_case.csv deleted file mode 100644 index d79ba8d8..00000000 --- a/tests/testdata/util/dup_keys_with_diff_case.csv +++ /dev/null @@ -1,2 +0,0 @@ -about_file,about_resource,copyright,name,version,Copyright -about.ABOUT,.,nexB,ABOUT tool,0.8.1,someone \ No newline at end of file diff --git a/tests/testdata/util/mapping_output b/tests/testdata/util/mapping_output deleted file mode 100644 index 662e125b..00000000 --- a/tests/testdata/util/mapping_output +++ /dev/null @@ -1 +0,0 @@ -Component: name \ No newline at end of file diff --git a/tests/testing_utils.py b/tests/testing_utils.py index ff60e16b..2fe3ed74 100644 --- a/tests/testing_utils.py +++ b/tests/testing_utils.py @@ -18,16 +18,17 @@ from __future__ import print_function from __future__ import unicode_literals -import codecs import logging import ntpath import os import posixpath import stat +import subprocess import sys import tempfile import zipfile + from attributecode.util import add_unc from attributecode.util import to_posix @@ -44,7 +45,7 @@ on_posix = not on_windows -def get_test_loc(path): +def get_test_loc(path, must_exists=True): """ Return the location of a test file or directory given a path relative to the testdata directory. @@ -52,26 +53,10 @@ def get_test_loc(path): base = to_posix(TESTDATA_DIR) path = to_posix(path) path = posixpath.join(base, path) - # path = to_native(path) + if must_exists: + assert os.path.exists(path) return path - -def get_unicode_content(location): - """ - Read file at location and return a unicode. - """ - with codecs.open(location, 'rb', encoding='utf-8') as doc: - return doc.read() - - -def get_test_lines(path): - """ - Return a list of text lines loaded from the location of a test file or - directory given a path relative to the testdata directory. - """ - return get_unicode_content(get_test_loc(path)).splitlines(True) - - def create_dir(location): """ Create directory or directory tree at location, ensuring it is readable @@ -158,11 +143,70 @@ def extract_test_loc(path, extract_func=extract_zip): return target_dir -class FakeResponse(object): - response_content = None - - def __init__(self, response_content): - self.response_content = response_content - - def read(self): - return self.response_content +def run_about_command_test(options, expected_rc=0): + """ + Run an "about" command as a plain subprocess with the `options` list of options. + Assert that rc equals `expected_rc`. + On success, return stdout and stderr. + """ + root_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) + about_cmd = os.path.join(root_dir, 'about') + args = [about_cmd] + options + about = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True if on_windows else False) + stdout, stderr = about.communicate() + rc = about.poll() + if rc != expected_rc: + opts = ' '.join(args) + error = ( + 'Failure to run command: %(opts)s\n' + 'stdout:\n' + '{stdout}\n' + '\n' + 'stderr:\n' + '{stderr}\n' + ).format(**locals()) + assert rc == expected_rc, error + return stdout, stderr + + +def run_about_command_test_click(options, expected_rc=0, monkeypatch=None, ): + """ + Run an "about" command as a Click-controlled subprocess with the `options` + list of options. Return a click.testing.Result object. + + If monkeypatch is provided, a tty with a size (80, 43) is mocked. + """ + import click + from click.testing import CliRunner + from attributecode import cmd + if monkeypatch: + monkeypatch.setattr(click._termui_impl, 'isatty', lambda _: True) + monkeypatch.setattr(click , 'get_terminal_size', lambda : (80, 43,)) + runner = CliRunner() + + result = runner.invoke(cmd.about, options, catch_exceptions=False) + + output = result.output + if result.exit_code != expected_rc: + opts = get_opts(options) + error = ''' +Failure to run: about %(opts)s +output: +%(output)s +''' % locals() + assert result.exit_code == expected_rc, error + return result + + +def get_opts(options): + try: + return ' '.join(options) + except: + try: + return b' '.join(options) + except: + return b' '.join(map(repr, options)) diff --git a/thirdparty/saneyaml-0.1-py2.py3-none-any.whl b/thirdparty/saneyaml-0.1-py2.py3-none-any.whl new file mode 100644 index 00000000..c8aa66fa Binary files /dev/null and b/thirdparty/saneyaml-0.1-py2.py3-none-any.whl differ diff --git a/thirdparty/saneyaml-0.1-py2.py3-none-any.whl.ABOUT b/thirdparty/saneyaml-0.1-py2.py3-none-any.whl.ABOUT new file mode 100644 index 00000000..ae857b3e --- /dev/null +++ b/thirdparty/saneyaml-0.1-py2.py3-none-any.whl.ABOUT @@ -0,0 +1,20 @@ +about_resource: saneyaml-0.1-py2.py3-none-any.whl +attribute: true +checksum_md5: 53509e4f1ee9f6565158163a56513b7c +checksum_sha1: a66920309794ead47711473a21f1c7d003acd005 +contact: http://www.nexb.com/contactus.html +copyright: Copyright (c) nexB Inc. and others. +download_url: https://files.pythonhosted.org/packages/21/13/3d639adcd97fc22cfc4acfd23330cc26f96a85a5b992d93086684e24ac14/saneyaml-0.1-py2.py3-none-any.whl +homepage_url: https://github.com/nexB/saneyaml +license_expression: apache-2.0 +licenses: +- file: apache-2.0.LICENSE + key: apache-2.0 + name: Apache License 2.0 +name: saneyaml +owner: nexB +owner_url: http://www.nexb.com/ +package_url: pkg:pypi/saneyaml@0.1 +track_changes: true +vcs_repository: https://github.com/nexB/saneyaml +version: '0.1' diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..d7bd368f --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py{27,36} + +[testenv] +deps = pytest + mock +commands = {posargs:pytest} -vvs + +[pytest] +testpaths = src tests/