diff options
Diffstat (limited to 'testing/tests/benchmarks')
| -rw-r--r-- | testing/tests/benchmarks/assets/cert_default.conf | 15 | ||||
| -rw-r--r-- | testing/tests/benchmarks/conftest.py | 57 | ||||
| -rw-r--r-- | testing/tests/benchmarks/pytest.ini | 2 | ||||
| -rw-r--r-- | testing/tests/benchmarks/test_crypto.py | 99 | ||||
| -rw-r--r-- | testing/tests/benchmarks/test_misc.py | 8 | ||||
| -rw-r--r-- | testing/tests/benchmarks/test_sqlcipher.py | 40 | ||||
| -rw-r--r-- | testing/tests/benchmarks/test_sync.py | 64 | 
7 files changed, 285 insertions, 0 deletions
diff --git a/testing/tests/benchmarks/assets/cert_default.conf b/testing/tests/benchmarks/assets/cert_default.conf new file mode 100644 index 00000000..8043cea3 --- /dev/null +++ b/testing/tests/benchmarks/assets/cert_default.conf @@ -0,0 +1,15 @@ +[ req ] +default_bits           = 1024 +default_keyfile        = keyfile.pem +distinguished_name     = req_distinguished_name +prompt                 = no +output_password        = mypass + +[ req_distinguished_name ] +C                      = GB +ST                     = Test State or Province +L                      = Test Locality +O                      = Organization Name +OU                     = Organizational Unit Name +CN                     = localhost +emailAddress           = test@email.address diff --git a/testing/tests/benchmarks/conftest.py b/testing/tests/benchmarks/conftest.py new file mode 100644 index 00000000..a9cc3464 --- /dev/null +++ b/testing/tests/benchmarks/conftest.py @@ -0,0 +1,57 @@ +import pytest +import random +import base64 + +from twisted.internet import threads, reactor + + +# we have to manually setup the events server in order to be able to signal +# events. This is usually done by the enclosing application using soledad +# client (i.e. bitmask client). +from leap.common.events import server +server.ensure_server() + + +def pytest_addoption(parser): +    parser.addoption( +        "--num-docs", type="int", default=100, +        help="the number of documents to use in performance tests") + + +@pytest.fixture() +def payload(): +    def generate(size): +        random.seed(1337)  # same seed to avoid different bench results +        payload_bytes = bytearray(random.getrandbits(8) for _ in xrange(size)) +        # encode as base64 to avoid ascii encode/decode errors +        return base64.b64encode(payload_bytes)[:size]  # remove b64 overhead +    return generate + + +@pytest.fixture() +def txbenchmark(benchmark): +    def blockOnThread(*args, **kwargs): +        return threads.deferToThread( +            benchmark, threads.blockingCallFromThread, +            reactor, *args, **kwargs) +    return blockOnThread + + +@pytest.fixture() +def txbenchmark_with_setup(benchmark): +    def blockOnThreadWithSetup(setup, f): +        def blocking_runner(*args, **kwargs): +            return threads.blockingCallFromThread(reactor, f, *args, **kwargs) + +        def blocking_setup(): +            args = threads.blockingCallFromThread(reactor, setup) +            try: +                return tuple(arg for arg in args), {} +            except TypeError: +                    return ((args,), {}) if args else None + +        def bench(): +            return benchmark.pedantic(blocking_runner, setup=blocking_setup, +                                      rounds=4, warmup_rounds=1) +        return threads.deferToThread(bench) +    return blockOnThreadWithSetup diff --git a/testing/tests/benchmarks/pytest.ini b/testing/tests/benchmarks/pytest.ini new file mode 100644 index 00000000..7a0508ce --- /dev/null +++ b/testing/tests/benchmarks/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +twisted = yes diff --git a/testing/tests/benchmarks/test_crypto.py b/testing/tests/benchmarks/test_crypto.py new file mode 100644 index 00000000..ab586bea --- /dev/null +++ b/testing/tests/benchmarks/test_crypto.py @@ -0,0 +1,99 @@ +""" +Benchmarks for crypto operations. +If you don't want to stress your local machine too much, you can pass the +SIZE_LIMT environment variable. + +For instance, to keep the maximum payload at 1MB: + +SIZE_LIMIT=1E6 py.test -s tests/perf/test_crypto.py +""" +import pytest +import os +import json +from uuid import uuid4 + +from leap.soledad.common.document import SoledadDocument +from leap.soledad.client import _crypto + +LIMIT = int(float(os.environ.get('SIZE_LIMIT', 50 * 1000 * 1000))) + + +pytestmark = pytest.mark.benchmark + + +def create_doc_encryption(size): +    @pytest.mark.benchmark(group="test_crypto_encrypt_doc") +    def test_doc_encryption(soledad_client, benchmark, payload): +        crypto = soledad_client()._crypto + +        DOC_CONTENT = {'payload': payload(size)} +        doc = SoledadDocument( +            doc_id=uuid4().hex, rev='rev', +            json=json.dumps(DOC_CONTENT)) + +        benchmark(crypto.encrypt_doc, doc) +    return test_doc_encryption + + +# TODO this test is really bullshit, because it's still including +# the json serialization. + +def create_doc_decryption(size): +    @pytest.inlineCallbacks +    @pytest.mark.benchmark(group="test_crypto_decrypt_doc") +    def test_doc_decryption(soledad_client, benchmark, payload): +        crypto = soledad_client()._crypto + +        DOC_CONTENT = {'payload': payload(size)} +        doc = SoledadDocument( +            doc_id=uuid4().hex, rev='rev', +            json=json.dumps(DOC_CONTENT)) + +        encrypted_doc = yield crypto.encrypt_doc(doc) +        doc.set_json(encrypted_doc) + +        benchmark(crypto.decrypt_doc, doc) +    return test_doc_decryption + + +def create_raw_encryption(size): +    @pytest.mark.benchmark(group="test_crypto_raw_encrypt") +    def test_raw_encrypt(benchmark, payload): +        key = payload(32) +        benchmark(_crypto.encrypt_sym, payload(size), key) +    return test_raw_encrypt + + +def create_raw_decryption(size): +    @pytest.mark.benchmark(group="test_crypto_raw_decrypt") +    def test_raw_decrypt(benchmark, payload): +        key = payload(32) +        iv, ciphertext = _crypto.encrypt_sym(payload(size), key) +        benchmark(_crypto.decrypt_sym, ciphertext, key, iv) +    return test_raw_decrypt + + +# Create the TESTS in the global namespace, they'll be picked by the benchmark +# plugin. + +encryption_tests = [ +    ('10k', 1E4), +    ('100k', 1E5), +    ('500k', 5E5), +    ('1M', 1E6), +    ('10M', 1E7), +    ('50M', 5E7), +] + +for name, size in encryption_tests: +    if size < LIMIT: +        sz = int(size) +        globals()['test_encrypt_doc_' + name] = create_doc_encryption(sz) +        globals()['test_decrypt_doc_' + name] = create_doc_decryption(sz) + + +for name, size in encryption_tests: +    if size < LIMIT: +        sz = int(size) +        globals()['test_encrypt_raw_' + name] = create_raw_encryption(sz) +        globals()['test_decrypt_raw_' + name] = create_raw_decryption(sz) diff --git a/testing/tests/benchmarks/test_misc.py b/testing/tests/benchmarks/test_misc.py new file mode 100644 index 00000000..2f32ad7c --- /dev/null +++ b/testing/tests/benchmarks/test_misc.py @@ -0,0 +1,8 @@ +import pytest + +pytestmark = pytest.mark.benchmark + + +@pytest.mark.benchmark(group="test_instance") +def test_initialization(soledad_client, benchmark): +    benchmark(soledad_client) diff --git a/testing/tests/benchmarks/test_sqlcipher.py b/testing/tests/benchmarks/test_sqlcipher.py new file mode 100644 index 00000000..7f8842bd --- /dev/null +++ b/testing/tests/benchmarks/test_sqlcipher.py @@ -0,0 +1,40 @@ +''' +Tests SoledadClient/SQLCipher interaction +''' +import pytest + +from twisted.internet.defer import gatherResults + +pytestmark = pytest.mark.benchmark + + +def load_up(client, amount, payload, defer=True): +    results = [client.create_doc({'content': payload}) for _ in xrange(amount)] +    if defer: +        return gatherResults(results) + + +def build_test_sqlcipher_async_create(amount, size): +    @pytest.inlineCallbacks +    @pytest.mark.benchmark(group="test_sqlcipher_async_create") +    def test(soledad_client, txbenchmark, payload): +        client = soledad_client() +        yield txbenchmark(load_up, client, amount, payload(size)) +    return test + + +def build_test_sqlcipher_create(amount, size): +    @pytest.mark.benchmark(group="test_sqlcipher_create") +    def test(soledad_client, benchmark, payload): +        client = soledad_client()._dbsyncer +        benchmark(load_up, client, amount, payload(size), defer=False) +    return test + + +test_async_create_20_500k = build_test_sqlcipher_async_create(20, 500 * 1000) +test_async_create_100_100k = build_test_sqlcipher_async_create(100, 100 * 1000) +test_async_create_1000_10k = build_test_sqlcipher_async_create(1000, 10 * 1000) +# synchronous +test_create_20_500k = build_test_sqlcipher_create(20, 500 * 1000) +test_create_100_100k = build_test_sqlcipher_create(100, 100 * 1000) +test_create_1000_10k = build_test_sqlcipher_create(1000, 10 * 1000) diff --git a/testing/tests/benchmarks/test_sync.py b/testing/tests/benchmarks/test_sync.py new file mode 100644 index 00000000..88afe9f8 --- /dev/null +++ b/testing/tests/benchmarks/test_sync.py @@ -0,0 +1,64 @@ +import pytest + +pytestmark = pytest.mark.benchmark + + +@pytest.inlineCallbacks +def load_up(client, amount, payload): +    # create a bunch of local documents +    for i in xrange(amount): +        yield client.create_doc({'content': payload}) + + +def create_upload(uploads, size): +    @pytest.inlineCallbacks +    @pytest.mark.benchmark(group="test_upload") +    def test(soledad_client, txbenchmark_with_setup, payload): +        client = soledad_client() + +        def setup(): +            return load_up(client, uploads, payload(size)) + +        yield txbenchmark_with_setup(setup, client.sync) +    return test + + +test_upload_20_500k = create_upload(20, 500 * 1000) +test_upload_100_100k = create_upload(100, 100 * 1000) +test_upload_1000_10k = create_upload(1000, 10 * 1000) + + +def create_download(downloads, size): +    @pytest.inlineCallbacks +    @pytest.mark.benchmark(group="test_download") +    def test(soledad_client, txbenchmark_with_setup, payload): +        client = soledad_client() + +        yield load_up(client, downloads, payload(size)) +        yield client.sync() +        # We could create them directly on couch, but sending them +        # ensures we are dealing with properly encrypted docs + +        def setup(): +            return soledad_client() + +        def sync(clean_client): +            return clean_client.sync() +        yield txbenchmark_with_setup(setup, sync) +    return test + + +test_download_20_500k = create_download(20, 500 * 1000) +test_download_100_100k = create_download(100, 100 * 1000) +test_download_1000_10k = create_download(1000, 10 * 1000) + + +@pytest.inlineCallbacks +@pytest.mark.benchmark(group="test_nothing_to_sync") +def test_nothing_to_sync(soledad_client, txbenchmark_with_setup): +    def setup(): +        return soledad_client() + +    def sync(clean_client): +        return clean_client.sync() +    yield txbenchmark_with_setup(setup, sync)  | 
