diff --git a/HISTORY.rst b/HISTORY.rst index aac8e1c8..e5b44604 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,6 +7,7 @@ Release Notes .. |INTERPOLATION| replace:: `Interpolation of Values `__ .. |UNITTESTING| replace:: `Unit Testing `__ .. |DEBUGGING| replace:: `Debugging `__ +.. |STATICCODEANALYSIS| replace:: `Static Code Analysis `__ .. _release_notes_6: @@ -23,6 +24,8 @@ test-driven methodologies, and modern toolchains for unrivaled success. * Introduced the ``--json-output`` option to the `pio test `__ command, enabling users to generate test results in the JSON format * Broadened version support for the ``pyelftools`` dependency, enabling compatibility with lower versions and facilitating integration with a wider range of third-party tools (`issue #4834 `_) * Addressed an issue where passing a relative path (``--project-dir``) to the `pio project init `__ command resulted in an error (`issue #4847 `_) +* Enhanced |STATICCODEANALYSIS| to accommodate scenarios where custom ``src_dir`` or ``include_dir`` are located outside the project folder (`pull #4874 `_) +* Corrected the validation of ``symlink://`` `package specifications `__ , resolving an issue that caused the package manager to repeatedly reinstall dependencies (`pull #4870 `_) * Resolved an issue related to the relative package path in the `pio pkg publish `__ command * Resolved an issue where the |LDF| selected an incorrect library version (`issue #4860 `_) * Resolved an issue with the ``hexlify`` filter in the `device monitor `__ command, ensuring proper representation of characters with Unicode code points higher than 127 (`issue #4732 `_) diff --git a/docs b/docs index 1c8479f3..ad1f7f90 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 1c8479f3d0af2b9ac6f3a2b7f9405bd9a4cdac3f +Subproject commit ad1f7f90c58347d8375ab88a77bbc588cf53b6c1 diff --git a/platformio/__init__.py b/platformio/__init__.py index 7c60f35b..35aa5939 100644 --- a/platformio/__init__.py +++ b/platformio/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = (6, 1, "14b1") +VERSION = (6, 1, "14rc1") __version__ = ".".join([str(s) for s in VERSION]) __title__ = "platformio" diff --git a/platformio/check/cli.py b/platformio/check/cli.py index 3e0c1759..9adf7fb9 100644 --- a/platformio/check/cli.py +++ b/platformio/check/cli.py @@ -103,10 +103,21 @@ def cli( "%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v) ) - default_src_filters = [ - "+<%s>" % os.path.basename(config.get("platformio", "src_dir")), - "+<%s>" % os.path.basename(config.get("platformio", "include_dir")), - ] + default_src_filters = [] + for d in ( + config.get("platformio", "src_dir"), + config.get("platformio", "include_dir"), + ): + try: + default_src_filters.append("+<%s>" % os.path.relpath(d)) + except ValueError as exc: + # On Windows if sources are located on a different logical drive + if not json_output and not silent: + click.echo( + "Error: Project cannot be analyzed! The project folder `%s`" + " is located on a different logical drive\n" % d + ) + raise exception.ReturnErrorCode(1) from exc env_src_filters = ( src_filters diff --git a/platformio/package/manager/base.py b/platformio/package/manager/base.py index f197ea12..a4ad07ba 100644 --- a/platformio/package/manager/base.py +++ b/platformio/package/manager/base.py @@ -280,11 +280,15 @@ class BasePackageManager( # pylint: disable=too-many-public-methods,too-many-in # external "URL" mismatch if spec.external: - # local folder mismatch - if os.path.abspath(spec.uri) == os.path.abspath(pkg.path) or ( + # local/symlinked folder mismatch + check_conds = [ + os.path.abspath(spec.uri) == os.path.abspath(pkg.path), spec.uri.startswith("file://") - and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[7:]) - ): + and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[7:]), + spec.uri.startswith("symlink://") + and os.path.abspath(pkg.path) == os.path.abspath(spec.uri[10:]), + ] + if any(check_conds): return True if spec.uri != pkg.metadata.spec.uri: return False diff --git a/platformio/package/unpack.py b/platformio/package/unpack.py index 9d8919c3..f819fd2f 100644 --- a/platformio/package/unpack.py +++ b/platformio/package/unpack.py @@ -13,6 +13,7 @@ # limitations under the License. import os +import sys from tarfile import open as tarfile_open from time import mktime from zipfile import ZipFile @@ -82,19 +83,23 @@ class TARArchiver(BaseArchiver): ).startswith(base) def extract_item(self, item, dest_dir): + if sys.version_info >= (3, 12): + self._afo.extract(item, dest_dir, filter="data") + return self.after_extract(item, dest_dir) + + # apply custom security logic dest_dir = self.resolve_path(dest_dir) bad_conds = [ self.is_bad_path(item.name, dest_dir), self.is_link(item) and self.is_bad_link(item, dest_dir), ] - if not any(bad_conds): - super().extract_item(item, dest_dir) - else: - click.secho( + if any(bad_conds): + return click.secho( "Blocked insecure item `%s` from TAR archive" % item.name, fg="red", err=True, ) + return super().extract_item(item, dest_dir) class ZIPArchiver(BaseArchiver): diff --git a/platformio/pipdeps.py b/platformio/pipdeps.py index e86301e2..11775801 100644 --- a/platformio/pipdeps.py +++ b/platformio/pipdeps.py @@ -36,7 +36,7 @@ def get_pip_dependencies(): # PIO Home requirements "ajsonrpc == 1.2.*", "starlette >=0.19, <0.38", - "uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.28"), + "uvicorn %s" % ("== 0.16.0" if PY36 else ">=0.16, <0.29"), "wsproto == 1.*", ] diff --git a/tests/commands/pkg/test_outdated.py b/tests/commands/pkg/test_outdated.py index 91180b42..58f3b0f7 100644 --- a/tests/commands/pkg/test_outdated.py +++ b/tests/commands/pkg/test_outdated.py @@ -24,7 +24,7 @@ PROJECT_OUTDATED_CONFIG_TPL = """ platform = platformio/atmelavr@^2 framework = arduino board = attiny88 -lib_deps = milesburton/DallasTemperature@~3.8.0 +lib_deps = milesburton/DallasTemperature@~3.9.0 """ PROJECT_UPDATED_CONFIG_TPL = """ @@ -32,7 +32,7 @@ PROJECT_UPDATED_CONFIG_TPL = """ platform = platformio/atmelavr@<4 framework = arduino board = attiny88 -lib_deps = milesburton/DallasTemperature@^3.8.0 +lib_deps = milesburton/DallasTemperature@^3.9.0 """ @@ -56,7 +56,7 @@ def test_project(clirunner, validate_cliresult, isolated_pio_core, tmp_path): re.MULTILINE, ) assert re.search( - r"^DallasTemperature\s+3\.8\.1\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Library\s+devkit", + r"^DallasTemperature\s+3\.\d\.1\s+3\.\d+\.\d+\s+3\.\d+\.\d+\s+Library\s+devkit", result.output, re.MULTILINE, ) diff --git a/tests/commands/pkg/test_update.py b/tests/commands/pkg/test_update.py index 5656daac..1fd953ed 100644 --- a/tests/commands/pkg/test_update.py +++ b/tests/commands/pkg/test_update.py @@ -26,12 +26,14 @@ from platformio.package.manager.tool import ToolPackageManager from platformio.package.meta import PackageSpec from platformio.project.config import ProjectConfig +DALLASTEMPERATURE_LATEST_VERSION = "3.11.0" + PROJECT_OUTDATED_CONFIG_TPL = """ [env:devkit] platform = platformio/atmelavr@^2 framework = arduino board = attiny88 -lib_deps = milesburton/DallasTemperature@~3.8.0 +lib_deps = milesburton/DallasTemperature@^3.8.0 """ PROJECT_UPDATED_CONFIG_TPL = """ @@ -162,7 +164,7 @@ def test_project( os.path.join(config.get("platformio", "libdeps_dir"), "devkit") ) assert pkgs_to_specs(lm.get_installed()) == [ - PackageSpec("DallasTemperature@3.8.1"), + PackageSpec(f"DallasTemperature@{DALLASTEMPERATURE_LATEST_VERSION}"), PackageSpec( "OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire") ), @@ -176,7 +178,7 @@ def test_project( PackageSpec("toolchain-atmelavr@1.50400.190710"), ] assert config.get("env:devkit", "lib_deps") == [ - "milesburton/DallasTemperature@~3.8.0" + "milesburton/DallasTemperature@^3.8.0" ] # update packages @@ -227,7 +229,7 @@ def test_custom_project_libraries( project_dir = tmp_path / "project" project_dir.mkdir() (project_dir / "platformio.ini").write_text(PROJECT_OUTDATED_CONFIG_TPL) - spec = "milesburton/DallasTemperature@~3.8.0" + spec = "milesburton/DallasTemperature@^3.8.0" result = clirunner.invoke( package_install_cmd, ["-d", str(project_dir), "-e", "devkit", "-l", spec], @@ -240,7 +242,7 @@ def test_custom_project_libraries( os.path.join(config.get("platformio", "libdeps_dir"), "devkit") ) assert pkgs_to_specs(lm.get_installed()) == [ - PackageSpec("DallasTemperature@3.8.1"), + PackageSpec(f"DallasTemperature@{DALLASTEMPERATURE_LATEST_VERSION}"), PackageSpec( "OneWire@%s" % get_pkg_latest_version("paulstoffregen/OneWire") ), diff --git a/tests/commands/test_check.py b/tests/commands/test_check.py index a9a99d65..5f32e77d 100644 --- a/tests/commands/test_check.py +++ b/tests/commands/test_check.py @@ -803,3 +803,49 @@ check_src_filters = assert errors + warnings + style == EXPECTED_DEFECTS assert "test.cpp" in result.output assert "main.cpp" not in result.output + + +def test_check_sources_in_project_root(clirunner, validate_cliresult, tmpdir_factory): + tmpdir = tmpdir_factory.mktemp("project") + + config = ( + """ +[platformio] +src_dir = ./ + """ + + DEFAULT_CONFIG + ) + tmpdir.join("platformio.ini").write(config) + tmpdir.join("main.cpp").write(TEST_CODE) + tmpdir.mkdir("spi").join("uart.cpp").write(TEST_CODE) + + result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)]) + validate_cliresult(result) + + errors, warnings, style = count_defects(result.output) + + assert result.exit_code == 0 + assert errors + warnings + style == EXPECTED_DEFECTS * 2 + + +def test_check_sources_in_external_dir(clirunner, validate_cliresult, tmpdir_factory): + tmpdir = tmpdir_factory.mktemp("project") + external_src_dir = tmpdir_factory.mktemp("external_src_dir") + + config = ( + f""" +[platformio] +src_dir = {external_src_dir} + """ + + DEFAULT_CONFIG + ) + tmpdir.join("platformio.ini").write(config) + external_src_dir.join("main.cpp").write(TEST_CODE) + + result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)]) + validate_cliresult(result) + + errors, warnings, style = count_defects(result.output) + + assert result.exit_code == 0 + assert errors + warnings + style == EXPECTED_DEFECTS diff --git a/tests/commands/test_lib.py b/tests/commands/test_lib.py index 8f47ad61..221d788f 100644 --- a/tests/commands/test_lib.py +++ b/tests/commands/test_lib.py @@ -42,7 +42,7 @@ board = devkit framework = foo lib_deps = CustomLib - ArduinoJson @ 5.10.1 + ArduinoJson @ 6.18.5 """ ) result = clirunner.invoke( @@ -163,7 +163,7 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory storage_dir = tmpdir_factory.mktemp("test-updates") result = clirunner.invoke( cmd_lib, - ["-d", str(storage_dir), "install", "ArduinoJson @ 5.10.1", "Blynk @ ~0.5.0"], + ["-d", str(storage_dir), "install", "ArduinoJson @ 6.18.5", "Blynk @ ~1.2"], ) validate_cliresult(result) result = clirunner.invoke( @@ -173,17 +173,17 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory outdated = json.loads(result.stdout) assert len(outdated) == 2 # ArduinoJson - assert outdated[0]["version"] == "5.10.1" + assert outdated[0]["version"] == "6.18.5" assert outdated[0]["versionWanted"] is None assert semantic_version.Version( outdated[0]["versionLatest"] - ) > semantic_version.Version("6.16.0") + ) > semantic_version.Version("6.18.5") # Blynk - assert outdated[1]["version"] == "0.5.4" + assert outdated[1]["version"] == "1.2.0" assert outdated[1]["versionWanted"] is None assert semantic_version.Version( outdated[1]["versionLatest"] - ) > semantic_version.Version("0.6.0") + ) > semantic_version.Version("1.2.0") # check with spec result = clirunner.invoke( @@ -194,19 +194,19 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory "update", "--dry-run", "--json-output", - "ArduinoJson @ ^5", + "ArduinoJson @ ^6", ], ) validate_cliresult(result) outdated = json.loads(result.stdout) - assert outdated[0]["version"] == "5.10.1" - assert outdated[0]["versionWanted"] == "5.13.4" + assert outdated[0]["version"] == "6.18.5" + assert outdated[0]["versionWanted"] == "6.21.5" assert semantic_version.Version( outdated[0]["versionLatest"] ) > semantic_version.Version("6.16.0") # update with spec result = clirunner.invoke( - cmd_lib, ["-d", str(storage_dir), "update", "--silent", "ArduinoJson @ ^5.10.1"] + cmd_lib, ["-d", str(storage_dir), "update", "--silent", "ArduinoJson @ ^6.18.5"] ) validate_cliresult(result) result = clirunner.invoke( @@ -215,12 +215,12 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory validate_cliresult(result) items = json.loads(result.stdout) assert len(items) == 2 - assert items[0]["version"] == "5.13.4" - assert items[1]["version"] == "0.5.4" + assert items[0]["version"] == "6.21.5" + assert items[1]["version"] == "1.2.0" # Check incompatible result = clirunner.invoke( - cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^5"] + cmd_lib, ["-d", str(storage_dir), "update", "--dry-run", "ArduinoJson @ ^6"] ) with pytest.raises( AssertionError, @@ -228,7 +228,7 @@ def test_update(clirunner, validate_cliresult, isolated_pio_core, tmpdir_factory ): validate_cliresult(result) result = clirunner.invoke( - cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^5"] + cmd_lib, ["-d", str(storage_dir), "update", "ArduinoJson @ ^6"] ) validate_cliresult(result) - assert "ArduinoJson@5.13.4 is already up-to-date" in result.stdout + assert "ArduinoJson@6.21.5 is already up-to-date" in result.stdout diff --git a/tests/commands/test_lib_complex.py b/tests/commands/test_lib_complex.py index eb56c5b7..55563be5 100644 --- a/tests/commands/test_lib_complex.py +++ b/tests/commands/test_lib_complex.py @@ -23,6 +23,7 @@ from platformio.package.exception import UnknownPackageError from platformio.util import strip_ansi_codes PlatformioCLI.leftover_args = ["--json-output"] # hook for click +ARDUINO_JSON_VERSION = "6.21.5" def test_search(clirunner, validate_cliresult): @@ -44,10 +45,10 @@ def test_global_install_registry(clirunner, validate_cliresult, isolated_pio_cor "-g", "install", "64", - "ArduinoJson@~5.10.0", - "547@2.2.4", + "ArduinoJson@~6", + "547@2.7.3", "AsyncMqttClient@<=0.8.2", - "Adafruit PN532@1.2.0", + "Adafruit PN532@1.3.2", ], ) validate_cliresult(result) @@ -60,7 +61,7 @@ def test_global_install_registry(clirunner, validate_cliresult, isolated_pio_cor items1 = [d.basename for d in isolated_pio_core.join("lib").listdir()] items2 = [ "ArduinoJson", - "ArduinoJson@5.10.1", + f"ArduinoJson@{ARDUINO_JSON_VERSION}", "NeoPixelBus", "AsyncMqttClient", "ESPAsyncTCP", @@ -79,7 +80,7 @@ def test_global_install_archive(clirunner, validate_cliresult, isolated_pio_core "install", "https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip", "https://github.com/bblanchon/ArduinoJson/archive/v5.8.2.zip@5.8.2", - "SomeLib=https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.8.1/DallasTemperature-3.8.1.tar.gz", + "SomeLib=https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.11.0/DallasTemperature-3.11.0.tar.gz", "https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip", ], ) @@ -142,7 +143,7 @@ def test_install_duplicates( # pylint: disable=unused-argument [ "-g", "install", - "https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.8.1/DallasTemperature-3.8.1.tar.gz", + "https://dl.registry.platformio.org/download/milesburton/library/DallasTemperature/3.11.0/DallasTemperature-3.11.0.tar.gz", ], ) validate_cliresult(result) @@ -176,11 +177,11 @@ def test_global_lib_list(clirunner, validate_cliresult): n in result.output for n in ( "required: https://github.com/Pedroalbuquerque/ESP32WebServer/archive/master.zip", - "ArduinoJson @ 5.10.1", + f"ArduinoJson @ {ARDUINO_JSON_VERSION}", "required: git+https://github.com/gioblu/PJON.git#3.0", "PJON @ 3.0.0+sha.1fb26f", ) - ) + ), result.output result = clirunner.invoke(cmd_lib, ["-g", "list", "--json-output"]) assert all( @@ -188,7 +189,7 @@ def test_global_lib_list(clirunner, validate_cliresult): for n in ( "__pkg_dir", '"__src_url": "git+https://github.com/gioblu/PJON.git#6.2"', - '"version": "5.10.1"', + f'"version": "{ARDUINO_JSON_VERSION}"', ) ) items1 = [i["name"] for i in json.loads(result.output)] @@ -218,13 +219,13 @@ def test_global_lib_list(clirunner, validate_cliresult): ] versions2 = [ "ArduinoJson@5.8.2", - "ArduinoJson@5.10.1", + f"ArduinoJson@{ARDUINO_JSON_VERSION}", "AsyncMqttClient@0.8.2", - "NeoPixelBus@2.2.4", + "NeoPixelBus@2.7.3", "PJON@6.2.0+sha.07fe9aa", "PJON@3.0.0+sha.1fb26fd", "PubSubClient@2.6.0+sha.bef5814", - "Adafruit PN532@1.2.0", + "Adafruit PN532@1.3.2", ] assert set(versions1) >= set(versions2) @@ -249,7 +250,7 @@ def test_global_lib_update(clirunner, validate_cliresult): assert "__pkg_dir" in oudated[0] result = clirunner.invoke(cmd_lib, ["-g", "update", oudated[0]["__pkg_dir"]]) validate_cliresult(result) - assert "Removing NeoPixelBus @ 2.2.4" in strip_ansi_codes(result.output) + assert "Removing NeoPixelBus @ 2.7.3" in strip_ansi_codes(result.output) # update all libraries result = clirunner.invoke( diff --git a/tests/commands/test_platform.py b/tests/commands/test_platform.py index 967bf51e..b832a366 100644 --- a/tests/commands/test_platform.py +++ b/tests/commands/test_platform.py @@ -63,7 +63,7 @@ def test_install_unknown_from_registry(clirunner): def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( cli_platform.platform_install, - ["atmelavr@1.2.0", "--skip-default-package"], + ["atmelavr@2.2.0", "--skip-default-package"], ) assert result.exit_code == 0 @@ -71,11 +71,11 @@ def test_install_core_3_dev_platform(clirunner, validate_cliresult, isolated_pio def test_install_known_version(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( cli_platform.platform_install, - ["atmelavr@2.0.0", "--skip-default-package", "--with-package", "tool-avrdude"], + ["atmelavr@4.2.0", "--skip-default-package", "--with-package", "tool-avrdude"], ) validate_cliresult(result) output = strip_ansi_codes(result.output) - assert "atmelavr @ 2.0.0" in output + assert "atmelavr@4.2.0" in output assert not os.path.isdir(str(isolated_pio_core.join("packages"))) @@ -128,14 +128,14 @@ def test_update_raw(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke(cli_platform.platform_update, ["atmelavr"]) validate_cliresult(result) output = strip_ansi_codes(result.output) - assert "Removing atmelavr @ 2.0.0" in output + assert "Removing atmelavr @ 4.2.0" in output assert "Platform Manager: Installing platformio/atmelavr @" in output assert len(isolated_pio_core.join("packages").listdir()) == 2 def test_uninstall(clirunner, validate_cliresult, isolated_pio_core): result = clirunner.invoke( - cli_platform.platform_uninstall, ["atmelavr@1.2.0", "atmelavr", "espressif8266"] + cli_platform.platform_uninstall, ["atmelavr@2.2.0", "atmelavr", "espressif8266"] ) validate_cliresult(result) assert not isolated_pio_core.join("platforms").listdir() diff --git a/tests/package/test_manager.py b/tests/package/test_manager.py index fa3f215c..fc210467 100644 --- a/tests/package/test_manager.py +++ b/tests/package/test_manager.py @@ -219,7 +219,7 @@ def test_install_from_registry(isolated_pio_core, tmpdir_factory): # test conflicted names lm = LibraryPackageManager(str(tmpdir_factory.mktemp("conflicted-storage"))) lm.set_log_level(logging.ERROR) - lm.install("z3t0/IRremote@2.6.1") + lm.install("z3t0/IRremote") lm.install("mbed-yuhki50/IRremote") assert len(lm.get_installed()) == 2 @@ -554,14 +554,14 @@ def test_uninstall(isolated_pio_core, tmpdir_factory): assert not lm.get_installed() # test uninstall dependencies - assert lm.install("AsyncMqttClient-esphome @ 0.8.4") + assert lm.install("AsyncMqttClient-esphome") assert len(lm.get_installed()) == 3 assert lm.uninstall("AsyncMqttClient-esphome", skip_dependencies=True) assert len(lm.get_installed()) == 2 lm = LibraryPackageManager(str(storage_dir)) lm.set_log_level(logging.ERROR) - assert lm.install("AsyncMqttClient-esphome @ 0.8.4") + assert lm.install("AsyncMqttClient-esphome") assert lm.uninstall("AsyncMqttClient-esphome") assert not lm.get_installed() @@ -604,23 +604,23 @@ def test_update_with_metadata(isolated_pio_core, tmpdir_factory): assert str(outdated.current) == "1.8.7" assert outdated.latest > semantic_version.Version("1.10.0") - pkg = lm.install("ArduinoJson @ 5.10.1") + pkg = lm.install("ArduinoJson @ 6.19.4") # test latest outdated = lm.outdated(pkg) - assert str(outdated.current) == "5.10.1" + assert str(outdated.current) == "6.19.4" assert outdated.wanted is None assert outdated.latest > outdated.current assert outdated.latest > semantic_version.Version("5.99.99") # test wanted - outdated = lm.outdated(pkg, PackageSpec("ArduinoJson@~5")) - assert str(outdated.current) == "5.10.1" - assert str(outdated.wanted) == "5.13.4" + outdated = lm.outdated(pkg, PackageSpec("ArduinoJson@~6")) + assert str(outdated.current) == "6.19.4" + assert str(outdated.wanted) == "6.21.5" assert outdated.latest > semantic_version.Version("6.16.0") - # update to the wanted 5.x - new_pkg = lm.update("ArduinoJson@^5", PackageSpec("ArduinoJson@^5")) - assert str(new_pkg.metadata.version) == "5.13.4" + # update to the wanted 6.x + new_pkg = lm.update("ArduinoJson@^6", PackageSpec("ArduinoJson@^6")) + assert str(new_pkg.metadata.version) == "6.21.5" # check that old version is removed assert len(lm.get_installed()) == 2