Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/num/tests.nct

tests.nct

//! Public-surface numeric text contract tests.

see ./index.nct

use /mem
use /string.String

func require(condition: bool, message: &str): void! {
    if !condition { return error.new("std.num.contract", message) }
    return
}

func is_none<T>(value: T?): bool {
    let _present = move value otherwise { return true }
    return false
}

func require_text(actual: String, expected: &str): void! {
    if ((&actual) as &str) != expected {
        return error.new("std.num.text", "decimal spelling changed")
    }
    return
}

test every_integer_type_owns_the_same_decimal_surface {
    let signed8 = i8.parse("-128") otherwise {
        return error.new("std.num.parse", "i8 parse failed")
    }
    let signed16 = i16.parse("-32768") otherwise {
        return error.new("std.num.parse", "i16 parse failed")
    }
    let signed32 = i32.parse("-2147483648") otherwise {
        return error.new("std.num.parse", "i32 parse failed")
    }
    let signed64 = i64.parse("-9223372036854775808") otherwise {
        return error.new("std.num.parse", "i64 parse failed")
    }
    let signed_size = isize.parse("-9223372036854775808") otherwise {
        return error.new("std.num.parse", "isize parse failed")
    }
    let unsigned8 = u8.parse("255") otherwise {
        return error.new("std.num.parse", "u8 parse failed")
    }
    let unsigned16 = u16.parse("65535") otherwise {
        return error.new("std.num.parse", "u16 parse failed")
    }
    let unsigned32 = u32.parse("4294967295") otherwise {
        return error.new("std.num.parse", "u32 parse failed")
    }
    let unsigned64 = u64.parse("18446744073709551615") otherwise {
        return error.new("std.num.parse", "u64 parse failed")
    }
    let unsigned_size = usize.parse("18446744073709551615") otherwise {
        return error.new("std.num.parse", "usize parse failed")
    }

    require_text(signed8.to_string(), "-128")?
    require_text(signed16.to_string(), "-32768")?
    require_text(signed32.to_string(), "-2147483648")?
    require_text(signed64.to_string(), "-9223372036854775808")?
    require_text(signed_size.to_string(), "-9223372036854775808")?
    require_text(unsigned8.to_string(), "255")?
    require_text(unsigned16.to_string(), "65535")?
    require_text(unsigned32.to_string(), "4294967295")?
    require_text(unsigned64.to_string(), "18446744073709551615")?
    require_text(unsigned_size.to_string(), "18446744073709551615")?
    return
}

test decimal_grammar_is_exact_and_complete_input {
    let leading_zeroes = u64.parse("00012") otherwise {
        return error.new("std.num.grammar", "leading zeroes were rejected")
    }
    let negative_zero = i64.parse("-0") otherwise {
        return error.new("std.num.grammar", "negative zero was rejected")
    }
    let zero: i64 = 0
    require(leading_zeroes == 12, "leading zero value changed")?
    require(negative_zero == 0, "negative zero value changed")?
    require_text(zero.to_string(), "0")?
    require(is_none(u64.parse("")), "empty unsigned input was accepted")?
    require(is_none(i64.parse("")), "empty signed input was accepted")?
    require(is_none(i64.parse("-")), "bare minus was accepted")?
    require(is_none(i64.parse("+1")), "leading plus was accepted")?
    require(is_none(u64.parse("-1")), "unsigned minus was accepted")?
    require(is_none(i64.parse("--1")), "repeated minus was accepted")?
    require(is_none(i64.parse(" 1")), "leading space was accepted")?
    require(is_none(i64.parse("1 ")), "trailing space was accepted")?
    require(is_none(i64.parse("1a")), "trailing character was accepted")?
    require(is_none(i64.parse("١")), "non-ASCII digit was accepted")?
    return
}

test every_integer_range_rejects_adjacent_values {
    require(is_none(i8.parse("-129")), "i8 lower overflow was accepted")?
    require(is_none(i8.parse("128")), "i8 upper overflow was accepted")?
    require(is_none(i16.parse("-32769")), "i16 lower overflow was accepted")?
    require(is_none(i16.parse("32768")), "i16 upper overflow was accepted")?
    require(is_none(i32.parse("-2147483649")), "i32 lower overflow was accepted")?
    require(is_none(i32.parse("2147483648")), "i32 upper overflow was accepted")?
    require(
        is_none(i64.parse("-9223372036854775809")),
        "i64 lower overflow was accepted",
    )?
    require(
        is_none(i64.parse("9223372036854775808")),
        "i64 upper overflow was accepted",
    )?
    require(
        is_none(isize.parse("-9223372036854775809")),
        "isize lower overflow was accepted",
    )?
    require(
        is_none(isize.parse("9223372036854775808")),
        "isize upper overflow was accepted",
    )?
    require(is_none(u8.parse("256")), "u8 overflow was accepted")?
    require(is_none(u16.parse("65536")), "u16 overflow was accepted")?
    require(is_none(u32.parse("4294967296")), "u32 overflow was accepted")?
    require(
        is_none(u64.parse("18446744073709551616")),
        "u64 overflow was accepted",
    )?
    require(
        is_none(usize.parse("18446744073709551616")),
        "usize overflow was accepted",
    )?

    let signed8 = i8.parse("127") otherwise {
        return error.new("std.num.range", "i8 maximum was rejected")
    }
    let signed16 = i16.parse("32767") otherwise {
        return error.new("std.num.range", "i16 maximum was rejected")
    }
    let signed32 = i32.parse("2147483647") otherwise {
        return error.new("std.num.range", "i32 maximum was rejected")
    }
    let signed64 = i64.parse("9223372036854775807") otherwise {
        return error.new("std.num.range", "i64 maximum was rejected")
    }
    let signed_size = isize.parse("9223372036854775807") otherwise {
        return error.new("std.num.range", "isize maximum was rejected")
    }
    require(signed8 == 127, "i8 maximum changed")?
    require(signed16 == 32767, "i16 maximum changed")?
    require(signed32 == 2147483647, "i32 maximum changed")?
    require(signed64 == 9223372036854775807, "i64 maximum changed")?
    require(signed_size == 9223372036854775807, "isize maximum changed")?
    return
}

test every_integer_round_trips_through_its_owned_text {
    let signed8: i8 = -128
    let signed16: i16 = -32768
    let signed32: i32 = -2147483648
    let signed64: i64 = -9223372036854775808
    let signed_size: isize = -9223372036854775808
    let unsigned8: u8 = 255
    let unsigned16: u16 = 65535
    let unsigned32: u32 = 4294967295
    let unsigned64: u64 = 18446744073709551615
    let unsigned_size: usize = 18446744073709551615

    let signed8_text = signed8.to_string()
    let signed16_text = signed16.to_string()
    let signed32_text = signed32.to_string()
    let signed64_text = signed64.to_string()
    let signed_size_text = signed_size.to_string()
    let unsigned8_text = unsigned8.to_string()
    let unsigned16_text = unsigned16.to_string()
    let unsigned32_text = unsigned32.to_string()
    let unsigned64_text = unsigned64.to_string()
    let unsigned_size_text = unsigned_size.to_string()

    let reparsed_i8 = i8.parse(&signed8_text) otherwise {
        return error.new("std.num.round_trip", "i8 text was rejected")
    }
    let reparsed_i16 = i16.parse(&signed16_text) otherwise {
        return error.new("std.num.round_trip", "i16 text was rejected")
    }
    let reparsed_i32 = i32.parse(&signed32_text) otherwise {
        return error.new("std.num.round_trip", "i32 text was rejected")
    }
    let reparsed_i64 = i64.parse(&signed64_text) otherwise {
        return error.new("std.num.round_trip", "i64 text was rejected")
    }
    let reparsed_isize = isize.parse(&signed_size_text) otherwise {
        return error.new("std.num.round_trip", "isize text was rejected")
    }
    let reparsed_u8 = u8.parse(&unsigned8_text) otherwise {
        return error.new("std.num.round_trip", "u8 text was rejected")
    }
    let reparsed_u16 = u16.parse(&unsigned16_text) otherwise {
        return error.new("std.num.round_trip", "u16 text was rejected")
    }
    let reparsed_u32 = u32.parse(&unsigned32_text) otherwise {
        return error.new("std.num.round_trip", "u32 text was rejected")
    }
    let reparsed_u64 = u64.parse(&unsigned64_text) otherwise {
        return error.new("std.num.round_trip", "u64 text was rejected")
    }
    let reparsed_usize = usize.parse(&unsigned_size_text) otherwise {
        return error.new("std.num.round_trip", "usize text was rejected")
    }
    require(reparsed_i8 == signed8, "i8 round trip failed")?
    require(reparsed_i16 == signed16, "i16 round trip failed")?
    require(reparsed_i32 == signed32, "i32 round trip failed")?
    require(reparsed_i64 == signed64, "i64 round trip failed")?
    require(reparsed_isize == signed_size, "isize round trip failed")?
    require(reparsed_u8 == unsigned8, "u8 round trip failed")?
    require(reparsed_u16 == unsigned16, "u16 round trip failed")?
    require(reparsed_u32 == unsigned32, "u32 round trip failed")?
    require(reparsed_u64 == unsigned64, "u64 round trip failed")?
    require(reparsed_usize == unsigned_size, "usize round trip failed")?
    return
}

test recoverable_integer_text_uses_the_supplied_allocator {
    var allocator = mem.page_try_allocator()
    let signed8: i8 = -128
    let signed16: i16 = -32768
    let signed32: i32 = -2147483648
    let signed64: i64 = -9223372036854775808
    let signed_size: isize = -9223372036854775808
    let unsigned8: u8 = 255
    let unsigned16: u16 = 65535
    let unsigned32: u32 = 4294967295
    let unsigned64: u64 = 18446744073709551615
    let unsigned_size: usize = 18446744073709551615

    require_text(signed8.try_to_string(&+allocator)?, "-128")?
    require_text(signed16.try_to_string(&+allocator)?, "-32768")?
    require_text(signed32.try_to_string(&+allocator)?, "-2147483648")?
    require_text(
        signed64.try_to_string(&+allocator)?,
        "-9223372036854775808",
    )?
    require_text(
        signed_size.try_to_string(&+allocator)?,
        "-9223372036854775808",
    )?
    require_text(unsigned8.try_to_string(&+allocator)?, "255")?
    require_text(unsigned16.try_to_string(&+allocator)?, "65535")?
    require_text(unsigned32.try_to_string(&+allocator)?, "4294967295")?
    require_text(
        unsigned64.try_to_string(&+allocator)?,
        "18446744073709551615",
    )?
    require_text(
        unsigned_size.try_to_string(&+allocator)?,
        "18446744073709551615",
    )?
    return
}