Blob Blame Raw
Index: test/functional/test_copy.py
===================================================================
--- test/functional/test_copy.py	(revision 7052)
+++ test/functional/test_copy.py	(revision 7054)
@@ -1,6 +1,5 @@
 import unittest
 import utils
-import shutil
 import os
 from base import TestBase
 
@@ -113,5 +112,24 @@
         os.unlink(dst_path)
         os.rmdir('/tmp/make/')
 
+    def test_copy_pseudotty(self):
+        """
+        Regression test for DMC-522
+        Trick gfal-copy into thinking it is inside a tty so we trigger some logic that would not
+        be executed otherwise
+        """
+        ffname3 = self.ffname1 + "_copy"
+        self.assertFalse(os.path.isfile(ffname3))
+
+        (ret, out, err) = utils.run_command_pty('gfal-copy', \
+                            'file://' + self.ffname1 + ' file://' + ffname3)
+
+        self.assertTrue(os.path.isfile(ffname3))
+        self.assertNotEqual(len(out), 0) # this makes sure the interactive mode works!
+        self.assertEqual(ret, 0)
+
+        if os.path.isfile(ffname3):
+            os.remove(ffname3)
+
 if __name__ == '__main__':
     unittest.main()
Index: test/functional/utils.py
===================================================================
--- test/functional/utils.py	(revision 7052)
+++ test/functional/utils.py	(revision 7054)
@@ -1,6 +1,8 @@
 import subprocess
 import datetime
 import os
+import pty
+import select
 import stat
 import inspect
 
@@ -8,22 +10,48 @@
     fout = open(path, 'w')
     fout.write(os.urandom(size))
     fout.close()
-        
-        
+
+
 def create_random_suffix():
     return datetime.datetime.now().strftime("%y%m%d_%H%M%S")
 
 def remove_file(path):
     os.remove(path)
-    
+
 def run_command(cmd, args):
     script_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
     cmd = script_path + '/../../src/' + cmd
     p = subprocess.Popen([cmd] +  args.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    out,err = p.communicate()
-    
+    out, err = p.communicate()
+
     return (p.returncode, out, err)
 
+def run_command_pty(cmd, args):
+    script_path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+    cmd = script_path + '/../../src/' + cmd
+
+    master, slave = pty.openpty()
+    p = subprocess.Popen([cmd] +  args.split(), stdout=slave, stderr=slave, close_fds=True)
+    os.close(slave)
+
+    output = ''
+    while True:
+        ready, _, _ = select.select([master], [], [], 1)
+        if ready:
+            try:
+                data = os.read(ready[0], 512)
+            except:
+                data = None
+            if data:
+                output += data
+            else:
+                break
+    os.close(master)
+
+    rstatus = p.wait()
+
+    return (rstatus, output, None)
+
 def num_entries(directory):
     return len([name for name in os.listdir(directory)])
 
Index: src/gfal2_util/progress.py
===================================================================
--- src/gfal2_util/progress.py	(revision 7052)
+++ src/gfal2_util/progress.py	(revision 7054)
@@ -3,7 +3,7 @@
 
 @author: Duarte Meneses <duarte.meneses@cern.ch>
 """
-import os
+import subprocess
 import sys
 import datetime
 import math
@@ -47,8 +47,10 @@
             if self.stopped or not self.started:
                 break
 
-            self._update()
-            self.lock.release()
+            try:
+                self._update()
+            finally:
+                self.lock.release()
             time.sleep(0.5)
 
         self.lock.release()
@@ -163,7 +165,7 @@
         if not self.started:
             return
 
-        if self.t_main.is_alive():
+        if self.t_main.isAlive():
             self.lock.acquire()
             if self.stopped:
                 self.lock.release()
@@ -194,7 +196,11 @@
 
     @staticmethod
     def _get_width():
-        return int(os.popen('stty size', 'r').read().split()[1])
+        p = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        out, err = p.communicate()
+        if p.returncode != 0:
+            return 80  # Asume default
+        return int(out.split()[1])
 
     @staticmethod
     def _clean():