Bug 1441287 - [mozcrash] Add support for unicode paths.
To let mozcrash handle minidump files located in profile paths
with unicode characters, support for that has to be added. It
also applies to the locations for the stackwalk binary, minidump
save path, and symbols.
MozReview-Commit-ID: EROVmK21a5Y
--- a/testing/mozbase/mozcrash/mozcrash/mozcrash.py
+++ b/testing/mozbase/mozcrash/mozcrash/mozcrash.py
@@ -89,30 +89,33 @@ def check_for_crashes(dump_directory,
crash_info = CrashInfo(dump_directory, symbols_path, dump_save_path=dump_save_path,
stackwalk_binary=stackwalk_binary)
crash_count = 0
for info in crash_info:
crash_count += 1
if not quiet:
- stackwalk_output = ["Crash dump filename: %s" % info.minidump_path]
+ stackwalk_output = [u"Crash dump filename: {}".format(info.minidump_path)]
if info.stackwalk_stderr:
stackwalk_output.append("stderr from minidump_stackwalk:")
stackwalk_output.append(info.stackwalk_stderr)
elif info.stackwalk_stdout is not None:
stackwalk_output.append(info.stackwalk_stdout)
if info.stackwalk_retcode is not None and info.stackwalk_retcode != 0:
- stackwalk_output.append("minidump_stackwalk exited with return code %d" %
- info.stackwalk_retcode)
+ stackwalk_output.append("minidump_stackwalk exited with return code {}".format(
+ info.stackwalk_retcode))
signature = info.signature if info.signature else "unknown top frame"
- print("PROCESS-CRASH | %s | application crashed [%s]" % (test_name,
- signature))
- print('\n'.join(stackwalk_output))
- print('\n'.join(info.stackwalk_errors))
+
+ output = u"PROCESS-CRASH | {name} | application crashed [{sig}]\n{out}\n{err}".format(
+ name=test_name,
+ sig=signature,
+ out="\n".join(stackwalk_output),
+ err="\n".join(info.stackwalk_errors))
+ print(output.encode("utf-8"))
return crash_count
def log_crashes(logger,
dump_directory,
symbols_path,
process=None,
@@ -242,17 +245,17 @@ class CrashInfo(object):
os.path.exists(self.stackwalk_binary) and
os.access(self.stackwalk_binary, os.X_OK)):
command = [
self.stackwalk_binary,
path,
self.symbols_path
]
- self.logger.info('Copy/paste: ' + ' '.join(command))
+ self.logger.info(u"Copy/paste: {}".format(' '.join(command)))
# run minidump_stackwalk
p = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
(out, err) = p.communicate()
retcode = p.returncode
@@ -308,23 +311,25 @@ class CrashInfo(object):
os.unlink(self.dump_save_path)
if not os.path.isdir(self.dump_save_path):
try:
os.makedirs(self.dump_save_path)
except OSError:
pass
shutil.move(path, self.dump_save_path)
- self.logger.info("Saved minidump as %s" %
- os.path.join(self.dump_save_path, os.path.basename(path)))
+ self.logger.info(u"Saved minidump as {}".format(
+ os.path.join(self.dump_save_path, os.path.basename(path))
+ ))
if os.path.isfile(extra):
shutil.move(extra, self.dump_save_path)
- self.logger.info("Saved app info as %s" %
- os.path.join(self.dump_save_path, os.path.basename(extra)))
+ self.logger.info(u"Saved app info as {}".format(
+ os.path.join(self.dump_save_path, os.path.basename(extra))
+ ))
def check_for_java_exception(logcat, test_name=None, quiet=False):
"""
Print a summary of a fatal Java exception, if present in the provided
logcat output.
Example:
@@ -365,22 +370,25 @@ def check_for_java_exception(logcat, tes
logre = re.compile(r".*\): \t?(.*)")
m = logre.search(logcat[i + 1])
if m and m.group(1):
exception_type = m.group(1)
m = logre.search(logcat[i + 2])
if m and m.group(1):
exception_location = m.group(1)
if not quiet:
- print("PROCESS-CRASH | %s | java-exception %s %s" % (test_name,
- exception_type,
- exception_location))
+ output = u"PROCESS-CRASH | {name} | java-exception {type} {loc}".format(
+ name=test_name,
+ type=exception_type,
+ loc=exception_location
+ )
+ print(output.encode("utf-8"))
else:
- print("Automation Error: java exception in logcat at line "
- "%d of %d: %s" % (i, len(logcat), line))
+ print(u"Automation Error: java exception in logcat at line "
+ "{0} of {1}: {2}".format(i, len(logcat), line))
break
return found_exception
if mozinfo.isWin:
import ctypes
import uuid
@@ -411,20 +419,20 @@ if mozinfo.isWin:
utility_path):
# We're not going to be able to write a minidump with ctypes if our
# python process was compiled for a different architecture than
# firefox, so we invoke the minidumpwriter utility program.
log = get_logger()
minidumpwriter = os.path.normpath(os.path.join(utility_path,
"minidumpwriter.exe"))
- log.info("Using %s to write a dump to %s for [%d]" %
- (minidumpwriter, file_name, pid))
+ log.info(u"Using {} to write a dump to {} for [{}]".format(
+ minidumpwriter, file_name, pid))
if not os.path.exists(minidumpwriter):
- log.error("minidumpwriter not found in %s" % utility_path)
+ log.error(u"minidumpwriter not found in {}".format(utility_path))
return
if isinstance(file_name, unicode):
# Convert to a byte string before sending to the shell.
file_name = file_name.encode(sys.getfilesystemencoding())
status = subprocess.Popen([minidumpwriter, str(pid), file_name]).wait()
if status:
--- a/testing/mozbase/mozcrash/tests/test_basic.py
+++ b/testing/mozbase/mozcrash/tests/test_basic.py
@@ -1,21 +1,48 @@
#!/usr/bin/env python
+# coding=UTF-8
from __future__ import absolute_import
import mozunit
import pytest
+from conftest import fspath
+
def test_no_dump_files(check_for_crashes):
"""Test that check_for_crashes returns 0 if no dumps are present."""
assert 0 == check_for_crashes()
@pytest.mark.parametrize('minidump_files', [3], indirect=True)
def test_dump_count(check_for_crashes, minidump_files):
"""Test that check_for_crashes returns the number of crash dumps."""
assert 3 == check_for_crashes()
+def test_dump_directory_unicode(request, check_for_crashes, tmpdir, capsys):
+ """Test that check_for_crashes can handle unicode in dump_directory."""
+ from conftest import minidump_files
+
+ tmpdir = tmpdir.ensure(u"🍪", dir=1)
+ minidump_files = minidump_files(request, tmpdir)
+
+ assert 1 == check_for_crashes(dump_directory=fspath(tmpdir),
+ quiet=False)
+
+ out, _ = capsys.readouterr()
+ assert fspath(minidump_files[0]["dmp"]) in out
+ assert u"🍪" in out
+
+
+def test_test_name_unicode(check_for_crashes, minidump_files, capsys):
+ """Test that check_for_crashes can handle unicode in dump_directory."""
+ assert 1 == check_for_crashes(test_name=u"🍪",
+ quiet=False)
+
+ out, err = capsys.readouterr()
+ assert u"| 🍪 |" in out
+
+
if __name__ == '__main__':
mozunit.main()
--- a/testing/mozbase/mozcrash/tests/test_java_exception.py
+++ b/testing/mozbase/mozcrash/tests/test_java_exception.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python
+# coding=UTF-8
from __future__ import absolute_import
import mozunit
import pytest
@pytest.fixture
@@ -36,10 +37,17 @@ def test_unchecked_exception(check_for_j
"""Test for an exception which should not be caught."""
passable_log = list(test_log)
passable_log[0] = "01-30 20:15:41.937 E/GeckoAppShell( 1703):" \
" >>> NOT-SO-BAD EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")"
assert 0 == check_for_java_exception(passable_log)
+def test_test_name_unicode(check_for_java_exception, test_log):
+ """Test that check_for_crashes can handle unicode in dump_directory."""
+ assert 1 == check_for_java_exception(test_log,
+ test_name=u"🍪",
+ quiet=False)
+
+
if __name__ == '__main__':
mozunit.main()
--- a/testing/mozbase/mozcrash/tests/test_stackwalk.py
+++ b/testing/mozbase/mozcrash/tests/test_stackwalk.py
@@ -1,22 +1,47 @@
#!/usr/bin/env python
+# coding=UTF-8
from __future__ import absolute_import
import os
import mozunit
from conftest import fspath
+def test_stackwalk_not_found(check_for_crashes, minidump_files, tmpdir, capsys):
+ """Test that check_for_crashes can handle unicode in dump_directory."""
+ stackwalk = tmpdir.join('stackwalk')
+
+ assert 1 == check_for_crashes(stackwalk_binary=fspath(stackwalk),
+ quiet=False)
+
+ out, _ = capsys.readouterr()
+ assert "MINIDUMP_STACKWALK binary not found" in out
+
+
def test_stackwalk_envvar(check_for_crashes, minidump_files, stackwalk):
"""Test that check_for_crashes uses the MINIDUMP_STACKWALK environment var."""
os.environ['MINIDUMP_STACKWALK'] = fspath(stackwalk)
try:
assert 1 == check_for_crashes(stackwalk_binary=None)
finally:
del os.environ['MINIDUMP_STACKWALK']
+def test_stackwalk_unicode(check_for_crashes, minidump_files, tmpdir, capsys):
+ """Test that check_for_crashes can handle unicode in dump_directory."""
+ stackwalk = tmpdir.mkdir(u"🍪").join('stackwalk')
+ stackwalk.write('fake binary')
+ stackwalk.chmod(0o744)
+
+ assert 1 == check_for_crashes(stackwalk_binary=fspath(stackwalk),
+ quiet=False)
+
+ out, err = capsys.readouterr()
+ assert fspath(stackwalk) in out
+
+
if __name__ == '__main__':
mozunit.main()
--- a/testing/mozbase/mozcrash/tests/test_symbols_path.py
+++ b/testing/mozbase/mozcrash/tests/test_symbols_path.py
@@ -1,25 +1,39 @@
#!/usr/bin/env python
+# coding=UTF-8
from __future__ import absolute_import
import urlparse
import zipfile
import StringIO
import mozhttpd
import mozunit
+from conftest import fspath
+
def test_symbols_path_not_present(check_for_crashes, minidump_files):
"""Test that no symbols path let mozcrash try to find the symbols."""
assert 1 == check_for_crashes(symbols_path=None)
+def test_symbols_path_unicode(check_for_crashes, minidump_files, tmpdir, capsys):
+ """Test that check_for_crashes can handle unicode in dump_directory."""
+ symbols_path = tmpdir.mkdir(u"🍪")
+
+ assert 1 == check_for_crashes(symbols_path=fspath(symbols_path),
+ quiet=False)
+
+ out, _ = capsys.readouterr()
+ assert fspath(symbols_path) in out
+
+
def test_symbols_path_url(check_for_crashes, minidump_files):
"""Test that passing a URL as symbols_path correctly fetches the URL."""
data = {"retrieved": False}
def make_zipfile():
data = StringIO.StringIO()
z = zipfile.ZipFile(data, 'w')
z.writestr("symbols.txt", "abc/xyz")