Merge branch 'master' into poly-proc-fix

This commit is contained in:
Brody
2026-08-05 00:19:06 +10:00
72 changed files with 3678 additions and 1523 deletions

View File

@@ -380,6 +380,13 @@ glob_tests := []Glob_Test{
},
err = {},
},
{
pattern = ODIN_ROOT + "tests/core/os/path.odin",
matches = {
ODIN_ROOT + "tests/core/os/path.odin",
},
err = {},
},
}
@(test)

View File

@@ -425,3 +425,196 @@ test_memory_compare_zero :: proc(t: ^testing.T) {
}
}
}
// Runs identical append/inject/remove sequences for a #soa[dynamic] array
// and an AoS [dynamic] reference, comparing all elements after each
// stage. Covers appends, injections (interior, at the end, and past the
// end where the gap must read as zero elements), unordered and ordered
// removes.
@(test)
test_soa_array_append_inject_remove :: proc(t: ^testing.T) {
check :: proc(t: ^testing.T, $E: typeid, mk: proc(i: int) -> E) {
expect_same :: proc(t: ^testing.T, soa: #soa[dynamic]$T, model: [dynamic]T) {
testing.expect_value(t, len(soa), len(model))
for i in 0..<min(len(soa), len(model)) {
testing.expect_value(t, soa[i], model[i])
}
}
soa: #soa[dynamic]E
defer delete(soa)
ref: [dynamic]E
defer delete(ref)
// single appends
for i in 0..<10 {
n, err := append(&soa, mk(i))
testing.expect_value(t, n, 1)
testing.expect_value(t, err, nil)
append(&ref, mk(i))
}
expect_same(t, soa, ref)
// batch appends
buf: [32]E
for i in 0..<32 { buf[i] = mk(123 + i) }
BATCH_LEN :: 5
n, err := append(&soa, ..buf[:BATCH_LEN])
testing.expect_value(t, n, BATCH_LEN)
testing.expect_value(t, err, nil)
append(&ref, ..buf[:BATCH_LEN])
expect_same(t, soa, ref)
n, err = append(&soa, ..buf[:])
testing.expect_value(t, n, 32)
testing.expect_value(t, err, nil)
append(&ref, ..buf[:])
expect_same(t, soa, ref)
// single injections
ok: bool
ok, err = inject_at_soa(&soa, 0, mk(300))
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, 0, mk(300))
expect_same(t, soa, ref)
ok, err = inject_at_soa(&soa, len(soa)/2, mk(301))
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, len(ref)/2, mk(301))
expect_same(t, soa, ref)
// inject at the end
ok, err = inject_at_soa(&soa, len(soa), mk(302))
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, len(ref), mk(302))
expect_same(t, soa, ref)
// batch injections
ok, err = inject_at_soa(&soa, 3, ..buf[:BATCH_LEN])
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, 3, ..buf[:BATCH_LEN])
expect_same(t, soa, ref)
ok, err = inject_at_soa(&soa, len(soa), ..buf[:])
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, len(ref), ..buf[:])
expect_same(t, soa, ref)
// injecting nothing is a no-op
ok, err = inject_at_soa(&soa, 4, ..buf[:0])
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, 4, ..buf[:0])
expect_same(t, soa, ref)
// single injection past the end: the gap [len, index) reads as zero
// elements. Poison the spare capacity first (fresh heap pages are
// already zero, which would hide a missing gap zero), then shrink back.
for i in 0..<8 {
n, err = append(&soa, mk(900 + i))
testing.expect_value(t, n, 1)
testing.expect_value(t, err, nil)
}
testing.expect_value(t, resize_soa(&soa, len(soa) - 8), nil)
ok, err = inject_at_soa(&soa, len(soa) + 3, mk(500))
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, len(ref) + 3, mk(500))
expect_same(t, soa, ref)
// batch injection past the end (its gap slots reuse the poison above)
ok, err = inject_at_soa(&soa, len(soa) + 2, ..buf[:BATCH_LEN])
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, len(ref) + 2, ..buf[:BATCH_LEN])
expect_same(t, soa, ref)
// unordered removes
unordered_remove_soa(&soa, 20)
unordered_remove(&ref, 20)
unordered_remove_soa(&soa, 0)
unordered_remove(&ref, 0)
unordered_remove_soa(&soa, len(soa)-1)
unordered_remove(&ref, len(ref)-1)
expect_same(t, soa, ref)
// ordered removes
ordered_remove_soa(&soa, 17)
ordered_remove(&ref, 17)
ordered_remove_soa(&soa, 0)
ordered_remove(&ref, 0)
ordered_remove_soa(&soa, len(soa)-1)
ordered_remove(&ref, len(ref)-1)
expect_same(t, soa, ref)
// interleaved removes, appends and injections
for i in 0..<8 {
unordered_remove_soa(&soa, i)
unordered_remove(&ref, i)
n, err = append(&soa, mk(200 + i))
testing.expect_value(t, n, 1)
testing.expect_value(t, err, nil)
append(&ref, mk(200 + i))
ok, err = inject_at_soa(&soa, i, mk(400 + i))
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, i, mk(400 + i))
}
expect_same(t, soa, ref)
// remove till empty, then reuse
for len(soa) > 0 {
unordered_remove_soa(&soa, 0)
unordered_remove(&ref, 0)
}
testing.expect_value(t, len(soa), 0)
// inject into an empty array
ok, err = inject_at_soa(&soa, 0, mk(998))
testing.expect(t, ok)
testing.expect_value(t, err, nil)
inject_at(&ref, 0, mk(998))
expect_same(t, soa, ref)
// non-zero append
n, err = non_zero_append(&soa, mk(42))
testing.expect_value(t, n, 1)
testing.expect_value(t, err, nil)
non_zero_append(&ref, mk(42))
expect_same(t, soa, ref)
}
// mixed field widths + padding
Padded :: struct { a: u8, b: u64, c: u16 }
check(t, Padded, proc(i: int) -> Padded { return {u8(i*3), u64(i)*257 + 7, u16(i*5 + 1)} })
// array element type
check(t, [4]u16, proc(i: int) -> [4]u16 { return {u16(i), u16(i + 1), u16(i*3), u16(i*7)} })
// eight fields at varying widths, no two neighbouring fields share a stride
Eight :: struct { a: u8, b: u16, c: u32, d: u64, e: i8, f: i16, g: f32, h: f64 }
check(t, Eight, proc(i: int) -> Eight {
return {
u8(i), u16(i*3 + 1), u32(i)*5 + 2, u64(i)*7 + 3,
i8(i >> 1), i16(i*11 + 4), f32(i)*1.5, f64(i)*2.25,
}
})
// #packed struct with > 16 fields, field offsets diverging from default
// layout (u8/u64 alternate, align_of == 1), and the field count takes
// the type-erased batch appends compile time branch.
Packed17 :: struct #packed {
f0: u8, f1: u64, f2: u8, f3: u64, f4: u8, f5: u64, f6: u8, f7: u64,
f8: u8, f9: u64, f10: u8, f11: u64, f12: u8, f13: u64, f14: u8, f15: u64,
f16: u8,
}
check(t, Packed17, proc(i: int) -> Packed17 {
return {
u8(i), u64(i)*3 + 1, u8(i >> 1), u64(i)*5 + 2, u8(i >> 2), u64(i)*7 + 3, u8(i >> 3), u64(i)*11 + 4,
u8(i >> 4), u64(i)*13 + 5, u8(i >> 5), u64(i)*17 + 6, u8(i >> 6), u64(i)*19 + 7, u8(i >> 7), u64(i)*23 + 8,
u8(i*3),
}
})
}

View File

@@ -5,4 +5,5 @@ package tests_core
@(require) import "crypto/bigint"
@(require) import "hash"
@(require) import "image"
@(require) import "math/big"
@(require) import "math/big"
@(require) import "runtime"

View File

@@ -4,7 +4,9 @@ set -eu
mkdir -p build
pushd build
ODIN=../../../odin
COMMON="-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused-defineables"
COMMON="-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused-defineables -microarch:native"
COMMON_CHECK="-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused-defineables"
set -x
@@ -82,12 +84,19 @@ fi
$ODIN check ../test_issue_6484.odin -no-entry-point $COMMON
$ODIN test ../test_issue_6753.odin $COMMON
if [[ $($ODIN check ../test_issue_6874.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then
$ODIN check ../test_issue_6484.odin -no-entry-point $COMMON_CHECK
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
$ODIN check ../test_issue_6979.odin -no-entry-point $COMMON
if [[ $($ODIN check ../test_issue_6874.odin $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
$ODIN check ../test_issue_6979.odin -no-entry-point $COMMON_CHECK
$ODIN build ../test_issue_7037.odin $COMMON -o:none
if [[ $($ODIN build ../test_issue_7108.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then