|
| 1 | +# This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | +# License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| 3 | +# You can obtain one at http://mozilla.org/MPL/2.0/. |
| 4 | +# |
| 5 | +# Copyright (c) 2015, Lars Asplund lars.anders.asplund@gmail.com |
| 6 | + |
| 7 | +import unittest |
| 8 | +from os.path import join, dirname, splitext, abspath, commonprefix |
| 9 | +from os import walk |
| 10 | +from re import compile |
| 11 | +from datetime import datetime |
| 12 | +from subprocess import Popen, PIPE, STDOUT |
| 13 | + |
| 14 | +class TestLicense(unittest.TestCase): |
| 15 | + _re_license_notice = compile(r"""(?P<comment_start>#|--|//) This Source Code Form is subject to the terms of the Mozilla Public |
| 16 | +(?P=comment_start) License, v\. 2\.0\. If a copy of the MPL was not distributed with this file, |
| 17 | +(?P=comment_start) You can obtain one at http://mozilla\.org/MPL/2\.0/\. |
| 18 | +(?P=comment_start) |
| 19 | +(?P=comment_start) Copyright \(c\) (?P<first_year>20\d\d)(-(?P<last_year>20\d\d))?, Lars Asplund lars\.anders\.asplund@gmail\.com""") |
| 20 | + _re_log_date = compile(r'Date:\s*(?P<year>20\d\d)-\d\d-\d\d') |
| 21 | + |
| 22 | + def test_that_a_valid_license_notice_exists_in_every_source_file_and_that_global_licensing_information_is_correct(self): |
| 23 | + licensed_files = [] |
| 24 | + repo_root = abspath(join(dirname(__file__), '..', '..')) |
| 25 | + for root, dirs, files in walk(repo_root): |
| 26 | + for file_name in files: |
| 27 | + if 'preprocessed' in root: |
| 28 | + continue |
| 29 | + osvvm_directory = abspath(join(repo_root, 'vhdl', 'osvvm')) |
| 30 | + if commonprefix([osvvm_directory, abspath(join(root, file_name))]) == osvvm_directory: |
| 31 | + continue |
| 32 | + osvvm_integration_example_directory = abspath(join(repo_root, 'examples', 'osvvm_integration', 'src')) |
| 33 | + if commonprefix([osvvm_integration_example_directory, abspath(join(root, file_name))]) == osvvm_integration_example_directory: |
| 34 | + continue |
| 35 | + if splitext(file_name)[1] in ['.vhd', '.vhdl', '.py', '.v', '.sv']: |
| 36 | + licensed_files.append(join(root, file_name)) |
| 37 | + |
| 38 | + for file_name in licensed_files: |
| 39 | + self._check_license(file_name) |
| 40 | + |
| 41 | + def _check_license(self, file_name): |
| 42 | + proc = Popen(['git', 'log', '--follow', '--date=short', file_name], \ |
| 43 | + bufsize=0, stdout=PIPE, stdin=PIPE, stderr=STDOUT, universal_newlines=True) |
| 44 | + out, _ = proc.communicate() |
| 45 | + first_year = None |
| 46 | + last_year = None |
| 47 | + for date in self._re_log_date.finditer(out): |
| 48 | + first_year = int(date.group('year')) if first_year is None else min(int(date.group('year')), first_year) |
| 49 | + last_year = int(date.group('year')) if last_year is None else max(int(date.group('year')), last_year) |
| 50 | + |
| 51 | + if first_year is None and last_year is None: |
| 52 | + # File not in log yet, set to current year |
| 53 | + first_year = datetime.now().year |
| 54 | + last_year = first_year |
| 55 | + |
| 56 | + with open(file_name) as fp: |
| 57 | + code = fp.read() |
| 58 | + match = self._re_license_notice.search(code) |
| 59 | + self.assertIsNotNone(match, "Failed to find license notice in %s" % file_name) |
| 60 | + if first_year == last_year: |
| 61 | + self.assertEqual(int(match.group('first_year')), first_year, 'Expected copyright year to be %d in %s' % (first_year, file_name)) |
| 62 | + self.assertIsNone(match.group('last_year'), 'Expected no copyright years range in %s' % file_name) |
| 63 | + else: |
| 64 | + self.assertIsNotNone(match.group('last_year'), 'Expected copyright year range %d-%d in %s' % (first_year, last_year, file_name)) |
| 65 | + self.assertEqual(int(match.group('first_year')), first_year, 'Expected copyright year range to start with %d in %s' % (first_year, file_name)) |
| 66 | + self.assertEqual(int(match.group('last_year')), last_year, 'Expected copyright year range to end with %d in %s' % (last_year, file_name)) |
0 commit comments