3 # Copyright 2004-present Facebook, Inc.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
24 class FatalTests(unittest.TestCase):
26 fatal_helper_env = os.environ.get('FOLLY_FATAL_HELPER')
28 self.helper = fatal_helper_env
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')
34 def run_helper(self, *args, **kwargs):
37 Check that it crashes with SIGABRT and prints nothing on stdout.
38 Returns the data printed to stderr.
40 env = kwargs.pop('env', None)
42 raise TypeError('unexpected keyword arguments: %r' %
43 (list(kwargs.keys())))
47 p = subprocess.Popen(cmd,
48 stdout=subprocess.PIPE,
49 stderr=subprocess.PIPE,
51 out, err = p.communicate()
54 self.assertEqual(status, -signal.SIGABRT)
55 self.assertEqual(out, b'')
58 def get_crash_regex(self, msg=b'test program crashing!', glog=True):
60 prefix = br'^C[0-9]{4} .* FatalHelper.cpp:[0-9]+\] '
62 prefix = br'^FATAL:.*FatalHelper.cpp:[0-9]+: '
63 regex = prefix + re.escape(msg) + b'$'
64 return re.compile(regex, re.MULTILINE)
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'])
72 handler_setings = 'default=file,stream=stderr,async=true'
73 err = self.run_helper('--logging=;' + handler_setings)
74 self.assertRegex(err, self.get_crash_regex())
76 def test_immediate(self):
77 handler_setings = 'default=file,stream=stderr,async=false'
78 err = self.run_helper('--logging=;' + handler_setings)
79 self.assertRegex(err, self.get_crash_regex())
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))
87 def test_other_category(self):
88 err = self.run_helper('--category=foo.bar',
89 '--logging', '.=FATAL')
91 br'^C[0-9]{4} .* FatalHelper.cpp:[0-9]+\] '
92 br'crashing to category foo\.bar$',
94 self.assertRegex(err, regex)
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',
100 self.assertRegex(err, regex)
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$',
112 self.assertRegex(err, regex)