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

vortex-data / vortex / 16810944809

07 Aug 2025 05:04PM UTC coverage: 84.877% (+0.03%) from 84.847%
16810944809

Pull #4159

github

web-flow
Merge 7eb1dd80c into 30635faae
Pull Request #4159: chore: Bump msrv to 1.89

107 of 157 new or added lines in 34 files covered. (68.15%)

2 existing lines in 2 files now uncovered.

50629 of 59650 relevant lines covered (84.88%)

567829.65 hits per line

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

93.15
/vortex-array/src/compute/conformance/cast.rs
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3

4
use vortex_dtype::{DType, Nullability, PType};
5
use vortex_error::VortexUnwrap;
6

7
use crate::Array;
8
use crate::compute::cast;
9

10
/// Test conformance of the cast compute function for an array.
11
///
12
/// This function tests various casting scenarios including:
13
/// - Casting between numeric types (widening and narrowing)
14
/// - Casting between signed and unsigned types
15
/// - Casting between integral and floating-point types
16
/// - Casting with nullability changes
17
/// - Casting between string types (Utf8/Binary)
18
/// - Edge cases like overflow behavior
19
pub fn test_cast_conformance(array: &dyn Array) {
2,760✔
20
    let dtype = array.dtype();
2,760✔
21

22
    // Always test identity cast and nullability changes
23
    test_cast_identity(array);
2,760✔
24

25
    // Test AllValid to NonNullable and back if applicable
26
    test_cast_allvalid_to_nonnullable_and_back(array);
2,760✔
27

28
    // Test based on the specific DType
29
    match dtype {
2,760✔
30
        DType::Null => test_cast_from_null(array),
5✔
31
        DType::Bool(nullability) => test_cast_from_bool(array, *nullability),
240✔
32
        DType::Primitive(ptype, nullability) => {
2,054✔
33
            test_cast_nullability_changes_primitive(array, *ptype, *nullability);
2,054✔
34
            match ptype {
2,054✔
35
                PType::U8 => test_cast_from_u8(array),
161✔
36
                PType::U16 => test_cast_from_u16(array),
79✔
37
                PType::U32 => test_cast_from_u32(array),
200✔
38
                PType::U64 => test_cast_from_u64(array),
157✔
39
                PType::I8 => test_cast_from_i8(array),
1✔
40
                PType::I16 => test_cast_from_i16(array),
79✔
41
                PType::I32 => test_cast_from_i32(array),
591✔
42
                PType::I64 => test_cast_from_i64(array),
118✔
43
                PType::F16 => test_cast_from_f16(array),
×
44
                PType::F32 => test_cast_from_f32(array),
394✔
45
                PType::F64 => test_cast_from_f64(array),
274✔
46
            }
47
        }
48
        DType::Decimal(_, nullability) => test_cast_from_decimal(array, *nullability),
199✔
49
        DType::Utf8(nullability) => test_cast_from_utf8(array, *nullability),
125✔
50
        DType::Binary(nullability) => test_cast_from_binary(array, *nullability),
5✔
51
        DType::Struct(_, nullability) => test_cast_from_struct(array, *nullability),
5✔
52
        DType::List(_, nullability) => test_cast_from_list(array, *nullability),
6✔
53
        DType::Extension(_) => test_cast_from_extension(array),
121✔
54
    }
55
}
2,760✔
56

57
fn test_cast_identity(array: &dyn Array) {
2,760✔
58
    // Casting to the same type should be a no-op
59
    let result = cast(array, array.dtype()).vortex_unwrap();
2,760✔
60
    assert_eq!(result.len(), array.len());
2,760✔
61
    assert_eq!(result.dtype(), array.dtype());
2,760✔
62

63
    // Verify values are unchanged
64
    for i in 0..array.len().min(10) {
13,706✔
65
        assert_eq!(
13,706✔
66
            array.scalar_at(i).vortex_unwrap(),
13,706✔
67
            result.scalar_at(i).vortex_unwrap()
13,706✔
68
        );
69
    }
70
}
2,760✔
71

72
fn test_cast_from_null(array: &dyn Array) {
5✔
73
    // Null can be cast to itself
74
    let result = cast(array, &DType::Null).vortex_unwrap();
5✔
75
    assert_eq!(result.len(), array.len());
5✔
76
    assert_eq!(result.dtype(), &DType::Null);
5✔
77

78
    // Null can also be cast to any nullable type
79
    let nullable_types = vec![
5✔
80
        DType::Bool(Nullability::Nullable),
5✔
81
        DType::Primitive(PType::I32, Nullability::Nullable),
5✔
82
        DType::Primitive(PType::F64, Nullability::Nullable),
5✔
83
        DType::Utf8(Nullability::Nullable),
5✔
84
        DType::Binary(Nullability::Nullable),
5✔
85
    ];
86

87
    for dtype in nullable_types {
30✔
88
        let result = cast(array, &dtype).vortex_unwrap();
25✔
89
        assert_eq!(result.len(), array.len());
25✔
90
        assert_eq!(result.dtype(), &dtype);
25✔
91

92
        // Verify all values are null
93
        for i in 0..array.len().min(10) {
105✔
94
            assert!(result.scalar_at(i).vortex_unwrap().is_null());
105✔
95
        }
96
    }
97

98
    // Casting to non-nullable types should fail
99
    let non_nullable_types = vec![
5✔
100
        DType::Bool(Nullability::NonNullable),
5✔
101
        DType::Primitive(PType::I32, Nullability::NonNullable),
5✔
102
    ];
103

104
    for dtype in non_nullable_types {
15✔
105
        assert!(cast(array, &dtype).is_err());
10✔
106
    }
107
}
5✔
108

109
fn test_cast_from_bool(array: &dyn Array, nullability: Nullability) {
240✔
110
    // Test nullability changes
111
    test_cast_nullability_changes(array, &DType::Bool(Nullability::Nullable));
240✔
112
    if nullability == Nullability::Nullable {
240✔
113
        // Try casting to non-nullable (may fail if nulls present)
196✔
114
        let _ = cast(array, &DType::Bool(Nullability::NonNullable));
196✔
115
    }
196✔
116

117
    // Test bool to numeric casts (true -> 1, false -> 0)
118
    test_cast_to_primitive(array, PType::U8);
240✔
119
    test_cast_to_primitive(array, PType::I32);
240✔
120
    test_cast_to_primitive(array, PType::F32);
240✔
121
}
240✔
122

123
fn test_cast_from_decimal(array: &dyn Array, nullability: Nullability) {
199✔
124
    // Test nullability changes for the same decimal type
125
    if let DType::Decimal(decimal_type, _) = array.dtype() {
199✔
126
        test_cast_nullability_changes(array, &DType::Decimal(*decimal_type, Nullability::Nullable));
199✔
127
        if nullability == Nullability::Nullable {
199✔
128
            // Try casting to non-nullable (may fail if nulls present)
40✔
129
            let _ = cast(
40✔
130
                array,
40✔
131
                &DType::Decimal(*decimal_type, Nullability::NonNullable),
40✔
132
            );
40✔
133
        }
159✔
134
    }
×
135
}
199✔
136

137
fn test_cast_from_utf8(array: &dyn Array, nullability: Nullability) {
125✔
138
    // Test nullability changes
139
    test_cast_nullability_changes(array, &DType::Utf8(Nullability::Nullable));
125✔
140
    if nullability == Nullability::Nullable {
125✔
141
        // Try casting to non-nullable (may fail if nulls present)
42✔
142
        let _ = cast(array, &DType::Utf8(Nullability::NonNullable));
42✔
143
    }
83✔
144

145
    // UTF-8 strings can potentially be cast to Binary
146
    test_cast_to_type_safe(array, &DType::Binary(nullability));
125✔
147
}
125✔
148

149
fn test_cast_from_binary(array: &dyn Array, nullability: Nullability) {
5✔
150
    // Test nullability changes
151
    test_cast_nullability_changes(array, &DType::Binary(Nullability::Nullable));
5✔
152
    if nullability == Nullability::Nullable {
5✔
153
        // Try casting to non-nullable (may fail if nulls present)
3✔
154
        let _ = cast(array, &DType::Binary(Nullability::NonNullable));
3✔
155
    }
3✔
156

157
    // Binary might be castable to UTF-8 if it contains valid UTF-8
158
    test_cast_to_type_safe(array, &DType::Utf8(nullability));
5✔
159
}
5✔
160

161
fn test_cast_from_struct(array: &dyn Array, nullability: Nullability) {
5✔
162
    // Test nullability changes for the same struct type
163
    if let DType::Struct(fields, _) = array.dtype() {
5✔
164
        test_cast_nullability_changes(array, &DType::Struct(fields.clone(), Nullability::Nullable));
5✔
165
        if nullability == Nullability::Nullable {
5✔
166
            // Try casting to non-nullable (may fail if nulls present)
1✔
167
            let _ = cast(
1✔
168
                array,
1✔
169
                &DType::Struct(fields.clone(), Nullability::NonNullable),
1✔
170
            );
1✔
171
        }
4✔
172
    }
×
173
}
5✔
174

175
fn test_cast_from_list(array: &dyn Array, nullability: Nullability) {
6✔
176
    // Test nullability changes for the same list type
177
    if let DType::List(element_type, _) = array.dtype() {
6✔
178
        test_cast_nullability_changes(
6✔
179
            array,
6✔
180
            &DType::List(element_type.clone(), Nullability::Nullable),
6✔
181
        );
182
        if nullability == Nullability::Nullable {
6✔
183
            // Try casting to non-nullable (may fail if nulls present)
1✔
184
            let _ = cast(
1✔
185
                array,
1✔
186
                &DType::List(element_type.clone(), Nullability::NonNullable),
1✔
187
            );
1✔
188
        }
5✔
189
    }
×
190
}
6✔
191

192
fn test_cast_from_extension(array: &dyn Array) {
121✔
193
    // Extension types typically only cast to themselves
194
    // The specific casting rules depend on the extension type
195
    if let DType::Extension(ext_dtype) = array.dtype() {
121✔
196
        let result = cast(array, &DType::Extension(ext_dtype.clone())).vortex_unwrap();
121✔
197
        assert_eq!(result.len(), array.len());
121✔
198
        assert_eq!(result.dtype(), array.dtype());
121✔
199
    }
×
200
}
121✔
201

202
fn test_cast_allvalid_to_nonnullable_and_back(array: &dyn Array) {
2,760✔
203
    // Skip if array is null type (special case)
204
    if array.dtype() == &DType::Null {
2,760✔
205
        return;
5✔
206
    }
2,755✔
207

208
    // Only test if array has no nulls
209
    if let Ok(null_count) = array.invalid_count()
2,755✔
210
        && null_count == 0
2,755✔
211
    {
212
        // Test casting to NonNullable if currently Nullable
213
        if array.dtype().nullability() == Nullability::Nullable {
2,232✔
214
            let non_nullable_dtype = array.dtype().with_nullability(Nullability::NonNullable);
118✔
215

216
            // Cast to NonNullable
217
            if let Ok(non_nullable) = cast(array, &non_nullable_dtype) {
118✔
218
                assert_eq!(non_nullable.dtype(), &non_nullable_dtype);
118✔
219
                assert_eq!(non_nullable.len(), array.len());
118✔
220

221
                // Cast back to Nullable
222
                let nullable_dtype = array.dtype().with_nullability(Nullability::Nullable);
118✔
223
                let back_to_nullable = cast(&non_nullable, &nullable_dtype).vortex_unwrap();
118✔
224
                assert_eq!(back_to_nullable.dtype(), &nullable_dtype);
118✔
225
                assert_eq!(back_to_nullable.len(), array.len());
118✔
226

227
                // Verify values are unchanged
228
                for i in 0..array.len().min(10) {
276✔
229
                    assert_eq!(
276✔
230
                        array.scalar_at(i).vortex_unwrap(),
276✔
231
                        back_to_nullable.scalar_at(i).vortex_unwrap()
276✔
232
                    );
233
                }
234
            }
×
235
        }
236
        // Test casting to Nullable if currently NonNullable
237
        else if array.dtype().nullability() == Nullability::NonNullable {
2,114✔
238
            let nullable_dtype = array.dtype().with_nullability(Nullability::Nullable);
2,114✔
239

240
            // Cast to Nullable
241
            let nullable = cast(array, &nullable_dtype).vortex_unwrap();
2,114✔
242
            assert_eq!(nullable.dtype(), &nullable_dtype);
2,114✔
243
            assert_eq!(nullable.len(), array.len());
2,114✔
244

245
            // Cast back to NonNullable
246
            let non_nullable_dtype = array.dtype().with_nullability(Nullability::NonNullable);
2,114✔
247
            let back_to_non_nullable = cast(&nullable, &non_nullable_dtype).vortex_unwrap();
2,114✔
248
            assert_eq!(back_to_non_nullable.dtype(), &non_nullable_dtype);
2,114✔
249
            assert_eq!(back_to_non_nullable.len(), array.len());
2,114✔
250

251
            // Verify values are unchanged
252
            for i in 0..array.len().min(10) {
10,775✔
253
                assert_eq!(
10,775✔
254
                    array.scalar_at(i).vortex_unwrap(),
10,775✔
255
                    back_to_non_nullable.scalar_at(i).vortex_unwrap()
10,775✔
256
                );
257
            }
NEW
258
        }
×
259
    }
523✔
260
}
2,760✔
261

262
fn test_cast_nullability_changes(array: &dyn Array, nullable_version: &DType) {
580✔
263
    // Test casting to nullable version
264
    if array.dtype().nullability() == Nullability::NonNullable {
580✔
265
        let result = cast(array, nullable_version).vortex_unwrap();
297✔
266
        assert_eq!(result.len(), array.len());
297✔
267
        assert_eq!(result.dtype(), nullable_version);
297✔
268

269
        // IMPORTANT: Nullability casting should preserve the encoding
270
        assert_eq!(
297✔
271
            result.encoding().id(),
297✔
272
            array.encoding().id(),
297✔
273
            "Nullability cast should preserve encoding"
×
274
        );
275

276
        // Values should be unchanged
277
        for i in 0..array.len().min(10) {
1,195✔
278
            assert_eq!(
1,195✔
279
                array.scalar_at(i).vortex_unwrap(),
1,195✔
280
                result.scalar_at(i).vortex_unwrap()
1,195✔
281
            );
282
        }
283
    }
283✔
284
}
580✔
285

286
fn test_cast_nullability_changes_primitive(
2,054✔
287
    array: &dyn Array,
2,054✔
288
    ptype: PType,
2,054✔
289
    nullability: Nullability,
2,054✔
290
) {
2,054✔
291
    // Test casting to nullable version
292
    if nullability == Nullability::NonNullable {
2,054✔
293
        let nullable_dtype = DType::Primitive(ptype, Nullability::Nullable);
1,737✔
294
        let result = cast(array, &nullable_dtype).vortex_unwrap();
1,737✔
295
        assert_eq!(result.len(), array.len());
1,737✔
296
        assert_eq!(result.dtype(), &nullable_dtype);
1,737✔
297

298
        // IMPORTANT: Nullability casting should preserve the encoding
299
        assert_eq!(
1,737✔
300
            result.encoding().id(),
1,737✔
301
            array.encoding().id(),
1,737✔
302
            "Nullability cast should preserve encoding"
×
303
        );
304

305
        // Values should be unchanged
306
        for i in 0..array.len().min(10) {
9,336✔
307
            assert_eq!(
9,336✔
308
                array.scalar_at(i).vortex_unwrap(),
9,336✔
309
                result.scalar_at(i).vortex_unwrap()
9,336✔
310
            );
311
        }
312
    }
317✔
313

314
    // Test casting from nullable to non-nullable (only if no nulls present)
315
    if nullability == Nullability::Nullable {
2,054✔
316
        // Try to cast to non-nullable and see if it succeeds
317
        let non_nullable_dtype = DType::Primitive(ptype, Nullability::NonNullable);
317✔
318
        if let Ok(result) = cast(array, &non_nullable_dtype) {
317✔
319
            assert_eq!(result.len(), array.len());
×
320
            assert_eq!(result.dtype(), &non_nullable_dtype);
×
321

322
            // IMPORTANT: Nullability casting should preserve the encoding
323
            assert_eq!(
×
324
                result.encoding().id(),
×
325
                array.encoding().id(),
×
326
                "Nullability cast should preserve encoding"
×
327
            );
328

329
            // Values should be unchanged
330
            for i in 0..array.len().min(10) {
×
331
                assert_eq!(
×
332
                    array.scalar_at(i).vortex_unwrap(),
×
333
                    result.scalar_at(i).vortex_unwrap()
×
334
                );
335
            }
336
        }
317✔
337
    }
1,737✔
338
}
2,054✔
339

340
fn test_cast_from_u8(array: &dyn Array) {
161✔
341
    // Test widening casts
342
    test_cast_to_primitive(array, PType::U16);
161✔
343
    test_cast_to_primitive(array, PType::U32);
161✔
344
    test_cast_to_primitive(array, PType::U64);
161✔
345
    test_cast_to_primitive(array, PType::I16);
161✔
346
    test_cast_to_primitive(array, PType::I32);
161✔
347
    test_cast_to_primitive(array, PType::I64);
161✔
348
    test_cast_to_primitive(array, PType::F32);
161✔
349
    test_cast_to_primitive(array, PType::F64);
161✔
350

351
    // Test same-width cast
352
    test_cast_to_primitive(array, PType::I8);
161✔
353
}
161✔
354

355
fn test_cast_from_u16(array: &dyn Array) {
79✔
356
    // Test narrowing cast
357
    test_cast_to_primitive(array, PType::U8);
79✔
358

359
    // Test widening casts
360
    test_cast_to_primitive(array, PType::U32);
79✔
361
    test_cast_to_primitive(array, PType::U64);
79✔
362
    test_cast_to_primitive(array, PType::I32);
79✔
363
    test_cast_to_primitive(array, PType::I64);
79✔
364
    test_cast_to_primitive(array, PType::F32);
79✔
365
    test_cast_to_primitive(array, PType::F64);
79✔
366

367
    // Test same-width cast
368
    test_cast_to_primitive(array, PType::I16);
79✔
369
}
79✔
370

371
fn test_cast_from_u32(array: &dyn Array) {
200✔
372
    // Test narrowing casts
373
    test_cast_to_primitive(array, PType::U8);
200✔
374
    test_cast_to_primitive(array, PType::U16);
200✔
375
    test_cast_to_primitive(array, PType::I8);
200✔
376
    test_cast_to_primitive(array, PType::I16);
200✔
377

378
    // Test widening casts
379
    test_cast_to_primitive(array, PType::U64);
200✔
380
    test_cast_to_primitive(array, PType::I64);
200✔
381
    test_cast_to_primitive(array, PType::F64);
200✔
382

383
    // Test same-width casts
384
    test_cast_to_primitive(array, PType::I32);
200✔
385
    test_cast_to_primitive(array, PType::F32);
200✔
386
}
200✔
387

388
fn test_cast_from_u64(array: &dyn Array) {
157✔
389
    // Test narrowing casts
390
    test_cast_to_primitive(array, PType::U8);
157✔
391
    test_cast_to_primitive(array, PType::U16);
157✔
392
    test_cast_to_primitive(array, PType::U32);
157✔
393
    test_cast_to_primitive(array, PType::I8);
157✔
394
    test_cast_to_primitive(array, PType::I16);
157✔
395
    test_cast_to_primitive(array, PType::I32);
157✔
396
    test_cast_to_primitive(array, PType::F32);
157✔
397

398
    // Test same-width casts
399
    test_cast_to_primitive(array, PType::I64);
157✔
400
    test_cast_to_primitive(array, PType::F64);
157✔
401
}
157✔
402

403
fn test_cast_from_i8(array: &dyn Array) {
1✔
404
    // Test widening casts
405
    test_cast_to_primitive(array, PType::I16);
1✔
406
    test_cast_to_primitive(array, PType::I32);
1✔
407
    test_cast_to_primitive(array, PType::I64);
1✔
408
    test_cast_to_primitive(array, PType::F32);
1✔
409
    test_cast_to_primitive(array, PType::F64);
1✔
410

411
    // Test same-width cast (may fail for negative values)
412
    test_cast_to_primitive(array, PType::U8);
1✔
413
}
1✔
414

415
fn test_cast_from_i16(array: &dyn Array) {
79✔
416
    // Test narrowing cast
417
    test_cast_to_primitive(array, PType::I8);
79✔
418

419
    // Test widening casts
420
    test_cast_to_primitive(array, PType::I32);
79✔
421
    test_cast_to_primitive(array, PType::I64);
79✔
422
    test_cast_to_primitive(array, PType::F32);
79✔
423
    test_cast_to_primitive(array, PType::F64);
79✔
424

425
    // Test same-width cast (may fail for negative values)
426
    test_cast_to_primitive(array, PType::U16);
79✔
427
}
79✔
428

429
fn test_cast_from_i32(array: &dyn Array) {
591✔
430
    // Test narrowing casts
431
    test_cast_to_primitive(array, PType::I8);
591✔
432
    test_cast_to_primitive(array, PType::I16);
591✔
433

434
    // Test widening casts
435
    test_cast_to_primitive(array, PType::I64);
591✔
436
    test_cast_to_primitive(array, PType::F64);
591✔
437

438
    // Test same-width casts
439
    test_cast_to_primitive(array, PType::F32);
591✔
440
    test_cast_to_primitive(array, PType::U32);
591✔
441
}
591✔
442

443
fn test_cast_from_i64(array: &dyn Array) {
118✔
444
    // Test narrowing casts
445
    test_cast_to_primitive(array, PType::I8);
118✔
446
    test_cast_to_primitive(array, PType::I16);
118✔
447
    test_cast_to_primitive(array, PType::I32);
118✔
448
    test_cast_to_primitive(array, PType::F32);
118✔
449

450
    // Test same-width cast
451
    test_cast_to_primitive(array, PType::F64);
118✔
452
    test_cast_to_primitive(array, PType::U64);
118✔
453
}
118✔
454

455
fn test_cast_from_f16(array: &dyn Array) {
×
456
    // Test casts to other float types
457
    test_cast_to_primitive(array, PType::F32);
×
458
    test_cast_to_primitive(array, PType::F64);
×
459
}
×
460

461
fn test_cast_from_f32(array: &dyn Array) {
394✔
462
    // Test narrowing cast
463
    test_cast_to_primitive(array, PType::F16);
394✔
464

465
    // Test widening cast
466
    test_cast_to_primitive(array, PType::F64);
394✔
467

468
    // Test casts to integer types (truncation)
469
    test_cast_to_integral_types(array);
394✔
470
}
394✔
471

472
fn test_cast_from_f64(array: &dyn Array) {
274✔
473
    // Test narrowing casts
474
    test_cast_to_primitive(array, PType::F16);
274✔
475
    test_cast_to_primitive(array, PType::F32);
274✔
476

477
    // Test casts to integer types (truncation)
478
    test_cast_to_integral_types(array);
274✔
479
}
274✔
480

481
fn test_cast_to_integral_types(array: &dyn Array) {
668✔
482
    // Test casting to all integral types
483
    // Some may fail due to out-of-range values
484
    test_cast_to_primitive(array, PType::I8);
668✔
485
    test_cast_to_primitive(array, PType::U8);
668✔
486
    test_cast_to_primitive(array, PType::I16);
668✔
487
    test_cast_to_primitive(array, PType::U16);
668✔
488
    test_cast_to_primitive(array, PType::I32);
668✔
489
    test_cast_to_primitive(array, PType::U32);
668✔
490
    test_cast_to_primitive(array, PType::I64);
668✔
491
    test_cast_to_primitive(array, PType::U64);
668✔
492
}
668✔
493

494
fn test_cast_to_primitive(array: &dyn Array, target_ptype: PType) {
17,428✔
495
    let target_dtype = DType::Primitive(target_ptype, array.dtype().nullability());
17,428✔
496
    test_cast_to_type_safe(array, &target_dtype);
17,428✔
497
}
17,428✔
498

499
fn test_cast_to_type_safe(array: &dyn Array, target_dtype: &DType) {
17,558✔
500
    // Attempt the cast
501
    let result = match cast(array, target_dtype) {
17,558✔
502
        Ok(r) => r,
14,552✔
503
        Err(_) => {
504
            // Some casts may fail (e.g., negative to unsigned, out-of-range values)
505
            // This is expected behavior
506
            return;
3,006✔
507
        }
508
    };
509

510
    assert_eq!(result.len(), array.len());
14,552✔
511
    assert_eq!(result.dtype(), target_dtype);
14,552✔
512

513
    // For valid casts, verify the values are correctly converted
514
    // We verify up to the first 10 values (or all if less than 10)
515
    for i in 0..array.len().min(10) {
76,773✔
516
        let original = array.scalar_at(i).vortex_unwrap();
76,773✔
517
        let casted = result.scalar_at(i).vortex_unwrap();
76,773✔
518

519
        // For nullability-only changes, values should be identical
520
        if array.dtype().eq_ignore_nullability(target_dtype) {
76,773✔
521
            assert_eq!(
×
522
                original, casted,
523
                "Value at index {i} changed during nullability cast"
×
524
            );
525
        } else {
526
            // For type conversions, at least verify we can retrieve the values
527
            // and that null values remain null
528
            if original.is_null() {
76,773✔
529
                assert!(
5,458✔
530
                    casted.is_null(),
5,458✔
531
                    "Null value at index {i} became non-null after cast"
×
532
                );
533
            } else {
534
                assert!(
71,315✔
535
                    !casted.is_null(),
71,315✔
536
                    "Non-null value at index {i} became null after cast"
×
537
                );
538
            }
539
        }
540
    }
541
}
17,558✔
542

543
#[cfg(test)]
544
mod tests {
545
    use vortex_buffer::buffer;
546
    use vortex_dtype::{DType, FieldNames, Nullability};
547

548
    use super::*;
549
    use crate::IntoArray;
550
    use crate::arrays::{
551
        BoolArray, ListArray, NullArray, PrimitiveArray, StructArray, VarBinArray,
552
    };
553

554
    #[test]
555
    fn test_cast_conformance_u32() {
1✔
556
        let array = buffer![0u32, 100, 200, 65535, 1000000].into_array();
1✔
557
        test_cast_conformance(array.as_ref());
1✔
558
    }
1✔
559

560
    #[test]
561
    fn test_cast_conformance_i32() {
1✔
562
        let array = buffer![-100i32, -1, 0, 1, 100].into_array();
1✔
563
        test_cast_conformance(array.as_ref());
1✔
564
    }
1✔
565

566
    #[test]
567
    fn test_cast_conformance_f32() {
1✔
568
        let array = buffer![0.0f32, 1.5, -2.5, 100.0, 1e6].into_array();
1✔
569
        test_cast_conformance(array.as_ref());
1✔
570
    }
1✔
571

572
    #[test]
573
    fn test_cast_conformance_nullable() {
1✔
574
        let array = PrimitiveArray::from_option_iter([Some(1u8), None, Some(255), Some(0), None]);
1✔
575
        test_cast_conformance(array.as_ref());
1✔
576
    }
1✔
577

578
    #[test]
579
    fn test_cast_conformance_bool() {
1✔
580
        let array = BoolArray::from_iter(vec![true, false, true, false]);
1✔
581
        test_cast_conformance(array.as_ref());
1✔
582
    }
1✔
583

584
    #[test]
585
    fn test_cast_conformance_null() {
1✔
586
        let array = NullArray::new(5);
1✔
587
        test_cast_conformance(array.as_ref());
1✔
588
    }
1✔
589

590
    #[test]
591
    fn test_cast_conformance_utf8() {
1✔
592
        let array = VarBinArray::from_iter(
1✔
593
            vec![Some("hello"), None, Some("world")],
1✔
594
            DType::Utf8(Nullability::Nullable),
1✔
595
        );
596
        test_cast_conformance(array.as_ref());
1✔
597
    }
1✔
598

599
    #[test]
600
    fn test_cast_conformance_binary() {
1✔
601
        let array = VarBinArray::from_iter(
1✔
602
            vec![Some(b"data".as_slice()), None, Some(b"bytes".as_slice())],
1✔
603
            DType::Binary(Nullability::Nullable),
1✔
604
        );
605
        test_cast_conformance(array.as_ref());
1✔
606
    }
1✔
607

608
    #[test]
609
    fn test_cast_conformance_struct() {
1✔
610
        let names: FieldNames = vec!["a".into(), "b".into()].into();
1✔
611

612
        let a = buffer![1i32, 2, 3].into_array();
1✔
613
        let b = VarBinArray::from_iter(
1✔
614
            vec![Some("x"), None, Some("z")],
1✔
615
            DType::Utf8(Nullability::Nullable),
1✔
616
        )
617
        .into_array();
1✔
618

619
        let array =
1✔
620
            StructArray::try_new(names, vec![a, b], 3, crate::validity::Validity::NonNullable)
1✔
621
                .unwrap();
1✔
622
        test_cast_conformance(array.as_ref());
1✔
623
    }
1✔
624

625
    #[test]
626
    fn test_cast_conformance_list() {
1✔
627
        let data = buffer![1i32, 2, 3, 4, 5, 6].into_array();
1✔
628
        let offsets = buffer![0i64, 2, 2, 5, 6].into_array();
1✔
629

630
        let array =
1✔
631
            ListArray::try_new(data, offsets, crate::validity::Validity::NonNullable).unwrap();
1✔
632
        test_cast_conformance(array.as_ref());
1✔
633
    }
1✔
634
}
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