Repository: beeware/briefcase
Pull request #1389 (2023-07-28 23:37:47)

Support verbose logging for third-party tools
## Changes
- Ensure the name of distributable debug APKs have a different file name than release APKs.
- Verbose tool logging is now enabled with `-vv`
- Gradle builds
  - Enable (very verbose) debug logging via `--debug`
- linuxdeploy builds
  - Set `DEBUG=1` for plugin debug logging
  - Pass `-v0` to linuxdeploy for debug logging
- iOS
  - `xcodebuild` now runs with `-verbose`
- Flatpak
  - Calls to `flatpak` and `flatpak-builder` use `--verbose`
- macOS Xcode
  - `xcodebuild` now runs with `-verbose`
- Visual Studio
  - Runs `MSBuild` with `-verbosity:detailed`

## Notes
- Linux System package builds
  - Evaluated verbose logging for package builds....but there wasn't much value there
- ADB commands
  - `ADB_TRACE=all` can increase logging...but it breaks output parsing so omitted
  - If there's potential value here, I can look more deeply at this
- RCEdit
  - Doesn't seem to support additional logging....not sure if value anyway
- WiX
  - Verbose logging doesn't seem to add any value...mostly very cryptic messages from `light.exe`

## PR Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
- [x] All new features have been tested
- [x] All new features have been documented
- [X] I have read the **CONTRIBUTING.md** file
- [X] I will abide by the code of conduct



Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e62ca301..914e7da3 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -32,9 +32,3 @@ repos:
     rev: 6.0.0
     hooks:
       - id: flake8
-  - repo: https://github.com/codespell-project/codespell
-    rev: v2.2.5
-    hooks:
-    - id: codespell
-      additional_dependencies:
-        - tomli
diff --git a/changes/1384.feature.rst b/changes/1384.feature.rst
new file mode 100644
index 00000000..f5b638fd
--- /dev/null
+++ b/changes/1384.feature.rst
@@ -0,0 +1,1 @@
+An super verbose logging mode was added (enabled using ``-vv``). This turns on all Briefcase internal logging, but also enables verbose logging for all the third-party tools that Briefcase invokes.
diff --git a/changes/1388.misc.rst b/changes/1388.misc.rst
deleted file mode 100644
index 96a5c9d4..00000000
--- a/changes/1388.misc.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-Codespell was enabled as a pre-commit check.
diff --git a/pyproject.toml b/pyproject.toml
index cc1acc85..8a3bf1a2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -74,10 +74,3 @@ type = [
     { directory = "doc", name = "Documentation", showcontent = true },
     { directory = "misc", name = "Misc", showcontent = false },
  ]
-
-[tool.codespell]
-skip = '.git,*.pdf,*.svg'
-# the way to make case sensitive skips of words etc
-ignore-regex = '\bNd\b'
-# case insensitive
-# ignore-words-list = ''
diff --git a/src/briefcase/commands/base.py b/src/briefcase/commands/base.py
index 07d6d2c2..1c17acd5 100644
--- a/src/briefcase/commands/base.py
+++ b/src/briefcase/commands/base.py
@@ -666,8 +666,8 @@ any compatibility problems, and then add the compatibility declaration.
             "-v",
             "--verbosity",
             action="count",
-            default=1,
-            help="set the verbosity of output",
+            default=0,
+            help="Enable verbose logging. -vv enables super verbose mode.",
         )
         parser.add_argument("-V", "--version", action="version", version=__version__)
         parser.add_argument(
diff --git a/src/briefcase/config.py b/src/briefcase/config.py
index 9ecf2027..b58a2e44 100644
--- a/src/briefcase/config.py
+++ b/src/briefcase/config.py
@@ -373,7 +373,7 @@ def merge_pep621_config(global_config, pep621_config):
         else:
             global_config[field] = datum
 
-    # Keys that map directly
+    # Keys that map direclty
     maybe_update("description", "description")
     maybe_update("license", "license", "text")
     maybe_update("url", "urls", "Homepage")
diff --git a/src/briefcase/console.py b/src/briefcase/console.py
index 92923c18..16399eb9 100644
--- a/src/briefcase/console.py
+++ b/src/briefcase/console.py
@@ -117,15 +117,15 @@ class Log:
     """Manage logging output driven by verbosity flags."""
 
     # level of verbosity when debug output is shown in the console
-    DEBUG = 2
+    DEBUG = 1
     # printed at the beginning of all debug output
     DEBUG_PREFACE = ">>> "
     # subdirectory of command.base_path to store log files
     LOG_DIR = "logs"
 
-    def __init__(self, printer=Printer(), verbosity=1):
+    def __init__(self, printer=Printer(), verbosity=0):
         self.print = printer
-        # --verbosity flag: 1 for info, 2 for debug
+        # --verbosity flag: 0 for info, 1 for debug, 2 for super-verbose
         self.verbosity = verbosity
         # --log flag to force logfile creation
         self.save_log = False
@@ -230,6 +230,14 @@ class Log:
         """Log message at error level; always included in output."""
         self._log(prefix=prefix, message=message, markup=markup, style="bold red")
 
+    @property
+    def is_deep_debug(self):
+        """Is extra verbosity enabled via -vv?
+
+        Enables verbose output from third-party tools.
+        """
+        return self.verbosity >= 2
+
     def capture_stacktrace(self, label="Main thread"):
         """Preserve Rich stacktrace from exception while in except block.
 
diff --git a/src/briefcase/integrations/flatpak.py b/src/briefcase/integrations/flatpak.py
index 7a24cc69..f1c70640 100644
--- a/src/briefcase/integrations/flatpak.py
+++ b/src/briefcase/integrations/flatpak.py
@@ -154,7 +154,8 @@ You must install both flatpak and flatpak-builder.
                     "--if-not-exists",
                     repo_alias,
                     url,
-                ],
+                ]
+                + (["--verbose"] if self.tools.logger.is_deep_debug else []),
                 check=True,
             )
         except subprocess.CalledProcessError as e:
@@ -186,7 +187,8 @@ You must install both flatpak and flatpak-builder.
                     repo_alias,
                     f"{runtime}/{self.tools.host_arch}/{runtime_version}",
                     f"{sdk}/{self.tools.host_arch}/{runtime_version}",
-                ],
+                ]
+                + (["--verbose"] if self.tools.logger.is_deep_debug else []),
                 check=True,
                 # flatpak install uses many animations that cannot be disabled
                 stream_output=False,
@@ -222,7 +224,8 @@ You must install both flatpak and flatpak-builder.
                     "--user",
                     "build",
                     "manifest.yml",
-                ],
+                ]
+                + (["--verbose"] if self.tools.logger.is_deep_debug else []),
                 check=True,
                 cwd=path,
             )
@@ -269,13 +272,15 @@ flatpak run {bundle_identifier}
         else:
             kwargs = {}
 
+        flatpak_run_cmd = ["flatpak", "run", bundle_identifier]
+        flatpak_run_cmd.extend([] if args is None else args)
+
+        if self.tools.logger.is_deep_debug:
+            # Must come before bundle identifier; otherwise, it's passed as an arg to app
+            flatpak_run_cmd.insert(2, "--verbose")
+
         return self.tools.subprocess.Popen(
-            [
-                "flatpak",
-                "run",
-                bundle_identifier,
-            ]
-            + ([] if args is None else args),
+            flatpak_run_cmd,
             stdout=subprocess.PIPE,
             stderr=subprocess.STDOUT,
             bufsize=1,
@@ -319,7 +324,8 @@ flatpak run {bundle_identifier}
                     output_path,
                     bundle_identifier,
                     version,
-                ],
+                ]
+                + (["--verbose"] if self.tools.logger.is_deep_debug else []),
                 check=True,
                 cwd=build_path,
             )
diff --git a/src/briefcase/platforms/android/gradle.py b/src/briefcase/platforms/android/gradle.py
index 2826aba8..950d0197 100644
--- a/src/briefcase/platforms/android/gradle.py
+++ b/src/briefcase/platforms/android/gradle.py
@@ -1,9 +1,10 @@
+from __future__ import annotations
+
 import datetime
 import re
 import subprocess
 import time
 from pathlib import Path
-from typing import List
 
 from briefcase.commands import (
     BuildCommand,
@@ -17,6 +18,7 @@ from briefcase.commands import (
 from briefcase.config import BaseConfig, parsed_version
 from briefcase.exceptions import BriefcaseCommandError
 from briefcase.integrations.android_sdk import AndroidSDK
+from briefcase.integrations.subprocess import SubprocessArgT
 
 
 def safe_formal_name(name):
@@ -88,17 +90,14 @@ class GradleMixin:
         )
 
     def distribution_path(self, app):
-        packaging_format_extension = {
+        extension = {
             "aab": "aab",
             "apk": "apk",
-            "debug-apk": "apk",
-        }
-        return (
-            self.dist_path
-            / f"{app.formal_name}-{app.version}.{packaging_format_extension[app.packaging_format]}"
-        )
+            "debug-apk": "debug.apk",
+        }[app.packaging_format]
+        return self.dist_path / f"{app.formal_name}-{app.version}.{extension}"
 
-    def run_gradle(self, app, args):
+    def run_gradle(self, app, args: list[SubprocessArgT]):
         # Gradle may install the emulator via the dependency chain build-tools > tools >
         # emulator. (The `tools` package only shows up in sdkmanager if you pass
         # `--include_obsolete`.) However, the old sdkmanager built into Android Gradle
@@ -115,7 +114,13 @@ class GradleMixin:
             # Windows needs the full path to `gradlew`; macOS & Linux can find it
             # via `./gradlew`. For simplicity of implementation, we always provide
             # the full path.
-            [self.bundle_path(app) / gradlew] + args + ["--console", "plain"],
+            [
+                self.bundle_path(app) / gradlew,
+                "--console",
+                "plain",
+            ]
+            + (["--debug"] if self.tools.logger.is_deep_debug else [])
+            + args,
             env=self.tools.android_sdk.env,
             # Set working directory so gradle can use the app bundle path as its
             # project root, i.e., to avoid 'Task assembleDebug not found'.
@@ -250,7 +255,7 @@ class GradleRunCommand(GradleMixin, RunCommand):
         self,
         app: BaseConfig,
         test_mode: bool,
-        passthrough: List[str],
+        passthrough: list[str],
         device_or_avd=None,
         extra_emulator_args=None,
         shutdown_on_exit=False,
@@ -379,27 +384,18 @@ class GradlePackageCommand(GradleMixin, PackageCommand):
             prefix=app.app_name,
         )
         with self.input.wait_bar("Bundling..."):
+            build_type, build_artefact_path = {
+                "aab": ("bundleRelease", "bundle/release/app-release.aab"),
+                "apk": ("assembleRelease", "apk/release/app-release-unsigned.apk"),
+                "debug-apk": ("assembleDebug", "apk/debug/app-debug.apk"),
+            }[app.packaging_format]
+
             try:
-                self.run_gradle(
-                    app,
-                    [
-                        {
-                            "aab": "bundleRelease",
-                            "apk": "assembleRelease",
-                            "debug-apk": "assembleDebug",
-                        }[app.packaging_format]
-                    ],
-                )
+                self.run_gradle(app, [build_type])
             except subprocess.CalledProcessError as e:
                 raise BriefcaseCommandError("Error while building project.") from e
 
         # Move artefact to final location.
-        build_artefact_path = {
-            "aab": Path("bundle") / "release" / "app-release.aab",
-            "apk": Path("apk") / "release" / "app-release-unsigned.apk",
-            "debug-apk": Path("apk") / "debug" / "app-debug.apk",
-        }[app.packaging_format]
-
         self.tools.shutil.move(
             self.bundle_path(app) / "app" / "build" / "outputs" / build_artefact_path,
             self.distribution_path(app),
diff --git a/src/briefcase/platforms/iOS/xcode.py b/src/briefcase/platforms/iOS/xcode.py
index 676c787d..b28afca2 100644
--- a/src/briefcase/platforms/iOS/xcode.py
+++ b/src/briefcase/platforms/iOS/xcode.py
@@ -324,7 +324,7 @@ class iOSXcodeBuildCommand(iOSXcodePassiveMixin, BuildCommand):
                         self.tools.host_arch,
                         "-sdk",
                         "iphonesimulator",
-                        "-quiet",
+                        "-verbose" if self.tools.logger.is_deep_debug else "-quiet",
                     ],
                     check=True,
                 )
diff --git a/src/briefcase/platforms/linux/appimage.py b/src/briefcase/platforms/linux/appimage.py
index 5af145b2..d29575b0 100644
--- a/src/briefcase/platforms/linux/appimage.py
+++ b/src/briefcase/platforms/linux/appimage.py
@@ -274,6 +274,10 @@ class LinuxAppImageBuildCommand(LinuxAppImageMixin, BuildCommand):
                 # This can be used by some linuxdeploy plugins.
                 env["ARCH"] = self.tools.host_arch
 
+                # Enable debug logging for linuxdeploy GTK and Qt plugins
+                if self.logger.is_deep_debug:
+                    env["DEBUG"] = "1"
+
                 # Find all the .so files in app and app_packages,
                 # so they can be passed in to linuxdeploy to have their
                 # requirements added to the AppImage. Looks for any .so file
@@ -302,6 +306,7 @@ class LinuxAppImageBuildCommand(LinuxAppImageMixin, BuildCommand):
                         ),
                         "--output",
                         "appimage",
+                        "-v0" if self.logger.is_deep_debug else "-v1",
                     ]
                     + additional_args,
                     env=env,
diff --git a/src/briefcase/platforms/macOS/__init__.py b/src/briefcase/platforms/macOS/__init__.py
index 9522069d..52a242ac 100644
--- a/src/briefcase/platforms/macOS/__init__.py
+++ b/src/briefcase/platforms/macOS/__init__.py
@@ -350,7 +350,7 @@ or
             # Sign all embedded frameworks
             sign_targets.extend(folder.rglob("*.framework"))
 
-            # Sign all embedded app objects
+            # Sign all embedded app objets
             sign_targets.extend(folder.rglob("*.app"))
 
         # Sign the bundle path itself
@@ -360,7 +360,7 @@ or
         # However, we need to ensure that objects are signed from the inside out
         # (i.e., a folder must be signed *after* all it's contents has been
         # signed). To do this, we sort the list of signing targets in reverse
-        # lexicographic order, and then group all the signing targets by parent.
+        # lexigraphic order, and then group all the signing targets by parent.
         # This sorts all the signable files into folders; and sign all files in
         # a folder before sorting the next group. This ensures that longer paths
         # are signed first, and all files in a folder are signed before the
diff --git a/src/briefcase/platforms/macOS/xcode.py b/src/briefcase/platforms/macOS/xcode.py
index 5c8c24b3..820e85f2 100644
--- a/src/briefcase/platforms/macOS/xcode.py
+++ b/src/briefcase/platforms/macOS/xcode.py
@@ -64,7 +64,7 @@ class macOSXcodeBuildCommand(macOSXcodeMixin, BuildCommand):
                         "xcodebuild",
                         "-project",
                         self.project_path(app),
-                        "-quiet",
+                        "-verbose" if self.tools.logger.is_deep_debug else "-quiet",
                         "-configuration",
                         "Release",
                         "build",
diff --git a/src/briefcase/platforms/windows/visualstudio.py b/src/briefcase/platforms/windows/visualstudio.py
index 757fb58d..c55a64f3 100644
--- a/src/briefcase/platforms/windows/visualstudio.py
+++ b/src/briefcase/platforms/windows/visualstudio.py
@@ -57,6 +57,11 @@ class WindowsVisualStudioBuildCommand(WindowsVisualStudioMixin, BuildCommand):
                         "-property:RestorePackagesConfig=true",
                         f"-target:{app.formal_name}",
                         "-property:Configuration=Release",
+                        (
+                            "-verbosity:detailed"
+                            if self.logger.is_deep_debug
+                            else "-verbosity:normal"
+                        ),
                     ],
                     check=True,
                     cwd=self.bundle_path(app),


Test patch:
diff --git a/tests/commands/base/test_parse_options.py b/tests/commands/base/test_parse_options.py
index 49b83856..a6620027 100644
--- a/tests/commands/base/test_parse_options.py
+++ b/tests/commands/base/test_parse_options.py
@@ -11,7 +11,7 @@ def test_parse_options(base_command):
         "required": "important",
     }
     assert base_command.input.enabled
-    assert base_command.logger.verbosity == 1
+    assert base_command.logger.verbosity == 0
 
 
 def test_missing_option(base_command, capsys):
diff --git a/tests/console/test_Log.py b/tests/console/test_Log.py
index 1a3e9655..444e1599 100644
--- a/tests/console/test_Log.py
+++ b/tests/console/test_Log.py
@@ -38,6 +38,21 @@ def command(mock_now, tmp_path) -> DevCommand:
     return command
 
 
+@pytest.mark.parametrize(
+    "verbosity, enabled",
+    [
+        (0, False),
+        (1, False),
+        (2, True),
+        (3, True),
+        (4, True),
+    ],
+)
+def test_is_deep_debug(verbosity, enabled):
+    """Deep debug is enabled at =>2 verbosity."""
+    assert Log(verbosity=verbosity).is_deep_debug is enabled
+
+
 def test_capture_stacktrace():
     """capture_stacktrace sets Log.stacktrace."""
     logger = Log()
diff --git a/tests/integrations/android_sdk/ADB/test_run.py b/tests/integrations/android_sdk/ADB/test_run.py
index 95f56e4a..a6b114d5 100644
--- a/tests/integrations/android_sdk/ADB/test_run.py
+++ b/tests/integrations/android_sdk/ADB/test_run.py
@@ -102,7 +102,7 @@ def test_error_handling(mock_tools, adb, name, exception, tmp_path):
 
 
 def test_older_sdk_error(mock_tools, adb):
-    """Failure [INSTALL_FAILED_OLDER_SDK] needs to be caught manually."""
+    """Failure [INSTALL_FAILED_OLDER_SDK] needs to be catched manually."""
     mock_tools.subprocess.check_output.return_value = "\n".join(
         [
             "Performing Push Install",
diff --git a/tests/integrations/flatpak/test_Flatpak__build.py b/tests/integrations/flatpak/test_Flatpak__build.py
index 5d0fe797..17a75664 100644
--- a/tests/integrations/flatpak/test_Flatpak__build.py
+++ b/tests/integrations/flatpak/test_Flatpak__build.py
@@ -5,8 +5,13 @@ import pytest
 from briefcase.exceptions import BriefcaseCommandError
 
 
-def test_build(flatpak, tmp_path):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_build(flatpak, tool_debug_mode, tmp_path):
     """A Flatpak project can be built."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        flatpak.tools.logger.verbosity = 2
+
     flatpak.build(
         bundle_identifier="com.example.my-app",
         app_name="my-app",
@@ -24,7 +29,8 @@ def test_build(flatpak, tmp_path):
             "--user",
             "build",
             "manifest.yml",
-        ],
+        ]
+        + (["--verbose"] if tool_debug_mode else []),
         check=True,
         cwd=tmp_path,
     )
diff --git a/tests/integrations/flatpak/test_Flatpak__bundle.py b/tests/integrations/flatpak/test_Flatpak__bundle.py
index 7c6e7bf8..a88a5773 100644
--- a/tests/integrations/flatpak/test_Flatpak__bundle.py
+++ b/tests/integrations/flatpak/test_Flatpak__bundle.py
@@ -5,8 +5,12 @@ import pytest
 from briefcase.exceptions import BriefcaseCommandError
 
 
-def test_bundle(flatpak, tmp_path):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_bundle(flatpak, tool_debug_mode, tmp_path):
     """A Flatpak project can be bundled."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        flatpak.tools.logger.verbosity = 2
 
     flatpak.bundle(
         repo_url="https://example.com/flatpak",
@@ -28,7 +32,8 @@ def test_bundle(flatpak, tmp_path):
             tmp_path / "output" / "MyApp.flatpak",
             "com.example.my-app",
             "1.2.3",
-        ],
+        ]
+        + (["--verbose"] if tool_debug_mode else []),
         check=True,
         cwd=tmp_path / "build",
     )
diff --git a/tests/integrations/flatpak/test_Flatpak__run.py b/tests/integrations/flatpak/test_Flatpak__run.py
index f189a988..8c322ecb 100644
--- a/tests/integrations/flatpak/test_Flatpak__run.py
+++ b/tests/integrations/flatpak/test_Flatpak__run.py
@@ -1,9 +1,16 @@
 import subprocess
 from unittest import mock
 
+import pytest
 
-def test_run(flatpak):
+
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_run(flatpak, tool_debug_mode):
     """A Flatpak project can be executed."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        flatpak.tools.logger.verbosity = 2
+
     # Set up the log streamer to return a known stream
     log_popen = mock.MagicMock()
     flatpak.tools.subprocess.Popen.return_value = log_popen
@@ -16,8 +23,9 @@ def test_run(flatpak):
         [
             "flatpak",
             "run",
-            "com.example.my-app",
-        ],
+        ]
+        + (["--verbose"] if tool_debug_mode else [])
+        + ["com.example.my-app"],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
         bufsize=1,
@@ -27,8 +35,13 @@ def test_run(flatpak):
     assert result == log_popen
 
 
-def test_run_with_args(flatpak):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_run_with_args(flatpak, tool_debug_mode):
     """A Flatpak project can be executed with additional arguments."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        flatpak.tools.logger.verbosity = 2
+
     # Set up the log streamer to return a known stream
     log_popen = mock.MagicMock()
     flatpak.tools.subprocess.Popen.return_value = log_popen
@@ -44,10 +57,10 @@ def test_run_with_args(flatpak):
         [
             "flatpak",
             "run",
-            "com.example.my-app",
-            "foo",
-            "bar",
-        ],
+        ]
+        + (["--verbose"] if tool_debug_mode else [])
+        + ["com.example.my-app"]
+        + ["foo", "bar"],
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
         bufsize=1,
diff --git a/tests/integrations/flatpak/test_Flatpak__verify_repo.py b/tests/integrations/flatpak/test_Flatpak__verify_repo.py
index 0b96c049..e3ac136c 100644
--- a/tests/integrations/flatpak/test_Flatpak__verify_repo.py
+++ b/tests/integrations/flatpak/test_Flatpak__verify_repo.py
@@ -5,8 +5,12 @@ import pytest
 from briefcase.exceptions import BriefcaseCommandError
 
 
-def test_verify_repo(flatpak):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_verify_repo(flatpak, tool_debug_mode):
     """A Flatpak repo can be verified."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        flatpak.tools.logger.verbosity = 2
 
     flatpak.verify_repo(
         repo_alias="test-alias",
@@ -22,7 +26,8 @@ def test_verify_repo(flatpak):
             "--if-not-exists",
             "test-alias",
             "https://example.com/flatpak",
-        ],
+        ]
+        + (["--verbose"] if tool_debug_mode else []),
         check=True,
     )
 
diff --git a/tests/integrations/flatpak/test_Flatpak__verify_runtime.py b/tests/integrations/flatpak/test_Flatpak__verify_runtime.py
index e3a12de4..591adf29 100644
--- a/tests/integrations/flatpak/test_Flatpak__verify_runtime.py
+++ b/tests/integrations/flatpak/test_Flatpak__verify_runtime.py
@@ -5,8 +5,12 @@ import pytest
 from briefcase.exceptions import BriefcaseCommandError
 
 
-def test_verify_runtime(flatpak):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_verify_runtime(flatpak, tool_debug_mode):
     """A Flatpak runtime and SDK can be verified."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        flatpak.tools.logger.verbosity = 2
 
     flatpak.verify_runtime(
         repo_alias="test-alias",
@@ -25,7 +29,8 @@ def test_verify_runtime(flatpak):
             "test-alias",
             "org.beeware.flatpak.Platform/gothic/37.42",
             "org.beeware.flatpak.SDK/gothic/37.42",
-        ],
+        ]
+        + (["--verbose"] if tool_debug_mode else []),
         check=True,
         stream_output=False,
     )
diff --git a/tests/platforms/android/gradle/test_build.py b/tests/platforms/android/gradle/test_build.py
index f3b3bbb5..10a6b696 100644
--- a/tests/platforms/android/gradle/test_build.py
+++ b/tests/platforms/android/gradle/test_build.py
@@ -48,19 +48,28 @@ def test_unsupported_template_version(build_command, first_app_generated):
 
 
 @pytest.mark.parametrize(
-    "host_os, gradlew_name",
-    [("Windows", "gradlew.bat"), ("NonWindows", "gradlew")],
+    "host_os, gradlew_name, tool_debug_mode",
+    [
+        ("Windows", "gradlew.bat", True),
+        ("Windows", "gradlew.bat", False),
+        ("NonWindows", "gradlew", True),
+        ("NonWindows", "gradlew", False),
+    ],
 )
 def test_build_app(
     build_command,
     first_app_generated,
     host_os,
     gradlew_name,
+    tool_debug_mode,
     tmp_path,
 ):
     """The app can be built, invoking gradle."""
     # Mock out `host_os` so we can validate which name is used for gradlew.
     build_command.tools.host_os = host_os
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        build_command.tools.logger.verbosity = 2
     # Create mock environment with `key`, which we expect to be preserved, and
     # `ANDROID_SDK_ROOT`, which we expect to be overwritten.
     build_command.tools.os.environ = {"ANDROID_SDK_ROOT": "somewhere", "key": "value"}
@@ -69,10 +78,11 @@ def test_build_app(
     build_command.tools.subprocess.run.assert_called_once_with(
         [
             build_command.bundle_path(first_app_generated) / gradlew_name,
-            "assembleDebug",
             "--console",
             "plain",
-        ],
+        ]
+        + (["--debug"] if tool_debug_mode else [])
+        + ["assembleDebug"],
         cwd=build_command.bundle_path(first_app_generated),
         env=build_command.tools.android_sdk.env,
         check=True,
@@ -104,19 +114,28 @@ def test_build_app(
 
 
 @pytest.mark.parametrize(
-    "host_os, gradlew_name",
-    [("Windows", "gradlew.bat"), ("NonWindows", "gradlew")],
+    "host_os, gradlew_name, debug_mode",
+    [
+        ("Windows", "gradlew.bat", True),
+        ("Windows", "gradlew.bat", False),
+        ("NonWindows", "gradlew", True),
+        ("NonWindows", "gradlew", False),
+    ],
 )
 def test_build_app_test_mode(
     build_command,
     first_app_generated,
     host_os,
     gradlew_name,
+    debug_mode,
     tmp_path,
 ):
     """The app can be built in test mode, invoking gradle and rewriting app metadata."""
     # Mock out `host_os` so we can validate which name is used for gradlew.
     build_command.tools.host_os = host_os
+    # Enable verbose tool logging
+    if debug_mode:
+        build_command.tools.logger.verbosity = 2
     # Create mock environment with `key`, which we expect to be preserved, and
     # `ANDROID_SDK_ROOT`, which we expect to be overwritten.
     build_command.tools.os.environ = {"ANDROID_SDK_ROOT": "somewhere", "key": "value"}
@@ -125,10 +144,11 @@ def test_build_app_test_mode(
     build_command.tools.subprocess.run.assert_called_once_with(
         [
             build_command.bundle_path(first_app_generated) / gradlew_name,
-            "assembleDebug",
             "--console",
             "plain",
-        ],
+        ]
+        + (["--debug"] if debug_mode else [])
+        + ["assembleDebug"],
         cwd=build_command.bundle_path(first_app_generated),
         env=build_command.tools.android_sdk.env,
         check=True,
diff --git a/tests/platforms/android/gradle/test_package__aab.py b/tests/platforms/android/gradle/test_package__aab.py
index 5cbee423..dafe0dfb 100644
--- a/tests/platforms/android/gradle/test_package__aab.py
+++ b/tests/platforms/android/gradle/test_package__aab.py
@@ -42,14 +42,20 @@ def test_distribution_path(package_command, first_app_aab, tmp_path):
 
 
 @pytest.mark.parametrize(
-    "host_os,gradlew_name",
-    [("Windows", "gradlew.bat"), ("NonWindows", "gradlew")],
+    "host_os, gradlew_name, tool_debug_mode",
+    [
+        ("Windows", "gradlew.bat", True),
+        ("Windows", "gradlew.bat", False),
+        ("NonWindows", "gradlew", True),
+        ("NonWindows", "gradlew", False),
+    ],
 )
 def test_execute_gradle(
     package_command,
     first_app_aab,
     host_os,
     gradlew_name,
+    tool_debug_mode,
     tmp_path,
 ):
     """Validate that package_app() will launch `gradlew bundleRelease` with the
@@ -58,6 +64,10 @@ def test_execute_gradle(
     # Mock out `host_os` so we can validate which name is used for gradlew.
     package_command.tools.host_os = host_os
 
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        package_command.tools.logger.verbosity = 2
+
     # Set up a side effect of invoking gradle to create a bundle
     def create_bundle(*args, **kwargs):
         create_file(
@@ -88,10 +98,11 @@ def test_execute_gradle(
     package_command.tools.subprocess.run.assert_called_once_with(
         [
             package_command.bundle_path(first_app_aab) / gradlew_name,
-            "bundleRelease",
             "--console",
             "plain",
-        ],
+        ]
+        + (["--debug"] if tool_debug_mode else [])
+        + ["bundleRelease"],
         cwd=package_command.bundle_path(first_app_aab),
         env=package_command.tools.android_sdk.env,
         check=True,
diff --git a/tests/platforms/android/gradle/test_package__apk.py b/tests/platforms/android/gradle/test_package__apk.py
index 9614e267..c9b8e720 100644
--- a/tests/platforms/android/gradle/test_package__apk.py
+++ b/tests/platforms/android/gradle/test_package__apk.py
@@ -43,14 +43,20 @@ def test_distribution_path(package_command, first_app_apk, tmp_path):
 
 
 @pytest.mark.parametrize(
-    "host_os,gradlew_name",
-    [("Windows", "gradlew.bat"), ("NonWindows", "gradlew")],
+    "host_os, gradlew_name, tool_debug_mode",
+    [
+        ("Windows", "gradlew.bat", True),
+        ("Windows", "gradlew.bat", False),
+        ("NonWindows", "gradlew", True),
+        ("NonWindows", "gradlew", False),
+    ],
 )
 def test_execute_gradle(
     package_command,
     first_app_apk,
     host_os,
     gradlew_name,
+    tool_debug_mode,
     tmp_path,
 ):
     """Validate that package_app() will launch `gradlew bundleRelease` with the
@@ -59,6 +65,10 @@ def test_execute_gradle(
     # Mock out `host_os` so we can validate which name is used for gradlew.
     package_command.tools.host_os = host_os
 
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        package_command.tools.logger.verbosity = 3
+
     # Set up a side effect of invoking gradle to create a bundle
     def create_bundle(*args, **kwargs):
         create_file(
@@ -89,10 +99,11 @@ def test_execute_gradle(
     package_command.tools.subprocess.run.assert_called_once_with(
         [
             package_command.bundle_path(first_app_apk) / gradlew_name,
-            "assembleRelease",
             "--console",
             "plain",
-        ],
+        ]
+        + (["--debug"] if tool_debug_mode else [])
+        + ["assembleRelease"],
         cwd=package_command.bundle_path(first_app_apk),
         env=package_command.tools.android_sdk.env,
         check=True,
diff --git a/tests/platforms/android/gradle/test_package__debug_apk.py b/tests/platforms/android/gradle/test_package__debug_apk.py
index aeb9de34..0d1497ad 100644
--- a/tests/platforms/android/gradle/test_package__debug_apk.py
+++ b/tests/platforms/android/gradle/test_package__debug_apk.py
@@ -38,19 +38,25 @@ def test_distribution_path(package_command, first_app_apk, tmp_path):
     print(package_command.packaging_formats)
     assert (
         package_command.distribution_path(first_app_apk)
-        == tmp_path / "base_path" / "dist" / "First App-0.0.1.apk"
+        == tmp_path / "base_path" / "dist" / "First App-0.0.1.debug.apk"
     )
 
 
 @pytest.mark.parametrize(
-    "host_os,gradlew_name",
-    [("Windows", "gradlew.bat"), ("NonWindows", "gradlew")],
+    "host_os, gradlew_name, tool_debug_mode",
+    [
+        ("Windows", "gradlew.bat", True),
+        ("Windows", "gradlew.bat", False),
+        ("NonWindows", "gradlew", True),
+        ("NonWindows", "gradlew", False),
+    ],
 )
 def test_execute_gradle(
     package_command,
     first_app_apk,
     host_os,
     gradlew_name,
+    tool_debug_mode,
     tmp_path,
 ):
     """Validate that package_app() will launch `gradlew bundleRelease` with the
@@ -59,6 +65,10 @@ def test_execute_gradle(
     # Mock out `host_os` so we can validate which name is used for gradlew.
     package_command.tools.host_os = host_os
 
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        package_command.tools.logger.verbosity = 2
+
     # Set up a side effect of invoking gradle to create a bundle
     def create_bundle(*args, **kwargs):
         create_file(
@@ -89,17 +99,18 @@ def test_execute_gradle(
     package_command.tools.subprocess.run.assert_called_once_with(
         [
             package_command.bundle_path(first_app_apk) / gradlew_name,
-            "assembleDebug",
             "--console",
             "plain",
-        ],
+        ]
+        + (["--debug"] if tool_debug_mode else [])
+        + ["assembleDebug"],
         cwd=package_command.bundle_path(first_app_apk),
         env=package_command.tools.android_sdk.env,
         check=True,
     )
 
     # The release asset has been moved into the dist folder
-    assert (tmp_path / "base_path" / "dist" / "First App-0.0.1.apk").exists()
+    assert (tmp_path / "base_path" / "dist" / "First App-0.0.1.debug.apk").exists()
 
 
 def test_print_gradle_errors(package_command, first_app_apk):
diff --git a/tests/platforms/iOS/xcode/test_build.py b/tests/platforms/iOS/xcode/test_build.py
index c0ee8176..ec10eff3 100644
--- a/tests/platforms/iOS/xcode/test_build.py
+++ b/tests/platforms/iOS/xcode/test_build.py
@@ -20,13 +20,18 @@ def build_command(tmp_path):
     )
 
 
-def test_build_app(build_command, first_app_generated, tmp_path):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_build_app(build_command, first_app_generated, tool_debug_mode, tmp_path):
     """An iOS App can be built."""
     build_command.tools.subprocess = mock.MagicMock(spec_set=Subprocess)
 
     # Mock the host's CPU architecture to ensure it's reflected in the Xcode call
     build_command.tools.host_arch = "weird"
 
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        build_command.tools.logger.verbosity = 2
+
     build_command.build_app(first_app_generated)
 
     build_command.tools.subprocess.run.assert_called_with(
@@ -49,7 +54,7 @@ def test_build_app(build_command, first_app_generated, tmp_path):
             "weird",
             "-sdk",
             "iphonesimulator",
-            "-quiet",
+            "-verbose" if tool_debug_mode else "-quiet",
         ],
         check=True,
     )
diff --git a/tests/platforms/linux/appimage/test_build.py b/tests/platforms/linux/appimage/test_build.py
index 617cf9f0..8a4f7692 100644
--- a/tests/platforms/linux/appimage/test_build.py
+++ b/tests/platforms/linux/appimage/test_build.py
@@ -150,8 +150,12 @@ def test_verify_tools_download_failure(build_command):
     assert build_command.build_app.call_count == 0
 
 
-def test_build_appimage(build_command, first_app, tmp_path, sub_stream_kw):
+@pytest.mark.parametrize("debug_mode", (True, False))
+def test_build_appimage(build_command, first_app, debug_mode, tmp_path, sub_stream_kw):
     """A Linux app can be packaged as an AppImage."""
+    # Enable verbose tool logging
+    if debug_mode:
+        build_command.tools.logger.verbosity = 2
 
     build_command.verify_app_tools(first_app)
     build_command.build_app(first_app)
@@ -166,6 +170,15 @@ def test_build_appimage(build_command, first_app, tmp_path, sub_stream_kw):
         / "appimage"
         / "First App.AppDir"
     )
+    expected_env = {
+        "PATH": "/usr/local/bin:/usr/bin:/path/to/somewhere",
+        "LINUXDEPLOY_OUTPUT_VERSION": "0.0.1",
+        "DISABLE_COPYRIGHT_FILES_DEPLOYMENT": "1",
+        "APPIMAGE_EXTRACT_AND_RUN": "1",
+        "ARCH": "wonky",
+    }
+    if debug_mode:
+        expected_env["DEBUG"] = "1"
     build_command._subprocess.Popen.assert_called_with(
         [
             os.fsdecode(
@@ -177,6 +190,7 @@ def test_build_appimage(build_command, first_app, tmp_path, sub_stream_kw):
             os.fsdecode(app_dir / "com.example.first-app.desktop"),
             "--output",
             "appimage",
+            "-v0" if debug_mode else "-v1",
             "--deploy-deps-only",
             os.fsdecode(app_dir / "usr" / "app" / "support"),
             "--deploy-deps-only",
@@ -184,13 +198,7 @@ def test_build_appimage(build_command, first_app, tmp_path, sub_stream_kw):
             "--deploy-deps-only",
             os.fsdecode(app_dir / "usr" / "app_packages" / "secondlib"),
         ],
-        env={
-            "PATH": "/usr/local/bin:/usr/bin:/path/to/somewhere",
-            "LINUXDEPLOY_OUTPUT_VERSION": "0.0.1",
-            "DISABLE_COPYRIGHT_FILES_DEPLOYMENT": "1",
-            "APPIMAGE_EXTRACT_AND_RUN": "1",
-            "ARCH": "wonky",
-        },
+        env=expected_env,
         cwd=os.fsdecode(
             tmp_path / "base_path" / "build" / "first-app" / "linux" / "appimage"
         ),
@@ -262,6 +270,7 @@ def test_build_appimage_with_plugin(build_command, first_app, tmp_path, sub_stre
             os.fsdecode(app_dir / "com.example.first-app.desktop"),
             "--output",
             "appimage",
+            "-v1",
             "--deploy-deps-only",
             os.fsdecode(app_dir / "usr" / "app" / "support"),
             "--deploy-deps-only",
@@ -344,6 +353,7 @@ def test_build_failure(build_command, first_app, tmp_path, sub_stream_kw):
             os.fsdecode(app_dir / "com.example.first-app.desktop"),
             "--output",
             "appimage",
+            "-v1",
             "--deploy-deps-only",
             os.fsdecode(app_dir / "usr" / "app" / "support"),
             "--deploy-deps-only",
@@ -433,6 +443,7 @@ def test_build_appimage_in_docker(build_command, first_app, tmp_path, sub_stream
             "/app/First App.AppDir/com.example.first-app.desktop",
             "--output",
             "appimage",
+            "-v1",
             "--deploy-deps-only",
             "/app/First App.AppDir/usr/app/support",
             "--deploy-deps-only",
@@ -557,6 +568,7 @@ def test_build_appimage_with_plugins_in_docker(
             "/app/First App.AppDir/com.example.first-app.desktop",
             "--output",
             "appimage",
+            "-v1",
             "--deploy-deps-only",
             "/app/First App.AppDir/usr/app/support",
             "--deploy-deps-only",
@@ -662,6 +674,7 @@ def test_build_appimage_with_support_package_update(
             os.fsdecode(app_dir / "com.example.first-app.desktop"),
             "--output",
             "appimage",
+            "-v1",
             "--deploy-deps-only",
             os.fsdecode(app_dir / "usr" / "app" / "support"),
             "--deploy-deps-only",
diff --git a/tests/platforms/linux/system/test_mixin__verify_system_packages.py b/tests/platforms/linux/system/test_mixin__verify_system_packages.py
index 3b4196e6..88f35483 100644
--- a/tests/platforms/linux/system/test_mixin__verify_system_packages.py
+++ b/tests/platforms/linux/system/test_mixin__verify_system_packages.py
@@ -83,7 +83,7 @@ def test_missing_packages(build_command, first_app_config, capsys):
     """If there are missing system packages, an error is raised."""
     # Mock the system requirement tools; there's a base requirement of
     # a packaged called "compiler", verified using "check <pkg>", and
-    # installed using "system <pkg>"
+    # isntalled using "system <pkg>"
     build_command._system_requirement_tools = MagicMock(
         return_value=(
             ["compiler"],
@@ -116,7 +116,7 @@ def test_packages_installed(build_command, first_app_config, capsys):
     """If all required packages are installed, no error is raised."""
     # Mock the system requirement tools; there's a base requirement of
     # a packaged called "compiler", verified using "check <pkg>", and
-    # installed using "system <pkg>"
+    # isntalled using "system <pkg>"
     build_command._system_requirement_tools = MagicMock(
         return_value=(
             ["compiler"],
diff --git a/tests/platforms/linux/system/test_package.py b/tests/platforms/linux/system/test_package.py
index 1af710bb..d62b6b11 100644
--- a/tests/platforms/linux/system/test_package.py
+++ b/tests/platforms/linux/system/test_package.py
@@ -93,7 +93,7 @@ def test_distribution_path(
         ("debian", "deb", "deb"),
         ("redhat", "rpm", "rpm"),
         ("arch", "pkg", "pkg"),
-        # This is technically possible, but probably ill-advised
+        # This is technically posssible, but probably ill-advised
         ("debian", "rpm", "rpm"),
         # Unknown base vendor, but explicit packaging format
         (None, "deb", "deb"),
@@ -104,7 +104,7 @@ def test_distribution_path(
 def test_adjust_packaging_format(
     package_command, first_app, base_vendor, input_format, output_format
 ):
-    "The packaging format can be adjusted based on host system knowledge"
+    "The packaging format can be ajusted based on host system knowledge"
     first_app.target_vendor_base = base_vendor
     first_app.packaging_format = input_format
 
diff --git a/tests/platforms/macOS/xcode/test_build.py b/tests/platforms/macOS/xcode/test_build.py
index 5be5a323..40db9a27 100644
--- a/tests/platforms/macOS/xcode/test_build.py
+++ b/tests/platforms/macOS/xcode/test_build.py
@@ -19,8 +19,13 @@ def build_command(tmp_path):
     )
 
 
-def test_build_app(build_command, first_app_generated, tmp_path):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_build_app(build_command, first_app_generated, tool_debug_mode, tmp_path):
     """An macOS App can be built."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        build_command.tools.logger.verbosity = 2
+
     build_command.tools.subprocess = MagicMock(spec_set=Subprocess)
     build_command.build_app(first_app_generated, test_mode=False)
 
@@ -35,7 +40,7 @@ def test_build_app(build_command, first_app_generated, tmp_path):
             / "macos"
             / "xcode"
             / "First App.xcodeproj",
-            "-quiet",
+            "-verbose" if tool_debug_mode else "-quiet",
             "-configuration",
             "Release",
             "build",
diff --git a/tests/platforms/windows/visualstudio/test_build.py b/tests/platforms/windows/visualstudio/test_build.py
index 6184fb09..634417ec 100644
--- a/tests/platforms/windows/visualstudio/test_build.py
+++ b/tests/platforms/windows/visualstudio/test_build.py
@@ -46,8 +46,12 @@ def test_verify(build_command, monkeypatch):
     assert isinstance(build_command.tools.visualstudio, VisualStudio)
 
 
-def test_build_app(build_command, first_app_config, tmp_path):
+@pytest.mark.parametrize("tool_debug_mode", (True, False))
+def test_build_app(build_command, first_app_config, tool_debug_mode, tmp_path):
     """The solution will be compiled when the project is built."""
+    # Enable verbose tool logging
+    if tool_debug_mode:
+        build_command.tools.logger.verbosity = 2
 
     build_command.build_app(first_app_config)
 
@@ -62,6 +66,7 @@ def test_build_app(build_command, first_app_config, tmp_path):
                     "-property:RestorePackagesConfig=true",
                     "-target:First App",
                     "-property:Configuration=Release",
+                    "-verbosity:detailed" if tool_debug_mode else "-verbosity:normal",
                 ],
                 check=True,
                 cwd=tmp_path
diff --git a/tests/test_cmdline.py b/tests/test_cmdline.py
index e760f887..a557a621 100644
--- a/tests/test_cmdline.py
+++ b/tests/test_cmdline.py
@@ -101,7 +101,7 @@ def test_new_command(logger, console):
     assert cmd.platform == "all"
     assert cmd.output_format is None
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {"template": None, "template_branch": None}
@@ -150,7 +150,7 @@ def test_dev_command(monkeypatch, logger, console, cmdline, expected_options):
     assert cmd.platform == "macOS"
     assert cmd.output_format is None
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {
@@ -185,7 +185,7 @@ def test_run_command(monkeypatch, logger, console, cmdline, expected_options):
     assert cmd.platform == "macOS"
     assert cmd.output_format == "app"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {
@@ -212,7 +212,7 @@ def test_upgrade_command(monkeypatch, logger, console):
     assert cmd.platform == "macOS"
     assert cmd.output_format is None
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {
@@ -232,7 +232,7 @@ def test_bare_command(monkeypatch, logger, console):
     assert cmd.platform == "macOS"
     assert cmd.output_format == "app"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -248,7 +248,7 @@ def test_linux_default(logger, console):
     assert cmd.platform == "linux"
     assert cmd.output_format == "system"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -264,7 +264,7 @@ def test_macOS_default(logger, console):
     assert cmd.platform == "macOS"
     assert cmd.output_format == "app"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -280,7 +280,7 @@ def test_windows_default(logger, console):
     assert cmd.platform == "windows"
     assert cmd.output_format == "app"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -343,7 +343,7 @@ def test_command_explicit_platform(monkeypatch, logger, console):
     assert cmd.platform == "linux"
     assert cmd.output_format == "system"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -361,7 +361,7 @@ def test_command_explicit_platform_case_handling(monkeypatch, logger, console):
     assert cmd.platform == "macOS"
     assert cmd.output_format == "app"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -397,7 +397,7 @@ def test_command_explicit_format(monkeypatch, logger, console):
     assert cmd.platform == "macOS"
     assert cmd.output_format == "app"
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -463,7 +463,7 @@ def test_command_disable_input(monkeypatch, logger, console):
     assert cmd.platform == "macOS"
     assert cmd.output_format == "app"
     assert not cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {}
@@ -480,7 +480,7 @@ def test_command_options(monkeypatch, capsys, logger, console):
 
     assert isinstance(cmd, macOSAppPublishCommand)
     assert cmd.input.enabled
-    assert cmd.logger.verbosity == 1
+    assert cmd.logger.verbosity == 0
     assert cmd.logger is logger
     assert cmd.input is console
     assert options == {"channel": "s3"}
diff --git a/tests/utils.py b/tests/utils.py
index e15b8e79..1183b496 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -154,7 +154,7 @@ def mock_tgz_download(filename, content, role=None):
 def create_wheel(path, package="dummy", version="1.2.3", extra_content=None):
     """Create a sample wheel file.
 
-    :param path: The folder where the wheel should be written.
+    :param path: The folder where the wheel should be writter.
     :param package: The name of the package in the wheel. Defaults to ``dummy``
     :param version: The version number of the package. Defaults to ``1.2.3``
     :param extra_content: Optional. A list of tuples of ``(path, content)`` that