1# Copyright (c) 2014 Ahmed H. Ismail
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#     http://www.apache.org/licenses/LICENSE-2.0
6# Unless required by applicable law or agreed to in writing, software
7# distributed under the License is distributed on an "AS IS" BASIS,
8# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9# See the License for the specific language governing permissions and
10# limitations under the License.
11
12from __future__ import absolute_import
13from __future__ import print_function
14from __future__ import unicode_literals
15
16import os
17import shutil
18import tempfile
19from unittest import TestCase
20
21from spdx.checksum import Algorithm
22from spdx.config import LICENSE_MAP, EXCEPTION_MAP
23from spdx.creationinfo import Tool
24from spdx.document import Document, ExternalDocumentRef
25from spdx.document import License
26from spdx.file import File
27from spdx.package import Package
28from spdx.utils import NoAssert
29from spdx.version import Version
30
31from tests import utils_test
32
33
34class TestVersion(TestCase):
35    maxDiff = None
36
37    def test_creation(self):
38        v = Version(major=2, minor=1)
39        assert v.major == 2
40        assert v.minor == 1
41
42    def test_comparison(self):
43        v1 = Version(major=1, minor=2)
44        v2 = Version(major=2, minor=1)
45        assert v1 != v2
46        assert v1 < v2
47        assert v1 <= v2
48        assert v2 > v1
49        assert v2 >= v1
50        v3 = Version(major=1, minor=2)
51        assert v3 == v1
52        assert not v1 < v3
53        assert v1 <= v3
54
55
56class TestDocument(TestCase):
57    maxDiff = None
58
59    def test_creation(self):
60        document = Document(
61            version=Version(major=2, minor=1),
62            data_license=License(full_name='Academic Free License v1.1',
63                                identifier='AFL-1.1')
64        )
65        document.add_ext_document_reference(
66            ExternalDocumentRef('DocumentRef-spdx-tool-2.1',
67                                'https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301',
68                                Algorithm('SHA1', 'SOME-SHA1'))
69        )
70        assert document.comment is None
71        assert document.version == Version(2, 1)
72        assert document.data_license.identifier == 'AFL-1.1'
73        assert document.ext_document_references[-1].external_document_id == 'DocumentRef-spdx-tool-2.1'
74        assert document.ext_document_references[-1].spdx_document_uri == 'https://spdx.org/spdxdocs/spdx-tools-v2.1-3F2504E0-4F89-41D3-9A0C-0305E82C3301'
75        assert document.ext_document_references[-1].check_sum.identifier == 'SHA1'
76        assert document.ext_document_references[-1].check_sum.value == 'SOME-SHA1'
77
78    def test_document_validate_failures_returns_informative_messages(self):
79        doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'),
80                       'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT',
81                       namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301')
82        pack = doc.package = Package('some/path', NoAssert())
83        file1 = File('./some/path/tofile')
84        file1.name = './some/path/tofile'
85        file1.spdx_id = 'SPDXRef-File'
86        file1.chk_sum = Algorithm('SHA1', 'SOME-SHA1')
87        lic1 = License.from_identifier('LGPL-2.1-only')
88        file1.add_lics(lic1)
89        pack.add_lics_from_file(lic1)
90        messages = []
91        messages = doc.validate(messages)
92        expected = [
93            'No creators defined, must have at least one.',
94            'Creation info missing created date.',
95            'Package checksum must be instance of spdx.checksum.Algorithm',
96            'Package download_location can not be None.',
97            'Package verif_code can not be None.',
98            'Package cr_text can not be None.',
99            'Package must have at least one file.',
100            'Package concluded license must be instance of spdx.utils.SPDXNone '
101            'or spdx.utils.NoAssert or spdx.document.License',
102            'Package declared license must be instance of spdx.utils.SPDXNone '
103            'or spdx.utils.NoAssert or spdx.document.License'
104        ]
105        assert expected == messages
106
107    def test_document_is_valid_when_using_or_later_licenses(self):
108        doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'),
109                       'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT',
110                       namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301')
111        doc.creation_info.add_creator(Tool('ScanCode'))
112        doc.creation_info.set_created_now()
113
114        package = doc.package = Package(name='some/path', download_location=NoAssert())
115        package.spdx_id = 'SPDXRef-Package'
116        package.cr_text = 'Some copyrught'
117        package.verif_code = 'SOME code'
118        package.license_declared = NoAssert()
119        package.conc_lics = NoAssert()
120
121        file1 = File('./some/path/tofile')
122        file1.name = './some/path/tofile'
123        file1.spdx_id = 'SPDXRef-File'
124        file1.chk_sum = Algorithm('SHA1', 'SOME-SHA1')
125        file1.conc_lics = NoAssert()
126        file1.copyright = NoAssert()
127
128        lic1 = License.from_identifier('LGPL-2.1-or-later')
129        file1.add_lics(lic1)
130
131        package.add_lics_from_file(lic1)
132        package.add_file(file1)
133        messages = []
134        is_valid = doc.validate(messages)
135        assert is_valid
136        assert not messages
137
138
139class TestWriters(TestCase):
140    maxDiff = None
141
142    def _get_lgpl_doc(self, or_later=False):
143        doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'),
144                       'Sample_Document-V2.1', spdx_id='SPDXRef-DOCUMENT',
145                       namespace='https://spdx.org/spdxdocs/spdx-example-444504E0-4F89-41D3-9A0C-0305E82C3301')
146        doc.creation_info.add_creator(Tool('ScanCode'))
147        doc.creation_info.set_created_now()
148
149        package = doc.package = Package(name='some/path', download_location=NoAssert())
150        package.spdx_id = 'SPDXRef-Package'
151        package.cr_text = 'Some copyrught'
152        package.verif_code = 'SOME code'
153        package.check_sum = Algorithm('SHA1', 'SOME-SHA1')
154        package.license_declared = NoAssert()
155        package.conc_lics = NoAssert()
156
157        file1 = File('./some/path/tofile')
158        file1.name = './some/path/tofile'
159        file1.spdx_id = 'SPDXRef-File'
160        file1.chk_sum = Algorithm('SHA1', 'SOME-SHA1')
161        file1.conc_lics = NoAssert()
162        file1.copyright = NoAssert()
163
164        lic1 = License.from_identifier('LGPL-2.1-only')
165        if or_later:
166            lic1 = License.from_identifier('LGPL-2.1-or-later')
167
168        file1.add_lics(lic1)
169
170        package.add_lics_from_file(lic1)
171        package.add_file(file1)
172        return doc
173
174    def test_write_document_rdf_with_validate(self):
175        from spdx.writers.rdf import write_document
176        doc = self._get_lgpl_doc()
177        temp_dir = ''
178        try:
179            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
180            result_file = os.path.join(temp_dir, 'spdx-simple.rdf')
181            with open(result_file, 'wb') as output:
182                write_document(doc, output, validate=True)
183
184            expected_file = utils_test.get_test_loc(
185                'doc_write/rdf-simple.json',
186                test_data_dir=utils_test.test_data_dir)
187
188            utils_test.check_rdf_scan(expected_file, result_file, regen=False)
189        finally:
190            if temp_dir and os.path.exists(temp_dir):
191                shutil.rmtree(temp_dir)
192
193    def test_write_document_rdf_with_or_later_with_validate(self):
194        from spdx.writers.rdf import write_document
195        doc = self._get_lgpl_doc(or_later=True)
196
197        temp_dir = ''
198        try:
199            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
200            result_file = os.path.join(temp_dir, 'spdx-simple-plus.rdf')
201
202            # test proper!
203            with open(result_file, 'wb') as output:
204                write_document(doc, output, validate=True)
205
206            expected_file = utils_test.get_test_loc(
207                'doc_write/rdf-simple-plus.json',
208                test_data_dir=utils_test.test_data_dir)
209
210            utils_test.check_rdf_scan(expected_file, result_file, regen=False)
211        finally:
212            if temp_dir and os.path.exists(temp_dir):
213                shutil.rmtree(temp_dir)
214
215    def test_write_document_tv_with_validate(self):
216        from spdx.writers.tagvalue import write_document
217        doc = self._get_lgpl_doc()
218
219        temp_dir = ''
220        try:
221            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
222            result_file = os.path.join(temp_dir, 'spdx-simple.tv')
223            with open(result_file, 'w') as output:
224                write_document(doc, output, validate=True)
225
226            expected_file = utils_test.get_test_loc(
227                'doc_write/tv-simple.tv',
228                test_data_dir=utils_test.test_data_dir)
229
230            utils_test.check_tv_scan(expected_file, result_file, regen=False)
231        finally:
232            if temp_dir and os.path.exists(temp_dir):
233                shutil.rmtree(temp_dir)
234
235    def test_write_document_tv_with_or_later_with_validate(self):
236        from spdx.writers.tagvalue import write_document
237
238        doc = self._get_lgpl_doc(or_later=True)
239
240        temp_dir = ''
241        try:
242            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
243            result_file = os.path.join(temp_dir, 'spdx-simple-plus.tv')
244
245            # test proper!
246            with open(result_file, 'w') as output:
247                write_document(doc, output, validate=True)
248
249            expected_file = utils_test.get_test_loc(
250                'doc_write/tv-simple-plus.tv',
251                test_data_dir=utils_test.test_data_dir)
252
253            utils_test.check_tv_scan(expected_file, result_file, regen=False)
254        finally:
255            if temp_dir and os.path.exists(temp_dir):
256                shutil.rmtree(temp_dir)
257
258    def test_write_document_json_with_validate(self):
259        from spdx.writers.json import write_document
260        doc = self._get_lgpl_doc()
261
262        temp_dir = ''
263        try:
264            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
265            result_file = os.path.join(temp_dir, 'spdx-simple.json')
266            with open(result_file, 'w') as output:
267                write_document(doc, output, validate=True)
268
269            expected_file = utils_test.get_test_loc(
270                'doc_write/json-simple.json',
271                test_data_dir=utils_test.test_data_dir)
272
273            utils_test.check_json_scan(expected_file, result_file, regen=False)
274        finally:
275            if temp_dir and os.path.exists(temp_dir):
276                shutil.rmtree(temp_dir)
277
278    def test_write_document_json_with_or_later_with_validate(self):
279        from spdx.writers.json import write_document
280        doc = self._get_lgpl_doc(or_later=True)
281
282        temp_dir = ''
283        try:
284            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
285            result_file = os.path.join(temp_dir, 'spdx-simple-plus.json')
286            with open(result_file, 'w') as output:
287                write_document(doc, output, validate=True)
288
289            expected_file = utils_test.get_test_loc(
290                'doc_write/json-simple-plus.json',
291                test_data_dir=utils_test.test_data_dir)
292
293            utils_test.check_json_scan(expected_file, result_file, regen=False)
294        finally:
295            if temp_dir and os.path.exists(temp_dir):
296                shutil.rmtree(temp_dir)
297
298    def test_write_document_yaml_with_validate(self):
299        from spdx.writers.yaml import write_document
300        doc = self._get_lgpl_doc()
301
302        temp_dir = ''
303        try:
304            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
305            result_file = os.path.join(temp_dir, 'spdx-simple.yaml')
306            with open(result_file, 'w') as output:
307                write_document(doc, output, validate=True)
308
309            expected_file = utils_test.get_test_loc(
310                'doc_write/yaml-simple.yaml',
311                test_data_dir=utils_test.test_data_dir)
312
313            utils_test.check_yaml_scan(expected_file, result_file, regen=False)
314        finally:
315            if temp_dir and os.path.exists(temp_dir):
316                shutil.rmtree(temp_dir)
317
318    def test_write_document_yaml_with_or_later_with_validate(self):
319        from spdx.writers.yaml import write_document
320        doc = self._get_lgpl_doc(or_later=True)
321
322        temp_dir = ''
323        try:
324            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
325            result_file = os.path.join(temp_dir, 'spdx-simple-plus.yaml')
326            with open(result_file, 'w') as output:
327                write_document(doc, output, validate=True)
328
329            expected_file = utils_test.get_test_loc(
330                'doc_write/yaml-simple-plus.yaml',
331                test_data_dir=utils_test.test_data_dir)
332
333            utils_test.check_yaml_scan(expected_file, result_file, regen=False)
334        finally:
335            if temp_dir and os.path.exists(temp_dir):
336                shutil.rmtree(temp_dir)
337
338    def test_write_document_xml_with_validate(self):
339        from spdx.writers.xml import write_document
340        doc = self._get_lgpl_doc()
341
342        temp_dir = ''
343        try:
344            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
345            result_file = os.path.join(temp_dir, 'spdx-simple.xml')
346            with open(result_file, 'w') as output:
347                write_document(doc, output, validate=True)
348
349            expected_file = utils_test.get_test_loc(
350                'doc_write/xml-simple.xml',
351                test_data_dir=utils_test.test_data_dir)
352
353            utils_test.check_xml_scan(expected_file, result_file, regen=False)
354        finally:
355            if temp_dir and os.path.exists(temp_dir):
356                shutil.rmtree(temp_dir)
357
358    def test_write_document_xml_with_or_later_with_validate(self):
359        from spdx.writers.xml import write_document
360        doc = self._get_lgpl_doc(or_later=True)
361
362        temp_dir = ''
363        try:
364            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
365            result_file = os.path.join(temp_dir, 'spdx-simple-plus.xml')
366            with open(result_file, 'w') as output:
367                write_document(doc, output, validate=True)
368
369            expected_file = utils_test.get_test_loc(
370                'doc_write/xml-simple-plus.xml',
371                test_data_dir=utils_test.test_data_dir)
372
373            utils_test.check_xml_scan(expected_file, result_file, regen=False)
374        finally:
375            if temp_dir and os.path.exists(temp_dir):
376                shutil.rmtree(temp_dir)
377
378    def _get_mini_doc(self,):
379        doc = Document(Version(2, 1), License.from_identifier('CC0-1.0'))
380        doc.creation_info.add_creator(Tool('ScanCode'))
381        doc.creation_info.set_created_now()
382
383        package = doc.package = Package(download_location=NoAssert())
384        package.license_declared = NoAssert()
385        package.conc_lics = NoAssert()
386        return doc
387
388    def test_write_document_tv_mini(self):
389        from spdx.writers.tagvalue import write_document
390        doc = self._get_mini_doc()
391
392        temp_dir = ''
393        try:
394            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
395            result_file = os.path.join(temp_dir, 'spdx-simple.tv')
396            with open(result_file, 'w') as output:
397                write_document(doc, output, validate=False)
398            expected_file = utils_test.get_test_loc('doc_write/tv-mini.tv')
399            utils_test.check_tv_scan(expected_file, result_file, regen=False)
400        finally:
401            if temp_dir and os.path.exists(temp_dir):
402                shutil.rmtree(temp_dir)
403
404    def test_write_document_rdf_mini(self):
405        from spdx.writers.rdf import write_document
406        doc = self._get_mini_doc()
407        temp_dir = ''
408        try:
409            temp_dir = tempfile.mkdtemp(prefix='test_spdx')
410            result_file = os.path.join(temp_dir, 'spdx-simple.rdf')
411            with open(result_file, 'wb') as output:
412                write_document(doc, output, validate=False)
413            expected_file = utils_test.get_test_loc('doc_write/rdf-mini.json')
414            utils_test.check_rdf_scan(expected_file, result_file, regen=False)
415        finally:
416            if temp_dir and os.path.exists(temp_dir):
417                shutil.rmtree(temp_dir)
418
419
420class TestLicense(TestCase):
421    maxDiff = None
422
423    def test_url(self):
424        lic = License(full_name='Apache License 1.0', identifier='Apache-1.0')
425        assert lic.url == 'http://spdx.org/licenses/Apache-1.0'
426
427    def test_license_list(self):
428        assert LICENSE_MAP['Aladdin Free Public License'] == 'Aladdin'
429        assert LICENSE_MAP['Aladdin'] == 'Aladdin Free Public License'
430        assert LICENSE_MAP['MIT License'] == 'MIT'
431        assert LICENSE_MAP['MIT'] == 'MIT License'
432        assert LICENSE_MAP['BSD 4-Clause "Original" or "Old" License'] == 'BSD-4-Clause'
433        assert LICENSE_MAP['BSD-4-Clause'] == 'BSD 4-Clause "Original" or "Old" License'
434
435    def test_from_full_name(self):
436        mit = License.from_full_name('MIT License')
437        assert mit.identifier == 'MIT'
438        assert mit.url == 'http://spdx.org/licenses/MIT'
439
440    def test_from_identifier(self):
441        mit = License.from_identifier('MIT')
442        assert mit.full_name == 'MIT License'
443        assert mit.url == 'http://spdx.org/licenses/MIT'
444
445
446class TestException(TestCase):
447
448    def test_exception_list(self):
449        assert EXCEPTION_MAP['Linux Syscall Note'] == 'Linux-syscall-note'
450        assert EXCEPTION_MAP['Linux-syscall-note'] == 'Linux Syscall Note'
451        assert EXCEPTION_MAP['GCC Runtime Library exception 3.1'] == 'GCC-exception-3.1'
452        assert EXCEPTION_MAP['GCC-exception-3.1'] == 'GCC Runtime Library exception 3.1'
453