/// Custom Property-Based Testing Generators for uuidm
///
/// 分布制御付きジェネレータ:
/// - Normal (60-70%): 通常ケース
/// - Edge (15-20%): エッジケース (nil, max, 空文字列, etc.)
/// - Boundary (10-15%): 境界値
///|
/// Generate a random UUID with distribution control
/// Normal: v4() random UUIDs
/// Edge: nil and max UUIDs
/// Boundary: deterministic v4 pattern
fn gen_uuid() -> Uuid {
// Use random bytes for distribution control
let bytes = random_bytes(1)
let freq = bytes[0].to_int() & 0xFF
if freq < 165 {
// Normal: ~65% random v4
v4()
} else if freq < 216 {
// Edge: ~20% special cases (nil, max)
let special_bytes = random_bytes(1)
if (special_bytes[0].to_int() & 1) == 0 {
nil()
} else {
max()
}
} else {
// Boundary: ~15% custom bytes
let custom = random_bytes(16)
v8(custom)
}
}
///|
/// Generate a UUID string with distribution control
fn gen_uuid_string() -> String {
let bytes = random_bytes(1)
let freq = bytes[0].to_int() & 0xFF
if freq < 179 {
// Normal: ~70% standard format
gen_uuid().to_string()
} else if freq < 230 {
// Edge: ~20% URN format
gen_uuid().to_urn()
} else {
// Boundary: ~10% simple format (no hyphens)
gen_uuid().to_string_simple()
}
}
///|
/// Generate test names with distribution control
fn gen_test_name() -> String {
let bytes = random_bytes(1)
let freq = bytes[0].to_int() & 0xFF
if freq < 166 {
// Normal: ~65% typical strings
let len_bytes = random_bytes(2)
let len = ((len_bytes[0].to_int() << 8) | len_bytes[1].to_int()) % 50 + 1
gen_random_string(len)
} else if freq < 217 {
// Edge: ~20% special cases
let special_bytes = random_bytes(1)
let special = (special_bytes[0].to_int() & 0xFF) % 4
match special {
0 => ""
1 => "测试"
2 => "🌟"
_ => "test@example.com"
}
} else {
// Boundary: ~15% long strings
let len_bytes = random_bytes(2)
let len = ((len_bytes[0].to_int() << 8) | len_bytes[1].to_int()) % 400 + 100
gen_random_string(len)
}
}
///|
/// Generate a random string of given length
fn gen_random_string(len : Int) -> String {
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~"
let mut result = ""
for i = 0; i < len; i = i + 1 {
let idx_bytes = random_bytes(1)
let idx = (idx_bytes[0].to_int() & 0xFF) % chars.length()
let c = Int::unsafe_to_char(chars.code_unit_at(idx).to_int())
result = result + c.to_string()
}
result
}
///|
/// Generate a V7 timestamp with distribution control
fn gen_v7_timestamp() -> Int64 {
let bytes = random_bytes(1)
let freq = bytes[0].to_int() & 0xFF
if freq < 166 {
// Normal: ~65% current timestamp ± random offset
let base = @env.now().reinterpret_as_int64()
let offset_bytes = random_bytes(4)
let offset = ((offset_bytes[0].to_int() << 24) |
(offset_bytes[1].to_int() << 16) |
(offset_bytes[2].to_int() << 8) |
offset_bytes[3].to_int()).to_int64()
// Scale to ±1 day
let scaled = offset % 172800000 - 86400000
base + scaled
} else if freq < 217 {
// Edge: ~20% zero or small values
let edge_bytes = random_bytes(1)
let edge = edge_bytes[0].to_int() & 1
if edge == 0 {
0L
} else {
((edge_bytes[0].to_int() & 0xFF) % 1000 + 1).to_int64()
}
} else {
// Boundary: ~15% large values
let high_bytes = random_bytes(4)
let high = ((high_bytes[0].to_int() << 24) |
(high_bytes[1].to_int() << 16) |
(high_bytes[2].to_int() << 8) |
high_bytes[3].to_int()).to_int64()
0xFFFFFFFFFFFFL - high % 1000000
}
}
///|
/// Generate UUID bytes with distribution control
fn gen_uuid_bytes() -> FixedArray[Byte] {
let bytes = random_bytes(1)
let freq = bytes[0].to_int() & 0xFF
if freq < 179 {
// Normal: ~70% random
random_bytes(16)
} else if freq < 217 {
// Edge: ~15% special patterns
let special_bytes = random_bytes(1)
let special = special_bytes[0].to_int() & 1
if special == 0 {
FixedArray::make(16, b'\x00')
} else {
FixedArray::make(16, b'\xFF')
}
} else {
// Boundary: ~15% patterned (incrementing)
let result : FixedArray[Byte] = FixedArray::make(16, b'\x00')
for i = 0; i < 16; i = i + 1 {
result[i] = i.to_byte()
}
result
}
}
///|
/// Check if variant is RFC 9562 (for testing)
fn variant_is_rfc9562(uuid : Uuid) -> Bool {
let bytes = uuid.bytes()
let variant_bits = (bytes[8].to_int() >> 6) & 0x03
variant_bits == 2
}