Skip to content

Commit 94d870a

Browse files
author
dcbuilder.eth
committed
fix: address open PR review threads
1 parent 51eb84b commit 94d870a

File tree

3 files changed

+227
-31
lines changed

3 files changed

+227
-31
lines changed

.github/actions/mobench/action.yml

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,40 +152,69 @@ runs:
152152
- name: Install mobench
153153
if: inputs.install-mobench == 'true'
154154
shell: bash
155+
env:
156+
MOBENCH_VERSION: ${{ inputs.mobench-version }}
155157
run: |
156158
if command -v cargo-mobench >/dev/null 2>&1 || command -v mobench >/dev/null 2>&1; then
157159
exit 0
158160
fi
159-
version_flag=""
160-
if [ -n "${{ inputs.mobench-version }}" ]; then
161-
version_flag="--version ${{ inputs.mobench-version }}"
161+
install_args=()
162+
if [ -n "$MOBENCH_VERSION" ]; then
163+
install_args=(--version "$MOBENCH_VERSION")
162164
fi
163165
if command -v cargo-binstall >/dev/null 2>&1; then
164-
cargo binstall -y mobench $version_flag
166+
cargo binstall -y mobench "${install_args[@]}"
165167
else
166168
cargo install cargo-binstall
167-
cargo binstall -y mobench $version_flag
169+
cargo binstall -y mobench "${install_args[@]}"
168170
fi
169171
170172
- name: Run mobench
171173
shell: bash
174+
env:
175+
ANDROID_SDK_ROOT_INPUT: ${{ inputs.android-sdk-root }}
176+
NDK_VERSION_INPUT: ${{ inputs.ndk-version }}
177+
MOBENCH_CI: ${{ inputs.ci }}
178+
MOBENCH_COMMAND: ${{ inputs.command }}
179+
MOBENCH_RUN_ARGS: ${{ inputs.run-args }}
172180
run: |
173-
export ANDROID_SDK_ROOT="${{ inputs.android-sdk-root }}"
174-
export ANDROID_HOME="${{ inputs.android-sdk-root }}"
175-
export ANDROID_NDK_HOME="${{ inputs.android-sdk-root }}/ndk/${{ inputs.ndk-version }}"
176-
extra_args=""
177-
if [ "${{ inputs.ci }}" = "true" ] && [ "${{ inputs.command }}" = "cargo mobench run" ]; then
178-
extra_args="--ci"
181+
export ANDROID_SDK_ROOT="$ANDROID_SDK_ROOT_INPUT"
182+
export ANDROID_HOME="$ANDROID_SDK_ROOT_INPUT"
183+
export ANDROID_NDK_HOME="$ANDROID_SDK_ROOT_INPUT/ndk/$NDK_VERSION_INPUT"
184+
extra_args=()
185+
if [ "$MOBENCH_CI" = "true" ] && [ "$MOBENCH_COMMAND" = "cargo mobench run" ]; then
186+
extra_args=(--ci)
187+
fi
188+
case "$MOBENCH_COMMAND" in
189+
"cargo mobench run"|"cargo mobench ci run")
190+
;;
191+
*)
192+
echo "Unsupported command input: $MOBENCH_COMMAND"
193+
exit 1
194+
;;
195+
esac
196+
read -r -a command_parts <<< "$MOBENCH_COMMAND"
197+
run_args=()
198+
if [ -n "$MOBENCH_RUN_ARGS" ]; then
199+
while IFS= read -r line; do
200+
if [ -z "$line" ]; then
201+
continue
202+
fi
203+
read -r -a parts <<< "$line"
204+
run_args+=("${parts[@]}")
205+
done <<< "$MOBENCH_RUN_ARGS"
179206
fi
180-
${{ inputs.command }} $extra_args ${{ inputs.run-args }}
207+
"${command_parts[@]}" "${extra_args[@]}" "${run_args[@]}"
181208
182209
- name: Publish sticky PR comment
183210
if: always() && inputs.pr-comment == 'true'
184211
shell: bash
185212
env:
186213
GITHUB_TOKEN: ${{ inputs.github-token }}
214+
PR_NUMBER_INPUT: ${{ inputs.pr-number }}
215+
PR_COMMENT_MARKER: ${{ inputs.pr-comment-marker }}
187216
run: |
188-
pr_number="${{ inputs.pr-number }}"
217+
pr_number="$PR_NUMBER_INPUT"
189218
if [ -z "$pr_number" ] && [ -n "${GITHUB_REF:-}" ]; then
190219
pr_number="$(echo "$GITHUB_REF" | awk -F/ '/^refs\/pull\// { print $3 }')"
191220
fi
@@ -204,7 +233,7 @@ runs:
204233
cargo mobench report github \
205234
--pr "$pr_number" \
206235
--summary target/mobench/ci/summary.json \
207-
--marker "${{ inputs.pr-comment-marker }}" \
236+
--marker "$PR_COMMENT_MARKER" \
208237
--publish
209238
210239
- name: Upload artifacts

crates/mobench/src/lib.rs

Lines changed: 141 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,17 +1245,44 @@ pub fn run() -> Result<()> {
12451245
println!("No BrowserStack run to fetch (devices not provided?)");
12461246
}
12471247

1248+
let mut baseline_compare_path = None;
1249+
let mut baseline_snapshot_path = None;
1250+
if let Some(baseline_source) = baseline.as_deref() {
1251+
let resolved_baseline = resolve_baseline_source(baseline_source)?;
1252+
if paths_point_to_same_file(&resolved_baseline, &summary_paths.json)? {
1253+
if !resolved_baseline.exists() {
1254+
bail!(
1255+
"config_error: baseline source `{}` resolves to output path {}; provide an existing baseline file or a different path",
1256+
baseline_source,
1257+
summary_paths.json.display()
1258+
);
1259+
}
1260+
let snapshot = snapshot_baseline_for_compare(&resolved_baseline)?;
1261+
baseline_snapshot_path = Some(snapshot.clone());
1262+
baseline_compare_path = Some(snapshot);
1263+
} else {
1264+
baseline_compare_path = Some(resolved_baseline);
1265+
}
1266+
}
1267+
12481268
run_summary.summary = build_summary(&run_summary)?;
12491269
write_summary(&run_summary, &summary_paths, summary_csv)?;
12501270

12511271
let mut compare_report = None;
12521272
let mut regression_findings: Vec<RegressionFinding> = Vec::new();
1253-
if let Some(baseline_source) = baseline.as_deref() {
1254-
let baseline_path = resolve_baseline_source(baseline_source)?;
1273+
if let Some(baseline_path) = baseline_compare_path.as_deref() {
12551274
let report = compare_summaries(&baseline_path, &summary_paths.json)?;
12561275
regression_findings = detect_regressions(&report, regression_threshold_pct);
12571276
compare_report = Some(report);
12581277
}
1278+
if let Some(snapshot_path) = baseline_snapshot_path {
1279+
if let Err(err) = fs::remove_file(&snapshot_path) {
1280+
eprintln!(
1281+
"Warning: failed to remove baseline snapshot {}: {err}",
1282+
snapshot_path.display()
1283+
);
1284+
}
1285+
}
12591286
if let Some(report) = &compare_report {
12601287
inject_compare_into_summary(
12611288
&summary_paths.json,
@@ -2450,7 +2477,8 @@ fn resolve_run_spec(
24502477
) -> Result<RunSpec> {
24512478
if let Some(cfg_path) = config {
24522479
let cfg = load_config(cfg_path)?;
2453-
let matrix = load_device_matrix(&cfg.device_matrix)?;
2480+
let matrix_path = device_matrix.unwrap_or(cfg.device_matrix.as_path());
2481+
let matrix = load_device_matrix(matrix_path)?;
24542482
let resolved_tags = if !device_tags.is_empty() {
24552483
Some(device_tags)
24562484
} else {
@@ -3717,6 +3745,37 @@ fn resolve_baseline_source(source: &str) -> Result<PathBuf> {
37173745
Ok(PathBuf::from(trimmed))
37183746
}
37193747

3748+
fn normalized_path(path: &Path) -> Result<PathBuf> {
3749+
let absolute = if path.is_absolute() {
3750+
path.to_path_buf()
3751+
} else {
3752+
env::current_dir()
3753+
.context("resolving current directory for baseline path comparison")?
3754+
.join(path)
3755+
};
3756+
Ok(fs::canonicalize(&absolute).unwrap_or(absolute))
3757+
}
3758+
3759+
fn paths_point_to_same_file(lhs: &Path, rhs: &Path) -> Result<bool> {
3760+
Ok(normalized_path(lhs)? == normalized_path(rhs)?)
3761+
}
3762+
3763+
fn snapshot_baseline_for_compare(path: &Path) -> Result<PathBuf> {
3764+
let stamp = SystemTime::now()
3765+
.duration_since(UNIX_EPOCH)
3766+
.unwrap_or_else(|_| Duration::from_secs(0))
3767+
.as_nanos();
3768+
let snapshot_path = env::temp_dir().join(format!("mobench-baseline-{stamp}.json"));
3769+
fs::copy(path, &snapshot_path).with_context(|| {
3770+
format!(
3771+
"copying baseline snapshot from {} to {}",
3772+
path.display(),
3773+
snapshot_path.display()
3774+
)
3775+
})?;
3776+
Ok(snapshot_path)
3777+
}
3778+
37203779
fn resolve_artifact_baseline(reference: &str) -> Result<PathBuf> {
37213780
if reference.is_empty() {
37223781
bail!("config_error: baseline artifact reference is empty");
@@ -6692,6 +6751,7 @@ fn check_xcodegen() -> PrereqCheck {
66926751
mod tests {
66936752
use super::*;
66946753
use jsonschema::JSONSchema;
6754+
use tempfile::TempDir;
66956755

66966756
// Register a lightweight benchmark for tests so the inventory contains at least one entry.
66976757
#[mobench_sdk::benchmark]
@@ -6724,6 +6784,84 @@ mod tests {
67246784
assert!(spec.ios_xcuitest.is_none());
67256785
}
67266786

6787+
#[test]
6788+
fn resolve_run_spec_prefers_cli_device_matrix_with_config() {
6789+
let temp_dir = TempDir::new().expect("temp dir");
6790+
let config_matrix_path = temp_dir.path().join("config-matrix.yml");
6791+
let cli_matrix_path = temp_dir.path().join("cli-matrix.yml");
6792+
let config_path = temp_dir.path().join("bench-config.toml");
6793+
6794+
write_file(
6795+
&config_matrix_path,
6796+
br#"devices:
6797+
- name: Config Device
6798+
os: android
6799+
os_version: "14"
6800+
"#,
6801+
)
6802+
.expect("write config matrix");
6803+
write_file(
6804+
&cli_matrix_path,
6805+
br#"devices:
6806+
- name: CLI Device
6807+
os: android
6808+
os_version: "14"
6809+
"#,
6810+
)
6811+
.expect("write cli matrix");
6812+
6813+
let config_toml = format!(
6814+
r#"target = "android"
6815+
function = "sample_fns::fibonacci"
6816+
iterations = 10
6817+
warmup = 2
6818+
device_matrix = "{}"
6819+
6820+
[browserstack]
6821+
app_automate_username = "user"
6822+
app_automate_access_key = "key"
6823+
project = "proj"
6824+
"#,
6825+
config_matrix_path.display()
6826+
);
6827+
write_file(&config_path, config_toml.as_bytes()).expect("write config");
6828+
6829+
let spec = resolve_run_spec(
6830+
MobileTarget::Android,
6831+
"ignored::value".into(),
6832+
1,
6833+
0,
6834+
Vec::new(),
6835+
Some(config_path.as_path()),
6836+
Some(cli_matrix_path.as_path()),
6837+
Vec::new(),
6838+
None,
6839+
None,
6840+
false,
6841+
false,
6842+
)
6843+
.expect("resolve spec");
6844+
6845+
assert_eq!(spec.devices, vec!["CLI Device".to_string()]);
6846+
}
6847+
6848+
#[test]
6849+
fn snapshot_baseline_creates_distinct_copy() {
6850+
let temp_dir = TempDir::new().expect("temp dir");
6851+
let baseline = temp_dir.path().join("baseline.json");
6852+
write_file(&baseline, br#"{"ok":true}"#).expect("write baseline");
6853+
6854+
assert!(paths_point_to_same_file(&baseline, &baseline).expect("compare path"));
6855+
6856+
let snapshot = snapshot_baseline_for_compare(&baseline).expect("snapshot baseline");
6857+
assert_ne!(snapshot, baseline);
6858+
let original_contents = fs::read_to_string(&baseline).expect("read baseline");
6859+
let snapshot_contents = fs::read_to_string(&snapshot).expect("read snapshot");
6860+
assert_eq!(snapshot_contents, original_contents);
6861+
6862+
fs::remove_file(snapshot).expect("remove snapshot");
6863+
}
6864+
67276865
#[test]
67286866
fn local_smoke_produces_samples() {
67296867
let spec = RunSpec {

crates/mobench/templates/ci/action.yml

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -152,40 +152,69 @@ runs:
152152
- name: Install mobench
153153
if: inputs.install-mobench == 'true'
154154
shell: bash
155+
env:
156+
MOBENCH_VERSION: ${{ inputs.mobench-version }}
155157
run: |
156158
if command -v cargo-mobench >/dev/null 2>&1 || command -v mobench >/dev/null 2>&1; then
157159
exit 0
158160
fi
159-
version_flag=""
160-
if [ -n "${{ inputs.mobench-version }}" ]; then
161-
version_flag="--version ${{ inputs.mobench-version }}"
161+
install_args=()
162+
if [ -n "$MOBENCH_VERSION" ]; then
163+
install_args=(--version "$MOBENCH_VERSION")
162164
fi
163165
if command -v cargo-binstall >/dev/null 2>&1; then
164-
cargo binstall -y mobench $version_flag
166+
cargo binstall -y mobench "${install_args[@]}"
165167
else
166168
cargo install cargo-binstall
167-
cargo binstall -y mobench $version_flag
169+
cargo binstall -y mobench "${install_args[@]}"
168170
fi
169171
170172
- name: Run mobench
171173
shell: bash
174+
env:
175+
ANDROID_SDK_ROOT_INPUT: ${{ inputs.android-sdk-root }}
176+
NDK_VERSION_INPUT: ${{ inputs.ndk-version }}
177+
MOBENCH_CI: ${{ inputs.ci }}
178+
MOBENCH_COMMAND: ${{ inputs.command }}
179+
MOBENCH_RUN_ARGS: ${{ inputs.run-args }}
172180
run: |
173-
export ANDROID_SDK_ROOT="${{ inputs.android-sdk-root }}"
174-
export ANDROID_HOME="${{ inputs.android-sdk-root }}"
175-
export ANDROID_NDK_HOME="${{ inputs.android-sdk-root }}/ndk/${{ inputs.ndk-version }}"
176-
extra_args=""
177-
if [ "${{ inputs.ci }}" = "true" ] && [ "${{ inputs.command }}" = "cargo mobench run" ]; then
178-
extra_args="--ci"
181+
export ANDROID_SDK_ROOT="$ANDROID_SDK_ROOT_INPUT"
182+
export ANDROID_HOME="$ANDROID_SDK_ROOT_INPUT"
183+
export ANDROID_NDK_HOME="$ANDROID_SDK_ROOT_INPUT/ndk/$NDK_VERSION_INPUT"
184+
extra_args=()
185+
if [ "$MOBENCH_CI" = "true" ] && [ "$MOBENCH_COMMAND" = "cargo mobench run" ]; then
186+
extra_args=(--ci)
187+
fi
188+
case "$MOBENCH_COMMAND" in
189+
"cargo mobench run"|"cargo mobench ci run")
190+
;;
191+
*)
192+
echo "Unsupported command input: $MOBENCH_COMMAND"
193+
exit 1
194+
;;
195+
esac
196+
read -r -a command_parts <<< "$MOBENCH_COMMAND"
197+
run_args=()
198+
if [ -n "$MOBENCH_RUN_ARGS" ]; then
199+
while IFS= read -r line; do
200+
if [ -z "$line" ]; then
201+
continue
202+
fi
203+
read -r -a parts <<< "$line"
204+
run_args+=("${parts[@]}")
205+
done <<< "$MOBENCH_RUN_ARGS"
179206
fi
180-
${{ inputs.command }} $extra_args ${{ inputs.run-args }}
207+
"${command_parts[@]}" "${extra_args[@]}" "${run_args[@]}"
181208
182209
- name: Publish sticky PR comment
183210
if: always() && inputs.pr-comment == 'true'
184211
shell: bash
185212
env:
186213
GITHUB_TOKEN: ${{ inputs.github-token }}
214+
PR_NUMBER_INPUT: ${{ inputs.pr-number }}
215+
PR_COMMENT_MARKER: ${{ inputs.pr-comment-marker }}
187216
run: |
188-
pr_number="${{ inputs.pr-number }}"
217+
pr_number="$PR_NUMBER_INPUT"
189218
if [ -z "$pr_number" ] && [ -n "${GITHUB_REF:-}" ]; then
190219
pr_number="$(echo "$GITHUB_REF" | awk -F/ '/^refs\/pull\// { print $3 }')"
191220
fi
@@ -204,7 +233,7 @@ runs:
204233
cargo mobench report github \
205234
--pr "$pr_number" \
206235
--summary target/mobench/ci/summary.json \
207-
--marker "${{ inputs.pr-comment-marker }}" \
236+
--marker "$PR_COMMENT_MARKER" \
208237
--publish
209238
210239
- name: Upload artifacts

0 commit comments

Comments
 (0)