|
9 | 9 | from typing import Any |
10 | 10 |
|
11 | 11 | import pytest |
12 | | -from conftest import chdir, cmd, cmd_raises, update_env |
| 12 | +from conftest import WINDOWS, chdir, cmd, cmd_raises, tmp_west_topdir, update_env |
13 | 13 |
|
14 | 14 | from west import configuration as config |
15 | 15 | from west.util import PathType, WestNotFound |
@@ -614,6 +614,127 @@ def test_config_precedence(): |
614 | 614 | assert cfg(f=LOCAL)['pytest']['precedence'] == 'local' |
615 | 615 |
|
616 | 616 |
|
| 617 | +def test_config_multiple(config_tmpdir): |
| 618 | + # Verify that local settings take precedence over global ones, |
| 619 | + # but that both values are still available, and that setting |
| 620 | + # either doesn't affect system settings. |
| 621 | + def write_config(config_file, section, key1, value1, key2, value2): |
| 622 | + config_file.parent.mkdir(exist_ok=True) |
| 623 | + |
| 624 | + content = textwrap.dedent(f''' |
| 625 | + [{section}] |
| 626 | + {key1} = {value1} |
| 627 | + {key2} = {value2} |
| 628 | + ''') |
| 629 | + |
| 630 | + with open(config_file, 'w') as conf: |
| 631 | + conf.write(content) |
| 632 | + |
| 633 | + # config file paths |
| 634 | + config_dir = pathlib.Path(config_tmpdir) / 'configs' |
| 635 | + config_s1 = config_dir / 'system 1' |
| 636 | + config_s2 = config_dir / 'system 2' |
| 637 | + config_g1 = config_dir / 'global 1' |
| 638 | + config_g2 = config_dir / 'global 2' |
| 639 | + config_l1 = config_dir / 'local 1' |
| 640 | + config_l2 = config_dir / 'local 2' |
| 641 | + |
| 642 | + # create some configs with |
| 643 | + # - some individual option per config file |
| 644 | + # - the same option defined in multiple configs |
| 645 | + write_config(config_s1, 'sec', 's', '1 !"$&/()=?', 's1', '1 !"$&/()=?') |
| 646 | + write_config(config_s2, 'sec', 's', '2', 's2', '2') |
| 647 | + write_config(config_g1, 'sec', 'g', '1', 'g1', '1') |
| 648 | + write_config(config_g2, 'sec', 'g', '2', 'g2', '2') |
| 649 | + write_config(config_l1, 'sec', 'l', '1', 'l1', '1') |
| 650 | + write_config(config_l2, 'sec', 'l', '2', 'l2', '2') |
| 651 | + |
| 652 | + # use a non-readable config file (does not work on Windows) |
| 653 | + if not WINDOWS: |
| 654 | + config_non_readable = config_dir / 'non-readable' |
| 655 | + config_non_readable.touch() |
| 656 | + config_non_readable.chmod(0o000) |
| 657 | + os.environ["WEST_CONFIG_GLOBAL"] = f'{config_g1}{os.pathsep}{config_non_readable}' |
| 658 | + _, stderr = cmd_raises('config --global some.section', WestNotFound) |
| 659 | + expected = f"Error while reading one of '{config_g1}{os.pathsep}{config_non_readable}'" |
| 660 | + assert expected in stderr |
| 661 | + |
| 662 | + # specify multiple configs for each config level (separated by os.pathsep) |
| 663 | + os.environ["WEST_CONFIG_GLOBAL"] = f'{config_g1}{os.pathsep}{config_g2}' |
| 664 | + os.environ["WEST_CONFIG_SYSTEM"] = f'{config_s1}{os.pathsep}{config_s2}' |
| 665 | + os.environ["WEST_CONFIG_LOCAL"] = f'{config_l1}{os.pathsep}{config_l2}' |
| 666 | + |
| 667 | + # check that all individual options are applied |
| 668 | + stdout = cmd('config --system sec.s1').rstrip() |
| 669 | + assert stdout == '1 !"$&/()=?' |
| 670 | + stdout = cmd('config --system sec.s2').rstrip() |
| 671 | + assert stdout == '2' |
| 672 | + stdout = cmd('config --global sec.g1').rstrip() |
| 673 | + assert stdout == '1' |
| 674 | + stdout = cmd('config --global sec.g2').rstrip() |
| 675 | + assert stdout == '2' |
| 676 | + stdout = cmd('config --local sec.l1').rstrip() |
| 677 | + assert stdout == '1' |
| 678 | + stdout = cmd('config --local sec.l2').rstrip() |
| 679 | + assert stdout == '2' |
| 680 | + |
| 681 | + # check that options from latest config overrides |
| 682 | + stdout = cmd('config --system sec.s').rstrip() |
| 683 | + assert stdout == '2' |
| 684 | + stdout = cmd('config --global sec.g').rstrip() |
| 685 | + assert stdout == '2' |
| 686 | + stdout = cmd('config --local sec.l').rstrip() |
| 687 | + assert stdout == '2' |
| 688 | + |
| 689 | + # check that list-paths gives correct output |
| 690 | + stdout = cmd('config --global --list-paths') |
| 691 | + assert [str(config_g1), str(config_g2)] == stdout.rstrip().splitlines() |
| 692 | + stdout = cmd('config --system --list-paths') |
| 693 | + assert [str(config_s1), str(config_s2)] == stdout.rstrip().splitlines() |
| 694 | + stdout = cmd('config --local --list-paths') |
| 695 | + assert [str(config_l1), str(config_l2)] == stdout.rstrip().splitlines() |
| 696 | + |
| 697 | + # writing not possible if multiple configs are used |
| 698 | + _, stderr = cmd_raises('config --local sec.l3 3', ValueError) |
| 699 | + assert f'Cannot set value if multiple configs in use: {[config_l1, config_l2]}' in stderr |
| 700 | + |
| 701 | + # specify multiple configs for each config level (separated by os.pathsep). |
| 702 | + # The paths may be relative relative paths, which are always anchored to |
| 703 | + # west topdir. For the test, the cwd is changed to another cwd to ensure |
| 704 | + # that relative paths are anchored correctly. |
| 705 | + # - Step 1: if no west topdir exists it should fail as relative path |
| 706 | + # cannot be resolved |
| 707 | + # - Step 2: relative paths are correctly resolved (anchored to west topdir) |
| 708 | + msg = "'{file}' is relative but 'west topdir' is not defined" |
| 709 | + cwd = pathlib.Path('any cwd') |
| 710 | + cwd.mkdir() |
| 711 | + with chdir(cwd): |
| 712 | + config_rel = pathlib.Path('configs') / 'global 2' |
| 713 | + with update_env({'WEST_CONFIG_GLOBAL': f'{config_g1}{os.pathsep}{config_rel}'}): |
| 714 | + command = 'config --global --list-paths' |
| 715 | + exc, _ = cmd_raises(command, WestNotFound) |
| 716 | + assert msg.format(file=config_rel) in str(exc.value) |
| 717 | + with tmp_west_topdir('..'): |
| 718 | + stdout = cmd(command) |
| 719 | + assert [str(config_g1), str(config_g2)] == stdout.rstrip().splitlines() |
| 720 | + config_rel = pathlib.Path('configs') / 'system 2' |
| 721 | + with update_env({'WEST_CONFIG_SYSTEM': f'{config_s1}{os.pathsep}{config_rel}'}): |
| 722 | + command = 'config --system --list-paths' |
| 723 | + exc, _ = cmd_raises(command, WestNotFound) |
| 724 | + assert msg.format(file=config_rel) in str(exc.value) |
| 725 | + with tmp_west_topdir('..'): |
| 726 | + stdout = cmd(command) |
| 727 | + assert [str(config_s1), str(config_s2)] == stdout.rstrip().splitlines() |
| 728 | + config_rel = pathlib.Path('configs') / 'local 2' |
| 729 | + with update_env({'WEST_CONFIG_LOCAL': f'{config_l1}{os.pathsep}{config_rel}'}): |
| 730 | + command = 'config --local --list-paths' |
| 731 | + exc, _ = cmd_raises(command, WestNotFound) |
| 732 | + assert msg.format(file=config_rel) in str(exc.value) |
| 733 | + with tmp_west_topdir('..'): |
| 734 | + stdout = cmd(command) |
| 735 | + assert [str(config_l1), str(config_l2)] == stdout.rstrip().splitlines() |
| 736 | + |
| 737 | + |
617 | 738 | def test_config_missing_key(): |
618 | 739 | _, err_msg = cmd_raises('config pytest', SystemExit) |
619 | 740 | assert 'invalid configuration option "pytest"; expected "section.key" format' in err_msg |
|
0 commit comments