diff --git a/sdk/identity/azure-identity/tests/perfstress_tests/README.md b/sdk/identity/azure-identity/tests/perfstress_tests/README.md new file mode 100644 index 000000000000..46d2a604c04d --- /dev/null +++ b/sdk/identity/azure-identity/tests/perfstress_tests/README.md @@ -0,0 +1,35 @@ +# azure-identity Performance Tests + +In order to run the performance tests, the `azure-devtools` package must be installed. This is done as part of the +`dev_requirements` install. Start by creating a new Python 3 virtual environment. + +## Test commands + +Once `azure-devtools` is installed, you will have access to the `perfstress` command line tool, which will scan the +current module for runnable perf tests. Only a specific test can be run at a time (i.e. there is no "run all" feature). + +`perfstress` with no options will list all available tests: +``` +(env) ~/azure-identity/tests> perfstress +``` + +### Common perf command line options +These options are available for all perf tests: +- `--duration=10` Number of seconds to run as many operations (the "run" function) as possible. Default is 10. +- `--iterations=1` Number of test iterations to run. Default is 1. +- `--parallel=1` Number of tests to run in parallel. Default is 1. +- `--warm-up=5` Number of seconds to spend warming up the connection before measuring begins. Default is 5. +- `--sync` Whether to run the tests in sync or async. Default is False (async). +- `--no-cleanup` Whether to keep newly created resources after test run. Default is False (resources will be deleted). + +## Example command +``` +(env) ~/azure-identity/tests> perfstress BearerTokenPolicyTest +``` + +## Tests +- `BearerTokenPolicyTest` Runs a single request through `BearerTokenCredentialPolicy`, + and a mock transport +- `MemoryCacheRead` retrieves an access token from the default, in memory cache. + This is useful primarily as a baseline for `PersistentCacheRead`. +- `PersistentCacheRead` retrives an access token from the persistent cache diff --git a/sdk/identity/azure-identity/tests/perfstress_tests/__init__.py b/sdk/identity/azure-identity/tests/perfstress_tests/__init__.py new file mode 100644 index 000000000000..4a10bbeb47f4 --- /dev/null +++ b/sdk/identity/azure-identity/tests/perfstress_tests/__init__.py @@ -0,0 +1,5 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE.txt in the project root for +# license information. +# ------------------------------------------------------------------------- diff --git a/sdk/identity/azure-identity/tests/perfstress_tests/bearer_token_auth_policy.py b/sdk/identity/azure-identity/tests/perfstress_tests/bearer_token_auth_policy.py new file mode 100644 index 000000000000..1f3508295120 --- /dev/null +++ b/sdk/identity/azure-identity/tests/perfstress_tests/bearer_token_auth_policy.py @@ -0,0 +1,43 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See LICENSE.txt in the project root for +# license information. +# ------------------------------------------------------------------------- +import asyncio +import time +from unittest.mock import Mock + +from azure_devtools.perfstress_tests import PerfStressTest + +from azure.core.credentials import AccessToken +from azure.core.pipeline import AsyncPipeline, Pipeline +from azure.core.pipeline.policies import AsyncBearerTokenCredentialPolicy, BearerTokenCredentialPolicy +from azure.core.pipeline.transport import HttpRequest + + +class BearerTokenPolicyTest(PerfStressTest): + def __init__(self, arguments): + super().__init__(arguments) + + token = AccessToken("**", int(time.time() + 3600)) + + self.request = HttpRequest("GET", "https://localhost") + + credential = Mock(get_token=Mock(return_value=token)) + self.pipeline = Pipeline(transport=Mock(), policies=[BearerTokenCredentialPolicy(credential=credential)]) + + completed_future = asyncio.Future() + completed_future.set_result(token) + async_credential = Mock(get_token=Mock(return_value=completed_future)) + + # returning a token is okay because the policy does nothing with the transport's response + async_transport = Mock(send=Mock(return_value=completed_future)) + self.async_pipeline = AsyncPipeline( + async_transport, policies=[AsyncBearerTokenCredentialPolicy(credential=async_credential)] + ) + + def run_sync(self): + self.pipeline.run(self.request) + + async def run_async(self): + await self.async_pipeline.run(self.request) diff --git a/sdk/identity/azure-identity/tests/perfstress_tests/memory_cache_read.py b/sdk/identity/azure-identity/tests/perfstress_tests/memory_cache_read.py new file mode 100644 index 000000000000..269191bd5bd6 --- /dev/null +++ b/sdk/identity/azure-identity/tests/perfstress_tests/memory_cache_read.py @@ -0,0 +1,44 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from azure_devtools.perfstress_tests import PerfStressTest + +from azure.identity import ClientSecretCredential +from azure.identity.aio import ClientSecretCredential as AsyncClientSecretCredential + +try: + from dotenv import load_dotenv + + load_dotenv() +except ImportError: + pass + + +class MemoryCacheRead(PerfStressTest): + def __init__(self, arguments): + super().__init__(arguments) + + client_id = self.get_from_env("AZURE_CLIENT_ID") + tenant_id = self.get_from_env("AZURE_TENANT_ID") + secret = self.get_from_env("AZURE_CLIENT_SECRET") + self.credential = ClientSecretCredential(tenant_id, client_id, secret) + self.async_credential = AsyncClientSecretCredential(tenant_id, client_id, secret) + self.scope = "https://vault.azure.net/.default" + + async def global_setup(self): + """Cache an access token""" + + await super().global_setup() + self.credential.get_token(self.scope) + await self.async_credential.get_token(self.scope) + + def run_sync(self): + self.credential.get_token(self.scope) + + async def run_async(self): + await self.async_credential.get_token(self.scope) + + async def close(self): + await self.async_credential.close() + await super().close() diff --git a/sdk/identity/azure-identity/tests/perfstress_tests/persistent_cache_read.py b/sdk/identity/azure-identity/tests/perfstress_tests/persistent_cache_read.py new file mode 100644 index 000000000000..64761ee76baf --- /dev/null +++ b/sdk/identity/azure-identity/tests/perfstress_tests/persistent_cache_read.py @@ -0,0 +1,48 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +from azure_devtools.perfstress_tests import PerfStressTest + +from azure.identity import ClientSecretCredential, TokenCachePersistenceOptions +from azure.identity.aio import ClientSecretCredential as AsyncClientSecretCredential + +try: + from dotenv import load_dotenv + + load_dotenv() +except ImportError: + pass + + +class PersistentCacheRead(PerfStressTest): + def __init__(self, arguments): + super().__init__(arguments) + + client_id = self.get_from_env("AZURE_CLIENT_ID") + tenant_id = self.get_from_env("AZURE_TENANT_ID") + secret = self.get_from_env("AZURE_CLIENT_SECRET") + self.credential = ClientSecretCredential( + tenant_id, client_id, secret, cache_persistence_options=TokenCachePersistenceOptions() + ) + self.async_credential = AsyncClientSecretCredential( + tenant_id, client_id, secret, cache_persistence_options=TokenCachePersistenceOptions() + ) + self.scope = "https://vault.azure.net/.default" + + async def global_setup(self): + """Cache an access token""" + + await super().global_setup() + self.credential.get_token(self.scope) + await self.async_credential.get_token(self.scope) + + def run_sync(self): + self.credential.get_token(self.scope) + + async def run_async(self): + await self.async_credential.get_token(self.scope) + + async def close(self): + await self.async_credential.close() + await super().close()