| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you 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. |
| |
| import tempfile |
| import unittest |
| from pathlib import Path |
| |
| from scripts.release_licenses import ( |
| LicenseCorrection, |
| PYTHON_MIT_CORRECTIONS, |
| binary_notice, |
| correction_html, |
| resolved_packages, |
| ) |
| |
| |
| class LicenseCorrectionsTest(unittest.TestCase): |
| def test_python_corrections_cover_jieba_workspace(self) -> None: |
| corrections = [ |
| correction |
| for correction in PYTHON_MIT_CORRECTIONS |
| if correction.crates == ("jieba-macros", "jieba-rs") |
| ] |
| self.assertEqual(len(corrections), 1) |
| correction = corrections[0] |
| self.assertEqual(correction.license_crate, "jieba-rs") |
| self.assertEqual( |
| correction.license_path, |
| "third-party-licenses/jieba-rs-0.10.3.LICENSE", |
| ) |
| root = Path(__file__).resolve().parents[2] |
| self.assertTrue((root / correction.license_path).is_file()) |
| |
| def test_repository_license_correction_reads_checked_in_file(self) -> None: |
| with tempfile.TemporaryDirectory() as temp: |
| root = Path(temp) |
| license_path = root / "third-party-licenses" / "jieba.LICENSE" |
| license_path.parent.mkdir() |
| license_path.write_text("Jieba workspace license\n", encoding="utf-8") |
| metadata = { |
| "normal_packages": { |
| ("jieba-macros", "0.10.3"), |
| ("jieba-rs", "0.10.3"), |
| }, |
| "packages": [ |
| { |
| "name": name, |
| "version": "0.10.3", |
| "manifest_path": str(root / name / "Cargo.toml"), |
| "repository": "https://example.com/jieba-rs", |
| } |
| for name in ("jieba-macros", "jieba-rs") |
| ], |
| } |
| correction = LicenseCorrection( |
| crates=("jieba-macros", "jieba-rs"), |
| license_crate="jieba-rs", |
| license_path="third-party-licenses/jieba.LICENSE", |
| license_name="MIT License (jieba-rs workspace)", |
| anchor="mit-jieba-rs-workspace", |
| license_from_repository=True, |
| expected_versions=( |
| ("jieba-macros", "0.10.3"), |
| ("jieba-rs", "0.10.3"), |
| ), |
| ) |
| |
| result = correction_html(root, metadata, correction) |
| |
| self.assertIn("Jieba workspace license", result) |
| self.assertIn("jieba-macros 0.10.3", result) |
| self.assertIn("jieba-rs 0.10.3", result) |
| |
| def test_repository_license_correction_rejects_unverified_version(self) -> None: |
| root = Path(__file__).resolve().parents[2] |
| correction = next( |
| correction |
| for correction in PYTHON_MIT_CORRECTIONS |
| if correction.crates == ("jieba-macros", "jieba-rs") |
| ) |
| metadata = { |
| "normal_packages": { |
| ("jieba-macros", "9.9.9"), |
| ("jieba-rs", "9.9.9"), |
| }, |
| "packages": [ |
| { |
| "name": name, |
| "version": "9.9.9", |
| "manifest_path": str(root / name / "Cargo.toml"), |
| "repository": "https://example.com/jieba-rs", |
| } |
| for name in ("jieba-macros", "jieba-rs") |
| ], |
| } |
| |
| with self.assertRaisesRegex( |
| RuntimeError, |
| r"expected jieba-macros 0\.10\.3, found 9\.9\.9", |
| ): |
| correction_html(root, metadata, correction) |
| |
| def test_correction_supports_multiple_versions_with_same_license(self) -> None: |
| with tempfile.TemporaryDirectory() as temp: |
| root = Path(temp) |
| packages = [] |
| normal_packages = set() |
| for version in ("0.7.0", "0.11.0"): |
| package_dir = root / f"tantivy-common-{version}" |
| package_dir.mkdir() |
| manifest = package_dir / "Cargo.toml" |
| manifest.write_text("[package]\n", encoding="utf-8") |
| (package_dir / "LICENSE").write_text( |
| "Shared Tantivy license\n", |
| encoding="utf-8", |
| ) |
| packages.append( |
| { |
| "name": "tantivy-common", |
| "version": version, |
| "manifest_path": str(manifest), |
| "repository": "https://example.com/tantivy", |
| } |
| ) |
| normal_packages.add(("tantivy-common", version)) |
| metadata = { |
| "normal_packages": normal_packages, |
| "packages": packages, |
| } |
| correction = LicenseCorrection( |
| crates=("tantivy-common",), |
| license_crate="tantivy-common", |
| license_path="LICENSE", |
| license_name="MIT License (Tantivy workspace)", |
| anchor="mit-tantivy-workspace", |
| ) |
| |
| result = correction_html(root, metadata, correction) |
| |
| self.assertIn("tantivy-common 0.7.0", result) |
| self.assertIn("tantivy-common 0.11.0", result) |
| self.assertEqual(result.count("Shared Tantivy license"), 1) |
| |
| def test_jieba_license_source_is_documented_and_header_exempt(self) -> None: |
| root = Path(__file__).resolve().parents[2] |
| license_path = "third-party-licenses/jieba-rs-0.10.3.LICENSE" |
| license_config = (root / ".licenserc.yaml").read_text(encoding="utf-8") |
| third_party_readme = ( |
| root / "third-party-licenses" / "README.md" |
| ).read_text(encoding="utf-8") |
| |
| self.assertIn(f'"{license_path}"', license_config) |
| self.assertIn( |
| "messense/jieba-rs/blob/" |
| "c62e0df1f9dcc2cc1e014711c5aa4561ae260538/LICENSE", |
| third_party_readme, |
| ) |
| |
| |
| class ResolvedPackagesTest(unittest.TestCase): |
| def test_uses_normal_cargo_tree_packages(self) -> None: |
| metadata = { |
| "normal_packages": {("root", "1.0.0"), ("required", "2.0.0")}, |
| "packages": [ |
| {"name": "root", "version": "1.0.0"}, |
| {"name": "required", "version": "2.0.0"}, |
| {"name": "optional", "version": "3.0.0"}, |
| ], |
| } |
| self.assertEqual( |
| [package["name"] for package in resolved_packages(metadata)], |
| ["root", "required"], |
| ) |
| |
| def test_notice_uses_only_normal_dependencies(self) -> None: |
| with tempfile.TemporaryDirectory() as temp: |
| root = Path(temp) |
| (root / "NOTICE").write_text("Root notice\n", encoding="utf-8") |
| packages = [] |
| for name, notice in ( |
| ("required", "Required dependency notice\n"), |
| ("optional", "Optional dependency notice\n"), |
| ): |
| package_dir = root / name |
| package_dir.mkdir() |
| manifest = package_dir / "Cargo.toml" |
| manifest.write_text("[package]\n", encoding="utf-8") |
| (package_dir / "NOTICE").write_text(notice, encoding="utf-8") |
| packages.append( |
| { |
| "name": name, |
| "version": "1.0.0", |
| "manifest_path": str(manifest), |
| } |
| ) |
| |
| metadata = { |
| "normal_packages": {("required", "1.0.0")}, |
| "packages": packages, |
| } |
| notice = binary_notice(root, [metadata]) |
| self.assertIn("Required dependency notice", notice) |
| self.assertNotIn("Optional dependency notice", notice) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |