summaryrefslogtreecommitdiffstats
path: root/testing/mozbase/mozcrash/tests
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mozbase/mozcrash/tests')
-rw-r--r--testing/mozbase/mozcrash/tests/conftest.py127
-rw-r--r--testing/mozbase/mozcrash/tests/manifest.ini7
-rw-r--r--testing/mozbase/mozcrash/tests/test_basic.py43
-rw-r--r--testing/mozbase/mozcrash/tests/test_java_exception.py51
-rw-r--r--testing/mozbase/mozcrash/tests/test_save_path.py68
-rw-r--r--testing/mozbase/mozcrash/tests/test_stackwalk.py42
-rw-r--r--testing/mozbase/mozcrash/tests/test_symbols_path.py97
7 files changed, 435 insertions, 0 deletions
diff --git a/testing/mozbase/mozcrash/tests/conftest.py b/testing/mozbase/mozcrash/tests/conftest.py
new file mode 100644
index 0000000000..4827515723
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/conftest.py
@@ -0,0 +1,127 @@
+# coding=UTF-8
+
+import uuid
+
+import mozcrash
+import pytest
+from py._path.common import fspath
+
+
+@pytest.fixture(scope="session")
+def stackwalk(tmpdir_factory):
+ stackwalk = tmpdir_factory.mktemp("stackwalk_binary").join("stackwalk")
+ stackwalk.write("fake binary")
+ stackwalk.chmod(0o744)
+ return stackwalk
+
+
+@pytest.fixture
+def check_for_crashes(tmpdir, stackwalk, monkeypatch):
+ monkeypatch.delenv("MINIDUMP_SAVE_PATH", raising=False)
+
+ def wrapper(
+ dump_directory=fspath(tmpdir),
+ symbols_path="symbols_path",
+ stackwalk_binary=fspath(stackwalk),
+ dump_save_path=None,
+ test_name=None,
+ quiet=True,
+ ):
+ return mozcrash.check_for_crashes(
+ dump_directory,
+ symbols_path,
+ stackwalk_binary,
+ dump_save_path,
+ test_name,
+ quiet,
+ )
+
+ return wrapper
+
+
+@pytest.fixture
+def check_for_java_exception():
+ def wrapper(logcat=None, test_name=None, quiet=True):
+ return mozcrash.check_for_java_exception(logcat, test_name, quiet)
+
+ return wrapper
+
+
+def minidump_files(request, tmpdir):
+ files = []
+
+ for i in range(getattr(request, "param", 1)):
+ name = uuid.uuid4()
+
+ dmp = tmpdir.join("{}.dmp".format(name))
+ dmp.write("foo")
+
+ extra = tmpdir.join("{}.extra".format(name))
+
+ extra.write_text(
+ u"""
+{
+ "ContentSandboxLevel":"2",
+ "TelemetryEnvironment":"{🍪}",
+ "EMCheckCompatibility":"true",
+ "ProductName":"Firefox",
+ "ContentSandboxCapabilities":"119",
+ "TelemetryClientId":"",
+ "Vendor":"Mozilla",
+ "InstallTime":"1000000000",
+ "Theme":"classic/1.0",
+ "ReleaseChannel":"default",
+ "ServerURL":"https://crash-reports.mozilla.com",
+ "SafeMode":"0",
+ "ContentSandboxCapable":"1",
+ "useragent_locale":"en-US",
+ "Version":"55.0a1",
+ "BuildID":"20170512114708",
+ "ProductID":"{ec8030f7-c20a-464f-9b0e-13a3a9e97384}",
+ "MozCrashReason": "MOZ_CRASH()",
+ "TelemetryServerURL":"",
+ "DOMIPCEnabled":"1",
+ "Add-ons":"",
+ "CrashTime":"1494582646",
+ "UptimeTS":"14.9179586",
+ "ContentSandboxEnabled":"1",
+ "ProcessType":"content",
+ "StartupTime":"1000000000",
+ "URL":"about:home"
+}
+
+ """,
+ encoding="utf-8",
+ )
+
+ files.append({"dmp": dmp, "extra": extra})
+
+ return files
+
+
+@pytest.fixture(name="minidump_files")
+def minidump_files_fixture(request, tmpdir):
+ return minidump_files(request, tmpdir)
+
+
+@pytest.fixture(autouse=True)
+def mock_popen(monkeypatch):
+ """Generate a class that can mock subprocess.Popen.
+
+ :param stdouts: Iterable that should return an iterable for the
+ stdout of each process in turn.
+ """
+
+ class MockPopen(object):
+ def __init__(self, args, *args_rest, **kwargs):
+ # all_popens.append(self)
+ self.args = args
+ self.returncode = 0
+
+ def communicate(self):
+ return (u"Stackwalk command: {}".format(" ".join(self.args)), "")
+
+ def wait(self):
+ return self.returncode
+
+ monkeypatch.setattr(mozcrash.mozcrash.subprocess, "Popen", MockPopen)
diff --git a/testing/mozbase/mozcrash/tests/manifest.ini b/testing/mozbase/mozcrash/tests/manifest.ini
new file mode 100644
index 0000000000..5a7e75b832
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/manifest.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+subsuite = mozbase
+[test_basic.py]
+[test_java_exception.py]
+[test_save_path.py]
+[test_stackwalk.py]
+[test_symbols_path.py]
diff --git a/testing/mozbase/mozcrash/tests/test_basic.py b/testing/mozbase/mozcrash/tests/test_basic.py
new file mode 100644
index 0000000000..157a5785c1
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/test_basic.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# coding=UTF-8
+
+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()
diff --git a/testing/mozbase/mozcrash/tests/test_java_exception.py b/testing/mozbase/mozcrash/tests/test_java_exception.py
new file mode 100644
index 0000000000..f48ce2cc3e
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/test_java_exception.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# coding=UTF-8
+
+import mozunit
+import pytest
+
+
+@pytest.fixture
+def test_log():
+ return [
+ "01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> "
+ 'REPORTING UNCAUGHT EXCEPTION FROM THREAD 9 ("GeckoBackgroundThread")',
+ "01-30 20:15:41.937 E/GeckoAppShell( 1703): java.lang.NullPointerException",
+ "01-30 20:15:41.937 E/GeckoAppShell( 1703):"
+ " at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833)",
+ "01-30 20:15:41.937 E/GeckoAppShell( 1703):"
+ " at android.os.Handler.handleCallback(Handler.java:587)",
+ ]
+
+
+def test_uncaught_exception(check_for_java_exception, test_log):
+ """Test for an exception which should be caught."""
+ assert 1 == check_for_java_exception(test_log)
+
+
+def test_truncated_exception(check_for_java_exception, test_log):
+ """Test for an exception which should be caught which was truncated."""
+ truncated_log = list(test_log)
+ truncated_log[0], truncated_log[1] = truncated_log[1], truncated_log[0]
+
+ assert 1 == check_for_java_exception(truncated_log)
+
+
+def test_unchecked_exception(check_for_java_exception, test_log):
+ """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()
diff --git a/testing/mozbase/mozcrash/tests/test_save_path.py b/testing/mozbase/mozcrash/tests/test_save_path.py
new file mode 100644
index 0000000000..fad83ab71b
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/test_save_path.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+
+import os
+
+import mozunit
+import pytest
+from conftest import fspath
+
+
+def test_save_path_not_present(check_for_crashes, minidump_files, tmpdir):
+ """Test that dump_save_path works when the directory doesn't exist."""
+ save_path = tmpdir.join("saved")
+
+ assert 1 == check_for_crashes(dump_save_path=fspath(save_path))
+
+ assert save_path.join(minidump_files[0]["dmp"].basename).check()
+ assert save_path.join(minidump_files[0]["extra"].basename).check()
+
+
+def test_save_path(check_for_crashes, minidump_files, tmpdir):
+ """Test that dump_save_path works."""
+ save_path = tmpdir.mkdir("saved")
+
+ assert 1 == check_for_crashes(dump_save_path=fspath(save_path))
+
+ assert save_path.join(minidump_files[0]["dmp"].basename).check()
+ assert save_path.join(minidump_files[0]["extra"].basename).check()
+
+
+def test_save_path_isfile(check_for_crashes, minidump_files, tmpdir):
+ """Test that dump_save_path works when the path is a file and not a directory."""
+ save_path = tmpdir.join("saved")
+ save_path.write("junk")
+
+ assert 1 == check_for_crashes(dump_save_path=fspath(save_path))
+
+ assert save_path.join(minidump_files[0]["dmp"].basename).check()
+ assert save_path.join(minidump_files[0]["extra"].basename).check()
+
+
+def test_save_path_envvar(check_for_crashes, minidump_files, tmpdir):
+ """Test that the MINDUMP_SAVE_PATH environment variable works."""
+ save_path = tmpdir.mkdir("saved")
+
+ os.environ["MINIDUMP_SAVE_PATH"] = fspath(save_path)
+ try:
+ assert 1 == check_for_crashes(dump_save_path=None)
+ finally:
+ del os.environ["MINIDUMP_SAVE_PATH"]
+
+ assert save_path.join(minidump_files[0]["dmp"].basename).check()
+ assert save_path.join(minidump_files[0]["extra"].basename).check()
+
+
+@pytest.mark.parametrize("minidump_files", [3], indirect=True)
+def test_save_multiple(check_for_crashes, minidump_files, tmpdir):
+ """Test that all minidumps are saved."""
+ save_path = tmpdir.mkdir("saved")
+
+ assert 3 == check_for_crashes(dump_save_path=fspath(save_path))
+
+ for i in range(3):
+ assert save_path.join(minidump_files[i]["dmp"].basename).check()
+ assert save_path.join(minidump_files[i]["extra"].basename).check()
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/testing/mozbase/mozcrash/tests/test_stackwalk.py b/testing/mozbase/mozcrash/tests/test_stackwalk.py
new file mode 100644
index 0000000000..7be2d82c10
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/test_stackwalk.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+# coding=UTF-8
+
+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()
diff --git a/testing/mozbase/mozcrash/tests/test_symbols_path.py b/testing/mozbase/mozcrash/tests/test_symbols_path.py
new file mode 100644
index 0000000000..9aa281bd31
--- /dev/null
+++ b/testing/mozbase/mozcrash/tests/test_symbols_path.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+# coding=UTF-8
+
+import zipfile
+
+import mozhttpd
+import mozunit
+from conftest import fspath
+from six import BytesIO
+from six.moves.urllib.parse import urlunsplit
+
+
+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():
+ zdata = BytesIO()
+ z = zipfile.ZipFile(zdata, "w")
+ z.writestr("symbols.txt", "abc/xyz")
+ z.close()
+ return zdata.getvalue()
+
+ def get_symbols(req):
+ data["retrieved"] = True
+
+ headers = {}
+ return (200, headers, make_zipfile())
+
+ httpd = mozhttpd.MozHttpd(
+ port=0,
+ urlhandlers=[{"method": "GET", "path": "/symbols", "function": get_symbols}],
+ )
+ httpd.start()
+ symbol_url = urlunsplit(
+ ("http", "%s:%d" % httpd.httpd.server_address, "/symbols", "", "")
+ )
+
+ assert 1 == check_for_crashes(symbols_path=symbol_url)
+ assert data["retrieved"]
+
+
+def test_symbols_retry(check_for_crashes, minidump_files):
+ """Test that passing a URL as symbols_path succeeds on retry after temporary HTTP failure."""
+ data = {"retrieved": False}
+ get_symbols_calls = 0
+
+ def make_zipfile():
+ zdata = BytesIO()
+ z = zipfile.ZipFile(zdata, "w")
+ z.writestr("symbols.txt", "abc/xyz")
+ z.close()
+ return zdata.getvalue()
+
+ def get_symbols(req):
+ nonlocal get_symbols_calls
+ data["retrieved"] = True
+ if get_symbols_calls > 0:
+ ret = 200
+ else:
+ ret = 504
+ get_symbols_calls += 1
+
+ headers = {}
+ return (ret, headers, make_zipfile())
+
+ httpd = mozhttpd.MozHttpd(
+ port=0,
+ urlhandlers=[{"method": "GET", "path": "/symbols", "function": get_symbols}],
+ )
+ httpd.start()
+ symbol_url = urlunsplit(
+ ("http", "%s:%d" % httpd.httpd.server_address, "/symbols", "", "")
+ )
+
+ assert 1 == check_for_crashes(symbols_path=symbol_url)
+ assert data["retrieved"]
+ assert 2 == get_symbols_calls
+
+
+if __name__ == "__main__":
+ mozunit.main()