Skip to content

Commit 92aef33

Browse files
committed
finish up most of the python bindings
1 parent e9fcd08 commit 92aef33

File tree

6 files changed

+180
-52
lines changed

6 files changed

+180
-52
lines changed

benches/binary.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ fn binary(c: &mut Criterion) {
3838
b.iter(|| {
3939
let parsed = TestAnalytics::parse(&buf, 0).unwrap();
4040
for test in parsed.tests(0..60, None).unwrap() {
41+
let test = test.unwrap();
4142
let _name = black_box(test.name().unwrap());
4243
let _aggregates = black_box(test.aggregates());
4344
}

src/binary/bindings.rs

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,33 @@ use super::{TestAnalytics, TestAnalyticsWriter};
99

1010
#[pyclass]
1111
pub struct BinaryFormatWriter {
12-
writer: TestAnalyticsWriter,
12+
writer: Option<TestAnalyticsWriter>,
1313
}
1414

15+
#[pymethods]
1516
impl BinaryFormatWriter {
17+
#[new]
1618
pub fn new() -> Self {
1719
Self {
18-
writer: TestAnalyticsWriter::new(60),
20+
writer: Some(TestAnalyticsWriter::new(60)),
1921
}
2022
}
23+
24+
#[staticmethod]
25+
pub fn open(buffer: &[u8]) -> anyhow::Result<Self> {
26+
let format = TestAnalytics::parse(buffer, 0)?;
27+
let writer = TestAnalyticsWriter::from_existing_format(&format)?;
28+
Ok(Self {
29+
writer: Some(writer),
30+
})
31+
}
32+
2133
pub fn add_testruns(
2234
&mut self,
2335
timestamp: u32,
2436
commit_hash: &str,
25-
flags: &[&str],
26-
testruns: &[Testrun],
37+
flags: Vec<String>,
38+
testruns: Vec<Testrun>,
2739
) -> anyhow::Result<()> {
2840
let commit_hash_base16 = if commit_hash.len() > 40 {
2941
commit_hash
@@ -35,29 +47,61 @@ impl BinaryFormatWriter {
3547
let mut commit_hash = super::CommitHash::default();
3648
base16ct::mixed::decode(commit_hash_base16, &mut commit_hash.0)?;
3749

38-
let mut session = self.writer.start_session(timestamp, commit_hash, flags);
50+
let writer = self
51+
.writer
52+
.as_mut()
53+
.context("writer was already serialized")?;
54+
55+
let flags: Vec<_> = flags.iter().map(|s| s.as_str()).collect();
56+
let mut session = writer.start_session(timestamp, commit_hash, &flags);
3957
for test in testruns {
40-
session.insert(test);
58+
session.insert(&test);
4159
}
4260
Ok(())
4361
}
4462

45-
pub fn serialize(self) -> anyhow::Result<Vec<u8>> {
63+
pub fn serialize(&mut self) -> anyhow::Result<Vec<u8>> {
64+
let writer = self
65+
.writer
66+
.take()
67+
.context("writer was already serialized")?;
4668
let mut buffer = vec![];
47-
self.writer.serialize(&mut buffer)?;
69+
writer.serialize(&mut buffer)?;
4870
Ok(buffer)
4971
}
5072
}
5173

5274
#[pyclass]
5375
pub struct AggregationReader {
54-
buffer: Vec<u8>,
76+
_buffer: Vec<u8>,
5577
format: TestAnalytics<'static>,
5678
}
5779

58-
#[pyclass]
80+
#[pyclass(get_all)]
5981
pub struct TestAggregate {
60-
// TODO
82+
pub name: String,
83+
// TODO:
84+
pub test_id: String,
85+
86+
pub testsuite: Option<String>,
87+
pub flags: Vec<String>,
88+
89+
pub failure_rate: f32,
90+
pub flake_rate: f32,
91+
92+
// TODO:
93+
pub updated_at: u32,
94+
pub avg_duration: f64,
95+
96+
pub total_fail_count: u32,
97+
pub total_flaky_fail_count: u32,
98+
pub total_pass_count: u32,
99+
pub total_skip_count: u32,
100+
101+
pub commits_where_fail: usize,
102+
103+
// TODO:
104+
pub last_duration: f32,
61105
}
62106

63107
#[pymethods]
@@ -69,16 +113,48 @@ impl AggregationReader {
69113
// which we do not mutate, and which outlives the parsed format.
70114
let format = unsafe { transmute(format) };
71115

72-
Ok(Self { buffer, format })
116+
Ok(Self {
117+
_buffer: buffer,
118+
format,
119+
})
73120
}
74121

75-
#[pyo3(signature = (interval_start, interval_end, flag=None))]
122+
#[pyo3(signature = (interval_start, interval_end, flags=None))]
76123
pub fn get_test_aggregates(
77124
&self,
78125
interval_start: usize,
79126
interval_end: usize,
80-
flag: Option<&str>,
81-
) -> Vec<TestAggregate> {
82-
vec![]
127+
flags: Option<Vec<String>>,
128+
) -> anyhow::Result<Vec<TestAggregate>> {
129+
let flags: Option<Vec<_>> = flags
130+
.as_ref()
131+
.map(|flags| flags.iter().map(|flag| flag.as_str()).collect());
132+
let desired_range = interval_start..interval_end;
133+
134+
let tests = self.format.tests(desired_range, flags.as_deref())?;
135+
let mut collected_tests = vec![];
136+
137+
for test in tests {
138+
let test = test?;
139+
140+
collected_tests.push(TestAggregate {
141+
name: test.name()?.into(),
142+
test_id: "TODO".into(),
143+
testsuite: Some(test.testsuite()?.into()),
144+
flags: test.flags()?.into_iter().map(|s| s.into()).collect(),
145+
failure_rate: test.aggregates().failure_rate,
146+
flake_rate: test.aggregates().flake_rate,
147+
updated_at: 0, // TODO
148+
avg_duration: test.aggregates().avg_duration,
149+
total_fail_count: test.aggregates().total_fail_count,
150+
total_flaky_fail_count: test.aggregates().total_flaky_fail_count,
151+
total_pass_count: test.aggregates().total_pass_count,
152+
total_skip_count: test.aggregates().total_skip_count,
153+
commits_where_fail: test.aggregates().failing_commits,
154+
last_duration: 0., // TODO
155+
});
156+
}
157+
158+
Ok(collected_tests)
83159
}
84160
}

src/binary/format.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,18 @@ impl<'data> TestAnalytics<'data> {
8686
pub fn tests(
8787
&self,
8888
desired_range: Range<usize>,
89-
flag: Option<&str>,
90-
) -> Result<impl Iterator<Item = Test<'data, '_>> + '_, TestAnalyticsError> {
91-
let matching_flags_sets = if let Some(flag) = flag {
89+
flags: Option<&[&str]>,
90+
) -> Result<
91+
impl Iterator<Item = Result<Test<'data, '_>, TestAnalyticsError>> + '_,
92+
TestAnalyticsError,
93+
> {
94+
let matching_flags_sets = if let Some(flags) = flags {
9295
let flag_sets = self.flags_set.iter(self.string_bytes);
9396

9497
let mut matching_flags_sets: SmallVec<u32, 4> = Default::default();
9598
for res in flag_sets {
96-
let (offset, flags) = res?;
97-
if flags.contains(&flag) {
99+
let (offset, flag_set) = res?;
100+
if flags.iter().any(|flag| flag_set.contains(&flag.as_ref())) {
98101
matching_flags_sets.push(offset);
99102
}
100103
}
@@ -132,11 +135,11 @@ impl<'data> TestAnalytics<'data> {
132135
&self.testdata[adjusted_range],
133136
);
134137

135-
Some(Test {
138+
Some(aggregates.map(|aggregates| Test {
136139
container: self,
137140
data: test,
138141
aggregates,
139-
})
142+
}))
140143
});
141144
Ok(tests)
142145
}
@@ -211,7 +214,7 @@ impl Aggregates {
211214
commithashes_bytes: &[u8],
212215
all_failing_commits: &mut HashSet<CommitHash>,
213216
data: &[raw::TestData],
214-
) -> Self {
217+
) -> Result<Self, TestAnalyticsError> {
215218
let mut total_pass_count = 0;
216219
let mut total_fail_count = 0;
217220
let mut total_skip_count = 0;
@@ -225,10 +228,8 @@ impl Aggregates {
225228
total_flaky_fail_count += testdata.total_flaky_fail_count as u32;
226229
total_duration += testdata.total_duration as f64;
227230

228-
// TODO: make sure we validate this data ahead of time!
229231
let failing_commits =
230-
CommitHashesSet::read_raw(commithashes_bytes, testdata.failing_commits_set)
231-
.unwrap();
232+
CommitHashesSet::read_raw(commithashes_bytes, testdata.failing_commits_set)?;
232233
all_failing_commits.extend(failing_commits);
233234
}
234235

@@ -246,7 +247,7 @@ impl Aggregates {
246247
(0., 0., 0.)
247248
};
248249

249-
Aggregates {
250+
Ok(Aggregates {
250251
total_pass_count,
251252
total_fail_count,
252253
total_skip_count,
@@ -258,6 +259,6 @@ impl Aggregates {
258259
avg_duration,
259260

260261
failing_commits,
261-
}
262+
})
262263
}
263264
}

0 commit comments

Comments
 (0)