Skip to content

Commit f8db49d

Browse files
karthiknadigCopilot
andcommitted
test: account for every ambient refresh observation
Include churn observations in ambient maxima and reject overlap counts larger than their maxima. Address PR #563 review feedback without changing workload or coverage budgets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent de44782 commit f8db49d

4 files changed

Lines changed: 86 additions & 29 deletions

File tree

‎crates/pet/tests/session_performance.rs‎

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,19 @@ struct RefreshMeasurement {
4747
ambient_manager_count: usize,
4848
}
4949

50+
#[derive(Default)]
51+
struct AmbientCountPeak {
52+
environment_count: usize,
53+
manager_count: usize,
54+
}
55+
56+
impl AmbientCountPeak {
57+
fn observe(&mut self, environment_count: usize, manager_count: usize) {
58+
self.environment_count = self.environment_count.max(environment_count);
59+
self.manager_count = self.manager_count.max(manager_count);
60+
}
61+
}
62+
5063
struct Fixture {
5164
_root: TempDir,
5265
workspace: PathBuf,
@@ -426,7 +439,11 @@ fn first_result_timing_ignores_ambient_environments_and_managers() {
426439
);
427440
}
428441

429-
fn refresh_and_measure(client: &PetJsonRpcClient, workspace: &Path) -> RefreshMeasurement {
442+
fn refresh_and_measure(
443+
client: &PetJsonRpcClient,
444+
workspace: &Path,
445+
ambient_count_peak: &mut AmbientCountPeak,
446+
) -> RefreshMeasurement {
430447
client.clear_notifications();
431448
let timing = client
432449
.refresh_with_timing(Some(json!({ "searchPaths": [workspace] })))
@@ -448,13 +465,30 @@ fn refresh_and_measure(client: &PetJsonRpcClient, workspace: &Path) -> RefreshMe
448465
fixture_manager_count, 0,
449466
"fixture workspace unexpectedly reported an environment manager"
450467
);
451-
RefreshMeasurement {
468+
let measurement = RefreshMeasurement {
452469
inventory,
453470
round_trip_us: timing.round_trip.as_micros(),
454471
ttfe_us: ttfe.as_micros(),
455472
ambient_environment_count,
456473
ambient_manager_count,
457-
}
474+
};
475+
ambient_count_peak.observe(
476+
measurement.ambient_environment_count,
477+
measurement.ambient_manager_count,
478+
);
479+
measurement
480+
}
481+
482+
#[test]
483+
fn ambient_count_peak_retains_middle_refresh_maximum() {
484+
let mut peak = AmbientCountPeak::default();
485+
peak.observe(1, 2);
486+
peak.observe(7, 6);
487+
peak.observe(3, 4);
488+
peak.observe(2, 1);
489+
490+
assert_eq!(peak.environment_count, 7);
491+
assert_eq!(peak.manager_count, 6);
458492
}
459493

460494
fn directory_usage(root: &Path) -> (usize, u64) {
@@ -830,8 +864,7 @@ fn long_lived_session_benchmark() {
830864
let mut refresh_scenario_samples = Vec::new();
831865
let mut cache_usage = Vec::new();
832866
let mut inventory_resource_samples: Vec<(usize, ResourceSample)> = Vec::new();
833-
let mut max_ambient_environment_count = 0;
834-
let mut max_ambient_manager_count = 0;
867+
let mut ambient_count_peak = AmbientCountPeak::default();
835868

836869
let barrier = fixture.barrier.as_os_str();
837870
let python_path = fixture.python_path.as_os_str();
@@ -940,14 +973,12 @@ fn long_lived_session_benchmark() {
940973
"cacheDirectory": &fixture.cache,
941974
}))
942975
.expect("failed to configure first-process scenario server");
943-
let first = refresh_and_measure(&first_process, &fixture.workspace);
976+
let first =
977+
refresh_and_measure(&first_process, &fixture.workspace, &mut ambient_count_peak);
944978
assert_eq!(
945979
first.inventory, expected,
946980
"first-process refresh changed fixture identities at size {size}"
947981
);
948-
max_ambient_environment_count =
949-
max_ambient_environment_count.max(first.ambient_environment_count);
950-
max_ambient_manager_count = max_ambient_manager_count.max(first.ambient_manager_count);
951982
refresh_scenario_samples.push(json!({
952983
"scenario": "firstProcessEmptyDiskCache",
953984
"inventorySize": size,
@@ -966,14 +997,11 @@ fn long_lived_session_benchmark() {
966997
"cacheDirectory": &fixture.cache,
967998
}))
968999
.expect("failed to configure reused-cache scenario server");
969-
let reused = refresh_and_measure(&new_process, &fixture.workspace);
1000+
let reused = refresh_and_measure(&new_process, &fixture.workspace, &mut ambient_count_peak);
9701001
assert_eq!(
9711002
reused.inventory, expected,
9721003
"new-process refresh changed fixture identities at size {size}"
9731004
);
974-
max_ambient_environment_count =
975-
max_ambient_environment_count.max(reused.ambient_environment_count);
976-
max_ambient_manager_count = max_ambient_manager_count.max(reused.ambient_manager_count);
9771005
refresh_scenario_samples.push(json!({
9781006
"scenario": "newProcessAfterFirstRefresh",
9791007
"inventorySize": size,
@@ -985,14 +1013,12 @@ fn long_lived_session_benchmark() {
9851013
let mut warm_round_trip_us = Vec::with_capacity(samples_per_size);
9861014
let mut warm_ttfe_us = Vec::with_capacity(samples_per_size);
9871015
for _ in 0..samples_per_size {
988-
let warm = refresh_and_measure(&new_process, &fixture.workspace);
1016+
let warm =
1017+
refresh_and_measure(&new_process, &fixture.workspace, &mut ambient_count_peak);
9891018
assert_eq!(
9901019
warm.inventory, expected,
9911020
"same-process warm refresh changed fixture identities at size {size}"
9921021
);
993-
max_ambient_environment_count =
994-
max_ambient_environment_count.max(warm.ambient_environment_count);
995-
max_ambient_manager_count = max_ambient_manager_count.max(warm.ambient_manager_count);
9961022
warm_round_trip_us.push(warm.round_trip_us);
9971023
warm_ttfe_us.push(warm.ttfe_us);
9981024
}
@@ -1031,11 +1057,8 @@ fn long_lived_session_benchmark() {
10311057

10321058
let mut churn_expected = fixture.reset_inventory(10);
10331059
churn_expected.sort_unstable();
1034-
let initial = refresh_and_measure(&client, &fixture.workspace);
1060+
let initial = refresh_and_measure(&client, &fixture.workspace, &mut ambient_count_peak);
10351061
assert_eq!(initial.inventory, churn_expected);
1036-
max_ambient_environment_count =
1037-
max_ambient_environment_count.max(initial.ambient_environment_count);
1038-
max_ambient_manager_count = max_ambient_manager_count.max(initial.ambient_manager_count);
10391062

10401063
let removed_prefix = fixture.workspace.join("env-0000");
10411064
let removed_index = churn_expected
@@ -1046,7 +1069,7 @@ fn long_lived_session_benchmark() {
10461069
churn_expected.remove(removed_index);
10471070
churn_expected.push(fixture.create_fake_environment("replacement", "3.12.1"));
10481071
churn_expected.sort_unstable();
1049-
let replaced = refresh_and_measure(&client, &fixture.workspace);
1072+
let replaced = refresh_and_measure(&client, &fixture.workspace, &mut ambient_count_peak);
10501073
assert_eq!(replaced.inventory.len(), initial.inventory.len());
10511074
assert_ne!(
10521075
&replaced.inventory, &initial.inventory,
@@ -1074,7 +1097,7 @@ fn long_lived_session_benchmark() {
10741097
version: Some("3.13.2".to_string()),
10751098
});
10761099
churn_expected.sort_unstable();
1077-
let edited = refresh_and_measure(&client, &fixture.workspace);
1100+
let edited = refresh_and_measure(&client, &fixture.workspace, &mut ambient_count_peak);
10781101
assert_eq!(edited.inventory, churn_expected);
10791102

10801103
let alias_prefix = fixture.workspace.join("env-0002");
@@ -1095,7 +1118,7 @@ fn long_lived_session_benchmark() {
10951118
..previous
10961119
});
10971120
churn_expected.sort_unstable();
1098-
let aliased = refresh_and_measure(&client, &fixture.workspace);
1121+
let aliased = refresh_and_measure(&client, &fixture.workspace, &mut ambient_count_peak);
10991122
assert_eq!(aliased.inventory, churn_expected);
11001123

11011124
fixture.clear_barrier();
@@ -1176,9 +1199,10 @@ fn long_lived_session_benchmark() {
11761199
fixture_manager_count, 0,
11771200
"overlap fixture unexpectedly reported an environment manager"
11781201
);
1179-
max_ambient_environment_count =
1180-
max_ambient_environment_count.max(overlap_ambient_environment_count);
1181-
max_ambient_manager_count = max_ambient_manager_count.max(overlap_ambient_manager_count);
1202+
ambient_count_peak.observe(
1203+
overlap_ambient_environment_count,
1204+
overlap_ambient_manager_count,
1205+
);
11821206
let cache_after_overlap =
11831207
cache_contents(&fixture.cache).expect("failed to capture post-overlap cache contents");
11841208
assert!(
@@ -1325,8 +1349,8 @@ fn long_lived_session_benchmark() {
13251349
"overlapProcessesStarted": resolve_concurrency,
13261350
"overlapAmbientEnvironmentCount": overlap_ambient_environment_count,
13271351
"overlapAmbientManagerCount": overlap_ambient_manager_count,
1328-
"maxAmbientEnvironmentCount": max_ambient_environment_count,
1329-
"maxAmbientManagerCount": max_ambient_manager_count,
1352+
"maxAmbientEnvironmentCount": ambient_count_peak.environment_count,
1353+
"maxAmbientManagerCount": ambient_count_peak.manager_count,
13301354
"latencyProcessesStarted": resolve_concurrency * cold_resolve_batches,
13311355
"inventoryResourceSamples": inventory_resource_samples
13321356
.iter()

‎docs/SESSION_BENCHMARKS.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ Platform-global locators may also report host installations and managers. The
7878
benchmark converts only configured workspace entries to strict fixture
7979
identities, validates that fixture-scoped managers remain empty, and records
8080
only counts of unrelated global discoveries and managers, never their paths.
81+
The reported ambient maxima include every timed, churn, and overlap refresh;
82+
artifact validation rejects overlap counts above those maxima.
8183
Two fast or five stress pre-released batches then use fresh, distinct
8284
interpreters for unobstructed client-latency and resource-cycling samples.
8385
Every warm-up, overlap, and latency response is paired with its submitted

‎scripts/session_metrics.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,15 @@ def validate_success_metrics(value: Any, expected_mode: str) -> dict[str, Any]:
307307
raise MetricsError("overlapProcessesStarted does not match resolveConcurrency")
308308
if metrics["latencyProcessesStarted"] != expected_concurrency * expected_batches:
309309
raise MetricsError("latencyProcessesStarted does not match resolve batch work")
310+
for overlap_name, maximum_name in (
311+
("overlapAmbientEnvironmentCount", "maxAmbientEnvironmentCount"),
312+
("overlapAmbientManagerCount", "maxAmbientManagerCount"),
313+
):
314+
if metrics[overlap_name] > metrics[maximum_name]:
315+
raise MetricsError(
316+
f"{overlap_name} must not exceed {maximum_name}; "
317+
f"got {metrics[overlap_name]} and {metrics[maximum_name]}"
318+
)
310319

311320
inventory_resources = metrics["inventoryResourceSamples"]
312321
if not isinstance(inventory_resources, list) or len(inventory_resources) != len(expected_sizes):

‎scripts/tests/test_session_metrics.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,28 @@ def test_success_preserves_validated_metrics(self):
231231
self.assertTrue(metrics["metricsProduced"])
232232
self.assertEqual(len(metrics["refreshScenarioSamples"]), 9)
233233

234+
def test_ambient_overlap_counts_allow_equal_or_larger_maxima(self):
235+
for maximum_offset in (0, 1):
236+
with self.subTest(maximum_offset=maximum_offset):
237+
metrics = valid_metrics()
238+
metrics["maxAmbientEnvironmentCount"] += maximum_offset
239+
metrics["maxAmbientManagerCount"] += maximum_offset
240+
self.write_payloads(json.dumps(metrics))
241+
extract_metrics(self.input, self.output, 0, "fast")
242+
243+
def test_ambient_overlap_counts_reject_smaller_maxima(self):
244+
for overlap_name, maximum_name in (
245+
("overlapAmbientEnvironmentCount", "maxAmbientEnvironmentCount"),
246+
("overlapAmbientManagerCount", "maxAmbientManagerCount"),
247+
):
248+
with self.subTest(overlap_name=overlap_name):
249+
metrics = valid_metrics()
250+
metrics[overlap_name] = 2
251+
metrics[maximum_name] = 1
252+
self.assert_metrics_rejected(
253+
metrics, f"{overlap_name} must not exceed {maximum_name}"
254+
)
255+
234256
def test_resource_peak_rejects_low_and_high_values_for_every_field(self):
235257
for field in ("residentBytes", "threads", "handlesOrDescriptors"):
236258
for difference in (-1, 1):

0 commit comments

Comments
 (0)