• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-engine / geoengine / 22500486493

27 Feb 2026 07:22PM UTC coverage: 88.19%. First build
22500486493

push

github

web-flow
fix: Cache and Stacker should keep band order for subsets (#1120)

* fix(cache):filter returned band by query.band

* stacker: produce tiles with band selection stored

* update cache stored bands

* lint test

* add debug outputs on error tiles (only in debug mode)

* remove no needed method

* enhance expect method

* remove comment regarding TimeInstants

* remove band update in stored query since bands must not change

* remove redundant filterin in cache

* remove and cleanup code fix MockRasterSource

* back to old StackerAdapter stratey where bands are not selected

* baseline stacker approach. Fix tests

* improve comment

* lints

* add band selection logic to SimpleRasterStacker

* add regular time stackerwith band selection to RasterStacker Operator

* more tests and checks

* lints

* more lint

1002 of 1044 new or added lines in 20 files covered. (95.98%)

112379 of 127429 relevant lines covered (88.19%)

508212.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.2
/datatypes/src/util/test.rs
1
use float_cmp::approx_eq;
2

3
use crate::{
4
    primitives::TimeInterval,
5
    raster::{
6
        EmptyGrid, GeoTransform, Grid, GridIdx2D, GridIndexAccess, GridOrEmpty, GridSize,
7
        MaskedGrid, Pixel, RasterTile2D, grid_idx_iter_2d,
8
    },
9
};
10
use std::panic;
11

12
pub trait TestDefault {
13
    /// Generate a default value used for testing. Use this instead of the `Default` trait
14
    /// if the default value only makes sense in tests and not in production code.
15
    fn test_default() -> Self;
16
}
17

18
pub fn catch_unwind_silent<F: FnOnce() -> R + panic::UnwindSafe, R>(
2✔
19
    f: F,
2✔
20
) -> std::thread::Result<R> {
2✔
21
    let prev_hook = panic::take_hook();
2✔
22
    panic::set_hook(Box::new(|_| {}));
2✔
23
    let result = panic::catch_unwind(f);
2✔
24
    panic::set_hook(prev_hook);
2✔
25
    result
2✔
26
}
2✔
27

28
pub fn grid_or_empty_grid_eq<D, T>(g1: &GridOrEmpty<D, T>, g2: &GridOrEmpty<D, T>) -> bool
14✔
29
where
14✔
30
    D: PartialEq + GridSize + Clone,
14✔
31
    T: PartialEq + Copy,
14✔
32
{
33
    match (g1, g2) {
14✔
34
        (GridOrEmpty::Grid(g1), GridOrEmpty::Grid(g2)) => masked_grid_eq(g1, g2),
10✔
35
        (GridOrEmpty::Empty(g1), GridOrEmpty::Empty(g2)) => empty_grid_eq(g1, g2),
4✔
36
        _ => false,
×
37
    }
38
}
14✔
39

40
pub fn masked_grid_eq<D, T>(g1: &MaskedGrid<D, T>, g2: &MaskedGrid<D, T>) -> bool
17✔
41
where
17✔
42
    D: PartialEq + GridSize + Clone,
17✔
43
    T: PartialEq + Copy,
17✔
44
{
45
    grid_eq(g1.as_ref(), g2.as_ref()) && grid_eq(g1.mask_ref(), g2.mask_ref())
17✔
46
}
17✔
47

48
pub fn grid_eq<D, T>(g1: &Grid<D, T>, g2: &Grid<D, T>) -> bool
36✔
49
where
36✔
50
    D: PartialEq,
36✔
51
    T: PartialEq + Copy,
36✔
52
{
53
    if g1.data.len() != g2.data.len() || g1.shape.ne(&g2.shape) {
36✔
54
        return false;
2✔
55
    }
34✔
56

57
    for (l, r) in g1.data.iter().zip(g2.data.iter()) {
162✔
58
        if l != r {
162✔
59
            return false;
5✔
60
        }
157✔
61
    }
62
    true
29✔
63
}
36✔
64

65
pub fn empty_grid_eq<D, T>(g1: &EmptyGrid<D, T>, g2: &EmptyGrid<D, T>) -> bool
6✔
66
where
6✔
67
    D: PartialEq,
6✔
68
    T: PartialEq + Copy,
6✔
69
{
70
    g1.shape.eq(&g2.shape)
6✔
71
}
6✔
72

73
/// Save bytes into a file to be used for testing.
74
///
75
/// # Panics
76
///
77
/// This function panics if the file cannot be created or written.
78
///
79
pub fn save_test_bytes(bytes: &[u8], filename: &str) {
×
80
    use std::io::Write;
81

82
    std::fs::File::create(filename)
×
83
        .expect("it should be possible to create this file for testing")
×
84
        .write_all(bytes)
×
85
        .expect("it should be possible to write this file for testing");
×
86
}
×
87

88
/// Method that compares two lists of tiles and panics with a message why there is a difference.
89
///
90
/// # Panics
91
/// If there is a difference between two tiles or the length of the lists
92
pub fn assert_eq_two_list_of_tiles_u8(
4✔
93
    list_a: &[RasterTile2D<u8>],
4✔
94
    list_b: &[RasterTile2D<u8>],
4✔
95
    compare_cache_hint: bool,
4✔
96
) {
4✔
97
    assert_eq_two_list_of_tiles::<u8>(list_a, list_b, compare_cache_hint);
4✔
98
}
4✔
99

100
/// Method that compares two lists of tiles and panics with a message why there is a difference.
101
///
102
/// # Panics
103
/// If there is a difference between two tiles or the length of the lists
104
pub fn assert_eq_two_list_of_tiles<P: Pixel>(
22✔
105
    list_a: &[RasterTile2D<P>],
22✔
106
    list_b: &[RasterTile2D<P>],
22✔
107
    compare_cache_hint: bool,
22✔
108
) {
22✔
NEW
109
    fn tile_pos<T>(tile: &RasterTile2D<T>) -> (TimeInterval, GridIdx2D, u32) {
×
NEW
110
        (tile.time, tile.tile_position, tile.band)
×
NEW
111
    }
×
112

113
    assert_eq!(
22✔
114
        list_a.len(),
22✔
115
        list_b.len(),
22✔
116
        "len() of input_a: {}, len of input_b: {}",
117
        list_a.len(),
×
118
        list_b.len()
×
119
    );
120

121
    list_a
22✔
122
        .iter()
22✔
123
        .zip(list_b)
22✔
124
        .enumerate()
22✔
125
        .for_each(|(i, (a, b))| {
329✔
126
            assert_eq!(
329✔
127
                a.time, b.time,
128
                "time of tile {} input_a: {}, input_b: {}",
129
                i, a.time, b.time
130
            );
131
            assert_eq!(
329✔
132
                a.band, b.band,
133
                "band of tile {} input_a: {}, input_b: {}",
134
                i, a.band, b.band
135
            );
136
            assert_eq!(
329✔
137
                a.tile_position, b.tile_position,
138
                "tile position of tile {} input_a: {:?}, input_b: {:?}",
139
                i, a.tile_position, b.tile_position
140
            );
141

142
            let spatial_grid_a = a.global_pixel_spatial_grid_definition();
329✔
143
            let spatial_grid_b = b.global_pixel_spatial_grid_definition();
329✔
144
            assert_eq!(
329✔
145
                spatial_grid_a.grid_bounds(),
329✔
146
                spatial_grid_b.grid_bounds(),
329✔
147
                "grid bounds of tile {} input_a: {:?}, input_b {:?}",
148
                i,
149
                spatial_grid_a.grid_bounds(),
×
150
                spatial_grid_b.grid_bounds()
×
151
            );
152
            assert!(
329✔
153
                approx_eq!(
329✔
154
                    GeoTransform,
155
                    spatial_grid_a.geo_transform(),
329✔
156
                    spatial_grid_b.geo_transform()
329✔
157
                ),
158
                "geo transform of tile {} input_a: {:?}, input_b: {:?}",
159
                i,
160
                spatial_grid_a.geo_transform(),
×
161
                spatial_grid_b.geo_transform()
×
162
            );
163
            assert_eq!(
329✔
164
                a.grid_array.is_empty(),
329✔
165
                b.grid_array.is_empty(),
329✔
166
                "grid shape of tile {} input_a is_empty: {:?}, input_b is_empty: {:?}, a info: {:?}, b info: {:?}",
167
                i,
168
                a.grid_array.is_empty(),
×
169
                b.grid_array.is_empty(),
×
NEW
170
                tile_pos(a),
×
NEW
171
                tile_pos(b)
×
172
            );
173
            if !a.grid_array.is_empty() {
329✔
174
                let mat_a = a.grid_array.clone().into_materialized_masked_grid();
329✔
175
                let mat_b = b.grid_array.clone().into_materialized_masked_grid();
329✔
176

177
                assert_eq!(
329✔
178
                    mat_a.inner_grid.data.len(),
329✔
179
                    mat_b.inner_grid.data.len(),
329✔
180
                    "grid data len of tile {} input_a: {:?}, input_b: {:?}",
181
                    i,
182
                    mat_a.inner_grid.data.len(),
×
183
                    mat_b.inner_grid.data.len(),
×
184
                );
185

186
                for (pi, idx) in grid_idx_iter_2d(&mat_a).enumerate() {
72,614,096✔
187
                    let a_v = mat_a
72,614,096✔
188
                        .get_at_grid_index(idx)
72,614,096✔
189
                        .expect("tile a must contain idx inside tile bounds");
72,614,096✔
190
                    let b_v = mat_b
72,614,096✔
191
                        .get_at_grid_index(idx)
72,614,096✔
192
                        .expect("tile b must contain idx inside tile bounds");
72,614,096✔
193
                    assert_eq!(
72,614,096✔
194
                        a_v, b_v,
195
                        "tile {i} pixel {pi} at {idx:?} input_a: {a_v:?}, input_b: {b_v:?}",
196
                    );
197
                }
198
            }
×
199
            if compare_cache_hint {
329✔
200
                assert_eq!(
×
201
                    a.cache_hint, b.cache_hint,
202
                    "cache hint of tile {} input_a: {:?}, input_b: {:?}",
203
                    i, a.cache_hint, b.cache_hint
204
                );
205
            }
329✔
206
        });
329✔
207
}
22✔
208

209
#[cfg(test)]
210
mod tests {
211
    use crate::{
212
        raster::{EmptyGrid, EmptyGrid2D, Grid2D, GridOrEmpty, GridShape2D, MaskedGrid2D},
213
        util::test::{empty_grid_eq, grid_eq, masked_grid_eq},
214
    };
215

216
    use super::grid_or_empty_grid_eq;
217

218
    #[test]
219
    fn test_empty_grid_eq_ok() {
1✔
220
        let d1: GridShape2D = [3, 2].into();
1✔
221
        let d2: GridShape2D = [3, 2].into();
1✔
222

223
        let r1: EmptyGrid2D<u8> = EmptyGrid::new(d1);
1✔
224
        let r2: EmptyGrid2D<u8> = EmptyGrid::new(d2);
1✔
225

226
        assert!(empty_grid_eq(&r1, &r2));
1✔
227
    }
1✔
228

229
    #[test]
230
    fn test_empty_grid_eq_integral_fail_dim() {
1✔
231
        let d1: GridShape2D = [3, 2].into();
1✔
232
        let d2: GridShape2D = [3, 1].into();
1✔
233

234
        let r1: EmptyGrid2D<u8> = EmptyGrid::new(d1);
1✔
235
        let r2: EmptyGrid2D<u8> = EmptyGrid::new(d2);
1✔
236

237
        assert!(!empty_grid_eq(&r1, &r2));
1✔
238
    }
1✔
239

240
    #[test]
241
    fn test_grid_eq_integral_ok() {
1✔
242
        let d1: GridShape2D = [2, 2].into();
1✔
243
        let d2: GridShape2D = [2, 2].into();
1✔
244

245
        let r1 = Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap();
1✔
246
        let r2 = Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap();
1✔
247

248
        assert!(grid_eq(&r1, &r2));
1✔
249
    }
1✔
250

251
    #[test]
252
    fn test_grid_eq_with_integral_fail_dim() {
1✔
253
        let d1: GridShape2D = [4, 1].into();
1✔
254
        let d2: GridShape2D = [2, 2].into();
1✔
255
        let r1 = Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap();
1✔
256
        let r2 = Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap();
1✔
257

258
        assert!(!grid_eq(&r1, &r2));
1✔
259
    }
1✔
260

261
    #[test]
262
    fn test_grid_eq_integral_fail_data() {
1✔
263
        let d1: GridShape2D = [2, 2].into();
1✔
264
        let d2: GridShape2D = [2, 2].into();
1✔
265

266
        let r1 = Grid2D::new(d1, vec![2, 2, 3, 42]).unwrap();
1✔
267
        let r2 = Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap();
1✔
268

269
        assert!(!grid_eq(&r1, &r2));
1✔
270
    }
1✔
271

272
    #[test]
273
    fn test_grid_eq_float_some_ok() {
1✔
274
        let d1: GridShape2D = [2, 2].into();
1✔
275
        let d2: GridShape2D = [2, 2].into();
1✔
276

277
        let r1 = Grid2D::new(d1, vec![1_f32, 2_f32, 3_f32, 42_f32]).unwrap();
1✔
278
        let r2 = Grid2D::new(d2, vec![1_f32, 2_f32, 3_f32, 42_f32]).unwrap();
1✔
279

280
        assert!(grid_eq(&r1, &r2));
1✔
281
    }
1✔
282

283
    #[test]
284
    fn test_masked_grid_eq_no_mask_ok() {
1✔
285
        let d1: GridShape2D = [2, 2].into();
1✔
286
        let d2: GridShape2D = [2, 2].into();
1✔
287

288
        let r1 = MaskedGrid2D::new_with_data(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap());
1✔
289
        let r2 = MaskedGrid2D::new_with_data(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap());
1✔
290

291
        assert!(masked_grid_eq(&r1, &r2));
1✔
292
    }
1✔
293

294
    #[test]
295
    fn test_masked_grid_eq_mask_ok() {
1✔
296
        let d1: GridShape2D = [2, 2].into();
1✔
297
        let d2: GridShape2D = [2, 2].into();
1✔
298

299
        let m1 = Grid2D::new(d1, vec![true, true, true, false]).unwrap();
1✔
300
        let m2 = Grid2D::new(d2, vec![true, true, true, false]).unwrap();
1✔
301

302
        let r1 = MaskedGrid2D::new(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap(), m1).unwrap();
1✔
303
        let r2 = MaskedGrid2D::new(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap(), m2).unwrap();
1✔
304

305
        assert!(masked_grid_eq(&r1, &r2));
1✔
306
    }
1✔
307

308
    #[test]
309
    fn test_masked_grid_eq_no_mask_fail_data() {
1✔
310
        let d1: GridShape2D = [2, 2].into();
1✔
311
        let d2: GridShape2D = [2, 2].into();
1✔
312

313
        let r1 = MaskedGrid2D::new_with_data(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap());
1✔
314
        let r2 = MaskedGrid2D::new_with_data(Grid2D::new(d2, vec![42, 3, 2, 1]).unwrap());
1✔
315

316
        assert!(!masked_grid_eq(&r1, &r2));
1✔
317
    }
1✔
318

319
    #[test]
320
    fn test_masked_grid_eq_no_mask_fail_dim() {
1✔
321
        let d1: GridShape2D = [4, 1].into();
1✔
322
        let d2: GridShape2D = [2, 2].into();
1✔
323

324
        let r1 = MaskedGrid2D::new_with_data(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap());
1✔
325
        let r2 = MaskedGrid2D::new_with_data(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap());
1✔
326

327
        assert!(!masked_grid_eq(&r1, &r2));
1✔
328
    }
1✔
329

330
    #[test]
331
    fn test_masked_grid_eq_mask_fail_some_none() {
1✔
332
        let d1: GridShape2D = [2, 2].into();
1✔
333
        let d2: GridShape2D = [2, 2].into();
1✔
334

335
        let m1 = Grid2D::new(d1, vec![true, true, true, false]).unwrap();
1✔
336

337
        let r1 = MaskedGrid2D::new(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap(), m1).unwrap();
1✔
338
        let r2 = MaskedGrid2D::new_with_data(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap());
1✔
339

340
        assert!(!masked_grid_eq(&r1, &r2));
1✔
341
    }
1✔
342

343
    #[test]
344
    fn test_masked_grid_eq_mask_fail_none_some() {
1✔
345
        let d1: GridShape2D = [2, 2].into();
1✔
346
        let d2: GridShape2D = [2, 2].into();
1✔
347

348
        let m2 = Grid2D::new(d1, vec![true, true, true, false]).unwrap();
1✔
349

350
        let r1 = MaskedGrid2D::new_with_data(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap());
1✔
351
        let r2 = MaskedGrid2D::new(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap(), m2).unwrap();
1✔
352

353
        assert!(!masked_grid_eq(&r1, &r2));
1✔
354
    }
1✔
355

356
    #[test]
357
    fn test_masked_grid_eq_fail_mask() {
1✔
358
        let d1: GridShape2D = [2, 2].into();
1✔
359
        let d2: GridShape2D = [2, 2].into();
1✔
360

361
        let m1 = Grid2D::new(d1, vec![true, true, true, true]).unwrap();
1✔
362
        let m2 = Grid2D::new(d2, vec![true, true, true, false]).unwrap();
1✔
363

364
        let r1 = MaskedGrid2D::new(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap(), m1).unwrap();
1✔
365
        let r2 = MaskedGrid2D::new(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap(), m2).unwrap();
1✔
366

367
        assert!(!masked_grid_eq(&r1, &r2));
1✔
368
    }
1✔
369

370
    #[test]
371
    fn test_grid_or_empty_grid_eq_empty() {
1✔
372
        let d1: GridShape2D = [2, 2].into();
1✔
373
        let d2: GridShape2D = [2, 2].into();
1✔
374

375
        let e1 = EmptyGrid2D::<u8>::new(d1);
1✔
376
        let e2 = EmptyGrid2D::<u8>::new(d2);
1✔
377

378
        let goe1 = GridOrEmpty::from(e1);
1✔
379
        let goe2 = GridOrEmpty::from(e2);
1✔
380

381
        assert!(grid_or_empty_grid_eq(&goe1, &goe2));
1✔
382
    }
1✔
383

384
    #[test]
385
    fn test_grid_or_empty_grid_eq_grid_empty() {
1✔
386
        let d1: GridShape2D = [2, 2].into();
1✔
387
        let d2: GridShape2D = [2, 2].into();
1✔
388

389
        let e1 = MaskedGrid2D::from(EmptyGrid2D::<u8>::new(d1));
1✔
390
        let e2 = MaskedGrid2D::from(EmptyGrid2D::<u8>::new(d2));
1✔
391

392
        let goe1 = GridOrEmpty::from(e1);
1✔
393
        let goe2 = GridOrEmpty::from(e2);
1✔
394

395
        assert!(grid_or_empty_grid_eq(&goe1, &goe2));
1✔
396
    }
1✔
397

398
    #[test]
399
    fn test_grid_or_empty_grid_eq_grid_values() {
1✔
400
        let d1: GridShape2D = [2, 2].into();
1✔
401
        let d2: GridShape2D = [2, 2].into();
1✔
402

403
        let m1 = Grid2D::new(d1, vec![true, true, true, false]).unwrap();
1✔
404
        let m2 = Grid2D::new(d2, vec![true, true, true, false]).unwrap();
1✔
405

406
        let r1 = MaskedGrid2D::new(Grid2D::new(d1, vec![1, 2, 3, 42]).unwrap(), m1).unwrap();
1✔
407
        let r2 = MaskedGrid2D::new(Grid2D::new(d2, vec![1, 2, 3, 42]).unwrap(), m2).unwrap();
1✔
408

409
        let goe1 = GridOrEmpty::from(r1);
1✔
410
        let goe2 = GridOrEmpty::from(r2);
1✔
411

412
        assert!(grid_or_empty_grid_eq(&goe1, &goe2));
1✔
413
    }
1✔
414
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc