Skip to content

Commit b5ce1de

Browse files
committed
platform-specific expected values for cross-platform CI
- CI runs tests on both Ubuntu and macOS - Each platform has its own expected values: `testdata/{test}/linux.csv` and `macos.csv` - Comparison uses relative tolerance (1e-12) to handle float formatting differences - gen-expected workflow regenerates values on both platforms when needed - Remove merge script (platforms diverge too much to merge meaningfully)
1 parent 7fb5c23 commit b5ce1de

14 files changed

Lines changed: 748 additions & 985 deletions

File tree

.github/workflows/ci.yml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ env:
1010
RUST_LOG: info
1111
jobs:
1212
test:
13+
strategy:
14+
matrix:
15+
os: [ubuntu-latest, macos-latest]
16+
runs-on: ${{ matrix.os }}
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: Swatinem/rust-cache@v2
20+
21+
- run: cargo build
22+
- run: cargo test -- --nocapture
23+
24+
build-wasm:
1325
runs-on: ubuntu-latest
1426
permissions:
1527
contents: write
@@ -23,9 +35,6 @@ jobs:
2335
- name: Build WASM
2436
run: wasm-pack build --target web
2537

26-
- run: cargo build
27-
- run: cargo test -- --nocapture
28-
2938
- name: Push dist branch
3039
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
3140
uses: runsascoded/npm-dist@v1.1

.github/workflows/gen-expected.yml

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
name: macos-testdata
3838
path: testdata/*/macos.csv
3939

40-
merge:
40+
commit:
4141
needs: [gen-ubuntu, gen-macos]
4242
runs-on: ubuntu-latest
4343
steps:
@@ -46,50 +46,33 @@ jobs:
4646
- uses: actions/download-artifact@v4
4747
with:
4848
name: ubuntu-testdata
49-
path: artifacts/ubuntu
49+
path: testdata
5050

5151
- uses: actions/download-artifact@v4
5252
with:
5353
name: macos-testdata
54-
path: artifacts/macos
54+
path: testdata
5555

56-
- name: Show artifacts
57-
run: find artifacts -type f | head -20
58-
59-
- uses: actions/setup-python@v5
60-
with:
61-
python-version: '3.12'
62-
63-
- name: Merge expected values
56+
- name: Show generated files
6457
run: |
65-
python scripts/merge_expected.py --batch \
66-
artifacts/ubuntu \
67-
artifacts/macos \
68-
testdata
69-
70-
- name: Show merged files
71-
run: |
72-
echo "=== Merged expected.csv files ==="
73-
find testdata -name 'expected.csv' | while read f; do
58+
echo "=== Generated expected value files ==="
59+
find testdata -name '*.csv' -type f | sort | while read f; do
7460
echo "--- $f ---"
7561
head -3 "$f"
7662
echo "..."
7763
done
7864
79-
- name: Commit merged testdata
65+
- name: Commit testdata
8066
run: |
8167
git config user.name "github-actions[bot]"
8268
git config user.email "github-actions[bot]@users.noreply.github.com"
8369
84-
# Remove old platform-specific files
85-
git rm -f testdata/*/linux.csv testdata/*/macos.csv 2>/dev/null || true
86-
87-
git add testdata/*/expected.csv
70+
git add testdata/*/linux.csv testdata/*/macos.csv
8871
git status
8972
9073
if git diff --staged --quiet; then
9174
echo "No changes to commit"
9275
else
93-
git commit -m "Regenerate merged expected values from Ubuntu + macOS"
76+
git commit -m "Regenerate platform-specific expected values"
9477
git push
9578
fi

scripts/merge_expected.py

Lines changed: 0 additions & 212 deletions
This file was deleted.

src/optimization/history.rs

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,15 @@ pub use csv_io::*;
5656
// CSV load/save functionality - only available in tests (polars is a dev-dependency)
5757
#[cfg(test)]
5858
mod csv_io {
59-
/// Count decimal places in a numeric string (for precision-aware comparison)
60-
pub fn decimal_places(s: &str) -> usize {
61-
if let Some(dot_pos) = s.find('.') {
62-
s.len() - dot_pos - 1
59+
/// Compare f64 to expected string with relative tolerance
60+
pub fn float_eq(actual: f64, expected_str: &str) -> bool {
61+
let expected: f64 = expected_str.parse().expect("Failed to parse expected value");
62+
if expected == 0.0 {
63+
actual.abs() < 1e-14
6364
} else {
64-
0
65+
((actual - expected) / expected).abs() < 1e-12
6566
}
6667
}
67-
68-
/// Round f64 to specified decimal places, return as string
69-
pub fn round_to_string(val: f64, places: usize) -> String {
70-
format!("{:.prec$}", val, prec = places)
71-
}
72-
73-
/// Compare f64 to expected string, rounding actual to same precision as expected
74-
pub fn precision_eq(actual: f64, expected_str: &str) -> bool {
75-
let places = decimal_places(expected_str);
76-
let rounded_actual = round_to_string(actual, places);
77-
rounded_actual == expected_str
78-
}
7968
use std::collections::BTreeMap;
8069
use std::path::Path;
8170
use super::*;
@@ -231,18 +220,15 @@ mod csv_io {
231220
Ok(ExpectedHistory { col_names, steps })
232221
}
233222

234-
/// Compare actual HistoryStep against expected, using precision from expected strings
223+
/// Compare actual HistoryStep against expected with relative tolerance
235224
pub fn check_step(&self, step_idx: usize, actual: &HistoryStep) -> Result<(), String> {
236225
let expected = &self.steps[step_idx];
237226

238227
// Check error
239-
if !precision_eq(actual.error, &expected.error) {
228+
if !float_eq(actual.error, &expected.error) {
240229
return Err(format!(
241-
"Step {} error mismatch:\n expected: {}\n actual: {} (rounded: {})",
242-
step_idx,
243-
expected.error,
244-
actual.error,
245-
round_to_string(actual.error, decimal_places(&expected.error))
230+
"Step {} error mismatch:\n expected: {}\n actual: {}",
231+
step_idx, expected.error, actual.error
246232
));
247233
}
248234

@@ -256,16 +242,11 @@ mod csv_io {
256242
}
257243

258244
for (i, (actual_val, expected_str)) in actual_vals.iter().zip(expected.shape_vals.iter()).enumerate() {
259-
if !precision_eq(*actual_val, expected_str) {
245+
if !float_eq(*actual_val, expected_str) {
260246
let col_name = self.col_names.get(i + 1).map(|s| s.as_str()).unwrap_or("?");
261247
return Err(format!(
262-
"Step {} column {} ({}) mismatch:\n expected: {}\n actual: {} (rounded: {})",
263-
step_idx,
264-
i,
265-
col_name,
266-
expected_str,
267-
actual_val,
268-
round_to_string(*actual_val, decimal_places(expected_str))
248+
"Step {} column {} ({}) mismatch:\n expected: {}\n actual: {}",
249+
step_idx, i, col_name, expected_str, actual_val
269250
));
270251
}
271252
}

src/optimization/model.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,10 @@ mod tests {
160160
info!("Wrote expecteds to {}", platform_path);
161161
info!("{}", df);
162162
} else {
163-
// Test mode: compare against merged expected.csv with precision-aware comparison
164-
let expected_path = format!("testdata/{}/expected.csv", name);
163+
// Test mode: compare against platform-specific expected values
164+
let os = env::consts::OS;
165+
let os = if os == "macos" { "macos" } else { "linux" };
166+
let expected_path = format!("testdata/{}/{}.csv", name, os);
165167
let expected = ExpectedHistory::load(&expected_path)
166168
.unwrap_or_else(|e| panic!("Failed to load {}: {}", expected_path, e));
167169

0 commit comments

Comments
 (0)