use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use std::hint::black_box;
use bitbucket_cli::auth::Credential;
use bitbucket_cli::config::Config;
use bitbucket_cli::models::*;
const REPOSITORY_JSON: &str = r#"{
"uuid": "{12345678-1234-1234-1234-123456789abc}",
"name": "my-repo",
"full_name": "workspace/my-repo",
"slug": "my-repo",
"description": "A sample repository for benchmarking",
"is_private": true,
"scm": "git",
"created_on": "2024-01-15T10:30:00.000000+00:00",
"updated_on": "2024-06-20T15:45:00.000000+00:00",
"size": 1024000,
"language": "Rust",
"has_issues": true,
"has_wiki": false,
"fork_policy": "no_public_forks"
}"#;
const PULL_REQUEST_JSON: &str = r#"{
"id": 42,
"title": "Add new feature implementation",
"description": "This PR adds a comprehensive new feature with tests and documentation",
"state": "OPEN",
"author": {
"uuid": "{user-uuid-1234}",
"display_name": "John Developer",
"type": "user"
},
"source": {
"branch": {"name": "feature/new-feature"},
"commit": {"hash": "abc123def456"}
},
"destination": {
"branch": {"name": "main"},
"commit": {"hash": "789xyz000111"}
},
"created_on": "2024-06-01T09:00:00.000000+00:00",
"updated_on": "2024-06-15T14:30:00.000000+00:00",
"comment_count": 5,
"task_count": 2
}"#;
const ISSUE_JSON: &str = r#"{
"id": 123,
"title": "Bug: Application crashes on startup",
"content": {
"raw": "When starting the application with certain flags, it crashes immediately.",
"markup": "markdown"
},
"state": "open",
"kind": "bug",
"priority": "critical",
"votes": 15,
"watches": 8,
"created_on": "2024-05-10T08:00:00.000000+00:00",
"updated_on": "2024-06-18T16:00:00.000000+00:00"
}"#;
const PAGINATED_REPOS_JSON: &str = r#"{
"size": 100,
"page": 1,
"pagelen": 10,
"next": "https://api.bitbucket.org/2.0/repositories?page=2",
"values": [
{"uuid": "{repo-1}", "name": "repo-1", "full_name": "ws/repo-1", "slug": "repo-1", "is_private": true, "scm": "git"},
{"uuid": "{repo-2}", "name": "repo-2", "full_name": "ws/repo-2", "slug": "repo-2", "is_private": false, "scm": "git"},
{"uuid": "{repo-3}", "name": "repo-3", "full_name": "ws/repo-3", "slug": "repo-3", "is_private": true, "scm": "git"},
{"uuid": "{repo-4}", "name": "repo-4", "full_name": "ws/repo-4", "slug": "repo-4", "is_private": true, "scm": "git"},
{"uuid": "{repo-5}", "name": "repo-5", "full_name": "ws/repo-5", "slug": "repo-5", "is_private": false, "scm": "git"}
]
}"#;
const CONFIG_TOML: &str = r#"
[auth]
username = "testuser"
default_workspace = "myworkspace"
[defaults]
workspace = "myworkspace"
repository = "myrepo"
branch = "main"
[display]
color = true
pager = true
date_format = "%Y-%m-%d %H:%M"
"#;
fn create_sample_repository() -> Repository {
Repository {
uuid: "{12345678-1234-1234-1234-123456789abc}".to_string(),
name: "my-repo".to_string(),
full_name: "workspace/my-repo".to_string(),
slug: "my-repo".to_string(),
description: Some("A sample repository".to_string()),
is_private: true,
scm: "git".to_string(),
owner: None,
workspace: None,
project: None,
created_on: Some(chrono::Utc::now()),
updated_on: Some(chrono::Utc::now()),
size: Some(1024000),
language: Some("Rust".to_string()),
has_issues: Some(true),
has_wiki: Some(false),
fork_policy: Some("no_public_forks".to_string()),
mainbranch: Some(Branch {
name: "main".to_string(),
branch_type: Some("branch".to_string()),
}),
links: None,
}
}
fn create_sample_issue() -> Issue {
Issue {
id: 123,
title: "Bug: Application crashes on startup".to_string(),
content: Some(IssueContent {
raw: Some("Description of the issue".to_string()),
markup: Some("markdown".to_string()),
html: None,
}),
reporter: Some(User {
uuid: "{user-uuid}".to_string(),
username: Some("reporter".to_string()),
display_name: "Bug Reporter".to_string(),
account_id: Some("account123".to_string()),
user_type: "user".to_string(),
links: None,
}),
assignee: None,
state: IssueState::Open,
kind: IssueKind::Bug,
priority: IssuePriority::Critical,
milestone: None,
component: None,
version: None,
votes: Some(15),
watches: Some(8),
created_on: chrono::Utc::now(),
updated_on: Some(chrono::Utc::now()),
edited_on: None,
links: None,
}
}
fn create_sample_credential() -> Credential {
Credential::ApiKey {
username: "testuser".to_string(),
api_key: "super_secret_api_key_12345".to_string(),
}
}
fn bench_json_deserialization(c: &mut Criterion) {
let mut group = c.benchmark_group("json_deserialization");
group.throughput(Throughput::Bytes(REPOSITORY_JSON.len() as u64));
group.bench_function("repository", |b| {
b.iter(|| {
let repo: Repository = serde_json::from_str(black_box(REPOSITORY_JSON)).unwrap();
black_box(repo)
})
});
group.throughput(Throughput::Bytes(PULL_REQUEST_JSON.len() as u64));
group.bench_function("pull_request", |b| {
b.iter(|| {
let pr: PullRequest = serde_json::from_str(black_box(PULL_REQUEST_JSON)).unwrap();
black_box(pr)
})
});
group.throughput(Throughput::Bytes(ISSUE_JSON.len() as u64));
group.bench_function("issue", |b| {
b.iter(|| {
let issue: Issue = serde_json::from_str(black_box(ISSUE_JSON)).unwrap();
black_box(issue)
})
});
group.throughput(Throughput::Bytes(PAGINATED_REPOS_JSON.len() as u64));
group.bench_function("paginated_repositories", |b| {
b.iter(|| {
let paginated: Paginated<Repository> =
serde_json::from_str(black_box(PAGINATED_REPOS_JSON)).unwrap();
black_box(paginated)
})
});
group.finish();
}
fn bench_json_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("json_serialization");
let repo = create_sample_repository();
group.bench_function("repository", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&repo)).unwrap();
black_box(json)
})
});
let issue = create_sample_issue();
group.bench_function("issue", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&issue)).unwrap();
black_box(json)
})
});
group.finish();
}
fn bench_auth_header(c: &mut Criterion) {
let mut group = c.benchmark_group("auth");
let oauth = Credential::OAuth {
access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ".to_string(),
refresh_token: Some("refresh_token_value".to_string()),
expires_at: Some(chrono::Utc::now().timestamp() + 3600),
client_id: Some("test_client_id".to_string()),
client_secret: Some("test_client_secret".to_string()),
};
let api_key = Credential::ApiKey {
username: "testuser".to_string(),
api_key: "api_key_secret_value".to_string(),
};
group.bench_function("oauth_header", |b| {
b.iter(|| {
let header = black_box(&oauth).auth_header();
black_box(header)
})
});
group.bench_function("api_key_header", |b| {
b.iter(|| {
let header = black_box(&api_key).auth_header();
black_box(header)
})
});
group.bench_function("needs_refresh_check", |b| {
b.iter(|| {
let needs = black_box(&oauth).needs_refresh();
black_box(needs)
})
});
group.finish();
}
fn bench_config_parsing(c: &mut Criterion) {
let mut group = c.benchmark_group("config");
group.throughput(Throughput::Bytes(CONFIG_TOML.len() as u64));
group.bench_function("toml_parse", |b| {
b.iter(|| {
let config: Config = toml::from_str(black_box(CONFIG_TOML)).unwrap();
black_box(config)
})
});
let config = Config::default();
group.bench_function("toml_serialize", |b| {
b.iter(|| {
let toml_str = toml::to_string(black_box(&config)).unwrap();
black_box(toml_str)
})
});
group.finish();
}
fn bench_credential_serialization(c: &mut Criterion) {
let mut group = c.benchmark_group("credential_serialization");
let credential = create_sample_credential();
let credential_json = serde_json::to_string(&credential).unwrap();
group.bench_function("serialize", |b| {
b.iter(|| {
let json = serde_json::to_string(black_box(&credential)).unwrap();
black_box(json)
})
});
group.bench_function("deserialize", |b| {
b.iter(|| {
let cred: Credential = serde_json::from_str(black_box(&credential_json)).unwrap();
black_box(cred)
})
});
group.finish();
}
fn bench_payload_sizes(c: &mut Criterion) {
let mut group = c.benchmark_group("payload_sizes");
for size in [1, 10, 50, 100].iter() {
let repos: Vec<Repository> = (0..*size)
.map(|i| Repository {
uuid: format!("{{uuid-{}}}", i),
name: format!("repo-{}", i),
full_name: format!("workspace/repo-{}", i),
slug: format!("repo-{}", i),
description: Some(format!("Description for repo {}", i)),
is_private: i % 2 == 0,
scm: "git".to_string(),
owner: None,
workspace: None,
project: None,
created_on: Some(chrono::Utc::now()),
updated_on: Some(chrono::Utc::now()),
size: Some(1024 * (i as u64 + 1)),
language: Some("Rust".to_string()),
has_issues: Some(true),
has_wiki: Some(false),
fork_policy: None,
mainbranch: None,
links: None,
})
.collect();
let paginated = Paginated {
size: Some(*size as u32),
page: Some(1),
pagelen: Some(*size as u32),
next: None,
previous: None,
values: repos,
};
let json = serde_json::to_string(&paginated).unwrap();
group.throughput(Throughput::Elements(*size as u64));
group.bench_with_input(
BenchmarkId::new("deserialize_repos", size),
&json,
|b, json| {
b.iter(|| {
let p: Paginated<Repository> = serde_json::from_str(black_box(json)).unwrap();
black_box(p)
})
},
);
group.bench_with_input(
BenchmarkId::new("serialize_repos", size),
&paginated,
|b, paginated| {
b.iter(|| {
let json = serde_json::to_string(black_box(paginated)).unwrap();
black_box(json)
})
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_json_deserialization,
bench_json_serialization,
bench_auth_header,
bench_config_parsing,
bench_credential_serialization,
bench_payload_sizes,
);
criterion_main!(benches);