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 err = self.run_helper('--handler_style=async')
73 self.assertRegex(err, self.get_crash_regex())
75 def test_immediate(self):
76 err = self.run_helper('--handler_style=immediate')
77 self.assertRegex(err, self.get_crash_regex())
80 # The fatal message should be printed directly to stderr when there
81 # are no logging handlers configured.
82 err = self.run_helper('--handler_style=none')
83 self.assertRegex(err, self.get_crash_regex(glog=False))
85 def test_other_category(self):
86 err = self.run_helper('--category=foo.bar',
87 '--logging', '.=FATAL')
89 br'^C[0-9]{4} .* FatalHelper.cpp:[0-9]+\] '
90 br'crashing to category foo\.bar$',
92 self.assertRegex(err, regex)
94 def test_static_init(self):
95 err = self.run_helper(env={'CRASH_DURING_INIT': '1'})
96 regex = self.get_crash_regex(br'crashing during static initialization',
98 self.assertRegex(err, regex)
100 def test_static_destruction(self):
101 err = self.run_helper('--crash=no',
102 env={'CRASH_DURING_INIT': 'shutdown'})
103 # When crashing during static destruction we may or may not see a
104 # glog-formatted message. This depends on whether the crashing
105 # destructor runs before or after the code that uninstalls the log
106 # handlers, and it is valid for that to occur in either order.
107 regex = re.compile(br'^(FATAL|C[0-9]{4}).*FatalHelper.cpp:.* '
108 br'crashing during static destruction$',
110 self.assertRegex(err, regex)