@@ -1545,6 +1545,53 @@ def test_exception_bad_args_0(self):
1545
1545
else :
1546
1546
self .fail ("Expected OSError: %s" % desired_exception )
1547
1547
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
+
1548
1595
def test_restore_signals (self ):
1549
1596
# Code coverage for both values of restore_signals to make sure it
1550
1597
# at least does not blow up.
0 commit comments