File: test_license_expression_validator.py

package info (click to toggle)
python-spdx-tools 0.8.3-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,084 kB
  • sloc: python: 18,675; xml: 12,553; sh: 46; makefile: 7
file content (175 lines) | stat: -rw-r--r-- 7,373 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# SPDX-FileCopyrightText: 2022 spdx contributors
#
# SPDX-License-Identifier: Apache-2.0

from typing import List
from unittest import TestCase

import pytest
from license_expression import LicenseExpression

from spdx_tools.common.spdx_licensing import spdx_licensing
from spdx_tools.spdx.model import Document, SpdxNoAssertion, SpdxNone
from spdx_tools.spdx.validation.license_expression_validator import (
    validate_license_expression,
    validate_license_expressions,
)
from spdx_tools.spdx.validation.validation_message import SpdxElementType, ValidationContext, ValidationMessage
from tests.spdx.fixtures import document_fixture, external_document_ref_fixture, extracted_licensing_info_fixture

FIXTURE_LICENSE_ID = extracted_licensing_info_fixture().license_id
EXTERNAL_DOCUMENT_ID = external_document_ref_fixture().document_ref_id


@pytest.mark.parametrize(
    "expression_string",
    [
        "MIT",
        FIXTURE_LICENSE_ID,
        f"GPL-2.0-only with GPL-CC-1.0 and {FIXTURE_LICENSE_ID} with 389-exception or Beerware",
        f"{EXTERNAL_DOCUMENT_ID}:LicenseRef-007",
    ],
)
def test_valid_license_expression(expression_string):
    document: Document = document_fixture()
    license_expression: LicenseExpression = spdx_licensing.parse(expression_string)
    validation_messages: List[ValidationMessage] = validate_license_expression(
        license_expression, document, parent_id="SPDXRef-File"
    )

    assert validation_messages == []


@pytest.mark.parametrize("expression", [SpdxNone(), SpdxNoAssertion()])
def test_none_and_no_assertion(expression):
    document: Document = document_fixture()
    validation_messages: List[ValidationMessage] = validate_license_expression(
        expression, document, parent_id="SPDXRef-File"
    )
    assert validation_messages == []


@pytest.mark.parametrize(
    "expression_list",
    [
        [SpdxNone()],
        [SpdxNoAssertion()],
        [spdx_licensing.parse("MIT and GPL-3.0-only"), spdx_licensing.parse(FIXTURE_LICENSE_ID)],
        [SpdxNone(), spdx_licensing.parse("MIT"), SpdxNoAssertion()],
    ],
)
def test_valid_license_expressions(expression_list):
    document: Document = document_fixture()
    validation_messages: List[ValidationMessage] = validate_license_expressions(
        expression_list, document, parent_id="SPDXRef-File"
    )
    assert validation_messages == []


@pytest.mark.parametrize(
    "expression_string, unknown_symbols",
    [
        (f"{FIXTURE_LICENSE_ID} or LicenseRef-22", ["LicenseRef-22"]),
        ("nope with 389-exception and _.- or LicenseRef-10", ["nope", "_.-", "LicenseRef-10"]),
    ],
)
def test_invalid_license_expression_with_unknown_symbols(expression_string, unknown_symbols):
    document: Document = document_fixture()
    license_expression: LicenseExpression = spdx_licensing.parse(expression_string)
    parent_id = "SPDXRef-File"
    context = ValidationContext(
        parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression
    )

    validation_messages: List[ValidationMessage] = validate_license_expression(license_expression, document, parent_id)
    expected_messages = [
        ValidationMessage(
            f"Unrecognized license reference: {symbol}. license_expression must only use IDs from the license list or "
            f"extracted licensing info, but is: {license_expression}",
            context,
        )
        for symbol in unknown_symbols
    ]

    TestCase().assertCountEqual(validation_messages, expected_messages)


@pytest.mark.parametrize(
    "expression_string, expected_message",
    [
        (
            "MIT with MIT",
            'A plain license symbol cannot be used as an exception in a "WITH symbol" statement. for token: "MIT" at '
            "position: 9. for license_expression: MIT WITH MIT",
        ),
        (
            f"GPL-2.0-or-later and {FIXTURE_LICENSE_ID} with {FIXTURE_LICENSE_ID}",
            f'A plain license symbol cannot be used as an exception in a "WITH symbol" statement. for token: '
            f'"{FIXTURE_LICENSE_ID}" at position: 39. for license_expression: GPL-2.0-or-later AND '
            f"{FIXTURE_LICENSE_ID} WITH {FIXTURE_LICENSE_ID}",
        ),
        (
            f"GPL-2.0-or-later with MIT and {FIXTURE_LICENSE_ID} with GPL-2.0-or-later",
            f'A plain license symbol cannot be used as an exception in a "WITH symbol" statement. for token: "MIT" at '
            f"position: 22. for license_expression: GPL-2.0-or-later WITH MIT AND {FIXTURE_LICENSE_ID} "
            f"WITH GPL-2.0-or-later",
        ),
        (
            "389-exception with 389-exception",
            'A license exception symbol can only be used as an exception in a "WITH exception" statement. for token: '
            '"389-exception". for license_expression: 389-exception WITH 389-exception',
        ),
        (
            "389-exception with MIT",
            'A license exception symbol can only be used as an exception in a "WITH exception" statement. for token: '
            '"389-exception". for license_expression: 389-exception WITH MIT',
        ),
    ],
)
def test_invalid_license_expression_with_invalid_exceptions(expression_string, expected_message):
    document: Document = document_fixture()
    license_expression: LicenseExpression = spdx_licensing.parse(expression_string)
    parent_id = "SPDXRef-File"
    context = ValidationContext(
        parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression
    )

    validation_messages: List[ValidationMessage] = validate_license_expression(license_expression, document, parent_id)
    expected_messages = [ValidationMessage(expected_message, context)]

    assert validation_messages == expected_messages


@pytest.mark.parametrize(
    "expression_string, expected_message",
    [
        (
            f"{EXTERNAL_DOCUMENT_ID}:LicenseRef-007:4",
            f"Too many colons in license reference: {EXTERNAL_DOCUMENT_ID}:LicenseRef-007:4. "
            "A license reference must only contain a single colon to "
            "separate an external document reference from the license reference.",
        ),
        (
            f"{EXTERNAL_DOCUMENT_ID}:unknown_license",
            'A license reference must start with "LicenseRef-", but is: unknown_license '
            f"in external license reference {EXTERNAL_DOCUMENT_ID}:unknown_license.",
        ),
        (
            "DocumentRef-unknown:LicenseRef-1",
            'Did not find the external document reference "DocumentRef-unknown" in the SPDX document. '
            "From the external license reference DocumentRef-unknown:LicenseRef-1.",
        ),
    ],
)
def test_invalid_license_expression_with_external_reference(expression_string, expected_message):
    document: Document = document_fixture()
    license_expression: LicenseExpression = spdx_licensing.parse(expression_string)
    parent_id = "SPDXRef-File"
    context = ValidationContext(
        parent_id=parent_id, element_type=SpdxElementType.LICENSE_EXPRESSION, full_element=license_expression
    )

    validation_messages: List[ValidationMessage] = validate_license_expression(license_expression, document, parent_id)
    expected_messages = [ValidationMessage(expected_message, context)]

    assert validation_messages == expected_messages