Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/api_client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::fmt::Display;

use crate::executor::ExecutorName;
use crate::prelude::*;
use crate::run_environment::RepositoryProvider;
use crate::{app::Cli, config::CodSpeedConfig};
Expand Down Expand Up @@ -138,13 +139,14 @@ pub struct FetchLocalRunReportHeadReport {

#[derive(Debug, Deserialize, Serialize)]
pub struct FetchLocalRunBenchmarkResult {
pub time: f64,
pub value: f64,
pub benchmark: FetchLocalRunBenchmark,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct FetchLocalRunBenchmark {
pub name: String,
pub executor: ExecutorName,
}

nest! {
Expand Down
11 changes: 8 additions & 3 deletions src/exec/poll_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::api_client::{
};
use crate::prelude::*;
use crate::run::helpers::poll_results::{
POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table,
POLLING_INTERVAL, RUN_PROCESSING_MAX_DURATION, build_benchmark_table, build_detailed_summary,
};
use crate::run::uploader::UploadResult;

Expand Down Expand Up @@ -59,8 +59,13 @@ pub async fn poll_results(
if !response.run.results.is_empty() {
start_group!("Benchmark results");

let table = build_benchmark_table(&response.run.results);
info!("\n{table}");
if response.run.results.len() == 1 {
let summary = build_detailed_summary(&response.run.results[0]);
info!("\n{summary}");
} else {
let table = build_benchmark_table(&response.run.results);
info!("\n{table}");
}

end_group!();
}
Expand Down
5 changes: 3 additions & 2 deletions src/queries/FetchLocalRunReport.gql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ query FetchLocalRunReport($owner: String!, $name: String!, $runId: String!) {
allowedRegression
}
run(id: $runId) {
id
id
status
url
headReports {
Expand All @@ -13,9 +13,10 @@ query FetchLocalRunReport($owner: String!, $name: String!, $runId: String!) {
conclusion
}
results {
time
value
benchmark {
name
executor
}
}
}
Expand Down
84 changes: 84 additions & 0 deletions src/run/helpers/format_memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const BASE: f64 = 1024.0;
const UNITS: [&str; 6] = ["B", "KB", "MB", "GB", "TB", "PB"];

fn get_unit_index(bytes: f64) -> usize {
if bytes == 0.0 {
return 0;
}
let index = (bytes.ln() / BASE.ln()).floor() as usize;
index.min(UNITS.len() - 1)
}

fn format_shifted_value(value: f64, fraction_digits: usize) -> String {
let formatted_value = format!("{value:.fraction_digits$}");

if fraction_digits > 0 {
formatted_value
.trim_end_matches('0')
.trim_end_matches('.')
.to_owned()
} else {
formatted_value
}
}

pub(crate) fn format_memory(bytes: f64, fraction_digits: Option<usize>) -> String {
let fraction_digits = fraction_digits.unwrap_or(1); // Default to 1 decimal place

if bytes == 0.0 {
return "0 B".to_string();
}

let unit_index = get_unit_index(bytes);
let unit = UNITS[unit_index];
let shifted_value = bytes / BASE.powi(unit_index as i32);

format!(
"{} {}",
format_shifted_value(shifted_value, fraction_digits),
unit
)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_format_memory_bytes() {
assert_eq!(format_memory(100.0, None), "100 B");
assert_eq!(format_memory(100.0, Some(0)), "100 B");
assert_eq!(format_memory(123.45, Some(2)), "123.45 B");
}

#[test]
fn test_format_memory_kilobytes() {
assert_eq!(format_memory(1024.0, None), "1 KB");
assert_eq!(format_memory(1024.0, Some(0)), "1 KB");
assert_eq!(format_memory(1536.0, Some(1)), "1.5 KB");
assert_eq!(format_memory(1263.0, Some(2)), "1.23 KB");
}

#[test]
fn test_format_memory_megabytes() {
assert_eq!(format_memory(1048576.0, None), "1 MB"); // 1024^2
assert_eq!(format_memory(1258291.0, Some(2)), "1.2 MB");
}

#[test]
fn test_format_memory_gigabytes() {
assert_eq!(format_memory(1073741824.0, None), "1 GB"); // 1024^3
assert_eq!(format_memory(1288490188.8, Some(2)), "1.2 GB");
}

#[test]
fn test_format_memory_zero() {
assert_eq!(format_memory(0.0, None), "0 B");
}

#[test]
fn test_format_memory_no_trailing_zeros() {
assert_eq!(format_memory(2048.0, None), "2 KB"); // Should be "2 KB" not "2.0 KB"
assert_eq!(format_memory(3072.0, None), "3 KB"); // Should be "3 KB" not "3.0 KB"
}
}
2 changes: 2 additions & 0 deletions src/run/helpers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
mod download_file;
mod find_repository_root;
mod format_duration;
mod format_memory;
mod get_env_var;
mod parse_git_remote;
pub(crate) mod poll_results;

pub(crate) use download_file::download_file;
pub(crate) use find_repository_root::find_repository_root;
pub(crate) use format_duration::format_duration;
pub(crate) use format_memory::format_memory;
pub(crate) use get_env_var::get_env_variable;
pub(crate) use parse_git_remote::*;
73 changes: 53 additions & 20 deletions src/run/helpers/poll_results.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::api_client::FetchLocalRunBenchmarkResult;
use crate::executor::ExecutorName;
use crate::run::helpers;
use std::time::Duration;
use tabled::settings::Style;
Expand All @@ -6,68 +8,99 @@ use tabled::{Table, Tabled};
pub const RUN_PROCESSING_MAX_DURATION: Duration = Duration::from_secs(60 * 5); // 5 minutes
pub const POLLING_INTERVAL: Duration = Duration::from_secs(1);

fn format_measurement(value: f64, executor: &ExecutorName) -> String {
match executor {
ExecutorName::Memory => helpers::format_memory(value, Some(1)),
ExecutorName::Valgrind | ExecutorName::WallTime => helpers::format_duration(value, Some(2)),
}
}

#[derive(Tabled)]
struct BenchmarkRow {
#[tabled(rename = "Benchmark")]
name: String,
#[tabled(rename = "Time")]
time: String,
#[tabled(rename = "Measurement")]
measurement: String,
}

pub fn build_benchmark_table(
results: &[crate::api_client::FetchLocalRunBenchmarkResult],
) -> String {
pub fn build_benchmark_table(results: &[FetchLocalRunBenchmarkResult]) -> String {
let table_rows: Vec<BenchmarkRow> = results
.iter()
.map(|result| BenchmarkRow {
name: result.benchmark.name.clone(),
time: helpers::format_duration(result.time, Some(2)),
measurement: format_measurement(result.value, &result.benchmark.executor),
})
.collect();

Table::new(&table_rows).with(Style::modern()).to_string()
}

pub fn build_detailed_summary(result: &FetchLocalRunBenchmarkResult) -> String {
format!(
"{}: {}",
result.benchmark.name,
format_measurement(result.value, &result.benchmark.executor)
)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::api_client::{FetchLocalRunBenchmark, FetchLocalRunBenchmarkResult};
use crate::{api_client::FetchLocalRunBenchmark, executor::ExecutorName};

#[test]
fn test_benchmark_table_formatting() {
let results = vec![
FetchLocalRunBenchmarkResult {
benchmark: FetchLocalRunBenchmark {
name: "benchmark_fast".to_string(),
executor: ExecutorName::Valgrind,
},
time: 0.001234, // 1.23 ms
value: 0.001234, // 1.23 ms
},
FetchLocalRunBenchmarkResult {
benchmark: FetchLocalRunBenchmark {
name: "benchmark_slow".to_string(),
executor: ExecutorName::WallTime,
},
time: 1.5678, // 1.57 s
value: 1.5678, // 1.57 s
},
FetchLocalRunBenchmarkResult {
benchmark: FetchLocalRunBenchmark {
name: "benchmark_medium".to_string(),
name: "benchmark_memory".to_string(),
executor: ExecutorName::Memory,
},
time: 0.000567, // 567 µs
value: 2097152.0, // 2 MB (2 * 1024^2)
},
];

let table = build_benchmark_table(&results);

insta::assert_snapshot!(table, @r###"
┌──────────────────┬───────────┐
│ Benchmark │ Time
├──────────────────┼───────────┤
│ benchmark_fast │ 1.23 ms │
├──────────────────┼───────────┤
│ benchmark_slow │ 1.57 s │
├──────────────────┼───────────┤
benchmark_medium567.00 µs
└──────────────────┴───────────┘
┌──────────────────┬─────────────
│ Benchmark │ Measurement
├──────────────────┼─────────────
│ benchmark_fast │ 1.23 ms
├──────────────────┼─────────────
│ benchmark_slow │ 1.57 s
├──────────────────┼─────────────
benchmark_memory2 MB
└──────────────────┴─────────────
"###);
}

#[test]
fn test_detailed_summary_formatting() {
let result = FetchLocalRunBenchmarkResult {
benchmark: FetchLocalRunBenchmark {
name: "benchmark_fast".to_string(),
executor: ExecutorName::Valgrind,
},
value: 0.001234, // 1.23 ms
};

let summary = build_detailed_summary(&result);

insta::assert_snapshot!(summary, @"benchmark_fast: 1.23 ms");
}
}
2 changes: 1 addition & 1 deletion src/run/poll_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub async fn poll_results(
for result in response.run.results {
log_json!(format!(
"{{\"event\": \"benchmark_ran\", \"name\": \"{}\", \"time\": \"{}\"}}",
result.benchmark.name, result.time,
result.benchmark.name, result.value
));
}
}
Expand Down
Loading