-
Notifications
You must be signed in to change notification settings - Fork 318
Add JSON, XML mock perf tests for azure_core #3212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| Service: core | ||
|
|
||
| Project: azure_core_perf | ||
|
|
||
| PrimaryPackage: azure_core | ||
|
|
||
| PackageVersions: | ||
| # - azure_core: 1.0.0 | ||
| - azure_core: source | ||
|
|
||
| Tests: | ||
| - Test: mock_json | ||
| Class: mock_json | ||
| Arguments: | ||
| - --count 5 --parallel 64 | ||
| - --count 500 --parallel 32 | ||
| - --count 50000 --parallel 32 --warmup 60 --duration 60 | ||
| # - Test: mock_xml | ||
| # Class: mock_xml | ||
| # Arguments: | ||
| # - --count 5 --parallel 64 | ||
| # - --count 500 --parallel 32 | ||
| # - --count 50000 --parallel 32 --warmup 60 --duration 60 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| trigger: none | ||
|
|
||
| pr: none | ||
|
|
||
| schedules: | ||
| - cron: "0 7 * * *" | ||
| displayName: Daily midnight build | ||
| branches: | ||
| include: | ||
| - main | ||
| always: true | ||
|
|
||
| parameters: | ||
| - name: PackageVersions | ||
| displayName: PackageVersions (regex of package versions to run) | ||
| type: string | ||
| default: '12|source' | ||
| - name: Tests | ||
| displayName: Tests (regex of tests to run) | ||
| type: string | ||
| default: '^(mock_json|mock_xml)$' | ||
| - name: Arguments | ||
| displayName: Arguments (regex of arguments to run) | ||
| type: string | ||
| default: '(5)|(500)|(50000)' | ||
| - name: Iterations | ||
| displayName: Iterations (times to run each test) | ||
| type: number | ||
| default: '5' | ||
| - name: Profile | ||
| type: boolean | ||
| default: false | ||
| - name: AdditionalArguments | ||
| displayName: AdditionalArguments (passed to PerfAutomation) | ||
| type: string | ||
| default: '' | ||
|
|
||
| extends: | ||
| template: /eng/pipelines/templates/jobs/perf.yml | ||
| parameters: | ||
| ServiceDirectory: core/azure_core | ||
| PackageVersions: ${{ parameters.PackageVersions }} | ||
| Tests: ${{ parameters.Tests }} | ||
| Arguments: ${{ parameters.Arguments }} | ||
| Iterations: ${{ parameters.Iterations }} | ||
| AdditionalArguments: ${{ parameters.AdditionalArguments }} | ||
| Profile: ${{ parameters.Profile }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| pub mod json; | ||
| #[cfg(feature = "xml")] | ||
| pub mod xml; | ||
|
|
||
| use std::sync::Arc; | ||
|
|
||
| use azure_core::{ | ||
| base64::{ | ||
| self, | ||
| option::{deserialize, serialize}, | ||
| }, | ||
| http::{headers::Headers, BufResponse, ClientOptions, Pipeline, StatusCode, Transport}, | ||
| time::{self, OffsetDateTime}, | ||
| Bytes, | ||
| }; | ||
| use azure_core_test::http::MockHttpClient; | ||
| use futures::FutureExt as _; | ||
| use serde::{Deserialize, Serialize}; | ||
|
|
||
| const DEFAULT_COUNT: usize = 25; | ||
|
|
||
| #[derive(Debug, Default, Deserialize, Serialize)] | ||
| pub struct List { | ||
| #[serde(default, rename = "name")] | ||
| name: Option<String>, | ||
|
|
||
| #[serde(default, rename = "container")] | ||
| container: Option<ListItemsContainer>, | ||
|
|
||
| #[serde(default, rename = "next")] | ||
| next: Option<String>, | ||
| } | ||
|
|
||
| #[derive(Debug, Default, Deserialize, Serialize)] | ||
| pub struct ListItemsContainer { | ||
| #[serde(default, rename = "items")] | ||
| items: Option<Vec<ListItem>>, | ||
| } | ||
|
|
||
| #[derive(Debug, Default, Deserialize, Serialize)] | ||
| pub struct ListItem { | ||
| #[serde(default, rename = "name")] | ||
| name: Option<String>, | ||
|
|
||
| #[serde(default, rename = "properties")] | ||
| properties: Option<ListItemProperties>, | ||
| } | ||
|
|
||
| #[derive(Debug, Default, Deserialize, Serialize)] | ||
| pub struct ListItemProperties { | ||
| #[serde(default, rename = "etag")] | ||
| etag: Option<azure_core::http::Etag>, | ||
|
|
||
| #[serde(default, rename = "creationTime", with = "time::rfc7231::option")] | ||
| creation_time: Option<OffsetDateTime>, | ||
|
|
||
| #[serde(default, rename = "lastModified", with = "time::rfc7231::option")] | ||
| last_modified: Option<OffsetDateTime>, | ||
|
|
||
| #[serde( | ||
| default, | ||
| rename = "contentMD5", | ||
| serialize_with = "serialize", | ||
| deserialize_with = "deserialize" | ||
| )] | ||
| content_md5: Option<Vec<u8>>, | ||
| } | ||
|
|
||
| fn create_pipeline<F>(count: usize, f: F) -> azure_core::Result<Pipeline> | ||
| where | ||
| F: Fn(&List) -> azure_core::Result<Bytes>, | ||
| { | ||
| let mut list = List { | ||
| name: Some("t0123456789abcdef".into()), | ||
| ..Default::default() | ||
| }; | ||
| let mut items = Vec::with_capacity(count); | ||
| let now = OffsetDateTime::now_utc(); | ||
| for i in 0..count { | ||
| let name = format!("testItem{i}"); | ||
| let hash = base64::encode(&name).into_bytes(); | ||
| items.push(ListItem { | ||
| name: Some(name), | ||
| properties: Some(ListItemProperties { | ||
| etag: Some(i.to_string().into()), | ||
| creation_time: Some(now), | ||
| last_modified: Some(now), | ||
| content_md5: Some(hash), | ||
| }), | ||
| }); | ||
| } | ||
| list.container = Some(ListItemsContainer { items: Some(items) }); | ||
|
|
||
| let body = f(&list)?; | ||
| println!("Serialized {count} items in {} bytes", body.len()); | ||
|
|
||
| let client = Arc::new(MockHttpClient::new(move |_| { | ||
| let body = body.clone(); | ||
| async move { | ||
| // Yield simulates an expected network call but kills performance by ~45%. | ||
| tokio::task::yield_now().await; | ||
| Ok(BufResponse::from_bytes( | ||
| StatusCode::Ok, | ||
| Headers::new(), | ||
| body, | ||
| )) | ||
| } | ||
| .boxed() | ||
| })); | ||
| let options = ClientOptions { | ||
| transport: Some(Transport::new(client)), | ||
| ..Default::default() | ||
| }; | ||
| let pipeline = Pipeline::new( | ||
| Some("perf"), | ||
| Some("0.1.0"), | ||
| options, | ||
| Vec::new(), | ||
| Vec::new(), | ||
| None, | ||
| ); | ||
| Ok(pipeline) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| use super::List; | ||
| use azure_core::{ | ||
| http::{Context, JsonFormat, Method, Pipeline, RawResponse, Request, Response}, | ||
| json, | ||
| }; | ||
| use azure_core_test::{ | ||
| perf::{CreatePerfTestReturn, PerfRunner, PerfTest, PerfTestMetadata, PerfTestOption}, | ||
| TestContext, | ||
| }; | ||
| use futures::FutureExt as _; | ||
| use std::{hint::black_box, sync::Arc}; | ||
|
|
||
| pub struct MockJsonTest { | ||
| pipeline: Pipeline, | ||
| } | ||
|
|
||
| impl MockJsonTest { | ||
| fn create_items(runner: PerfRunner) -> CreatePerfTestReturn { | ||
| async move { | ||
| let count = runner | ||
| .try_get_test_arg("count")? | ||
| .cloned() | ||
| .unwrap_or(super::DEFAULT_COUNT); | ||
| let pipeline = super::create_pipeline(count, json::to_json)?; | ||
| Ok(Box::new(MockJsonTest { pipeline }) as Box<dyn PerfTest>) | ||
| } | ||
| .boxed() | ||
| } | ||
|
|
||
| pub fn test_metadata() -> PerfTestMetadata { | ||
| PerfTestMetadata { | ||
| name: "mock_json", | ||
| description: "Mock transport that returns JSON", | ||
| options: vec![PerfTestOption { | ||
| name: "count", | ||
| display_message: "Number of items per page", | ||
| mandatory: false, | ||
| short_activator: None, | ||
| long_activator: "count", | ||
| expected_args_len: 1, | ||
| ..Default::default() | ||
| }], | ||
| create_test: Self::create_items, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[async_trait::async_trait] | ||
| impl PerfTest for MockJsonTest { | ||
| async fn setup(&self, _context: Arc<TestContext>) -> azure_core::Result<()> { | ||
| Ok(()) | ||
| } | ||
|
|
||
| async fn run(&self, _context: Arc<TestContext>) -> azure_core::Result<()> { | ||
| let ctx = Context::new(); | ||
| let mut request = Request::new( | ||
| "https://contoso.com/containers/t0123456789abcdef?api-version=2025-10-15".parse()?, | ||
| Method::Get, | ||
| ); | ||
| let response = self.pipeline.send(&ctx, &mut request, None).await?; | ||
| // Make sure we deserialize the response. | ||
| let (status, headers, body) = response.deconstruct(); | ||
| let response: Response<List, JsonFormat> = | ||
| RawResponse::from_bytes(status, headers, body).into(); | ||
| let list: List = tokio::spawn(async move { | ||
| tokio::task::yield_now().await; | ||
| response.into_body() | ||
| }) | ||
| .await | ||
| .unwrap()?; | ||
| assert_eq!(black_box(list.name), Some("t0123456789abcdef".into())); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| async fn cleanup(&self, _context: Arc<TestContext>) -> azure_core::Result<()> { | ||
| Ok(()) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| use super::List; | ||
| use azure_core::{ | ||
| http::{Context, Method, Pipeline, RawResponse, Request, Response, XmlFormat}, | ||
| xml, | ||
| }; | ||
| use azure_core_test::{ | ||
| perf::{CreatePerfTestReturn, PerfRunner, PerfTest, PerfTestMetadata, PerfTestOption}, | ||
| TestContext, | ||
| }; | ||
| use futures::FutureExt as _; | ||
| use std::{hint::black_box, sync::Arc}; | ||
|
|
||
heaths marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| pub struct MockXmlTest { | ||
| pipeline: Pipeline, | ||
| } | ||
|
|
||
| impl MockXmlTest { | ||
| fn create_items(runner: PerfRunner) -> CreatePerfTestReturn { | ||
| async move { | ||
| let count = runner | ||
| .try_get_test_arg("count")? | ||
| .cloned() | ||
| .unwrap_or(super::DEFAULT_COUNT); | ||
| let pipeline = super::create_pipeline(count, xml::to_xml)?; | ||
| Ok(Box::new(MockXmlTest { pipeline }) as Box<dyn PerfTest>) | ||
| } | ||
| .boxed() | ||
| } | ||
|
|
||
| pub fn test_metadata() -> PerfTestMetadata { | ||
| PerfTestMetadata { | ||
| name: "mock_xml", | ||
| description: "Mock transport that returns XML", | ||
| options: vec![PerfTestOption { | ||
| name: "count", | ||
| display_message: "Number of items per page", | ||
| mandatory: false, | ||
| short_activator: None, | ||
| long_activator: "count", | ||
| expected_args_len: 1, | ||
| ..Default::default() | ||
| }], | ||
| create_test: Self::create_items, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[async_trait::async_trait] | ||
| impl PerfTest for MockXmlTest { | ||
| async fn setup(&self, _context: Arc<TestContext>) -> azure_core::Result<()> { | ||
| Ok(()) | ||
| } | ||
|
|
||
| async fn run(&self, _context: Arc<TestContext>) -> azure_core::Result<()> { | ||
| let ctx = Context::new(); | ||
| let mut request = Request::new( | ||
| "https://contoso.com/containers/t0123456789abcdef?api-version=2025-10-15".parse()?, | ||
| Method::Get, | ||
| ); | ||
| let response = self.pipeline.send(&ctx, &mut request, None).await?; | ||
| // Make sure we deserialize the response. | ||
| let (status, headers, body) = response.deconstruct(); | ||
| let response: Response<List, XmlFormat> = | ||
| RawResponse::from_bytes(status, headers, body).into(); | ||
| let list: List = tokio::spawn(async move { | ||
| tokio::task::yield_now().await; | ||
| response.into_body() | ||
| }) | ||
| .await | ||
| .unwrap()?; | ||
| assert_eq!(black_box(list.name), Some("t0123456789abcdef".into())); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| async fn cleanup(&self, _context: Arc<TestContext>) -> azure_core::Result<()> { | ||
| Ok(()) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| mod mock; | ||
|
|
||
| use azure_core_test::perf::PerfRunner; | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> azure_core::Result<()> { | ||
| let runner = PerfRunner::new( | ||
| env!("CARGO_MANIFEST_DIR"), | ||
| file!(), | ||
| vec![ | ||
| mock::json::MockJsonTest::test_metadata(), | ||
| #[cfg(feature = "xml")] | ||
| mock::xml::MockXmlTest::test_metadata(), | ||
| ], | ||
| )?; | ||
|
|
||
| runner.run().await?; | ||
|
|
||
| Ok(()) | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.