Files
bear/examples/tests.vr
T
2026-08-25 15:39:44 +02:00

462 lines
8.6 KiB
Plaintext

// tests.vr — functions named test_* are picked up by `vr test`.
// vr test examples/tests.vr
import os
import json
import strings
import math
import http
import time
enum Suit {
hearts
diamonds
clubs
spades
}
fn add(a, b) {
return a + b
}
fn test_add() {
assert add(2, 3) == 5
assert add(-1, 1) == 0
}
fn test_strings() {
let a = "foo"
let b = "bar"
assert a + b == "foobar"
assert a != b
assert a == "foo"
}
fn test_loops() {
let total = 0
let i = 1
while i <= 10 {
total = total + i
i = i + 1
}
assert total == 55
}
fn test_booleans() {
let x = 7
assert x > 3 and x < 10
assert x == 7 or x == 8
assert not (x == 0)
}
fn test_arrays() {
let a = [1, 2, 3]
assert len(a) == 3
assert a[0] == 1
assert a[2] == 3
a[1] = 42
assert a[1] == 42
push(a, 4)
assert len(a) == 4
assert a[3] == 4
let m = [[1, 2], [3, 4]]
assert m[1][0] == 3
}
fn test_for_range() {
let total = 0
for i in 0..5 {
total = total + i
}
assert total == 10 // 0+1+2+3+4
let inc = 0
for i in 1...3 {
inc = inc + i
}
assert inc == 6 // 1+2+3 (inclusive)
}
fn test_for_in() {
let a = [5, 6, 7]
let total = 0
for x in a {
total = total + x
}
assert total == 18
let squares = []
for i in 0..4 {
push(squares, i * i)
}
assert squares == squares // identity comparison
assert len(squares) == 4
assert squares[3] == 9
}
fn test_break_continue() {
// break exits the loop early
let total = 0
for i in 0..100 {
if i == 5 {
break
}
total = total + i
}
assert total == 10 // 0+1+2+3+4
// continue skips to the next iteration
let sum = 0
for i in 0..6 {
if i == 3 {
continue
}
sum = sum + i
}
assert sum == 12 // 0+1+2+4+5 (3 skipped)
// break and continue in while loops
let n = 0
while true {
n = n + 1
if n == 3 {
continue
}
if n == 5 {
break
}
}
assert n == 5
// continue advances the iterator in for-in loops (no infinite loop)
let odds = []
for x in [1, 2, 3, 4, 5] {
if x % 2 == 0 {
continue
}
push(odds, x)
}
assert odds == odds
assert len(odds) == 3
assert odds[2] == 5
}
fn test_else_if() {
let score = 85
let grade = ""
if score >= 90 {
grade = "A"
} else if score >= 80 {
grade = "B"
} else if score >= 70 {
grade = "C"
} else {
grade = "F"
}
assert grade == "B"
assert 10 > 5
}
fn test_match() {
let total = 0
match 2 {
1 {
total = total + 1
}
2 {
total = total + 10
}
else {
total = total + 100
}
}
assert total == 10 // matched the 2 arm
// match on strings
let word = "cat"
let sound = ""
match word {
"dog" {
sound = "woof"
}
"cat" {
sound = "meow"
}
else {
sound = "?"
}
}
assert sound == "meow"
// no else arm: nothing runs when nothing matches
let hit = 0
match 99 {
1 {
hit = 1
}
2 {
hit = 2
}
}
assert hit == 0
}
fn test_structs() {
let p = { name: "bob", age: 30 }
assert p.name == "bob"
assert p.age == 30
p.age = 31
assert p.age == 31
// structs are reference values
let q = p
q.age = 40
assert p.age == 40
// nested structs
let person = { name: "amy", addr: { city: "nyc", zip: 10001 } }
assert person.addr.city == "nyc"
person.addr.zip = 90001
assert person.addr.zip == 90001
// structs in arrays
let pts = [{ x: 1, y: 2 }, { x: 10, y: 20 }]
assert pts[1].x == 10
pts[0].x = 99
assert pts[0].x == 99
// arrays in structs
let holder = { items: [1, 2, 3] }
holder.items[1] = 42
assert holder.items[1] == 42
// structs passed to and returned from functions
let pt = { x: 3, y: 4 }
assert pt.x * pt.x + pt.y * pt.y == 25
// identity comparison
let a = { n: 1 }
let b = a
let c = { n: 1 }
assert a == b
assert a != c
}
fn test_json() {
// encoding
assert json_encode(42) == "42"
assert json_encode(3.14) == "3.14"
assert json_encode(1.0) == "1"
assert json_encode("hi") == "\"hi\""
assert json_encode([1, "two", 3.5]) == "[1,\"two\",3.5]"
let rec = { name: "amy", age: 30 }
assert json_encode(rec) == "{\"name\":\"amy\",\"age\":30}"
assert json_encode(none) == "null"
// decoding
assert json_decode("42") == 42
assert json_decode("3.14") == 3.14
assert json_decode("\"hi\"") == "hi"
assert json_decode("true") == 1
assert json_decode("null") == none
let obj = json_decode("{\"x\": 1, \"y\": [1, 2], \"z\": {\"deep\": true}}")
assert obj.x == 1
assert obj.y[1] == 2
assert obj.z.deep == 1
// round-trip
assert json_encode(json_decode("{\"a\": 1, \"b\": [2, 3]}")) == "{\"a\":1,\"b\":[2,3]}"
// unicode escapes
assert json_decode("\"\\u0041\"") == "A"
assert json_decode("\"\\ud83d\\ude00\"") == "😀"
}
fn test_format() {
assert format(42, "%d") == "42"
assert format(42, "%05d") == "00042"
assert format(-42, "%05d") == "-0042"
assert format(42, "%-5d") == "42 "
assert format(3.14159, "%.2f") == "3.14"
assert format(3.0, "%.2f") == "3.00"
assert format(255, "%X") == "FF"
assert format("hi", "%s") == "hi"
assert format(7, "%5d") == " 7"
}
fn test_string_tools() {
assert replace("hello world", "l", "L") == "heLLo worLd"
assert "hello world".replace("o", "0") == "hell0 w0rld"
assert len(split_lines("a\nb\nc")) == 3
assert "a\nb".split_lines()[1] == "b"
assert pad("x", 4) == "x "
assert "x".pad(4) == "x "
assert pad_left("x", 4) == " x"
assert "x".pad_left(4) == " x"
assert repeat("ab", 3) == "ababab"
assert "ab".repeat(2) == "abab"
assert pad("toolong", 2) == "toolong"
}
fn test_none() {
assert none == none
assert none == 0 == false
assert none != 5
if none {
assert false
}
assert not (none == 1)
}
fn test_module_imports() {
// os module
if not os.exists("examples") {
assert false
}
if not os.is_dir("examples") {
assert false
}
if not os.is_file("main.v") {
assert false
}
assert os.base("a/b/c.vr") == "c.vr"
assert os.dir("a/b/c.vr") == "a/b"
assert len(os.glob("lib/*.vr")) == 6
let tmp = os.join(os.cwd(), "tmp_test.txt")
os.write_lines(tmp, ["x", "y"])
assert len(os.read_lines(tmp)) == 2
os.remove(tmp)
// json module
let data = json.decode("{\"a\": 1, \"b\": [2, 3]}")
assert json.encode(data) == "{\"a\":1,\"b\":[2,3]}"
assert strings.contains(json.pretty(data), "\n")
// strings module
assert strings.join(["a", "b"], "-") == "a-b"
assert strings.capitalize("hello") == "Hello"
assert strings.replace("foo", "o", "0") == "f00"
assert strings.pad_left("7", 3) == " 7"
assert strings.format(3.14159, "%.2f") == "3.14"
// math module
assert math.clamp(150, 0, 100) == 100
assert math.sign(-3) == -1
assert math.abs(-7) == 7
}
fn test_native_errors_are_catchable() {
try {
read_file("/no/such/file.vr")
assert false
} catch e {
assert len(e) > 0
}
try {
json_decode("not json")
assert false
} catch e {
assert len(e) > 0
}
}
fn test_closures() {
// closures in expression contexts must not corrupt surrounding values
let pick = fn (x) {
if x > 10 {
return "big"
}
return "small"
}
assert "a: " + pick(50) == "a: big"
assert pick(3) + "!" == "small!"
assert pick(50) + " " + pick(3) == "big small"
let dbl = fn (x) {
return x * 2
}
assert str(dbl(21)) == "42"
assert "v=" + str(dbl(4)) == "v=8"
// closure containing a loop
let sum = fn () {
let total = 0
for i in 0..5 {
total = total + i
}
return total
}
assert sum() == 10
// closure call as a statement argument
let tag = fn (t) {
return "[" + t + "]"
}
assert tag("warn") == "[warn]"
}
fn test_enums() {
// variant values
assert Suit.hearts == 0
assert Suit.spades == 3
// enum.count()
assert Suit.hearts.count() == 4
// enum.to_string()
assert Suit.spades.to_string() == "spades"
// enums in expressions
let next = Suit.hearts + 1
assert next == 1
// enum iteration
let names = []
for s in Suit {
names = push(names, s.to_string())
}
assert len(names) == 4
assert names[0] == "hearts"
assert names[3] == "spades"
// match on enum values
assert suit_code(Suit.clubs) == "c"
assert suit_code(Suit.hearts) == "h"
}
fn suit_code(s) {
match s {
Suit.hearts { return "h" }
Suit.clubs { return "c" }
else { return "?" }
}
}
fn test_http_errors() {
// offline-safe: connecting to a closed local port fails fast
try {
http.get("http://127.0.0.1:1/nope")
assert false
} catch e {
assert len(e) > 0
}
}
fn test_time() {
let t = time.now()
assert t > 1700000000
// format produces fixed-width strings
let day = time.date(t)
assert len(day) == 10
assert day[4] == "-"[0]
let full = time.format(t, "YYYY-MM-DD HH:mm:ss")
assert len(full) == 19
// parse round-trips within a second
let back = time.parse(full)
assert abs(back - t) <= 1
// ms is more precise than seconds
assert time.ms() > t * 1000
// invalid input throws
try {
time.parse("not a date")
assert false
} catch e {
assert len(e) > 0
}
}
fn test_failing() {
// this one is meant to fail — shows up in `vr test` output
assert 1 == 2
}