Skip to content

Commit 5d9e0e7

Browse files
committed
test(rust): add fast unit tests for broadcast/error and i256 helpers; add rlib crate-type; run cargo test in CI
1 parent 269fe86 commit 5d9e0e7

File tree

4 files changed

+95
-2
lines changed

4 files changed

+95
-2
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ jobs:
8282
- name: Clippy
8383
run: cargo clippy --all-features -- -D warnings
8484

85+
rust-tests:
86+
runs-on: ubuntu-latest
87+
steps:
88+
- uses: actions/checkout@v4
89+
- name: Set up Rust
90+
run: rustup show
91+
- name: Run cargo tests
92+
run: cargo test --all-features --quiet
93+
8594
linux:
8695
runs-on: ${{ matrix.platform.runner }}
8796
strategy:
@@ -182,7 +191,7 @@ jobs:
182191
name: Release
183192
runs-on: ubuntu-latest
184193
if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
185-
needs: [linux-just-test, linux-min-versions-just-test, clippy, linux, windows, macos, sdist]
194+
needs: [linux-just-test, linux-min-versions-just-test, clippy, rust-tests, linux, windows, macos, sdist]
186195
environment: pypi
187196
permissions:
188197
# Use to sign the release artifacts

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.2.2"
44
edition = "2021"
55

66
[lib]
7-
crate-type = ["cdylib"]
7+
crate-type = ["cdylib", "rlib"]
88

99
[dependencies]
1010
polars = { version = "0.49.1", default-features = false }

src/expressions.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,3 +834,5 @@ pub fn i256_mean(inputs: &[Series]) -> PolarsResult<Series> {
834834
let q = if neg && q_abs != U256::from(0u8) { (!q_abs).overflowing_add(U256::from(1u8)).0 } else { q_abs };
835835
Ok(Series::new(s0.name().clone(), [Some(q.to_be_bytes::<32>().to_vec())]))
836836
}
837+
838+
// (unit tests moved to src/lib.rs to ensure visibility of module items)

src/lib.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,85 @@ fn i256_to_i64_opt(bytes: &[u8]) -> Option<i64> {
185185
}
186186
}
187187
}
188+
189+
// -------------------- Unit tests (Rust) --------------------
190+
#[cfg(test)]
191+
mod tests {
192+
use super::*;
193+
use ruint::aliases::U256;
194+
195+
fn be32_from_u64(v: u64) -> [u8; 32] {
196+
let mut out = [0u8; 32];
197+
out[24..].copy_from_slice(&v.to_be_bytes());
198+
out
199+
}
200+
201+
fn be32_from_i64(v: i64) -> [u8; 32] {
202+
if v >= 0 {
203+
be32_from_u64(v as u64)
204+
} else {
205+
let mag = (-v) as u64;
206+
let inv = U256::from(0u8).overflowing_sub(U256::from(mag)).0;
207+
inv.to_be_bytes()
208+
}
209+
}
210+
211+
fn series_from_be32(name: &str, vals: Vec<Option<[u8; 32]>>) -> Series {
212+
BinaryChunked::from_iter_options(name.into(), vals.into_iter()).into_series()
213+
}
214+
215+
#[test]
216+
fn test_broadcast_mismatch_err() {
217+
let a = series_from_be32(
218+
"a",
219+
vec![Some(be32_from_u64(1)), Some(be32_from_u64(2))],
220+
);
221+
let b = series_from_be32(
222+
"b",
223+
vec![Some(be32_from_u64(3)), Some(be32_from_u64(4)), Some(be32_from_u64(5))],
224+
);
225+
let a_bin = a.binary().unwrap();
226+
let b_bin = b.binary().unwrap();
227+
let err = broadcast_binary_pair(a_bin, b_bin).unwrap_err();
228+
let msg = format!("{err}");
229+
assert!(msg.contains("cannot do a binary operation on columns of different lengths"));
230+
}
231+
232+
#[test]
233+
fn test_map_pair_add_overflow_to_null() {
234+
let a = series_from_be32("a", vec![Some(U256::MAX.to_be_bytes())]);
235+
let b = series_from_be32("b", vec![Some(be32_from_u64(1))]);
236+
let out = map_pair_binary_to_binary_series(
237+
&PlSmallStr::from_static("out"),
238+
a.binary().unwrap(),
239+
b.binary().unwrap(),
240+
|la, rb| {
241+
let ua = u256_from_be32(la).ok()?;
242+
let ub = u256_from_be32(rb).ok()?;
243+
let (sum, overflow) = ua.overflowing_add(ub);
244+
if overflow { None } else { Some(u256_to_be32(&sum)) }
245+
},
246+
)
247+
.unwrap();
248+
let first = out.binary().unwrap().into_iter().next().unwrap();
249+
assert!(first.is_none());
250+
}
251+
252+
#[test]
253+
fn test_i256_twos_complement_roundtrip_small() {
254+
// For a small magnitude negative, tc(tc(x)) = x
255+
let x = be32_from_i64(-123);
256+
let tc = i256_twos_complement(&x);
257+
let back = i256_twos_complement(&tc);
258+
assert_eq!(back, x);
259+
}
260+
261+
#[test]
262+
fn test_i256_cmp_bytes_sign_ordering() {
263+
let neg = be32_from_i64(-1);
264+
let pos = be32_from_i64(1);
265+
assert!(i256_cmp_bytes(&neg, &pos).unwrap().is_lt());
266+
assert!(i256_cmp_bytes(&pos, &neg).unwrap().is_gt());
267+
assert!(i256_cmp_bytes(&neg, &neg).unwrap().is_eq());
268+
}
269+
}

0 commit comments

Comments
 (0)