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

vortex-data / vortex / 16760172789

05 Aug 2025 08:08PM UTC coverage: 84.027% (+0.2%) from 83.809%
16760172789

push

github

web-flow
chore: Reapply "feat: implement Cast Kernel everywhere" (#4110)

Reverts #4109 and reapplies #4086, with fix to RunEndArray casting +
tests

Blocked on #4113 and #4114

---------

Signed-off-by: Will Manning <will@willmanning.io>

886 of 926 new or added lines in 14 files covered. (95.68%)

1 existing line in 1 file now uncovered.

48339 of 57528 relevant lines covered (84.03%)

520686.9 hits per line

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

92.91
/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) {
1,437✔
20
    let dtype = array.dtype();
1,437✔
21

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

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

28
    // Test based on the specific DType
29
    match dtype {
1,437✔
30
        DType::Null => test_cast_from_null(array),
5✔
31
        DType::Bool(nullability) => test_cast_from_bool(array, *nullability),
44✔
32
        DType::Primitive(ptype, nullability) => {
1,128✔
33
            test_cast_nullability_changes_primitive(array, *ptype, *nullability);
1,128✔
34
            match ptype {
1,128✔
35
                PType::U8 => test_cast_from_u8(array),
119✔
36
                PType::U16 => test_cast_from_u16(array),
39✔
37
                PType::U32 => test_cast_from_u32(array),
119✔
38
                PType::U64 => test_cast_from_u64(array),
39✔
39
                PType::I8 => test_cast_from_i8(array),
1✔
40
                PType::I16 => test_cast_from_i16(array),
77✔
41
                PType::I32 => test_cast_from_i32(array),
386✔
42
                PType::I64 => test_cast_from_i64(array),
39✔
NEW
43
                PType::F16 => test_cast_from_f16(array),
×
44
                PType::F32 => test_cast_from_f32(array),
232✔
45
                PType::F64 => test_cast_from_f64(array),
77✔
46
            }
47
        }
48
        DType::Decimal(_, nullability) => test_cast_from_decimal(array, *nullability),
4✔
49
        DType::Utf8(nullability) => test_cast_from_utf8(array, *nullability),
122✔
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),
118✔
54
    }
55
}
1,437✔
56

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

63
    // Verify values are unchanged
64
    for i in 0..array.len().min(10) {
8,155✔
65
        assert_eq!(
8,155✔
66
            array.scalar_at(i).vortex_unwrap(),
8,155✔
67
            result.scalar_at(i).vortex_unwrap()
8,155✔
68
        );
69
    }
70
}
1,437✔
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) {
44✔
110
    // Test nullability changes
111
    test_cast_nullability_changes(array, &DType::Bool(Nullability::Nullable));
44✔
112
    if nullability == Nullability::Nullable {
44✔
113
        // Try casting to non-nullable (may fail if nulls present)
1✔
114
        let _ = cast(array, &DType::Bool(Nullability::NonNullable));
1✔
115
    }
43✔
116

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

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

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

145
    // UTF-8 strings can potentially be cast to Binary
146
    test_cast_to_type_safe(array, &DType::Binary(nullability));
122✔
147
}
122✔
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✔
NEW
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✔
NEW
189
    }
×
190
}
6✔
191

192
fn test_cast_from_extension(array: &dyn Array) {
118✔
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() {
118✔
196
        let result = cast(array, &DType::Extension(ext_dtype.clone())).vortex_unwrap();
118✔
197
        assert_eq!(result.len(), array.len());
118✔
198
        assert_eq!(result.dtype(), array.dtype());
118✔
NEW
199
    }
×
200
}
118✔
201

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

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

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

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

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

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

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

250
                // Verify values are unchanged
251
                for i in 0..array.len().min(10) {
6,209✔
252
                    assert_eq!(
6,209✔
253
                        array.scalar_at(i).vortex_unwrap(),
6,209✔
254
                        back_to_non_nullable.scalar_at(i).vortex_unwrap()
6,209✔
255
                    );
256
                }
NEW
257
            }
×
258
        }
358✔
NEW
259
    }
×
260
}
1,437✔
261

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

398
    // Test same-width casts
399
    test_cast_to_primitive(array, PType::I64);
39✔
400
    test_cast_to_primitive(array, PType::F64);
39✔
401
}
39✔
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) {
77✔
416
    // Test narrowing cast
417
    test_cast_to_primitive(array, PType::I8);
77✔
418

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

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

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

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

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

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

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

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

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

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

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

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

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

481
fn test_cast_to_integral_types(array: &dyn Array) {
309✔
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);
309✔
485
    test_cast_to_primitive(array, PType::U8);
309✔
486
    test_cast_to_primitive(array, PType::I16);
309✔
487
    test_cast_to_primitive(array, PType::U16);
309✔
488
    test_cast_to_primitive(array, PType::I32);
309✔
489
    test_cast_to_primitive(array, PType::U32);
309✔
490
    test_cast_to_primitive(array, PType::I64);
309✔
491
    test_cast_to_primitive(array, PType::U64);
309✔
492
}
309✔
493

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

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

510
    assert_eq!(result.len(), array.len());
7,609✔
511
    assert_eq!(result.dtype(), target_dtype);
7,609✔
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) {
45,638✔
516
        let original = array.scalar_at(i).vortex_unwrap();
45,638✔
517
        let casted = result.scalar_at(i).vortex_unwrap();
45,638✔
518

519
        // For nullability-only changes, values should be identical
520
        if array.dtype().eq_ignore_nullability(target_dtype) {
45,638✔
NEW
521
            assert_eq!(
×
522
                original, casted,
NEW
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() {
45,638✔
529
                assert!(
4,560✔
530
                    casted.is_null(),
4,560✔
NEW
531
                    "Null value at index {i} became non-null after cast"
×
532
                );
533
            } else {
534
                assert!(
41,078✔
535
                    !casted.is_null(),
41,078✔
NEW
536
                    "Non-null value at index {i} became null after cast"
×
537
                );
538
            }
539
        }
540
    }
541
}
9,172✔
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