Skip to content

Commit e4301a7

Browse files
Bug 2048730 - Do not transmute RecordedContext
1 parent 598d0b7 commit e4301a7

5 files changed

Lines changed: 65 additions & 108 deletions

File tree

components/nimbus/src/stateful/nimbus_client.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,9 @@ use crate::stateful::gecko_prefs::{
4848
};
4949
use crate::stateful::matcher::AppContext;
5050
use crate::stateful::persistence::{Database, StoreId, Writer};
51-
use crate::stateful::targeting::{RecordedContext, validate_event_queries};
51+
use crate::stateful::targeting::{RecordedContext, execute_event_queries, validate_event_queries};
5252
use crate::stateful::updating::{read_and_remove_pending_experiments, write_pending_experiments};
5353
use crate::strings::fmt_with_map;
54-
#[cfg(test)]
55-
use crate::tests::helpers::TestRecordedContext;
5654
use crate::{
5755
AvailableExperiment, AvailableRandomizationUnits, EnrolledExperiment, EnrollmentStatus,
5856
};
@@ -200,7 +198,7 @@ impl NimbusClient {
200198
Ok(v) => v,
201199
Err(e) => return Err(NimbusError::JSONError("targeting_helper = nimbus::stateful::nimbus_client::NimbusClient::begin_initialize::serde_json::to_value".into(), e.to_string()))
202200
});
203-
recorded_context.execute_queries(targeting_helper.as_ref())?;
201+
execute_event_queries(&**recorded_context, targeting_helper.as_ref())?;
204202
state
205203
.targeting_attributes
206204
.set_recorded_context(recorded_context.to_json());
@@ -913,23 +911,6 @@ impl NimbusClient {
913911
}))
914912
}
915913

916-
#[cfg(test)]
917-
pub fn get_recorded_context(&self) -> &&TestRecordedContext {
918-
self.recorded_context
919-
.clone()
920-
.map(|ref recorded_context|
921-
// SAFETY: The cast to TestRecordedContext is safe because the Rust instance is
922-
// guaranteed to be a TestRecordedContext instance. TestRecordedContext is the only
923-
// Rust-implemented version of RecordedContext, and, like this method, is only
924-
// used in tests.
925-
unsafe {
926-
std::mem::transmute::<&&dyn RecordedContext, &&TestRecordedContext>(
927-
&&**recorded_context,
928-
)
929-
})
930-
.expect("failed to unwrap RecordedContext object")
931-
}
932-
933914
pub fn set_install_time(&mut self, then: DateTime<Utc>) {
934915
let mut state = self.mutable_state.lock().unwrap();
935916
state.install_date = Some(then);

components/nimbus/src/stateful/targeting.rs

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -62,55 +62,49 @@ pub trait RecordedContext: Send + Sync {
6262
fn record(&self);
6363
}
6464

65-
impl dyn RecordedContext {
66-
pub fn execute_queries(
67-
&self,
68-
nimbus_targeting_helper: &NimbusTargetingHelper,
69-
) -> Result<HashMap<String, f64>> {
70-
let results: HashMap<String, f64> =
71-
HashMap::from_iter(self.get_event_queries().iter().filter_map(|(key, query)| {
72-
match nimbus_targeting_helper.evaluate_jexl_raw_value(query) {
73-
Ok(result) => match result.as_f64() {
74-
Some(v) => Some((key.clone(), v)),
75-
None => {
76-
warn!(
77-
"Value '{}' for query '{}' was not a string",
78-
result.to_string(),
79-
query
80-
);
81-
None
82-
}
83-
},
84-
Err(err) => {
85-
let error_string = format!(
86-
"error during jexl evaluation for query '{}' — {}",
87-
query, err
65+
pub fn execute_event_queries(
66+
recorded_context: &dyn RecordedContext,
67+
nimbus_targeting_helper: &NimbusTargetingHelper,
68+
) -> Result<HashMap<String, f64>> {
69+
let results: HashMap<String, f64> =
70+
HashMap::from_iter(recorded_context.get_event_queries().iter().filter_map(
71+
|(key, query)| match nimbus_targeting_helper.evaluate_jexl_raw_value(query) {
72+
Ok(result) => match result.as_f64() {
73+
Some(v) => Some((key.clone(), v)),
74+
None => {
75+
warn!(
76+
"Value '{}' for query '{}' was not a string",
77+
result.to_string(),
78+
query
8879
);
89-
warn!("{}", error_string);
9080
None
9181
}
82+
},
83+
Err(err) => {
84+
let error_string = format!(
85+
"error during jexl evaluation for query '{}' — {}",
86+
query, err
87+
);
88+
warn!("{}", error_string);
89+
None
9290
}
93-
}));
94-
self.set_event_query_values(results.clone());
95-
Ok(results)
96-
}
91+
},
92+
));
93+
recorded_context.set_event_query_values(results.clone());
94+
Ok(results)
95+
}
9796

98-
pub fn validate_queries(&self) -> Result<()> {
99-
for query in self.get_event_queries().values() {
100-
match EventQueryType::validate_query(query) {
101-
Ok(true) => continue,
102-
Ok(false) => {
103-
return Err(NimbusError::BehaviorError(
104-
BehaviorError::EventQueryParseError(query.clone()),
105-
));
106-
}
107-
Err(err) => return Err(err),
97+
pub fn validate_event_queries(recorded_context: Arc<dyn RecordedContext>) -> Result<()> {
98+
for query in recorded_context.get_event_queries().values() {
99+
match EventQueryType::validate_query(query) {
100+
Ok(true) => continue,
101+
Ok(false) => {
102+
return Err(NimbusError::BehaviorError(
103+
BehaviorError::EventQueryParseError(query.clone()),
104+
));
108105
}
106+
Err(err) => return Err(err),
109107
}
110-
Ok(())
111108
}
112-
}
113-
114-
pub fn validate_event_queries(recorded_context: Arc<dyn RecordedContext>) -> Result<()> {
115-
recorded_context.validate_queries()
109+
Ok(())
116110
}

components/nimbus/src/tests/helpers.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
#![allow(unexpected_cfgs)]
66

7-
pub use self::detail::*;
8-
use crate::metrics::EnrollmentStatusExtraDef;
97
#[cfg(feature = "stateful")]
108
use std::collections::HashMap;
119
use std::collections::HashSet;
@@ -16,12 +14,14 @@ use serde::Serialize;
1614
use serde_json::Map;
1715
use serde_json::{Value, json};
1816

17+
pub use self::detail::*;
1918
use crate::enrollment::{
2019
EnrolledFeatureConfig, EnrolledReason, EnrollmentChangeEvent, ExperimentEnrollment,
2120
NotEnrolledReason,
2221
};
2322
#[cfg(feature = "stateful")]
2423
use crate::json::JsonObject;
24+
use crate::metrics::EnrollmentStatusExtraDef;
2525
#[cfg(feature = "stateful")]
2626
use crate::stateful::behavior::EventStore;
2727
#[cfg(feature = "stateful")]
@@ -80,17 +80,17 @@ struct RecordedContextState {
8080
}
8181

8282
#[cfg(feature = "stateful")]
83-
#[derive(Clone, Default)]
83+
#[derive(Default)]
8484
pub struct TestRecordedContext {
85-
state: Arc<Mutex<RecordedContextState>>,
85+
state: Mutex<RecordedContextState>,
8686
}
8787

8888
#[cfg(feature = "stateful")]
8989
impl TestRecordedContext {
90-
pub fn new() -> Self {
91-
TestRecordedContext {
90+
pub fn new() -> Arc<Self> {
91+
Arc::new(TestRecordedContext {
9292
state: Default::default(),
93-
}
93+
})
9494
}
9595

9696
pub fn get_record_calls(&self) -> u64 {

components/nimbus/src/tests/stateful/test_nimbus.rs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,15 +1680,15 @@ fn test_recorded_context_recorded() -> Result<()> {
16801680
app_version: Some("124.0.0".to_string()),
16811681
..Default::default()
16821682
};
1683-
let recorded_context = Arc::new(TestRecordedContext::new());
1683+
let recorded_context = TestRecordedContext::new();
16841684
recorded_context.set_context(json!({
16851685
"app_version": "125.0.0",
16861686
"other": "stuff",
16871687
}));
16881688
let metrics = TestMetrics::new();
16891689
let client = NimbusClient::new(
16901690
app_context.clone(),
1691-
Some(recorded_context),
1691+
Some(recorded_context.clone()),
16921692
Default::default(),
16931693
temp_dir.path(),
16941694
metrics.clone(),
@@ -1707,7 +1707,7 @@ fn test_recorded_context_recorded() -> Result<()> {
17071707

17081708
let active_experiments = client.get_active_experiments()?;
17091709
assert_eq!(active_experiments.len(), 1);
1710-
assert_eq!(client.get_recorded_context().get_record_calls(), 1u64);
1710+
assert_eq!(recorded_context.get_record_calls(), 1u64);
17111711
assert_eq!(metrics.get_submit_targeting_context_calls(), 1u64);
17121712

17131713
Ok(())
@@ -1724,7 +1724,7 @@ fn test_recorded_context_event_queries() -> Result<()> {
17241724
app_version: Some("124.0.0".to_string()),
17251725
..Default::default()
17261726
};
1727-
let recorded_context = Arc::new(TestRecordedContext::new());
1727+
let recorded_context = TestRecordedContext::new();
17281728
recorded_context.set_context(json!({
17291729
"app_version": "125.0.0",
17301730
"other": "stuff",
@@ -1735,7 +1735,7 @@ fn test_recorded_context_event_queries() -> Result<()> {
17351735
)]));
17361736
let client = NimbusClient::new(
17371737
app_context,
1738-
Some(recorded_context),
1738+
Some(recorded_context.clone()),
17391739
Default::default(),
17401740
temp_dir.path(),
17411741
TestMetrics::new(),
@@ -1754,16 +1754,13 @@ fn test_recorded_context_event_queries() -> Result<()> {
17541754

17551755
info!(
17561756
"{}",
1757-
serde_json::to_string(&client.get_recorded_context().get_event_queries())?
1757+
serde_json::to_string(&recorded_context.get_event_queries())?
17581758
);
17591759

17601760
let active_experiments = client.get_active_experiments()?;
1761-
assert_eq!(
1762-
client.get_recorded_context().get_event_query_values()["TEST_QUERY"],
1763-
0.0
1764-
);
1761+
assert_eq!(recorded_context.get_event_query_values()["TEST_QUERY"], 0.0);
17651762
assert_eq!(active_experiments.len(), 1);
1766-
assert_eq!(client.get_recorded_context().get_record_calls(), 1u64);
1763+
assert_eq!(recorded_context.get_record_calls(), 1u64);
17671764

17681765
Ok(())
17691766
}
@@ -1779,7 +1776,6 @@ fn test_gecko_pref_enrollment() -> Result<()> {
17791776
app_version: Some("124.0.0".to_string()),
17801777
..Default::default()
17811778
};
1782-
let recorded_context = Arc::new(TestRecordedContext::new());
17831779

17841780
let pref_state = GeckoPrefState::new("test.pref", None)
17851781
.with_gecko_value(PrefValue::Null)
@@ -1792,7 +1788,7 @@ fn test_gecko_pref_enrollment() -> Result<()> {
17921788

17931789
let client = NimbusClient::new(
17941790
app_context,
1795-
Some(recorded_context),
1791+
Some(TestRecordedContext::new()),
17961792
Default::default(),
17971793
temp_dir.path(),
17981794
TestMetrics::new(),
@@ -1853,7 +1849,6 @@ fn test_gecko_pref_unenrollment() -> Result<()> {
18531849
app_version: Some("124.0.0".to_string()),
18541850
..Default::default()
18551851
};
1856-
let recorded_context = Arc::new(TestRecordedContext::new());
18571852

18581853
let pref_state = GeckoPrefState::new("test.pref", None).with_gecko_value(PrefValue::Null);
18591854
let handler = TestGeckoPrefHandler::new(create_feature_prop_pref_map(vec![(
@@ -1864,7 +1859,7 @@ fn test_gecko_pref_unenrollment() -> Result<()> {
18641859

18651860
let client = NimbusClient::new(
18661861
app_context,
1867-
Some(recorded_context),
1862+
Some(TestRecordedContext::new()),
18681863
Default::default(),
18691864
temp_dir.path(),
18701865
TestMetrics::new(),
@@ -1979,7 +1974,6 @@ fn test_gecko_pref_unenrollment_reverts() -> Result<()> {
19791974
app_version: Some("124.0.0".to_string()),
19801975
..Default::default()
19811976
};
1982-
let recorded_context = Arc::new(TestRecordedContext::new());
19831977

19841978
let pref_state_1 = GeckoPrefState::new("test.pref.1", None).with_gecko_value(PrefValue::Null);
19851979
let pref_state_2 = GeckoPrefState::new("test.pref.2", None).with_gecko_value(PrefValue::Null);
@@ -1990,7 +1984,7 @@ fn test_gecko_pref_unenrollment_reverts() -> Result<()> {
19901984

19911985
let client = NimbusClient::new(
19921986
app_context,
1993-
Some(recorded_context),
1987+
Some(TestRecordedContext::new()),
19941988
Default::default(),
19951989
temp_dir.path(),
19961990
TestMetrics::new(),
@@ -2118,7 +2112,6 @@ fn register_previous_gecko_pref_states() -> Result<()> {
21182112
app_version: Some("124.0.0".to_string()),
21192113
..Default::default()
21202114
};
2121-
let recorded_context = Arc::new(TestRecordedContext::new());
21222115
let pref_state = GeckoPrefState::new("test.pref", None).with_gecko_value(PrefValue::Null);
21232116
let handler = TestGeckoPrefHandler::new(create_feature_prop_pref_map(vec![(
21242117
"test_feature",
@@ -2127,7 +2120,7 @@ fn register_previous_gecko_pref_states() -> Result<()> {
21272120
)]));
21282121
let client = NimbusClient::new(
21292122
app_context.clone(),
2130-
Some(recorded_context),
2123+
Some(TestRecordedContext::new()),
21312124
Default::default(),
21322125
temp_dir.path(),
21332126
metrics.clone(),
@@ -2315,7 +2308,6 @@ fn test_add_prev_gecko_pref_states_for_experiment() -> Result<()> {
23152308
app_version: Some("124.0.0".to_string()),
23162309
..Default::default()
23172310
};
2318-
let recorded_context = Arc::new(TestRecordedContext::new());
23192311
let pref_state = GeckoPrefState::new("test.pref", None).with_gecko_value(PrefValue::Null);
23202312
let handler = TestGeckoPrefHandler::new(create_feature_prop_pref_map(vec![(
23212313
"test_feature",
@@ -2324,7 +2316,7 @@ fn test_add_prev_gecko_pref_states_for_experiment() -> Result<()> {
23242316
)]));
23252317
let client = NimbusClient::new(
23262318
app_context.clone(),
2327-
Some(recorded_context),
2319+
Some(TestRecordedContext::new()),
23282320
Default::default(),
23292321
temp_dir.path(),
23302322
metrics.clone(),

components/nimbus/src/tests/stateful/test_targeting.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use std::sync::{Arc, Mutex};
77

88
use serde_json::Map;
99

10-
use crate::stateful::{behavior::EventStore, targeting::RecordedContext};
10+
use crate::stateful::behavior::EventStore;
11+
use crate::stateful::targeting::{execute_event_queries, validate_event_queries};
1112
use crate::tests::helpers::TestRecordedContext;
1213
use crate::{NimbusTargetingHelper, Result};
1314

@@ -31,23 +32,14 @@ fn test_recorded_context_execute_queries() -> Result<()> {
3132

3233
let recorded_context = TestRecordedContext::new();
3334
recorded_context.set_event_queries(map.clone());
34-
let recorded_context: Box<dyn RecordedContext> = Box::new(recorded_context);
35+
execute_event_queries(&*recorded_context, &targeting_helper)?;
3536

36-
recorded_context.execute_queries(&targeting_helper)?;
37-
38-
// SAFETY: The cast to TestRecordedContext is safe because the Rust instance is
39-
// guaranteed to be a TestRecordedContext instance. TestRecordedContext is the only
40-
// Rust-implemented version of RecordedContext, and, like this method, is only
41-
// used in tests.
42-
let test_recorded_context = unsafe {
43-
std::mem::transmute::<&&dyn RecordedContext, &&TestRecordedContext>(&&*recorded_context)
44-
};
4537
assert_eq!(
46-
test_recorded_context.get_event_query_values()["TEST_QUERY_SUCCESS"],
38+
recorded_context.get_event_query_values()["TEST_QUERY_SUCCESS"],
4739
1.0
4840
);
4941
assert!(
50-
!test_recorded_context
42+
!recorded_context
5143
.get_event_query_values()
5244
.contains_key("TEST_QUERY_FAIL_NOT_VALID_QUERY")
5345
);
@@ -70,9 +62,7 @@ fn test_recorded_context_validate_queries() -> Result<()> {
7062

7163
let recorded_context = TestRecordedContext::new();
7264
recorded_context.set_event_queries(map.clone());
73-
let recorded_context: Box<dyn RecordedContext> = Box::new(recorded_context);
74-
75-
let result = recorded_context.validate_queries();
65+
let result = validate_event_queries(recorded_context);
7666
assert!(result.is_err_and(|e| {
7767
assert_eq!(e.to_string(), "Behavior error: EventQueryParseError: \"'event'|eventYolo('Days', 1, 0)\" is not a valid EventQuery".to_string());
7868
true

0 commit comments

Comments
 (0)