adb unittest: get test_unicode_paths passing on win32

The Python 2 subprocess class doesn't use Unicode, so as a work-around
write the command line to a UTF-8 batch file and run that.

I modified the test to use u'blah' without .encode('utf-8') because the
Python docs recommend dealing with string variables like that. When
formatting a string with a unicode parameter, use u'foo' on the constant
string to make it unicode.

I also tested this on Linux and it seems to work fine (I did ls in the
middle of the test to make sure the filenames came out right, etc.).

I had to close the temporary files before adb tries to read/write them
because filesystem semantics are different on Windows (technically I
might be able to modify adb to try to open files with more permissive
share flags, but then I'm not sure if Python uses the right share flags.
Basically, I'd be opening another can of worms.).

Fixed the test to delete a temp file on the device once it is done.

Change-Id: Id0c34e26d7697fbbb47a44ae45298bed5e8c59d6
Signed-off-by: Spencer Low <CompareAndSwap@gmail.com>
diff --git a/device.py b/device.py
index 5b33ff2..c5b5eea 100644
--- a/device.py
+++ b/device.py
@@ -17,6 +17,7 @@
 import os
 import re
 import subprocess
+import tempfile
 
 
 class FindDeviceError(RuntimeError):
@@ -99,6 +100,36 @@
 
     return _get_unique_device(product)
 
+# Call this instead of subprocess.check_output() to work-around issue in Python
+# 2's subprocess class on Windows where it doesn't support Unicode. This
+# writes the command line to a UTF-8 batch file that is properly interpreted
+# by cmd.exe.
+def _subprocess_check_output(*popenargs, **kwargs):
+    # Only do this slow work-around if Unicode is in the cmd line.
+    if (os.name == 'nt' and
+            any(isinstance(arg, unicode) for arg in popenargs[0])):
+        # cmd.exe requires a suffix to know that it is running a batch file
+        tf = tempfile.NamedTemporaryFile('wb', suffix='.cmd', delete=False)
+        # @ in batch suppresses echo of the current line.
+        # Change the codepage to 65001, the UTF-8 codepage.
+        tf.write('@chcp 65001 > nul\r\n')
+        tf.write('@')
+        # Properly quote all the arguments and encode in UTF-8.
+        tf.write(subprocess.list2cmdline(popenargs[0]).encode('utf-8'))
+        tf.close()
+
+        try:
+            result = subprocess.check_output(['cmd.exe', '/c', tf.name],
+                                             **kwargs)
+        except subprocess.CalledProcessError as e:
+            # Show real command line instead of the cmd.exe command line.
+            raise subprocess.CalledProcessError(e.returncode, popenargs[0],
+                                                output=e.output)
+        finally:
+            os.remove(tf.name)
+        return result
+    else:
+        return subprocess.check_output(*popenargs, **kwargs)
 
 class AndroidDevice(object):
     # Delimiter string to indicate the start of the exit code.
@@ -166,13 +197,13 @@
 
     def _simple_call(self, cmd):
         logging.info(' '.join(self.adb_cmd + cmd))
-        return subprocess.check_output(
+        return _subprocess_check_output(
             self.adb_cmd + cmd, stderr=subprocess.STDOUT)
 
     def shell(self, cmd):
         logging.info(' '.join(self.adb_cmd + ['shell'] + cmd))
         cmd = self._make_shell_cmd(cmd)
-        out = subprocess.check_output(cmd)
+        out = _subprocess_check_output(cmd)
         rc, out = self._parse_shell_output(out)
         if rc != 0:
             error = subprocess.CalledProcessError(rc, cmd)
diff --git a/test_device.py b/test_device.py
index c893ad4..024d163 100644
--- a/test_device.py
+++ b/test_device.py
@@ -451,19 +451,24 @@
 
     def test_unicode_paths(self):
         """Ensure that we can support non-ASCII paths, even on Windows."""
-        name = u'로보카 폴리'.encode('utf-8')
+        name = u'로보카 폴리'
 
         ## push.
-        tf = tempfile.NamedTemporaryFile('wb', suffix=name)
-        self.device.push(tf.name, '/data/local/tmp/adb-test-{}'.format(name))
+        tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+        tf.close()
+        self.device.push(tf.name, u'/data/local/tmp/adb-test-{}'.format(name))
+        os.remove(tf.name)
         self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
 
         # pull.
-        cmd = ['touch', '"/data/local/tmp/adb-test-{}"'.format(name)]
+        cmd = ['touch', u'"/data/local/tmp/adb-test-{}"'.format(name)]
         self.device.shell(cmd)
 
-        tf = tempfile.NamedTemporaryFile('wb', suffix=name)
-        self.device.pull('/data/local/tmp/adb-test-{}'.format(name), tf.name)
+        tf = tempfile.NamedTemporaryFile('wb', suffix=name, delete=False)
+        tf.close()
+        self.device.pull(u'/data/local/tmp/adb-test-{}'.format(name), tf.name)
+        os.remove(tf.name)
+        self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
 
 
 def main():