logging: rename the `DEBUG` log level to `DBG`
[folly.git] / folly / experimental / logging / test / fatal_test.py
1 #!/usr/bin/env python3
2 #
3 # Copyright 2004-present Facebook, Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #   http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 import os
18 import re
19 import signal
20 import subprocess
21 import unittest
22
23
24 class FatalTests(unittest.TestCase):
25     def setUp(self):
26         fatal_helper_env = os.environ.get('FOLLY_FATAL_HELPER')
27         if fatal_helper_env:
28             self.helper = fatal_helper_env
29         else:
30             build_dir = os.path.join(os.getcwd(), 'buck-out', 'gen')
31             self.helper = os.path.join(build_dir, 'folly', 'experimental',
32                                        'logging', 'test', 'fatal_helper')
33
34     def run_helper(self, *args, **kwargs):
35         '''
36         Run the helper.
37         Check that it crashes with SIGABRT and prints nothing on stdout.
38         Returns the data printed to stderr.
39         '''
40         env = kwargs.pop('env', None)
41         if kwargs:
42             raise TypeError('unexpected keyword arguments: %r' %
43                             (list(kwargs.keys())))
44
45         cmd = [self.helper]
46         cmd.extend(args)
47         p = subprocess.Popen(cmd,
48                              stdout=subprocess.PIPE,
49                              stderr=subprocess.PIPE,
50                              env=env)
51         out, err = p.communicate()
52         status = p.returncode
53
54         self.assertEqual(status, -signal.SIGABRT)
55         self.assertEqual(out, b'')
56         return err
57
58     def get_crash_regex(self, msg=b'test program crashing!', glog=True):
59         if glog:
60             prefix = br'^C[0-9]{4} .* FatalHelper.cpp:[0-9]+\] '
61         else:
62             prefix = br'^FATAL:.*FatalHelper.cpp:[0-9]+: '
63         regex = prefix + re.escape(msg) + b'$'
64         return re.compile(regex, re.MULTILINE)
65
66     def test_no_crash(self):
67         # Simple sanity check that the program runs without
68         # crashing when requested
69         subprocess.check_output([self.helper, '--crash=no'])
70
71     def test_async(self):
72         handler_setings = 'default=stream:stream=stderr,async=true'
73         err = self.run_helper('--logging=;' + handler_setings)
74         self.assertRegex(err, self.get_crash_regex())
75
76     def test_immediate(self):
77         handler_setings = 'default=stream:stream=stderr,async=false'
78         err = self.run_helper('--logging=;' + handler_setings)
79         self.assertRegex(err, self.get_crash_regex())
80
81     def test_none(self):
82         # The fatal message should be printed directly to stderr when there
83         # are no logging handlers configured.
84         err = self.run_helper('--logging=ERR:')
85         self.assertRegex(err, self.get_crash_regex(glog=False))
86
87     def test_other_category(self):
88         err = self.run_helper('--category=foo.bar',
89                               '--logging', '.=FATAL')
90         regex = re.compile(
91             br'^C[0-9]{4} .* FatalHelper.cpp:[0-9]+\] '
92             br'crashing to category foo\.bar$',
93             re.MULTILINE)
94         self.assertRegex(err, regex)
95
96     def test_static_init(self):
97         err = self.run_helper(env={'CRASH_DURING_INIT': '1'})
98         regex = self.get_crash_regex(br'crashing during static initialization',
99                                      glog=False)
100         self.assertRegex(err, regex)
101
102     def test_static_destruction(self):
103         err = self.run_helper('--crash=no',
104                               env={'CRASH_DURING_INIT': 'shutdown'})
105         # When crashing during static destruction we may or may not see a
106         # glog-formatted message.  This depends on whether the crashing
107         # destructor runs before or after the code that uninstalls the log
108         # handlers, and it is valid for that to occur in either order.
109         regex = re.compile(br'^(FATAL|C[0-9]{4}).*FatalHelper.cpp:.* '
110                            br'crashing during static destruction$',
111                            re.MULTILINE)
112         self.assertRegex(err, regex)