Skip to content

Commit

Permalink
Switch to multi-threaded async runtime and send correct headers
Browse files Browse the repository at this point in the history
The default single-threaded async runtime runner was deadlocking in `Recording::drop()`. I opened #2049 to revisit this but I don't foresee any problem. We already don't support test-proxy on wasm32 and our mix of agents we do test on support multi-threaded async runtimes just fine.
  • Loading branch information
heaths committed Feb 1, 2025
1 parent 9044a04 commit 18ffb68
Show file tree
Hide file tree
Showing 23 changed files with 404 additions and 99 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ indent_size = 4
indent_style = space
trim_trailing_whitespace = true

[*.bicep]
indent_size = 2

[*.json]
indent_size = 2

Expand Down
2 changes: 2 additions & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@
"hmac",
"iothub",
"keyvault",
"LLDB",
"maxresults",
"maxsize",
"msrc",
"newtonsoft",
"oidc",
"pageable",
"pageables",
"pkce",
"pkcs",
"posix",
Expand Down
76 changes: 75 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

When you run `cargo build`, toolchain version [1.80](https://releases.rs/docs/1.80.0/) and necessary components will be installed automatically.

- (Recommended) If you use [Visual Studio Code](https://code.visualstudio.com), install recommended extensions to improve your development experience.
- (Recommended) If you use [Visual Studio Code], install recommended extensions to improve your development experience.

## Generated code

Expand All @@ -21,6 +21,77 @@ To build any library in the Azure SDK for Rust navigate to the library's project
[TODO] Add instructions on how to run tests for a specific project.
[TODO] Add instructions for write new tests.

### Debugging with Visual Studio Code

[Visual Studio Code] with recommended extensions installed can be used to run and debug tests for a module or individual tests.

If you need to debug a test, you can use the LLDB extension and set environment variables as needed. For example, to debug recording a specific test,
your `.vscode/launch.json` file might look something like:

```json
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Record secret_roundtrip",
"cargo": {
"args": [
"test",
"--no-run",
"--test=secret_client",
"--package=azure_security_keyvault_secrets",
"secret_roundtrip"
],
"filter": {
"name": "secret_client",
"kind": "test"
},
"env": {
}
},
"cwd": "${workspaceFolder}",
"env": {
"AZURE_KEYVAULT_URL": "https://my-vault.vault.azure.net/",
"PROXY_MANUAL_START": "true",
"RUST_LOG": "trace"
}
},
{
"type": "lldb",
"request": "launch",
"name": "Play back secret_roundtrip",
"cargo": {
"args": [
"test",
"--no-run",
"--test=secret_client",
"--package=azure_security_keyvault_secrets",
"secret_roundtrip"
],
"filter": {
"name": "secret_client",
"kind": "test"
},
"env": {
"AZURE_TEST_MODE": "playback"
}
},
"cwd": "${workspaceFolder}",
"env": {
"RUST_LOG": "trace"
}
}
]
}
```

You can also start the [Test Proxy] manually, in which can you add to the outer `env` above to `"PROXY_MANUAL_START": "true"`.

To enable tracing, you can add the `RUST_LOG` environment variable as shown above using the [same format supported by `env_logger`](https://docs.rs/env_logger/latest/env_logger/#enabling-logging).
The targets are the crate names if you want to trace more or less for specific targets e.g., `RUST_LOG=info,azure_core=trace` to trace information messages by default but detailed traces for the `azure_core` crate.

## Code Review Process

Before a pull request will be considered by the Azure SDK team, the following requirements must be met:
Expand Down Expand Up @@ -94,3 +165,6 @@ Samples may take the following categories of dependencies:
- **Tiered licensed**: Offerings that enable readers to use the license tier that corresponds to their characteristics. For example, tiers may be available for students, hobbyists, or companies with defined revenue thresholds. For offerings with tiered licenses, strive to limit our use in tutorials to the features available in the lowest tier. This policy enables the widest audience for the article. [Docker](https://www.docker.com/), [IdentityServer](https://duendesoftware.com/products/identityserver), [ImageSharp](https://sixlabors.com/products/imagesharp/), and [Visual Studio](https://visualstudio.com) are examples of this license type.

In general, we prefer taking dependencies on licensed components in the order of the listed categories. In cases where the category may not be well known, we'll document the category so that readers understand the choice that they're making by using that dependency.

[Test Proxy]: https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/Azure.Sdk.Tools.TestProxy/README.md
[Visual Studio Code]: https://code.visualstudio.com
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions sdk/core/azure_core/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ impl fmt::Debug for TestMode {
}
}

impl From<&TestMode> for &'static str {
fn from(mode: &TestMode) -> Self {
impl From<TestMode> for &'static str {
fn from(mode: TestMode) -> Self {
match mode {
TestMode::Playback => "playback",
TestMode::Record => "record",
Expand All @@ -46,6 +46,12 @@ impl From<&TestMode> for &'static str {
}
}

impl From<&TestMode> for &'static str {
fn from(mode: &TestMode) -> Self {
TestMode::into(*mode)
}
}

impl FromStr for TestMode {
type Err = Error;

Expand Down
18 changes: 16 additions & 2 deletions sdk/core/azure_core_test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ edition.workspace = true
rust-version.workspace = true
publish = false

[features]
default = []
tracing = ["tracing-subscriber"]

[dependencies]
async-trait.workspace = true
azure_core = { workspace = true, features = ["test"] }
Expand All @@ -23,14 +27,24 @@ futures.workspace = true
serde.workspace = true
serde_json.workspace = true
tracing.workspace = true
tracing-subscriber = { workspace = true, features = [
"env-filter",
"fmt",
], optional = true }
typespec_client_core = { workspace = true, features = ["derive"] }
url.workspace = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { workspace = true, features = ["io-util", "process", "sync", "time"] }
tokio = { workspace = true, features = [
"io-util",
"process",
"rt-multi-thread",
"sync",
"time",
] }

[dev-dependencies]
clap.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { workspace = true, features = ["signal"] }
74 changes: 64 additions & 10 deletions sdk/core/azure_core_test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub use proxy::{matchers::*, sanitizers::*};
pub use recording::*;
use std::path::{Path, PathBuf};

#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
const ASSETS_FILE: &str = "assets.json";
const SPAN_TARGET: &str = "test-proxy";

/// Context information required by recorded client library tests.
Expand All @@ -21,6 +23,7 @@ const SPAN_TARGET: &str = "test-proxy";
/// to setup up the HTTP client to record or play back session records.
#[derive(Debug)]
pub struct TestContext {
repo_dir: &'static Path,
crate_dir: &'static Path,
service_directory: &'static str,
test_module: &'static str,
Expand All @@ -42,6 +45,7 @@ impl TestContext {
.to_str()
.ok_or_else(|| Error::message(ErrorKind::Other, "invalid test module"))?;
Ok(Self {
repo_dir: find_parent(crate_dir, ".git")?,
crate_dir: Path::new(crate_dir),
service_directory,
test_module,
Expand All @@ -66,6 +70,11 @@ impl TestContext {
.expect("not recording or playback started")
}

/// Gets the repository root.
pub fn repo_dir(&self) -> &'static Path {
self.repo_dir
}

/// Gets the service directory containing the current test.
///
/// This is the directory under `sdk/` within the repository e.g., "core" in `sdk/core`.
Expand All @@ -74,8 +83,18 @@ impl TestContext {
}

/// Gets the test data directory under [`Self::crate_dir`].
///
/// The path is relative to the repository root e.g., `sdk/core/azure_core/tests/data`.
///
/// # Panics
///
/// Panics if the [`TestContext::crate_dir()`] is not rooted within a Git repository.
pub fn test_data_dir(&self) -> PathBuf {
self.crate_dir.join("tests/data")
self.crate_dir
.join("tests/data")
.strip_prefix(self.repo_dir)
.expect("not rooted within repo")
.to_path_buf()
}

/// Gets the module name containing the current test.
Expand All @@ -89,17 +108,39 @@ impl TestContext {
}

/// Gets the recording assets file under the crate directory.
pub(crate) fn test_recording_assets_file(&self) -> Option<String> {
///
/// The path is relative to the repository root e.g., `sdk/core/assets.json`.
///
/// # Panics
///
/// Panics if the [`TestContext::crate_dir()`] is not rooted within a Git repository.
pub(crate) fn test_recording_assets_file(
&self,
#[cfg_attr(target_arch = "wasm32", allow(unused_variables))] mode: TestMode,
) -> Option<String> {
#[cfg(target_arch = "wasm32")]
{
None
}

#[cfg(not(target_arch = "wasm32"))]
{
let path =
find_ancestor(self.crate_dir, "assets.json").unwrap_or_else(|err| panic!("{err}"));
path.as_path().to_str().map(String::from)
let path = match find_ancestor(self.crate_dir, ASSETS_FILE) {
Ok(path) => path,
Err(_) if mode == TestMode::Record => {
return Path::new("sdk")
.join(self.service_directory)
.join(ASSETS_FILE)
.as_path()
.to_str()
.map(String::from);
}
Err(err) => panic!("{err}"),
};
path.strip_prefix(self.repo_dir)
.expect("not rooted within repo")
.to_str()
.map(String::from)
}
}

Expand Down Expand Up @@ -154,6 +195,20 @@ fn find_ancestor(dir: impl AsRef<Path>, name: &str) -> azure_core::Result<PathBu
))
}

fn find_parent(dir: &'static str, name: &'static str) -> azure_core::Result<&'static Path> {
let dir = Path::new(dir);
for dir in dir.ancestors() {
let path = dir.join(name);
if path.exists() {
return Ok(dir);
}
}
Err(azure_core::Error::new::<std::io::Error>(
azure_core::error::ErrorKind::Io,
std::io::ErrorKind::NotFound.into(),
))
}

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -171,11 +226,10 @@ mod tests {
.ends_with("sdk/core/azure_core_test"));
assert_eq!(ctx.test_module(), "lib");
assert_eq!(ctx.test_name(), "test_content_new");
assert!(ctx
.test_recording_file()
.as_str()
.replace("\\", "/")
.ends_with("sdk/core/azure_core_test/tests/data/lib/test_content_new.json"));
assert_eq!(
ctx.test_recording_file(),
"sdk/core/azure_core_test/tests/data/lib/test_content_new.json"
);
}

#[test]
Expand Down
Loading

0 comments on commit 18ffb68

Please sign in to comment.