Skip to content

Commit

Permalink
Add log streaming for macOS apps.
Browse files Browse the repository at this point in the history
  • Loading branch information
freakboy3742 committed Jun 5, 2021
1 parent 3908947 commit b60e1f1
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 5 deletions.
34 changes: 34 additions & 0 deletions src/briefcase/platforms/macOS/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def run_app(self, app: BaseConfig, **kwargs):
self.subprocess.run(
[
'open',
'-n', # Force a new app to be launched
str(self.binary_path(app)),
],
check=True,
Expand All @@ -94,6 +95,39 @@ def run_app(self, app: BaseConfig, **kwargs):
"Unable to start app {app.app_name}.".format(app=app)
)

# Find the PID of the new instance using pgrep
try:
pid = self.subprocess.check_output(
["pgrep", "-n", app.formal_name],
universal_newlines=True,
).strip()
except subprocess.CalledProcessError:
print()
raise BriefcaseCommandError(
"Unable to find PID for app {app.app_name}.".format(app=app)
)

# Start streaming logs for the app.
try:
print()
print("[{app.app_name}] Following system log output (type CTRL-C to stop log)...".format(app=app))
print("=" * 75)
self.subprocess.run(
[
"log",
"stream",
"--process", pid,
"--style", "compact",
"--type", "log",
],
check=True,
)
except subprocess.CalledProcessError:
print()
raise BriefcaseCommandError(
"Unable to start log stream for app {app.app_name} (pid {pid}).".format(app=app, pid=pid)
)


class macOSAppPackageCommand(macOSPackageMixin, macOSAppMixin, PackageCommand):
description = "Package a macOS app for distribution."
Expand Down
90 changes: 85 additions & 5 deletions tests/platforms/macOS/app/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,26 @@ def test_run_app(first_app_config, tmp_path):
"A macOS App can be started"
command = macOSAppRunCommand(base_path=tmp_path)
command.subprocess = mock.MagicMock()
command.subprocess.check_output.return_value = "3742"

command.run_app(first_app_config)

command.subprocess.run.assert_called_with(
['open', str(command.binary_path(first_app_config))],
check=True
# Calls were made to start the app and to start a log stream.
command.subprocess.run.assert_has_calls([
mock.call(
['open', '-n', str(command.binary_path(first_app_config))],
check=True
),
mock.call(
['log', 'stream', '--process', '3742', '--style', 'compact', '--type', 'log'],
check=True,
)
])

# A call to pgrep was also made
command.subprocess.check_output.assert_called_with(
['pgrep', '-n', 'First App'],
universal_newlines=True
)


Expand All @@ -25,7 +39,7 @@ def test_run_app_failed(first_app_config, tmp_path):
command = macOSAppRunCommand(base_path=tmp_path)
command.subprocess = mock.MagicMock()
command.subprocess.run.side_effect = subprocess.CalledProcessError(
cmd=['open', str(command.binary_path(first_app_config))],
cmd=['open', '-n', str(command.binary_path(first_app_config))],
returncode=1
)

Expand All @@ -34,6 +48,72 @@ def test_run_app_failed(first_app_config, tmp_path):

# The run command was still invoked, though
command.subprocess.run.assert_called_with(
['open', str(command.binary_path(first_app_config))],
['open', '-n', str(command.binary_path(first_app_config))],
check=True
)

# but no attempt was made to find the PID
command.subprocess.check_output.assert_not_called()


def test_run_app_no_pid(first_app_config, tmp_path):
"If there's a problem finding the PID of the app, an exception is raised"
command = macOSAppRunCommand(base_path=tmp_path)
command.subprocess = mock.MagicMock()
command.subprocess.check_output.side_effect = subprocess.CalledProcessError(
cmd=['pgrep', '-n', 'invalid app'],
returncode=1
)

with pytest.raises(BriefcaseCommandError):
command.run_app(first_app_config)

# The run command was still invoked, but the log stream wasn't
command.subprocess.run.assert_has_calls([
mock.call(
['open', '-n', str(command.binary_path(first_app_config))],
check=True
),
])

# and an attempt was made to but no attempt was made to find the PID
command.subprocess.check_output.assert_called_with(
['pgrep', '-n', 'First App'],
universal_newlines=True
)


def test_run_app_log_failed(first_app_config, tmp_path):
"If the log can't be streamed, the app still starts"
command = macOSAppRunCommand(base_path=tmp_path)
command.subprocess = mock.MagicMock()
command.subprocess.check_output.return_value = "3742"
command.subprocess.run.side_effect = [
None,
subprocess.CalledProcessError(
cmd=['log', 'stream'],
returncode=1
)
]

# The run command raises an error because the log stream couldn't start
with pytest.raises(BriefcaseCommandError):
command.run_app(first_app_config)

# Calls were made to start the app and to start a log stream.
command.subprocess.run.assert_has_calls([
mock.call(
['open', '-n', str(command.binary_path(first_app_config))],
check=True
),
mock.call(
['log', 'stream', '--process', '3742', '--style', 'compact', '--type', 'log'],
check=True,
)
])

# A call to pgrep was also made
command.subprocess.check_output.assert_called_with(
['pgrep', '-n', 'First App'],
universal_newlines=True
)

0 comments on commit b60e1f1

Please sign in to comment.