From cc3832d46e4730a3124765977b1e10b1d900814c Mon Sep 17 00:00:00 2001
From: Philipp Stephani
Date: Tue, 23 Apr 2024 13:42:26 +0200
Subject: [PATCH] Add a test for the output of a binary signaling an error
---
tests/BUILD | 25 ++++++++++++++
tests/signal.el | 25 ++++++++++++++
tests/signal_test.py | 77 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 127 insertions(+)
create mode 100644 tests/signal.el
create mode 100644 tests/signal_test.py
diff --git a/tests/BUILD b/tests/BUILD
index e3b37db9..0f56dc38 100644
--- a/tests/BUILD
+++ b/tests/BUILD
@@ -138,3 +138,28 @@ py_test(
"@io_abseil_py//absl/testing:absltest",
],
)
+
+elisp_binary(
+ name = "signal",
+ src = "signal.el",
+)
+
+py_test(
+ name = "signal_test",
+ size = "medium",
+ timeout = "short",
+ srcs = ["signal_test.py"],
+ args = [
+ # See https://github.com/bazelbuild/bazel/issues/12313 why we need to
+ # add additional quoting.
+ shell.quote("--binary=$(rlocationpath :signal)"),
+ ],
+ data = [":signal"],
+ python_version = "PY3",
+ srcs_version = "PY3",
+ deps = [
+ "//elisp:runfiles",
+ "@io_abseil_py//absl/flags",
+ "@io_abseil_py//absl/testing:absltest",
+ ],
+)
diff --git a/tests/signal.el b/tests/signal.el
new file mode 100644
index 00000000..a3790c75
--- /dev/null
+++ b/tests/signal.el
@@ -0,0 +1,25 @@
+;;; signal.el --- signal an error -*- lexical-binding: t; -*-
+
+;; Copyright 2023, 2024 Google LLC
+;;
+;; Licensed under the Apache License, Version 2.0 (the "License");
+;; you may not use this file except in compliance with the License.
+;; You may obtain a copy of the License at
+;;
+;; https://www.apache.org/licenses/LICENSE-2.0
+;;
+;; Unless required by applicable law or agreed to in writing, software
+;; distributed under the License is distributed on an "AS IS" BASIS,
+;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+;; See the License for the specific language governing permissions and
+;; limitations under the License.
+
+;;; Commentary:
+
+;; This is a trivial binary that only signals an error.
+
+;;; Code:
+
+(error "Foo")
+
+;;; signal.el ends here
diff --git a/tests/signal_test.py b/tests/signal_test.py
new file mode 100644
index 00000000..b5318862
--- /dev/null
+++ b/tests/signal_test.py
@@ -0,0 +1,77 @@
+# Copyright 2020, 2021, 2022, 2023, 2024 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for an Emacs Lisp binary that signals an error."""
+
+import pathlib
+import platform
+import subprocess
+
+from absl import flags
+from absl.testing import absltest
+
+from elisp import runfiles
+
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string(
+ 'binary', None, 'runfile location of the Emacs Lisp binary to test',
+ required=True)
+
+
+class SignalTest(absltest.TestCase):
+ """Unit tests for an Emacs Lisp binary that signals an error."""
+
+ def test_run(self) -> None:
+ """Tests that the binary exits as expected.
+
+ These tests test some implementation details to catch some unwanted
+ behavior like launcher warnings on standard error.
+ """
+ run_files = runfiles.Runfiles()
+ binary = run_files.resolve(pathlib.PurePosixPath(FLAGS.binary))
+ process = subprocess.run([str(binary)], env=run_files.environment(),
+ stdin=subprocess.DEVNULL, capture_output=True,
+ check=False, encoding='utf-8')
+ # Emacs exits with a code of −1 in case of a signal, which gets
+ # converted to an exit status with all bits set: 8 bits on Unix systems,
+ # 32 bits on Windows.
+ self.assertEqual(process.returncode,
+ 0xFFFFFFFF if platform.system() == 'Windows' else 0xFF)
+ if process.stdout:
+ # Emacs 29: error message on standard error, backtrace on standard
+ # output.
+ self.assertEqual(process.stderr, 'Foo\n')
+ self.assertStartsWith(process.stdout, '\nError: error ("Foo")\n')
+ self.assertEndsWith(process.stdout, '\n normal-top-level()\n')
+ self.assertContainsSubsequence(process.stdout.splitlines(),
+ ['Error: error ("Foo")',
+ ' error("Foo")',
+ ' normal-top-level()'])
+ else:
+ # Emacs 28: error message followed with backtrace on standard error,
+ # empty standard output.
+ self.assertStartsWith(
+ process.stderr,
+ 'Debugger entered--Lisp error: (error "Foo")\n')
+ self.assertEndsWith(process.stderr, '\n normal-top-level()\n\n')
+ self.assertContainsSubsequence(
+ process.stderr.splitlines(),
+ ['Debugger entered--Lisp error: (error "Foo")',
+ ' error("Foo")',
+ ' normal-top-level()'])
+
+
+if __name__ == '__main__':
+ absltest.main()