@@ -1545,6 +1545,53 @@ def test_exception_bad_args_0(self):
15451545 else :
15461546 self .fail ("Expected OSError: %s" % desired_exception )
15471547
1548+ # We mock the __del__ method for Popen in the next two tests
1549+ # because it does cleanup based on the pid returned by fork_exec
1550+ # along with issuing a resource warning if it still exists. Since
1551+ # we don't actually spawn a process in these tests we can forego
1552+ # the destructor. An alternative would be to set _child_created to
1553+ # False before the destructor is called but there is no easy way
1554+ # to do that
1555+ class PopenNoDestructor (subprocess .Popen ):
1556+ def __del__ (self ):
1557+ pass
1558+
1559+ @mock .patch ("subprocess._posixsubprocess.fork_exec" )
1560+ def test_exception_errpipe_normal (self , fork_exec ):
1561+ """Test error passing done through errpipe_write in the good case"""
1562+ def proper_error (* args ):
1563+ errpipe_write = args [13 ]
1564+ # Write the hex for the error code EISDIR: 'is a directory'
1565+ err_code = '{:x}' .format (errno .EISDIR ).encode ()
1566+ os .write (errpipe_write , b"OSError:" + err_code + b":" )
1567+ return 0
1568+
1569+ fork_exec .side_effect = proper_error
1570+
1571+ with self .assertRaises (IsADirectoryError ):
1572+ self .PopenNoDestructor (["non_existent_command" ])
1573+
1574+ @mock .patch ("subprocess._posixsubprocess.fork_exec" )
1575+ def test_exception_errpipe_bad_data (self , fork_exec ):
1576+ """Test error passing done through errpipe_write where its not
1577+ in the expected format"""
1578+ error_data = b"\xFF \x00 \xDE \xAD "
1579+ def bad_error (* args ):
1580+ errpipe_write = args [13 ]
1581+ # Anything can be in the pipe, no assumptions should
1582+ # be made about its encoding, so we'll write some
1583+ # arbitrary hex bytes to test it out
1584+ os .write (errpipe_write , error_data )
1585+ return 0
1586+
1587+ fork_exec .side_effect = bad_error
1588+
1589+ with self .assertRaises (subprocess .SubprocessError ) as e :
1590+ self .PopenNoDestructor (["non_existent_command" ])
1591+
1592+ self .assertIn (repr (error_data ), str (e .exception ))
1593+
1594+
15481595 def test_restore_signals (self ):
15491596 # Code coverage for both values of restore_signals to make sure it
15501597 # at least does not blow up.
0 commit comments