Added excessive logging threshold test (#37274)

* Added excessive logging threshold tests
* Excessive logs sample:

```
Warning: Excessive error output detected (7 lines):
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR

I0000 00:00:1724852923.140229      45 chttp2_transport.cc:1182] ipv6:%5B::1%5D:45545: Got goaway [2] err=UNAVAILABLE:GOAWAY received; Error code: 2; Debug Text: Cancelling all calls {file:"src/core/ext/transport/chttp2/transport/chttp2_transport.cc", file_line:1171, created_time:"2024-08-28T13:48:43.140192497+00:00", http2_error:2, grpc_status:14}
```

Closes #37274

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37274 from sourabhsinghs:unittest/logging-threshold 09a87693d0
PiperOrigin-RevId: 678742613
pull/37643/head^2
Sourabh Singh 2 months ago committed by Copybara-Service
parent 72c7cf88f9
commit dc12396125
  1. 10
      bazel/BUILD
  2. 93
      bazel/_logging_threshold_test_main.py
  3. 54
      bazel/_single_module_tester.py
  4. 3
      bazel/internal_python_rules.bzl
  5. 73
      bazel/logging_threshold_test.bzl
  6. 8
      src/python/grpcio_tests/tests/unit/_signal_handling_test.py

@ -20,7 +20,17 @@ licenses(["notice"])
package(default_visibility = ["//:__subpackages__"])
filegroup(
name = "_single_module_tester",
srcs = ["_single_module_tester.py"],
)
filegroup(
name = "_gevent_test_main",
srcs = ["_gevent_test_main.py"],
)
filegroup(
name = "_logging_threshold_test_main",
srcs = ["_logging_threshold_test_main.py"],
)

@ -0,0 +1,93 @@
# Copyright 2024 gRPC authors.
#
# 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
#
# http://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.
import os
import re
import subprocess
import sys
import tempfile
_OK_TEST_REGEX = r"^-+.*Ran ([\d]+) tests* in ([\d.]+)s.*OK(?: \(skipped=(\d+)\))?\n$"
# Tests with known exception logs.
# TODO(sourabhsinghs): Investigate and enable _rpc_part_1_test and _rpc_part_2_test tests.
_SKIP_TESTS = [
"_rpc_part_1_test",
"_server_shutdown_test",
"_xds_credentials_test",
"_server_test",
"_invalid_metadata_test",
"_reconnect_test",
"_channel_close_test",
"_rpc_part_2_test",
"_invocation_defects_test",
"_dynamic_stubs_test",
"_channel_connectivity_test",
]
if __name__ == "__main__":
if len(sys.argv) != 3:
print(f"USAGE: {sys.argv[0]} TARGET_MODULE", file=sys.stderr)
sys.exit(1)
test_script = sys.argv[1]
target_module = sys.argv[2]
if target_module in _SKIP_TESTS:
print(f"Skipping {target_module}")
sys.exit(0)
command = [
sys.executable,
os.path.realpath(test_script),
target_module,
os.path.dirname(os.path.relpath(__file__)),
]
with tempfile.TemporaryFile(mode="w+") as stdout_file:
with tempfile.TemporaryFile(mode="w+") as stderr_file:
result = subprocess.run(
command,
stdout=stdout_file,
stderr=stderr_file,
text=True,
check=True,
)
stdout_file.seek(0)
stderr_file.seek(0)
stdout_count = len(stdout_file.readlines())
stderr_count = len(stderr_file.readlines())
if result.returncode != 0:
sys.exit("Test failure")
stderr_file.seek(0)
if not re.fullmatch(_OK_TEST_REGEX, stderr_file.read(), re.DOTALL):
print(
f"Warning: Excessive error output detected ({stderr_count} lines):"
)
stderr_file.seek(0)
for line in stderr_file:
print(line)
if stdout_count > 0:
print(
f"Warning: Unexpected output detected ({stdout_count} lines):"
)
stdout_file.seek(0)
for line in stdout_file:
print(line)

@ -0,0 +1,54 @@
# Copyright 2024 gRPC authors.
#
# 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
#
# http://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.
from typing import Sequence, Optional
import unittest
import sys
import pkgutil
class SingleLoader(object):
def __init__(self, pattern: str, unittest_path: str):
loader = unittest.TestLoader()
self.suite = unittest.TestSuite()
tests = []
for importer, module_name, is_package in pkgutil.walk_packages([unittest_path]):
if pattern in module_name:
module = importer.find_module(module_name).load_module(module_name)
tests.append(loader.loadTestsFromModule(module))
if len(tests) != 1:
raise AssertionError("Expected only 1 test module. Found {}".format(tests))
self.suite.addTest(tests[0])
def loadTestsFromNames(self, names: Sequence[str], module: Optional[str] = None) -> unittest.TestSuite:
return self.suite
if __name__ == "__main__":
if len(sys.argv) != 3:
print(f"USAGE: {sys.argv[0]} TARGET_MODULE", file=sys.stderr)
sys.exit(1)
target_module = sys.argv[1]
unittest_path = sys.argv[2]
loader = SingleLoader(target_module, unittest_path)
runner = unittest.TextTestRunner(verbosity=0)
result = runner.run(loader.suite)
if not result.wasSuccessful():
sys.exit("Test failure.")

@ -14,6 +14,7 @@
"""Python-related rules intended only for use internal to the repo."""
load("//bazel:gevent_test.bzl", "py_grpc_gevent_test")
load("//bazel:logging_threshold_test.bzl", "py_grpc_logging_threshold_test")
def internal_py_grpc_test(name, **kwargs):
"""Runs a test under all supported environments.
@ -28,6 +29,7 @@ def internal_py_grpc_test(name, **kwargs):
**kwargs
)
py_grpc_gevent_test(name, **kwargs)
py_grpc_logging_threshold_test(name, **kwargs)
suite_kwargs = {}
if "visibility" in kwargs:
@ -38,6 +40,7 @@ def internal_py_grpc_test(name, **kwargs):
tests = [
name + ".native",
name + ".gevent",
name + ".logging_threshold",
],
**suite_kwargs
)

@ -0,0 +1,73 @@
# Copyright 2024 The gRPC Authors
#
# 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
#
# http://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.
"""
Houses py_grpc_logging_threshold_test.
"""
_COPIED_MAIN_SUFFIX = ".logging_threshold.main"
def py_grpc_logging_threshold_test(
name,
srcs,
main = None,
deps = None,
data = None,
**kwargs):
"""Runs a Python unit test and checks amount of logging against a threshold.
Args:
name: The name of the test.
srcs: The source files.
main: The main file of the test.
deps: The dependencies of the test.
data: The data dependencies of the test.
**kwargs: Any other test arguments.
"""
if main == None:
if len(srcs) != 1:
fail("When main is not provided, srcs must be of size 1.")
main = srcs[0]
deps = [] if deps == None else deps
data = [] if data == None else data
lib_name = name + ".logging_threshold.lib"
native.py_library(
name = lib_name,
srcs = srcs,
)
augmented_deps = deps + [
":{}".format(lib_name),
]
# The main file needs to be in the same package as the test file.
copied_main_name = name + _COPIED_MAIN_SUFFIX
copied_main_filename = copied_main_name + ".py"
native.genrule(
name = copied_main_name,
srcs = ["//bazel:_logging_threshold_test_main.py"],
outs = [copied_main_filename],
cmd = "cp $< $@",
)
native.py_test(
name = name + ".logging_threshold",
args = ["$(location //bazel:_single_module_tester)", name],
data = data + ["//bazel:_single_module_tester"],
deps = augmented_deps,
srcs = [copied_main_filename],
main = copied_main_filename,
python_version = "PY3",
flaky = False,
**kwargs
)

@ -208,8 +208,12 @@ class SignalHandlingTest(unittest.TestCase):
self._handler.await_connected_client()
client.send_signal(signal.SIGINT)
client.wait()
print(_read_stream(client_stderr))
self.assertEqual(0, client.returncode)
client_stderr_output = _read_stream(client_stderr)
try:
self.assertEqual(0, client.returncode)
except AssertionError:
print(client_stderr_output, file=sys.stderr)
raise
if __name__ == "__main__":

Loading…
Cancel
Save