| 10 |
10 |
import pytest
|
| 11 |
11 |
import json
|
| 12 |
12 |
import shutil
|
|
13 |
import functools as ft
|
| 13 |
14 |
|
| 14 |
15 |
from tempfile import TemporaryDirectory
|
| 15 |
16 |
from pathlib import Path, PurePosixPath
|
| ... | ... | |
| 255 |
256 |
|
| 256 |
257 |
yield sample_source
|
| 257 |
258 |
|
|
259 |
def collect(list):
|
|
260 |
"""Decorate function by appending it to the specified list."""
|
|
261 |
def decorator(function):
|
|
262 |
"""The actual decorator that will be applied."""
|
|
263 |
list.append(function)
|
|
264 |
return function
|
|
265 |
|
|
266 |
return decorator
|
|
267 |
|
| 258 |
268 |
variant_makers = []
|
| 259 |
|
def variant_maker(function):
|
| 260 |
|
"""Decorate function by placing it in variant_makers array."""
|
| 261 |
|
variant_makers.append(function)
|
| 262 |
|
return function
|
| 263 |
269 |
|
| 264 |
|
@variant_maker
|
|
270 |
@collect(variant_makers)
|
| 265 |
271 |
def sample_source_change_index_json(monkeypatch, sample_source):
|
| 266 |
272 |
"""
|
| 267 |
273 |
Return a non-standard path for index.json. Ensure parent directories exist.
|
| ... | ... | |
| 272 |
278 |
path.parent.mkdir()
|
| 273 |
279 |
return path
|
| 274 |
280 |
|
| 275 |
|
@variant_maker
|
|
281 |
@collect(variant_makers)
|
| 276 |
282 |
def sample_source_add_comments(monkeypatch, sample_source):
|
| 277 |
283 |
"""Add index.json comments that should be preserved."""
|
| 278 |
284 |
for dictionary in index_obj, *index_obj['definitions'], *expected:
|
| 279 |
285 |
monkeypatch.setitem(dictionary, 'comment', 'index.json comment')
|
| 280 |
286 |
|
| 281 |
|
@variant_maker
|
|
287 |
@collect(variant_makers)
|
| 282 |
288 |
def sample_source_remove_spdx(monkeypatch, sample_source):
|
| 283 |
289 |
"""Remove spdx report generation."""
|
| 284 |
290 |
monkeypatch.delitem(index_obj, 'reuse_generate_spdx_report')
|
| ... | ... | |
| 297 |
303 |
# raise an error if called.
|
| 298 |
304 |
(sample_source / 'mock_reuse_missing').touch()
|
| 299 |
305 |
|
| 300 |
|
@variant_maker
|
|
306 |
@collect(variant_makers)
|
| 301 |
307 |
def sample_source_remove_additional_files(monkeypatch, sample_source):
|
| 302 |
308 |
"""Use default value ([]) for 'additionall_files' property."""
|
| 303 |
309 |
monkeypatch.delitem(index_obj, 'additional_files')
|
| ... | ... | |
| 305 |
311 |
for name in 'README.txt', 'README.txt.license', '.reuse/dep5':
|
| 306 |
312 |
monkeypatch.delitem(src_files, name)
|
| 307 |
313 |
|
| 308 |
|
@variant_maker
|
|
314 |
@collect(variant_makers)
|
| 309 |
315 |
def sample_source_remove_script(monkeypatch, sample_source):
|
| 310 |
316 |
"""Use default value ([]) for 'scripts' property in one of the resources."""
|
| 311 |
317 |
monkeypatch.delitem(index_obj['definitions'][2], 'scripts')
|
| ... | ... | |
| 315 |
321 |
for files in dist_files, src_files:
|
| 316 |
322 |
monkeypatch.delitem(files, 'message.js')
|
| 317 |
323 |
|
| 318 |
|
@variant_maker
|
|
324 |
@collect(variant_makers)
|
| 319 |
325 |
def sample_source_remove_payloads(monkeypatch, sample_source):
|
| 320 |
326 |
"""Use default value ({}) for 'payloads' property in mapping."""
|
| 321 |
327 |
monkeypatch.delitem(index_obj['definitions'][0], 'payloads')
|
| 322 |
328 |
|
| 323 |
329 |
monkeypatch.setitem(expected_mapping, 'payloads', {})
|
| 324 |
330 |
|
| 325 |
|
@variant_maker
|
|
331 |
@collect(variant_makers)
|
| 326 |
332 |
def sample_source_remove_uuids(monkeypatch, sample_source):
|
| 327 |
333 |
"""Don't use UUIDs (they are optional)."""
|
| 328 |
334 |
for definition in index_obj['definitions']:
|
| ... | ... | |
| 332 |
338 |
if 'uuid' in description:
|
| 333 |
339 |
monkeypatch.delitem(description, 'uuid')
|
| 334 |
340 |
|
| 335 |
|
@variant_maker
|
|
341 |
@collect(variant_makers)
|
| 336 |
342 |
def sample_source_add_extra_props(monkeypatch, sample_source):
|
| 337 |
343 |
"""Add some unrecognized properties that should be stripped."""
|
| 338 |
344 |
to_process = [index_obj]
|
| ... | ... | |
| 346 |
352 |
if k != 'payloads')
|
| 347 |
353 |
monkeypatch.setitem(processed, 'spurious_property', 'some_value')
|
| 348 |
354 |
|
| 349 |
|
@variant_maker
|
|
355 |
@collect(variant_makers)
|
| 350 |
356 |
def sample_source_make_version_2(monkeypatch, sample_source,
|
| 351 |
357 |
expected_documents_to_modify=[]):
|
| 352 |
|
"""Increase sources' schema version from to 2."""
|
|
358 |
"""Increase sources' schema version from 1 to 2."""
|
| 353 |
359 |
for obj in index_obj, *expected_documents_to_modify:
|
| 354 |
360 |
monkeypatch.setitem(obj, '$schema', obj['$schema'].replace('1', '2'))
|
| 355 |
361 |
|
| 356 |
|
@variant_maker
|
| 357 |
|
def sample_source_cors_bypass_ignored(monkeypatch, sample_source, value=True):
|
|
362 |
permission_variant_makers = []
|
|
363 |
|
|
364 |
@collect(permission_variant_makers)
|
|
365 |
def sample_source_bool_perm_ignored(permission, monkeypatch, sample_source,
|
|
366 |
value=True):
|
| 358 |
367 |
"""
|
| 359 |
|
Specify CORS bypass permissions in sources, but keep sources' schema version
|
|
368 |
Specify a boolean permissions in sources, but keep sources' schema version
|
| 360 |
369 |
at 1.
|
| 361 |
370 |
"""
|
| 362 |
371 |
for definition in index_obj['definitions']:
|
| 363 |
|
monkeypatch.setitem(definition, 'permissions', {'cors_bypass': value})
|
|
372 |
monkeypatch.setitem(definition, 'permissions', {permission: value})
|
| 364 |
373 |
|
| 365 |
|
@variant_maker
|
| 366 |
|
def sample_source_cors_bypass(monkeypatch, sample_source):
|
| 367 |
|
"""Specify CORS bypass permissions in sources."""
|
| 368 |
|
sample_source_cors_bypass_ignored(monkeypatch, sample_source, value=True)
|
|
374 |
@collect(permission_variant_makers)
|
|
375 |
def sample_source_bool_perm(permission, monkeypatch, sample_source):
|
|
376 |
"""Specify a boolean permission in sources."""
|
|
377 |
sample_source_bool_perm_ignored(permission, monkeypatch, sample_source)
|
| 369 |
378 |
sample_source_make_version_2(monkeypatch, sample_source, expected_items)
|
| 370 |
379 |
|
| 371 |
380 |
for obj in expected_items:
|
| 372 |
|
monkeypatch.setitem(obj, 'permissions', {'cors_bypass': True})
|
|
381 |
monkeypatch.setitem(obj, 'permissions', {permission: True})
|
| 373 |
382 |
|
| 374 |
|
@variant_maker
|
| 375 |
|
def sample_source_cors_bypass_defaults(monkeypatch, sample_source):
|
|
383 |
@collect(permission_variant_makers)
|
|
384 |
def sample_source_bool_perm_defaults(permission, monkeypatch, sample_source):
|
| 376 |
385 |
"""
|
| 377 |
|
Specify CORS bypass permissions in sources but use the default value
|
| 378 |
|
("False").
|
|
386 |
Specify a boolean permission in sources but use the default value ("False").
|
| 379 |
387 |
"""
|
| 380 |
|
sample_source_cors_bypass_ignored(monkeypatch, sample_source, value=False)
|
|
388 |
sample_source_bool_perm_ignored(permission, monkeypatch, sample_source,
|
|
389 |
value=False)
|
| 381 |
390 |
sample_source_make_version_2(monkeypatch, sample_source)
|
| 382 |
391 |
|
| 383 |
|
@variant_maker
|
|
392 |
for permission in 'cors_bypass', 'eval':
|
|
393 |
for variant_maker in permission_variant_makers:
|
|
394 |
variant_makers.append(ft.partial(variant_maker, permission))
|
|
395 |
|
|
396 |
@collect(variant_makers)
|
| 384 |
397 |
def sample_source_req_mappings_ignored(monkeypatch, sample_source,
|
| 385 |
398 |
value=[{'identifier': 'mapping-dep'}]):
|
| 386 |
399 |
"""
|
| ... | ... | |
| 389 |
402 |
for definition in index_obj['definitions']:
|
| 390 |
403 |
monkeypatch.setitem(definition, 'required_mappings', value);
|
| 391 |
404 |
|
| 392 |
|
@variant_maker
|
|
405 |
@collect(variant_makers)
|
| 393 |
406 |
def sample_source_req_mappings(monkeypatch, sample_source):
|
| 394 |
407 |
"""Specify dependencies on mappings."""
|
| 395 |
408 |
sample_source_req_mappings_ignored(monkeypatch, sample_source)
|
| ... | ... | |
| 399 |
412 |
monkeypatch.setitem(obj, 'required_mappings',
|
| 400 |
413 |
[{'identifier': 'mapping-dep'}])
|
| 401 |
414 |
|
| 402 |
|
@variant_maker
|
|
415 |
@collect(variant_makers)
|
| 403 |
416 |
def sample_source_req_mappings_defaults(monkeypatch, sample_source):
|
| 404 |
417 |
"""Specify dependencies of a mapping, but use the default value ("[]")."""
|
| 405 |
418 |
sample_source_req_mappings_ignored(monkeypatch, sample_source, value=[])
|
| 406 |
419 |
sample_source_make_version_2(monkeypatch, sample_source)
|
| 407 |
420 |
|
| 408 |
|
@variant_maker
|
|
421 |
@collect(variant_makers)
|
| 409 |
422 |
def sample_source_combined_def(monkeypatch, sample_source):
|
| 410 |
423 |
"""Define mapping and resource together."""
|
| 411 |
424 |
sample_source_make_version_2(monkeypatch, sample_source)
|
| ... | ... | |
| 432 |
445 |
monkeypatch.setitem(expected_source_description['definitions'][0],
|
| 433 |
446 |
'version', new_mapping_ver)
|
| 434 |
447 |
|
| 435 |
|
@variant_maker
|
|
448 |
@collect(variant_makers)
|
| 436 |
449 |
def sample_source_minmax_haketilo_ver_ignored(monkeypatch, sample_source,
|
| 437 |
450 |
min_ver=[1, 2], max_ver=[1, 2]):
|
| 438 |
451 |
"""
|
| ... | ... | |
| 443 |
456 |
monkeypatch.setitem(mapping_def, 'min_haketilo_version', min_ver)
|
| 444 |
457 |
monkeypatch.setitem(mapping_def, 'max_haketilo_version', max_ver)
|
| 445 |
458 |
|
| 446 |
|
@variant_maker
|
|
459 |
@collect(variant_makers)
|
| 447 |
460 |
def sample_source_minmax_haketilo_ver(monkeypatch, sample_source):
|
| 448 |
461 |
"""Specify version constraints on Haketilo."""
|
| 449 |
462 |
sample_source_minmax_haketilo_ver_ignored(monkeypatch, sample_source)
|
| ... | ... | |
| 452 |
465 |
monkeypatch.setitem(expected_mapping, 'min_haketilo_version', [1, 2])
|
| 453 |
466 |
monkeypatch.setitem(expected_mapping, 'max_haketilo_version', [1, 2])
|
| 454 |
467 |
|
| 455 |
|
@variant_maker
|
|
468 |
@collect(variant_makers)
|
| 456 |
469 |
def sample_source_minmax_haketilo_ver_default(monkeypatch, sample_source):
|
| 457 |
470 |
"""Specify version constraints on Haketilo, but use default values."""
|
| 458 |
471 |
sample_source_minmax_haketilo_ver_ignored(monkeypatch, sample_source,
|
| ... | ... | |
| 466 |
479 |
'othersystem/other-something.tar.gz'
|
| 467 |
480 |
]
|
| 468 |
481 |
|
| 469 |
|
@variant_maker
|
|
482 |
@collect(variant_makers)
|
| 470 |
483 |
def sample_source_add_piggyback_ignored(monkeypatch, sample_source,
|
| 471 |
484 |
extra_build_args={}):
|
| 472 |
485 |
"""
|
| ... | ... | |
| 484 |
497 |
'dependencies': False
|
| 485 |
498 |
})
|
| 486 |
499 |
|
| 487 |
|
@variant_maker
|
|
500 |
@collect(variant_makers)
|
| 488 |
501 |
def sample_source_add_piggyback(monkeypatch, sample_source,
|
| 489 |
502 |
extra_build_args={}):
|
| 490 |
503 |
"""Add piggybacked foreign system packages."""
|
| ... | ... | |
| 528 |
541 |
archive_path.parent.mkdir(parents=True, exist_ok=True)
|
| 529 |
542 |
archive_path.write_text(f'dummy {archive_path.name}')
|
| 530 |
543 |
|
| 531 |
|
@variant_maker
|
|
544 |
@collect(variant_makers)
|
| 532 |
545 |
def sample_source_add_piggyback_pass_archives(monkeypatch, sample_source):
|
| 533 |
546 |
"""
|
| 534 |
547 |
Add piggybacked foreign system packages, use pre-downloaded foreign package
|
| ... | ... | |
| 542 |
555 |
sample_source_add_piggyback(monkeypatch, sample_source,
|
| 543 |
556 |
{'piggyback_files': foreign_packages_dir})
|
| 544 |
557 |
|
| 545 |
|
@variant_maker
|
|
558 |
@collect(variant_makers)
|
| 546 |
559 |
def sample_source_add_piggyback_find_archives(monkeypatch, sample_source):
|
| 547 |
560 |
"""
|
| 548 |
561 |
Add piggybacked foreign system packages, use pre-downloaded foreign package
|
| ... | ... | |
| 555 |
568 |
|
| 556 |
569 |
sample_source_add_piggyback(monkeypatch, sample_source)
|
| 557 |
570 |
|
| 558 |
|
@variant_maker
|
|
571 |
@collect(variant_makers)
|
| 559 |
572 |
def sample_source_add_piggyback_no_download(monkeypatch, sample_source,
|
| 560 |
573 |
pass_directory_to_build=False):
|
| 561 |
574 |
"""
|
| ... | ... | |
| 678 |
691 |
try_validate('api_source_description', expected_source_description)
|
| 679 |
692 |
|
| 680 |
693 |
error_makers = []
|
| 681 |
|
def error_maker(function):
|
| 682 |
|
"""Decorate function by placing it in error_makers array."""
|
| 683 |
|
error_makers.append(function)
|
| 684 |
694 |
|
| 685 |
|
@error_maker
|
|
695 |
@collect(error_makers)
|
| 686 |
696 |
def sample_source_error_missing_file(monkeypatch, sample_source):
|
| 687 |
697 |
"""
|
| 688 |
698 |
Modify index.json to expect missing report.spdx file and cause an error.
|
| ... | ... | |
| 690 |
700 |
monkeypatch.delitem(index_obj, 'reuse_generate_spdx_report')
|
| 691 |
701 |
return FileReferenceError, '^referenced_file_report.spdx_missing$'
|
| 692 |
702 |
|
| 693 |
|
@error_maker
|
|
703 |
@collect(error_makers)
|
| 694 |
704 |
def sample_source_error_index_schema(monkeypatch, sample_source):
|
| 695 |
705 |
"""Modify index.json to be incompliant with the schema."""
|
| 696 |
706 |
monkeypatch.delitem(index_obj, 'definitions')
|
| 697 |
707 |
return ValidationError,
|
| 698 |
708 |
|
| 699 |
|
@error_maker
|
|
709 |
@collect(error_makers)
|
| 700 |
710 |
def sample_source_error_bad_comment(monkeypatch, sample_source):
|
| 701 |
711 |
"""Modify index.json to have an invalid '/' in it."""
|
| 702 |
712 |
return json.JSONDecodeError, '^bad_comment: .*', \
|
| 703 |
713 |
json.dumps(index_obj) + '/something\n'
|
| 704 |
714 |
|
| 705 |
|
@error_maker
|
|
715 |
@collect(error_makers)
|
| 706 |
716 |
def sample_source_error_bad_json(monkeypatch, sample_source):
|
| 707 |
717 |
"""Modify index.json to not be valid json even after comment stripping."""
|
| 708 |
718 |
return json.JSONDecodeError, '', json.dumps(index_obj) + '???\n'
|
| 709 |
719 |
|
| 710 |
|
@error_maker
|
|
720 |
@collect(error_makers)
|
| 711 |
721 |
def sample_source_error_missing_reuse(monkeypatch, sample_source):
|
| 712 |
722 |
"""Cause mocked reuse process invocation to fail with FileNotFoundError."""
|
| 713 |
723 |
(sample_source / 'mock_reuse_missing').touch()
|
| 714 |
724 |
return build.ReuseError, '^couldnt_execute_reuse_is_it_installed$'
|
| 715 |
725 |
|
| 716 |
|
@error_maker
|
|
726 |
@collect(error_makers)
|
| 717 |
727 |
def sample_source_error_missing_license(monkeypatch, sample_source):
|
| 718 |
728 |
"""Remove a file to make package REUSE-incompliant."""
|
| 719 |
729 |
(sample_source / 'README.txt.license').unlink()
|
| ... | ... | |
| 733 |
743 |
|
| 734 |
744 |
return build.ReuseError, error_regex
|
| 735 |
745 |
|
| 736 |
|
@error_maker
|
|
746 |
@collect(error_makers)
|
| 737 |
747 |
def sample_source_error_file_outside(monkeypatch, sample_source):
|
| 738 |
748 |
"""Make index.json illegally reference a file outside srcdir."""
|
| 739 |
749 |
new_list = [*index_obj['copyright'], {'file': '../abc'}]
|
| 740 |
750 |
monkeypatch.setitem(index_obj, 'copyright', new_list)
|
| 741 |
751 |
return FileReferenceError, '^path_contains_double_dot_\\.\\./abc$'
|
| 742 |
752 |
|
| 743 |
|
@error_maker
|
|
753 |
@collect(error_makers)
|
| 744 |
754 |
def sample_source_error_reference_itself(monkeypatch, sample_source):
|
| 745 |
755 |
"""Make index.json illegally reference index.json."""
|
| 746 |
756 |
new_list = [*index_obj['copyright'], {'file': 'index.json'}]
|
| 747 |
757 |
monkeypatch.setitem(index_obj, 'copyright', new_list)
|
| 748 |
758 |
return FileReferenceError, '^loading_reserved_index_json$'
|
| 749 |
759 |
|
| 750 |
|
@error_maker
|
|
760 |
@collect(error_makers)
|
| 751 |
761 |
def sample_source_error_report_excluded(monkeypatch, sample_source):
|
| 752 |
762 |
"""
|
| 753 |
763 |
Make index.json require generation of report.spdx but don't include it among
|
| ... | ... | |
| 758 |
768 |
monkeypatch.setitem(index_obj, 'copyright', new_list)
|
| 759 |
769 |
return FileReferenceError, '^report_spdx_not_in_copyright_list$'
|
| 760 |
770 |
|
| 761 |
|
@error_maker
|
|
771 |
@collect(error_makers)
|
| 762 |
772 |
def sample_source_error_combined_unsupported(monkeypatch, sample_source):
|
| 763 |
773 |
"""
|
| 764 |
774 |
Define mapping and resource together but leave source schema version at 1.x
|
add "eval" permission