Skip to content

Commit

Permalink
Merge pull request #166 from espressif/fix/unset_cli_args_overwrite_c…
Browse files Browse the repository at this point in the history
…onfig_files_with_default_value

fix: unset CLI argument wrongly overwrite config file settings with default value
  • Loading branch information
hfudev authored Sep 27, 2024
2 parents 0754b66 + 87f3004 commit 5e18626
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 6 deletions.
16 changes: 11 additions & 5 deletions idf_build_apps/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,9 @@ def validate_by_validate_methods(cls, v: t.Any, info: ValidationInfo):
if info.field_name and info.field_name in cls.model_fields:
f = cls.model_fields[info.field_name]
meta = get_meta(f)
if meta and meta.validate_method and ValidateMethod.TO_LIST in meta.validate_method:
return to_list(v)
if meta and meta.validate_method:
if ValidateMethod.TO_LIST in meta.validate_method:
return to_list(v)

return v

Expand Down Expand Up @@ -269,7 +270,7 @@ class DependencyDrivenBuildArguments(GlobalArguments):
)
compare_manifest_sha_filepath: t.Optional[str] = field(
None,
description='Path to the file containing the sha256 hash of the manifest rules. '
description='Path to the file containing the hash of the manifest rules. '
'Compare the hash with the current manifest rules. '
'All matched apps will be built if the corresponding manifest rule is modified',
default=None,
Expand Down Expand Up @@ -800,14 +801,19 @@ def add_args_to_parser(argument_cls: t.Type[BaseArguments], parser: argparse.Arg
kwargs['required'] = True
if f_meta.action:
kwargs['action'] = f_meta.action
# to make the CLI override config file work
if f_meta.action == 'store_true':
kwargs['default'] = None

if f_meta.nargs:
kwargs['nargs'] = f_meta.nargs
if f_meta.choices:
kwargs['choices'] = f_meta.choices
if f_meta.default:
kwargs['default'] = f_meta.default
if 'default' not in kwargs:
kwargs['default'] = f.default

# here in CLI arguments, don't set the default to field.default
# otherwise it will override the config file settings

parser.add_argument(
*names,
Expand Down
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
setup_logging,
)
from idf_build_apps.args import apply_config_file
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN, SUPPORTED_TARGETS
from idf_build_apps.manifest.manifest import FolderRule


@pytest.fixture(autouse=True)
def clean_cls_attr(tmp_path):
App.MANIFEST = None
FolderRule.DEFAULT_BUILD_TARGETS = SUPPORTED_TARGETS
idf_build_apps.SESSION_ARGS.clean()
apply_config_file(IDF_BUILD_APPS_TOML_FN)
os.chdir(tmp_path)
Expand Down
97 changes: 97 additions & 0 deletions tests/test_args.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
from xml.etree import ElementTree

import pytest
from conftest import (
create_project,
)

from idf_build_apps import App
from idf_build_apps.args import (
Expand Down Expand Up @@ -197,3 +201,96 @@ def test_ignore_extra_fields(self):

args = FindArguments()
assert not hasattr(args, 'dry_run')

def test_config_file(self, tmp_path, monkeypatch):
create_project('foo', tmp_path)

with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
fw.write("""paths = ["foo"]
target = "esp32"
build_dir = "build_@t"
junitxml = "test.xml"
keep_going = true
""")

# test basic config
with monkeypatch.context() as m:
m.setenv('PATH', 'foo') # let build fail
m.setattr('sys.argv', ['idf-build-apps', 'build'])
with pytest.raises(SystemExit):
main()

with open('test.xml') as f:
xml = ElementTree.fromstring(f.read())
test_suite = xml.findall('testsuite')[0]
assert test_suite.attrib['failures'] == '1'
assert test_suite.attrib['errors'] == '0'
assert test_suite.attrib['skipped'] == '0'
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'

# test cli overrides config
with monkeypatch.context() as m:
m.setenv('PATH', 'foo') # let build fail
m.setattr('sys.argv', ['idf-build-apps', 'build', '--build-dir', 'build_hi_@t'])
with pytest.raises(SystemExit):
main()

with open('test.xml') as f:
xml = ElementTree.fromstring(f.read())
test_suite = xml.findall('testsuite')[0]
assert test_suite.attrib['failures'] == '1'
assert test_suite.attrib['errors'] == '0'
assert test_suite.attrib['skipped'] == '0'
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_hi_esp32'

# test cli action_true
with monkeypatch.context() as m:
m.setattr('sys.argv', ['idf-build-apps', 'build', '--dry-run'])
main()

with open('test.xml') as f:
xml = ElementTree.fromstring(f.read())
test_suite = xml.findall('testsuite')[0]
assert test_suite.attrib['failures'] == '0'
assert test_suite.attrib['errors'] == '0'
assert test_suite.attrib['skipped'] == '1'
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'

# test config store_true set to true
with open(IDF_BUILD_APPS_TOML_FN, 'a') as fw:
fw.write('\ndry_run = true\n')

with monkeypatch.context() as m:
m.setattr('sys.argv', ['idf-build-apps', 'build'])
main()

with open('test.xml') as f:
xml = ElementTree.fromstring(f.read())
test_suite = xml.findall('testsuite')[0]
assert test_suite.attrib['failures'] == '0'
assert test_suite.attrib['errors'] == '0'
assert test_suite.attrib['skipped'] == '1'
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'

# test config store_true set to false, but CLI set to true
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
fw.write("""paths = ["foo"]
build_dir = "build_@t"
junitxml = "test.xml"
dry_run = false
""")

with monkeypatch.context() as m:
m.setattr(
'sys.argv', ['idf-build-apps', 'build', '--default-build-targets', 'esp32', 'esp32s2', '--dry-run']
)
main()

with open('test.xml') as f:
xml = ElementTree.fromstring(f.read())
test_suite = xml.findall('testsuite')[0]
assert test_suite.attrib['failures'] == '0'
assert test_suite.attrib['errors'] == '0'
assert test_suite.attrib['skipped'] == '2'
assert test_suite.findall('testcase')[0].attrib['name'] == 'foo/build_esp32'
assert test_suite.findall('testcase')[1].attrib['name'] == 'foo/build_esp32s2'

1 comment on commit 5e18626

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
idf_build_apps
   __main__.py330%4–9
   app.py5238185%206, 251, 260–262, 294, 306, 323, 364–365, 368, 378–379, 390–393, 453–458, 478, 516, 572–580, 590–591, 601, 619–620, 622, 638–647, 663–701, 706–707, 711–712, 723–725, 731, 832–840, 850–880, 884–894, 985–991, 994, 1016
   args.py2753189%291, 298, 319–324, 334–339, 505, 508–509, 515, 525–527, 530, 555–556, 559–560, 689–690, 751, 753, 813, 832–842
   autocompletions.py292417%16–23, 31–54
   constants.py63986%20–21, 33–38, 47–48, 61–62
   finder.py83693%78–79, 149, 166–168
   log.py48492%37, 50, 55, 80
   main.py1996567%76–80, 94–99, 140–144, 174, 198–200, 204, 209–222, 237–240, 251–276, 366–373, 382–383, 396, 404–418, 422–423, 429–431
   session_args.py54787%46–50, 56, 70
   utils.py1882388%26, 35, 113, 130–131, 151–155, 203, 246, 273–279, 292–295, 314–315, 380
idf_build_apps/junit
   report.py93990%82, 92, 109–111, 137, 144–145, 170
   utils.py291066%18, 26–35
idf_build_apps/manifest
   if_parser.py109595%62, 99, 105, 169, 174
   manifest.py2191195%90, 92, 139, 161, 211–216, 306, 337–338, 357, 401
idf_build_apps/vendors
   pydantic_sources.py61789%35, 47, 66–69, 108, 115
TOTAL210329586% 

Tests Skipped Failures Errors Time
108 0 💤 0 ❌ 0 🔥 15m 55s ⏱️

Please sign in to comment.